view gcc/d/dmd/mtype.c @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
line wrap: on
line source


/* Compiler implementation of the D programming language
 * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
 * written by Walter Bright
 * http://www.digitalmars.com
 * Distributed under the Boost Software License, Version 1.0.
 * http://www.boost.org/LICENSE_1_0.txt
 * https://github.com/D-Programming-Language/dmd/blob/master/src/mtype.c
 */

#include "root/dsystem.h"
#include "root/checkedint.h"
#include "root/rmem.h"

#include "mars.h"
#include "mangle.h"
#include "dsymbol.h"
#include "mtype.h"
#include "scope.h"
#include "init.h"
#include "expression.h"
#include "statement.h"
#include "attrib.h"
#include "declaration.h"
#include "template.h"
#include "id.h"
#include "enum.h"
#include "module.h"
#include "import.h"
#include "aggregate.h"
#include "hdrgen.h"
#include "target.h"

bool symbolIsVisible(Scope *sc, Dsymbol *s);
typedef int (*ForeachDg)(void *ctx, size_t paramidx, Parameter *param);
int Parameter_foreach(Parameters *parameters, ForeachDg dg, void *ctx, size_t *pn = NULL);
FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL);
Expression *extractSideEffect(Scope *sc, const char *name, Expression **e0, Expression *e, bool alwaysCopy = false);
Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
Expression *semantic(Expression *e, Scope *sc);
Expression *semanticY(DotIdExp *exp, Scope *sc, int flag);
Expression *semanticY(DotTemplateInstanceExp *exp, Scope *sc, int flag);
Expression *typeToExpression(Type *t);
Expression *typeToExpressionHelper(TypeQualified *t, Expression *e, size_t i = 0);
Initializer *semantic(Initializer *init, Scope *sc, Type *t, NeedInterpret needInterpret);

int Tsize_t = Tuns32;
int Tptrdiff_t = Tint32;

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

ClassDeclaration *Type::dtypeinfo;
ClassDeclaration *Type::typeinfoclass;
ClassDeclaration *Type::typeinfointerface;
ClassDeclaration *Type::typeinfostruct;
ClassDeclaration *Type::typeinfopointer;
ClassDeclaration *Type::typeinfoarray;
ClassDeclaration *Type::typeinfostaticarray;
ClassDeclaration *Type::typeinfoassociativearray;
ClassDeclaration *Type::typeinfovector;
ClassDeclaration *Type::typeinfoenum;
ClassDeclaration *Type::typeinfofunction;
ClassDeclaration *Type::typeinfodelegate;
ClassDeclaration *Type::typeinfotypelist;
ClassDeclaration *Type::typeinfoconst;
ClassDeclaration *Type::typeinfoinvariant;
ClassDeclaration *Type::typeinfoshared;
ClassDeclaration *Type::typeinfowild;

TemplateDeclaration *Type::rtinfo;

Type *Type::tvoid;
Type *Type::tint8;
Type *Type::tuns8;
Type *Type::tint16;
Type *Type::tuns16;
Type *Type::tint32;
Type *Type::tuns32;
Type *Type::tint64;
Type *Type::tuns64;
Type *Type::tint128;
Type *Type::tuns128;
Type *Type::tfloat32;
Type *Type::tfloat64;
Type *Type::tfloat80;

Type *Type::timaginary32;
Type *Type::timaginary64;
Type *Type::timaginary80;

Type *Type::tcomplex32;
Type *Type::tcomplex64;
Type *Type::tcomplex80;

Type *Type::tbool;
Type *Type::tchar;
Type *Type::twchar;
Type *Type::tdchar;

Type *Type::tshiftcnt;
Type *Type::terror;
Type *Type::tnull;

Type *Type::tsize_t;
Type *Type::tptrdiff_t;
Type *Type::thash_t;

Type *Type::tvoidptr;
Type *Type::tstring;
Type *Type::twstring;
Type *Type::tdstring;
Type *Type::tvalist;
Type *Type::basic[TMAX];
unsigned char Type::sizeTy[TMAX];
StringTable Type::stringtable;

void initTypeMangle();

Type::Type(TY ty)
{
    this->ty = ty;
    this->mod = 0;
    this->deco = NULL;
    this->cto = NULL;
    this->ito = NULL;
    this->sto = NULL;
    this->scto = NULL;
    this->wto = NULL;
    this->wcto = NULL;
    this->swto = NULL;
    this->swcto = NULL;
    this->pto = NULL;
    this->rto = NULL;
    this->arrayof = NULL;
    this->vtinfo = NULL;
    this->ctype = NULL;
}

const char *Type::kind()
{
    assert(false); // should be overridden
    return NULL;
}

Type *Type::copy()
{
    void *pt = mem.xmalloc(sizeTy[ty]);
    Type *t = (Type *)memcpy(pt, (void *)this, sizeTy[ty]);
    return t;
}

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

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

bool Type::equivalent(Type *t)
{
    return immutableOf()->equals(t->immutableOf());
}

void Type::_init()
{
    stringtable._init(14000);

    for (size_t i = 0; i < TMAX; i++)
        sizeTy[i] = sizeof(TypeBasic);
    sizeTy[Tsarray] = sizeof(TypeSArray);
    sizeTy[Tarray] = sizeof(TypeDArray);
    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[Tstruct] = sizeof(TypeStruct);
    sizeTy[Tclass] = sizeof(TypeClass);
    sizeTy[Ttuple] = sizeof(TypeTuple);
    sizeTy[Tslice] = sizeof(TypeSlice);
    sizeTy[Treturn] = sizeof(TypeReturn);
    sizeTy[Terror] = sizeof(TypeError);
    sizeTy[Tnull] = sizeof(TypeNull);
    sizeTy[Tvector] = sizeof(TypeVector);

    initTypeMangle();

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

    for (size_t i = 0; basetab[i] != Terror; i++)
    {
        Type *t = new TypeBasic(basetab[i]);
        t = t->merge();
        basic[basetab[i]] = t;
    }
    basic[Terror] = new TypeError();

    tvoid = basic[Tvoid];
    tint8 = basic[Tint8];
    tuns8 = basic[Tuns8];
    tint16 = basic[Tint16];
    tuns16 = basic[Tuns16];
    tint32 = basic[Tint32];
    tuns32 = basic[Tuns32];
    tint64 = basic[Tint64];
    tuns64 = basic[Tuns64];
    tint128 = basic[Tint128];
    tuns128 = basic[Tuns128];
    tfloat32 = basic[Tfloat32];
    tfloat64 = basic[Tfloat64];
    tfloat80 = basic[Tfloat80];

    timaginary32 = basic[Timaginary32];
    timaginary64 = basic[Timaginary64];
    timaginary80 = basic[Timaginary80];

    tcomplex32 = basic[Tcomplex32];
    tcomplex64 = basic[Tcomplex64];
    tcomplex80 = basic[Tcomplex80];

    tbool = basic[Tbool];
    tchar = basic[Tchar];
    twchar = basic[Twchar];
    tdchar = basic[Tdchar];

    tshiftcnt = tint32;
    terror = basic[Terror];
    tnull = basic[Tnull];
    tnull = new TypeNull();
    tnull->deco = tnull->merge()->deco;

    tvoidptr = tvoid->pointerTo();
    tstring = tchar->immutableOf()->arrayOf();
    twstring = twchar->immutableOf()->arrayOf();
    tdstring = tdchar->immutableOf()->arrayOf();
    tvalist = Target::va_listType();

    if (global.params.isLP64)
    {
        Tsize_t = Tuns64;
        Tptrdiff_t = Tint64;
    }
    else
    {
        Tsize_t = Tuns32;
        Tptrdiff_t = Tint32;
    }

    tsize_t = basic[Tsize_t];
    tptrdiff_t = basic[Tptrdiff_t];
    thash_t = tsize_t;
}

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

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

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

Type *Type::semantic(Loc loc, Scope *)
{
    if (ty == Tint128 || ty == Tuns128)
    {
        error(loc, "cent and ucent types not implemented");
        return terror;
    }

    return merge();
}

Type *Type::trySemantic(Loc loc, Scope *sc)
{
    //printf("+trySemantic(%s) %d\n", toChars(), global.errors);
    unsigned errors = global.startGagging();
    Type *t = semantic(loc, sc);
    if (global.endGagging(errors) || t->ty == Terror)        // if any errors happened
    {
        t = NULL;
    }
    //printf("-trySemantic(%s) %d\n", toChars(), global.errors);
    return t;
}

/********************************
 * Return a copy of this type with all attributes null-initialized.
 * Useful for creating a type with different modifiers.
 */

Type *Type::nullAttributes()
{
    unsigned sz = sizeTy[ty];
    void *pt = mem.xmalloc(sz);
    Type *t = (Type *)memcpy(pt, (void *)this, sz);
    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->wto = NULL;
    t->wcto = NULL;
    t->swto = NULL;
    t->swcto = NULL;
    t->vtinfo = NULL;
    t->ctype = NULL;
    if (t->ty == Tstruct) ((TypeStruct *)t)->att = RECfwdref;
    if (t->ty == Tclass) ((TypeClass *)t)->att = RECfwdref;
    return t;
}

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

Type *Type::constOf()
{
    //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, t->toChars());
    return t;
}

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

Type *Type::immutableOf()
{
    //printf("Type::immutableOf() %p %s\n", this, toChars());
    if (isImmutable())
        return this;
    if (ito)
    {
        assert(ito->isImmutable());
        return ito;
    }
    Type *t = makeImmutable();
    t = t->merge();
    t->fixTo(this);
    //printf("\t%p\n", t);
    return t;
}

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

Type *Type::mutableOf()
{
    //printf("Type::mutableOf() %p, %s\n", this, toChars());
    Type *t = this;
    if (isImmutable())
    {
        t = ito;                // immutable => naked
        assert(!t || (t->isMutable() && !t->isShared()));
    }
    else if (isConst())
    {
        if (isShared())
        {
            if (isWild())
                t = swcto;      // shared wild const -> shared
            else
                t = sto;        // shared const => shared
        }
        else
        {
            if (isWild())
                t = wcto;       // wild const -> naked
            else
                t = cto;        // const => naked
        }
        assert(!t || t->isMutable());
    }
    else if (isWild())
    {
        if (isShared())
            t = sto;            // shared wild => shared
        else
            t = wto;            // wild => naked
        assert(!t || t->isMutable());
    }
    if (!t)
    {
        t = makeMutable();
        t = t->merge();
        t->fixTo(this);
    }
    else
        t = t->merge();
    assert(t->isMutable());
    return t;
}

Type *Type::sharedOf()
{
    //printf("Type::sharedOf() %p, %s\n", this, toChars());
    if (mod == MODshared)
        return this;
    if (sto)
    {
        assert(sto->mod == MODshared);
        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;
}


/********************************
 * Make type unshared.
 *      0            => 0
 *      const        => const
 *      immutable    => immutable
 *      shared       => 0
 *      shared const => const
 *      wild         => wild
 *      wild const   => wild const
 *      shared wild  => wild
 *      shared wild const => wild const
 */

Type *Type::unSharedOf()
{
    //printf("Type::unSharedOf() %p, %s\n", this, toChars());
    Type *t = this;

    if (isShared())
    {
        if (isWild())
        {
            if (isConst())
                t = wcto;   // shared wild const => wild const
            else
                t = wto;    // shared wild => wild
        }
        else
        {
            if (isConst())
                t = cto;    // shared const => const
            else
                t = sto;    // shared => naked
        }
        assert(!t || !t->isShared());
    }

    if (!t)
    {
        t = this->nullAttributes();
        t->mod = mod & ~MODshared;
        t->ctype = ctype;
        t = t->merge();

        t->fixTo(this);
    }
    else
        t = t->merge();
    assert(!t->isShared());
    return t;
}

/********************************
 * Convert to 'wild'.
 */

Type *Type::wildOf()
{
    //printf("Type::wildOf() %p %s\n", this, toChars());
    if (mod == MODwild)
        return this;
    if (wto)
    {
        assert(wto->mod == MODwild);
        return wto;
    }
    Type *t = makeWild();
    t = t->merge();
    t->fixTo(this);
    //printf("\t%p %s\n", t, t->toChars());
    return t;
}

Type *Type::wildConstOf()
{
    //printf("Type::wildConstOf() %p %s\n", this, toChars());
    if (mod == MODwildconst)
        return this;
    if (wcto)
    {
        assert(wcto->mod == MODwildconst);
        return wcto;
    }
    Type *t = makeWildConst();
    t = t->merge();
    t->fixTo(this);
    //printf("\t%p %s\n", t, t->toChars());
    return t;
}

Type *Type::sharedWildOf()
{
    //printf("Type::sharedWildOf() %p, %s\n", this, toChars());
    if (mod == (MODshared | MODwild))
        return this;
    if (swto)
    {
        assert(swto->mod == (MODshared | MODwild));
        return swto;
    }
    Type *t = makeSharedWild();
    t = t->merge();
    t->fixTo(this);
    //printf("\t%p %s\n", t, t->toChars());
    return t;
}

Type *Type::sharedWildConstOf()
{
    //printf("Type::sharedWildConstOf() %p, %s\n", this, toChars());
    if (mod == (MODshared | MODwildconst))
        return this;
    if (swcto)
    {
        assert(swcto->mod == (MODshared | MODwildconst));
        return swcto;
    }
    Type *t = makeSharedWildConst();
    t = t->merge();
    t->fixTo(this);
    //printf("\t%p %s\n", t, t->toChars());
    return t;
}

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

void Type::fixTo(Type *t)
{
    // If fixing this: immutable(T*) by t: immutable(T)*,
    // cache t to this->xto won't break transitivity.
    Type *mto = NULL;
    Type *tn = nextOf();
    if (!tn || (ty != Tsarray && tn->mod == t->nextOf()->mod))
    {
        switch (t->mod)
        {
            case 0:                           mto = t;  break;
            case MODconst:                    cto = t;  break;
            case MODwild:                     wto = t;  break;
            case MODwildconst:               wcto = t;  break;
            case MODshared:                   sto = t;  break;
            case MODshared | MODconst:       scto = t;  break;
            case MODshared | MODwild:        swto = t;  break;
            case MODshared | MODwildconst:  swcto = t;  break;
            case MODimmutable:                ito = t;  break;
        }
    }

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

        case MODconst:
            cto = mto;
            t->cto = this;
            break;

        case MODwild:
            wto = mto;
            t->wto = this;
            break;

        case MODwildconst:
            wcto = mto;
            t->wcto = this;
            break;

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

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

        case MODshared | MODwild:
            swto = mto;
            t->swto = this;
            break;

        case MODshared | MODwildconst:
            swcto = mto;
            t->swcto = this;
            break;

        case MODimmutable:
            t->ito = this;
            if (t->  cto) t->  cto->ito = this;
            if (t->  sto) t->  sto->ito = this;
            if (t-> scto) t-> scto->ito = this;
            if (t->  wto) t->  wto->ito = this;
            if (t-> wcto) t-> wcto->ito = this;
            if (t-> swto) t-> swto->ito = this;
            if (t->swcto) t->swcto->ito = 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 == MODimmutable);
            if (sto) assert(sto->mod == MODshared);
            if (scto) assert(scto->mod == (MODshared | MODconst));
            if (wto) assert(wto->mod == MODwild);
            if (wcto) assert(wcto->mod == MODwildconst);
            if (swto) assert(swto->mod == (MODshared | MODwild));
            if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
            break;

        case MODconst:
            if (cto) assert(cto->mod == 0);
            if (ito) assert(ito->mod == MODimmutable);
            if (sto) assert(sto->mod == MODshared);
            if (scto) assert(scto->mod == (MODshared | MODconst));
            if (wto) assert(wto->mod == MODwild);
            if (wcto) assert(wcto->mod == MODwildconst);
            if (swto) assert(swto->mod == (MODshared | MODwild));
            if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
            break;

        case MODwild:
            if (cto) assert(cto->mod == MODconst);
            if (ito) assert(ito->mod == MODimmutable);
            if (sto) assert(sto->mod == MODshared);
            if (scto) assert(scto->mod == (MODshared | MODconst));
            if (wto) assert(wto->mod == 0);
            if (wcto) assert(wcto->mod == MODwildconst);
            if (swto) assert(swto->mod == (MODshared | MODwild));
            if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
            break;

        case MODwildconst:
            assert(!  cto ||   cto->mod == MODconst);
            assert(!  ito ||   ito->mod == MODimmutable);
            assert(!  sto ||   sto->mod == MODshared);
            assert(! scto ||  scto->mod == (MODshared | MODconst));
            assert(!  wto ||   wto->mod == MODwild);
            assert(! wcto ||  wcto->mod == 0);
            assert(! swto ||  swto->mod == (MODshared | MODwild));
            assert(!swcto || swcto->mod == (MODshared | MODwildconst));
            break;

        case MODshared:
            if (cto) assert(cto->mod == MODconst);
            if (ito) assert(ito->mod == MODimmutable);
            if (sto) assert(sto->mod == 0);
            if (scto) assert(scto->mod == (MODshared | MODconst));
            if (wto) assert(wto->mod == MODwild);
            if (wcto) assert(wcto->mod == MODwildconst);
            if (swto) assert(swto->mod == (MODshared | MODwild));
            if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
            break;

        case MODshared | MODconst:
            if (cto) assert(cto->mod == MODconst);
            if (ito) assert(ito->mod == MODimmutable);
            if (sto) assert(sto->mod == MODshared);
            if (scto) assert(scto->mod == 0);
            if (wto) assert(wto->mod == MODwild);
            if (wcto) assert(wcto->mod == MODwildconst);
            if (swto) assert(swto->mod == (MODshared | MODwild));
            if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
            break;

        case MODshared | MODwild:
            if (cto) assert(cto->mod == MODconst);
            if (ito) assert(ito->mod == MODimmutable);
            if (sto) assert(sto->mod == MODshared);
            if (scto) assert(scto->mod == (MODshared | MODconst));
            if (wto) assert(wto->mod == MODwild);
            if (wcto) assert(wcto->mod == MODwildconst);
            if (swto) assert(swto->mod == 0);
            if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
            break;

        case MODshared | MODwildconst:
            assert(!  cto ||   cto->mod == MODconst);
            assert(!  ito ||   ito->mod == MODimmutable);
            assert(!  sto ||   sto->mod == MODshared);
            assert(! scto ||  scto->mod == (MODshared | MODconst));
            assert(!  wto ||   wto->mod == MODwild);
            assert(! wcto ||  wcto->mod == MODwildconst);
            assert(! swto ||  swto->mod == (MODshared | MODwild));
            assert(!swcto || swcto->mod == 0);
            break;

        case MODimmutable:
            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));
            if (wto) assert(wto->mod == MODwild);
            if (wcto) assert(wcto->mod == MODwildconst);
            if (swto) assert(swto->mod == (MODshared | MODwild));
            if (swcto) assert(swcto->mod == (MODshared | MODwildconst));
            break;

        default:
            assert(0);
    }

    Type *tn = nextOf();
    if (tn && ty != Tfunction && tn->ty != Tfunction && ty != Tenum)
    {
        // Verify transitivity
        switch (mod)
        {
            case 0:
            case MODconst:
            case MODwild:
            case MODwildconst:
            case MODshared:
            case MODshared | MODconst:
            case MODshared | MODwild:
            case MODshared | MODwildconst:
            case MODimmutable:
                assert(tn->mod == MODimmutable || (tn->mod & mod) == mod);
                break;

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

Type *Type::makeConst()
{
    //printf("Type::makeConst() %p, %s\n", this, toChars());
    if (cto) return cto;
    Type *t = this->nullAttributes();
    t->mod = MODconst;
    //printf("-Type::makeConst() %p, %s\n", t, toChars());
    return t;
}

Type *Type::makeImmutable()
{
    if (ito) return ito;
    Type *t = this->nullAttributes();
    t->mod = MODimmutable;
    return t;
}

Type *Type::makeShared()
{
    if (sto) return sto;
    Type *t = this->nullAttributes();
    t->mod = MODshared;
    return t;
}

Type *Type::makeSharedConst()
{
    if (scto) return scto;
    Type *t = this->nullAttributes();
    t->mod = MODshared | MODconst;
    return t;
}

Type *Type::makeWild()
{
    if (wto) return wto;
    Type *t = this->nullAttributes();
    t->mod = MODwild;
    return t;
}

Type *Type::makeWildConst()
{
    if (wcto) return wcto;
    Type *t = this->nullAttributes();
    t->mod = MODwildconst;
    return t;
}

Type *Type::makeSharedWild()
{
    if (swto) return swto;
    Type *t = this->nullAttributes();
    t->mod = MODshared | MODwild;
    return t;
}

Type *Type::makeSharedWildConst()
{
    if (swcto) return swcto;
    Type *t = this->nullAttributes();
    t->mod = MODshared | MODwildconst;
    return t;
}

Type *Type::makeMutable()
{
    Type *t = this->nullAttributes();
    t->mod = mod & MODshared;
    return t;
}

/*************************************
 * Apply STCxxxx bits to existing type.
 * Use *before* semantic analysis is run.
 */

Type *Type::addSTC(StorageClass stc)
{
    Type *t = this;
    if (t->isImmutable())
        ;
    else if (stc & STCimmutable)
    {
        t = t->makeImmutable();
    }
    else
    {
        if ((stc & STCshared) && !t->isShared())
        {
            if (t->isWild())
            {
                if (t->isConst())
                    t = t->makeSharedWildConst();
                else
                    t = t->makeSharedWild();
            }
            else
            {
                if (t->isConst())
                    t = t->makeSharedConst();
                else
                    t = t->makeShared();
            }
        }
        if ((stc & STCconst) && !t->isConst())
        {
            if (t->isShared())
            {
                if (t->isWild())
                    t = t->makeSharedWildConst();
                else
                    t = t->makeSharedConst();
            }
            else
            {
                if (t->isWild())
                    t = t->makeWildConst();
                else
                    t = t->makeConst();
            }
        }
        if ((stc & STCwild) && !t->isWild())
        {
            if (t->isShared())
            {
                if (t->isConst())
                    t = t->makeSharedWildConst();
                else
                    t = t->makeSharedWild();
            }
            else
            {
                if (t->isConst())
                    t = t->makeWildConst();
                else
                    t = t->makeWild();
            }
        }
    }
    return t;
}

/************************************
 * Convert MODxxxx to STCxxx
 */

StorageClass ModToStc(unsigned mod)
{
    StorageClass stc = 0;
    if (mod & MODimmutable) stc |= STCimmutable;
    if (mod & MODconst)     stc |= STCconst;
    if (mod & MODwild)      stc |= STCwild;
    if (mod & MODshared)    stc |= STCshared;
    return stc;
}

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

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

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

        case MODconst:
            t = unSharedOf()->constOf();
            break;

        case MODwild:
            t = unSharedOf()->wildOf();
            break;

        case MODwildconst:
            t = unSharedOf()->wildConstOf();
            break;

        case MODshared:
            t = mutableOf()->sharedOf();
            break;

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

        case MODshared | MODwild:
            t = sharedWildOf();
            break;

        case MODshared | MODwildconst:
            t = sharedWildConstOf();
            break;

        case MODimmutable:
            t = immutableOf();
            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(MOD mod)
{
    /* Add anything to immutable, and it remains immutable
     */
    Type *t = this;
    if (!t->isImmutable())
    {
        //printf("addMod(%x) %s\n", mod, toChars());
        switch (mod)
        {
            case 0:
                break;

            case MODconst:
                if (isShared())
                {
                    if (isWild())
                        t = sharedWildConstOf();
                    else
                        t = sharedConstOf();
                }
                else
                {
                    if (isWild())
                        t = wildConstOf();
                    else
                        t = constOf();
                }
                break;

            case MODwild:
                if (isShared())
                {
                    if (isConst())
                        t = sharedWildConstOf();
                    else
                        t = sharedWildOf();
                }
                else
                {
                    if (isConst())
                        t = wildConstOf();
                    else
                        t = wildOf();
                }
                break;

            case MODwildconst:
                if (isShared())
                    t = sharedWildConstOf();
                else
                    t = wildConstOf();
                break;

            case MODshared:
                if (isWild())
                {
                    if (isConst())
                        t = sharedWildConstOf();
                    else
                        t = sharedWildOf();
                }
                else
                {
                    if (isConst())
                        t = sharedConstOf();
                    else
                        t = sharedOf();
                }
                break;

            case MODshared | MODconst:
                if (isWild())
                    t = sharedWildConstOf();
                else
                    t = sharedConstOf();
                break;

            case MODshared | MODwild:
                if (isConst())
                    t = sharedWildConstOf();
                else
                    t = sharedWildOf();
                break;

            case MODshared | MODwildconst:
                t = sharedWildConstOf();
                break;

            case MODimmutable:
                t = immutableOf();
                break;

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

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

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

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

Type *Type::pointerTo()
{
    if (ty == Terror)
        return this;
    if (!pto)
    {
        Type *t = new TypePointer(this);
        if (ty == Tfunction)
        {
            t->deco = t->merge()->deco;
            pto = t;
        }
        else
            pto = t->merge();
    }
    return pto;
}

Type *Type::referenceTo()
{
    if (ty == Terror)
        return this;
    if (!rto)
    {
        Type *t = new TypeReference(this);
        rto = t->merge();
    }
    return rto;
}

Type *Type::arrayOf()
{
    if (ty == Terror)
        return this;
    if (!arrayof)
    {
        Type *t = new TypeDArray(this);
        arrayof = t->merge();
    }
    return arrayof;
}

// Make corresponding static array type without semantic
Type *Type::sarrayOf(dinteger_t dim)
{
    assert(deco);
    Type *t = new TypeSArray(this, new IntegerExp(Loc(), dim, Type::tsize_t));

    // according to TypeSArray::semantic()
    t = t->addMod(mod);
    t = t->merge();

    return t;
}

Type *Type::aliasthisOf()
{
    AggregateDeclaration *ad = isAggregate(this);
    if (ad && ad->aliasthis)
    {
        Dsymbol *s = ad->aliasthis;
        if (s->isAliasDeclaration())
            s = s->toAlias();
        Declaration *d = s->isDeclaration();
        if (d && !d->isTupleDeclaration())
        {
            assert(d->type);
            Type *t = d->type;
            if (d->isVarDeclaration() && d->needThis())
            {
                t = t->addMod(this->mod);
            }
            else if (d->isFuncDeclaration())
            {
                FuncDeclaration *fd = resolveFuncCall(Loc(), NULL, d, NULL, this, NULL, 1);
                if (fd && fd->errors)
                    return Type::terror;
                if (fd && !fd->type->nextOf() && !fd->functionSemantic())
                    fd = NULL;
                if (fd)
                {
                    t = fd->type->nextOf();
                    if (!t) // issue 14185
                        return Type::terror;
                    t = t->substWildTo(mod == 0 ? MODmutable : (MODFlags)mod);
                }
                else
                    return Type::terror;
            }
            return t;
        }
        EnumDeclaration *ed = s->isEnumDeclaration();
        if (ed)
        {
            Type *t = ed->type;
            return t;
        }
        TemplateDeclaration *td = s->isTemplateDeclaration();
        if (td)
        {
            assert(td->_scope);
            FuncDeclaration *fd = resolveFuncCall(Loc(), NULL, td, NULL, this, NULL, 1);
            if (fd && fd->errors)
                return Type::terror;
            if (fd && fd->functionSemantic())
            {
                Type *t = fd->type->nextOf();
                t = t->substWildTo(mod == 0 ? MODmutable : (MODFlags)mod);
                return t;
            }
            else
                return Type::terror;
        }
        //printf("%s\n", s->kind());
    }
    return NULL;
}

bool Type::checkAliasThisRec()
{
    Type *tb = toBasetype();
    AliasThisRec* pflag;
    if (tb->ty == Tstruct)
        pflag = &((TypeStruct *)tb)->att;
    else if (tb->ty == Tclass)
        pflag = &((TypeClass *)tb)->att;
    else
        return false;

    AliasThisRec flag = (AliasThisRec)(*pflag & RECtypeMask);
    if (flag == RECfwdref)
    {
        Type *att = aliasthisOf();
        flag = att && att->implicitConvTo(this) ? RECyes : RECno;
    }
    *pflag = (AliasThisRec)(flag | (*pflag & ~RECtypeMask));
    return flag == RECyes;
}

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

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

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

/***************************
 * Return !=0 if modfrom can be implicitly converted to modto
 */
bool MODimplicitConv(MOD modfrom, MOD modto)
{
    if (modfrom == modto)
        return true;

    //printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto);
    #define X(m, n) (((m) << 4) | (n))
    switch (X(modfrom & ~MODshared, modto & ~MODshared))
    {
        case X(0,            MODconst):
        case X(MODwild,      MODconst):
        case X(MODwild,      MODwildconst):
        case X(MODwildconst, MODconst):
            return (modfrom & MODshared) == (modto & MODshared);

        case X(MODimmutable, MODconst):
        case X(MODimmutable, MODwildconst):
            return true;

        default:
            return false;
    }
    #undef X
}

/***************************
 * Return MATCHexact or MATCHconst if a method of type '() modfrom' can call a method of type '() modto'.
 */
MATCH MODmethodConv(MOD modfrom, MOD modto)
{
    if (modfrom == modto)
        return MATCHexact;
    if (MODimplicitConv(modfrom, modto))
        return MATCHconst;

    #define X(m, n) (((m) << 4) | (n))
    switch (X(modfrom, modto))
    {
        case X(0,            MODwild):
        case X(MODimmutable, MODwild):
        case X(MODconst,     MODwild):
        case X(MODwildconst, MODwild):
        case X(MODshared,              MODshared|MODwild):
        case X(MODshared|MODimmutable, MODshared|MODwild):
        case X(MODshared|MODconst,     MODshared|MODwild):
        case X(MODshared|MODwildconst, MODshared|MODwild):
            return MATCHconst;

        default:
            return MATCHnomatch;
    }
    #undef X
}

/***************************
 * Merge mod bits to form common mod.
 */
MOD MODmerge(MOD mod1, MOD mod2)
{
    if (mod1 == mod2)
        return mod1;

    //printf("MODmerge(1 = %x, 2 = %x)\n", mod1, mod2);
    MOD result = 0;
    if ((mod1 | mod2) & MODshared)
    {
        // If either type is shared, the result will be shared
        result |= MODshared;
        mod1 &= ~MODshared;
        mod2 &= ~MODshared;
    }
    if (mod1 == 0 || mod1 == MODmutable || mod1 == MODconst ||
        mod2 == 0 || mod2 == MODmutable || mod2 == MODconst)
    {
        // If either type is mutable or const, the result will be const.
        result |= MODconst;
    }
    else
    {
        // MODimmutable vs MODwild
        // MODimmutable vs MODwildconst
        //      MODwild vs MODwildconst
        assert(mod1 & MODwild || mod2 & MODwild);
        result |= MODwildconst;
    }
    return result;
}

/*********************************
 * Store modifier name into buf.
 */
void MODtoBuffer(OutBuffer *buf, MOD mod)
{
    switch (mod)
    {
        case 0:
            break;

        case MODimmutable:
            buf->writestring(Token::tochars[TOKimmutable]);
            break;

        case MODshared:
            buf->writestring(Token::tochars[TOKshared]);
            break;

        case MODshared | MODconst:
            buf->writestring(Token::tochars[TOKshared]);
            buf->writeByte(' ');
            /* fall through */
        case MODconst:
            buf->writestring(Token::tochars[TOKconst]);
            break;

        case MODshared | MODwild:
            buf->writestring(Token::tochars[TOKshared]);
            buf->writeByte(' ');
            /* fall through */
        case MODwild:
            buf->writestring(Token::tochars[TOKwild]);
            break;

        case MODshared | MODwildconst:
            buf->writestring(Token::tochars[TOKshared]);
            buf->writeByte(' ');
            /* fall through */
        case MODwildconst:
            buf->writestring(Token::tochars[TOKwild]);
            buf->writeByte(' ');
            buf->writestring(Token::tochars[TOKconst]);
            break;

        default:
            assert(0);
    }
}


/*********************************
 * Return modifier name.
 */
char *MODtoChars(MOD mod)
{
    OutBuffer buf;
    buf.reserve(16);
    MODtoBuffer(&buf, mod);
    return buf.extractString();
}

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

const char *Type::toChars()
{
    OutBuffer buf;
    buf.reserve(16);
    HdrGenState hgs;
    hgs.fullQual = (ty == Tclass && !mod);

    ::toCBuffer(this, &buf, NULL, &hgs);
    return buf.extractString();
}

char *Type::toPrettyChars(bool QualifyTypes)
{
    OutBuffer buf;
    buf.reserve(16);
    HdrGenState hgs;
    hgs.fullQual = QualifyTypes;

    ::toCBuffer(this, &buf, NULL, &hgs);
    return buf.extractString();
}

/*********************************
 * Store this type's modifier name into buf.
 */
void Type::modToBuffer(OutBuffer *buf)
{
    if (mod)
    {
        buf->writeByte(' ');
        MODtoBuffer(buf, mod);
    }
}

/*********************************
 * Return this type's modifier name.
 */
char *Type::modToChars()
{
    OutBuffer buf;
    buf.reserve(16);
    modToBuffer(&buf);
    return buf.extractString();
}

/** For each active modifier (MODconst, MODimmutable, etc) call fp with a
void* for the work param and a string representation of the attribute. */
int Type::modifiersApply(void *param, int (*fp)(void *, const char *))
{
    static unsigned char modsArr[] = { MODconst, MODimmutable, MODwild, MODshared };

    for (size_t idx = 0; idx < 4; ++idx)
    {
        if (mod & modsArr[idx])
        {
            if (int res = fp(param, MODtoChars(modsArr[idx])))
                return res;
        }
    }
    return 0;
}

/************************************
 * Strip all parameter's idenfiers and their default arguments for merging types.
 * If some of parameter types or return type are function pointer, delegate, or
 * the types which contains either, then strip also from them.
 */

Type *stripDefaultArgs(Type *t)
{
  struct N
  {
    static Parameters *stripParams(Parameters *parameters)
    {
        Parameters *params = parameters;
        if (params && params->dim > 0)
        {
            for (size_t i = 0; i < params->dim; i++)
            {
                Parameter *p = (*params)[i];
                Type *ta = stripDefaultArgs(p->type);
                if (ta != p->type || p->defaultArg || p->ident)
                {
                    if (params == parameters)
                    {
                        params = new Parameters();
                        params->setDim(parameters->dim);
                        for (size_t j = 0; j < params->dim; j++)
                            (*params)[j] = (*parameters)[j];
                    }
                    (*params)[i] = new Parameter(p->storageClass, ta, NULL, NULL);
                }
            }
        }
        return params;
    }
  };

    if (t == NULL)
        return t;

    if (t->ty == Tfunction)
    {
        TypeFunction *tf = (TypeFunction *)t;
        Type *tret = stripDefaultArgs(tf->next);
        Parameters *params = N::stripParams(tf->parameters);
        if (tret == tf->next && params == tf->parameters)
            goto Lnot;
        tf = (TypeFunction *)tf->copy();
        tf->parameters = params;
        tf->next = tret;
        //printf("strip %s\n   <- %s\n", tf->toChars(), t->toChars());
        t = tf;
    }
    else if (t->ty == Ttuple)
    {
        TypeTuple *tt = (TypeTuple *)t;
        Parameters *args = N::stripParams(tt->arguments);
        if (args == tt->arguments)
            goto Lnot;
        t = t->copy();
        ((TypeTuple *)t)->arguments = args;
    }
    else if (t->ty == Tenum)
    {
        // TypeEnum::nextOf() may be != NULL, but it's not necessary here.
        goto Lnot;
    }
    else
    {
        Type *tn = t->nextOf();
        Type *n = stripDefaultArgs(tn);
        if (n == tn)
            goto Lnot;
        t = t->copy();
        ((TypeNext *)t)->next = n;
    }
    //printf("strip %s\n", t->toChars());
Lnot:
    return t;
}

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

Type *Type::merge()
{
    if (ty == Terror) return this;
    if (ty == Ttypeof) return this;
    if (ty == Tident) return this;
    if (ty == Tinstance) return this;
    if (ty == Taarray && !((TypeAArray *)this)->index->merge()->deco)
        return this;
    if (ty != Tenum && nextOf() && !nextOf()->deco)
        return this;

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

        mangleToBuffer(this, &buf);

        StringValue *sv = stringtable.update((char *)buf.data, buf.offset);
        if (sv->ptrvalue)
        {
            t = (Type *) sv->ptrvalue;
            assert(t->deco);
            //printf("old value, deco = '%s' %p\n", t->deco, t->deco);
        }
        else
        {
            sv->ptrvalue = (char *)(t = stripDefaultArgs(t));
            deco = t->deco = const_cast<char *>(sv->toDchars());
            //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 = stringtable.lookup((char *)t->deco, strlen(t->deco));
    if (sv && sv->ptrvalue)
    {   t = (Type *) sv->ptrvalue;
        assert(t->deco);
    }
    else
        assert(0);
    return t;
}

bool Type::isintegral()
{
    return false;
}

bool Type::isfloating()
{
    return false;
}

bool Type::isreal()
{
    return false;
}

bool Type::isimaginary()
{
    return false;
}

bool Type::iscomplex()
{
    return false;
}

bool Type::isscalar()
{
    return false;
}

bool Type::isunsigned()
{
    return false;
}

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

bool Type::isscope()
{
    return false;
}

bool Type::isString()
{
    return false;
}

/**************************
 * When T is mutable,
 * Given:
 *      T a, b;
 * Can we bitwise assign:
 *      a = b;
 * ?
 */
bool Type::isAssignable()
{
    return true;
}

/**************************
 * Returns true if T can be converted to boolean value.
 */
bool Type::isBoolean()
{
    return isscalar();
}

/********************************
 * true if when type goes out of scope, it needs a destructor applied.
 * Only applies to value types, not ref types.
 */
bool Type::needsDestruction()
{
    return false;
}

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

bool Type::needsNested()
{
    return false;
}

/*********************************
 * 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)
{
    return NULL;
}

/***************************************
 * Use when we prefer the default initializer to be a literal,
 * rather than a global immutable variable.
 */
Expression *Type::defaultInitLiteral(Loc loc)
{
    return defaultInit(loc);
}

bool Type::isZeroInit(Loc)
{
    return false;           // assume not
}

bool Type::isBaseOf(Type *, int *)
{
    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->equals(to))
        return MATCHexact;
    return MATCHnomatch;
}

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

MATCH Type::constConv(Type *to)
{
    //printf("Type::constConv(this = %s, to = %s)\n", toChars(), to->toChars());
    if (equals(to))
        return MATCHexact;
    if (ty == to->ty && MODimplicitConv(mod, to->mod))
        return MATCHconst;
    return MATCHnomatch;
}

/***************************************
 * Return MOD bits matching this type to wild parameter type (tprm).
 */

unsigned char Type::deduceWild(Type *t, bool)
{
    //printf("Type::deduceWild this = '%s', tprm = '%s'\n", toChars(), tprm->toChars());

    if (t->isWild())
    {
        if (isImmutable())
            return MODimmutable;
        else if (isWildConst())
        {
            if (t->isWildConst())
                return MODwild;
            else
                return MODwildconst;
        }
        else if (isWild())
            return MODwild;
        else if (isConst())
            return MODconst;
        else if (isMutable())
            return MODmutable;
        else
            assert(0);
    }
    return 0;
}

Type *Type::unqualify(unsigned m)
{
    Type *t = mutableOf()->unSharedOf();

    Type *tn = ty == Tenum ? NULL : nextOf();
    if (tn && tn->ty != Tfunction)
    {
        Type *utn = tn->unqualify(m);
        if (utn != tn)
        {
            if (ty == Tpointer)
                t = utn->pointerTo();
            else if (ty == Tarray)
                t = utn->arrayOf();
            else if (ty == Tsarray)
                t = new TypeSArray(utn, ((TypeSArray *)this)->dim);
            else if (ty == Taarray)
            {
                t = new TypeAArray(utn, ((TypeAArray *)this)->index);
                ((TypeAArray *)t)->sc = ((TypeAArray *)this)->sc;   // duplicate scope
            }
            else
                assert(0);

            t = t->merge();
        }
    }
    t = t->addMod(mod & ~m);
    return t;
}

Type *Type::substWildTo(unsigned mod)
{
    //printf("+Type::substWildTo this = %s, mod = x%x\n", toChars(), mod);
    Type *t;

    if (Type *tn = nextOf())
    {
        // substitution has no effect on function pointer type.
        if (ty == Tpointer && tn->ty == Tfunction)
        {
            t = this;
            goto L1;
        }

        t = tn->substWildTo(mod);
        if (t == tn)
            t = this;
        else
        {
            if (ty == Tpointer)
                t = t->pointerTo();
            else if (ty == Tarray)
                t = t->arrayOf();
            else if (ty == Tsarray)
                t = new TypeSArray(t, ((TypeSArray *)this)->dim->syntaxCopy());
            else if (ty == Taarray)
            {
                t = new TypeAArray(t, ((TypeAArray *)this)->index->syntaxCopy());
                ((TypeAArray *)t)->sc = ((TypeAArray *)this)->sc;   // duplicate scope
            }
            else if (ty == Tdelegate)
            {
                t = new TypeDelegate(t);
            }
            else
                assert(0);

            t = t->merge();
        }
    }
    else
        t = this;

L1:
    if (isWild())
    {
        if (mod == MODimmutable)
        {
            t = t->immutableOf();
        }
        else if (mod == MODwildconst)
        {
            t = t->wildConstOf();
        }
        else if (mod == MODwild)
        {
            if (isWildConst())
                t = t->wildConstOf();
            else
                t = t->wildOf();
        }
        else if (mod == MODconst)
        {
            t = t->constOf();
        }
        else
        {
            if (isWildConst())
                t = t->constOf();
            else
                t = t->mutableOf();
        }
    }
    if (isConst())
        t = t->addMod(MODconst);
    if (isShared())
        t = t->addMod(MODshared);

    //printf("-Type::substWildTo t = %s\n", t->toChars());
    return t;
}

Type *TypeFunction::substWildTo(unsigned)
{
    if (!iswild && !(mod & MODwild))
        return this;

    // Substitude inout qualifier of function type to mutable or immutable
    // would break type system. Instead substitude inout to the most weak
    // qualifer - const.
    unsigned m = MODconst;

    assert(next);
    Type *tret = next->substWildTo(m);
    Parameters *params = parameters;
    if (mod & MODwild)
        params = parameters->copy();
    for (size_t i = 0; i < params->dim; i++)
    {
        Parameter *p = (*params)[i];
        Type *t = p->type->substWildTo(m);
        if (t == p->type)
            continue;
        if (params == parameters)
            params = parameters->copy();
        (*params)[i] = new Parameter(p->storageClass, t, NULL, NULL);
    }
    if (next == tret && params == parameters)
        return this;

    // Similar to TypeFunction::syntaxCopy;
    TypeFunction *t = new TypeFunction(params, tret, varargs, linkage);
    t->mod = ((mod & MODwild) ? (mod & ~MODwild) | MODconst : mod);
    t->isnothrow = isnothrow;
    t->isnogc = isnogc;
    t->purity = purity;
    t->isproperty = isproperty;
    t->isref = isref;
    t->isreturn = isreturn;
    t->isscope = isscope;
    t->isscopeinferred = isscopeinferred;
    t->iswild = 0;
    t->trust = trust;
    t->fargs = fargs;
    return t->merge();
}

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

/***************************************
 * Calculate built-in properties which just the type is necessary.
 *
 * If flag & 1, don't report "not a property" error and just return NULL.
 */
Expression *Type::getProperty(Loc loc, Identifier *ident, int flag)
{
    Expression *e;

    if (ident == Id::__sizeof)
    {
        d_uns64 sz = size(loc);
        if (sz == SIZE_INVALID)
            return new ErrorExp();
        e = new IntegerExp(loc, sz, Type::tsize_t);
    }
    else if (ident == Id::__xalignof)
    {
        e = new IntegerExp(loc, alignsize(), Type::tsize_t);
    }
    else if (ident == Id::_init)
    {
        Type *tb = toBasetype();
        e = defaultInitLiteral(loc);
        if (tb->ty == Tstruct && tb->needsNested())
        {
            StructLiteralExp *se = (StructLiteralExp *)e;
            se->useStaticInit = true;
        }
    }
    else if (ident == Id::_mangleof)
    {
        if (!deco)
        {
            error(loc, "forward reference of type %s.mangleof", toChars());
            e = new ErrorExp();
        }
        else
        {
            e = new StringExp(loc, (char *)deco, strlen(deco));
            Scope sc;
            e = ::semantic(e, &sc);
        }
    }
    else if (ident == Id::stringof)
    {
        const char *s = toChars();
        e = new StringExp(loc, const_cast<char *>(s), strlen(s));
        Scope sc;
        e = ::semantic(e, &sc);
    }
    else if (flag && this != Type::terror)
    {
        return NULL;
    }
    else
    {
        Dsymbol *s = NULL;
        if (ty == Tstruct || ty == Tclass || ty == Tenum)
            s = toDsymbol(NULL);
        if (s)
            s = s->search_correct(ident);
        if (this != Type::terror)
        {
            if (s)
                error(loc, "no property '%s' for type '%s', did you mean '%s'?", ident->toChars(), toChars(), s->toChars());
            else
                error(loc, "no property '%s' for type '%s'", ident->toChars(), toChars());
        }
        e = new ErrorExp();
    }
    return e;
}

/***************************************
 * Access the members of the object e. This type is same as e->type.
 *
 * If flag & 1, don't report "not a property" error and just return NULL.
 */
Expression *Type::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    VarDeclaration *v = NULL;

    Expression *ex = e;
    while (ex->op == TOKcomma)
        ex = ((CommaExp *)ex)->e2;
    if (ex->op == TOKdotvar)
    {
        DotVarExp *dv = (DotVarExp *)ex;
        v = dv->var->isVarDeclaration();
    }
    else if (ex->op == TOKvar)
    {
        VarExp *ve = (VarExp *)ex;
        v = ve->var->isVarDeclaration();
    }
    if (v)
    {
        if (ident == Id::offsetof)
        {
            if (v->isField())
            {
                AggregateDeclaration *ad = v->toParent()->isAggregateDeclaration();
                ad->size(e->loc);
                if (ad->sizeok != SIZEOKdone)
                    return new ErrorExp();
                e = new IntegerExp(e->loc, v->offset, Type::tsize_t);
                return e;
            }
        }
        else if (ident == Id::_init)
        {
            Type *tb = toBasetype();
            e = defaultInitLiteral(e->loc);
            if (tb->ty == Tstruct && tb->needsNested())
            {
                StructLiteralExp *se = (StructLiteralExp *)e;
                se->useStaticInit = true;
            }
            goto Lreturn;
        }
    }
    if (ident == Id::stringof)
    {
        /* Bugzilla 3796: this should demangle e->type->deco rather than
         * pretty-printing the type.
         */
        const char *s = e->toChars();
        e = new StringExp(e->loc, const_cast<char *>(s), strlen(s));
    }
    else
        e = getProperty(e->loc, ident, flag & 1);

Lreturn:
    if (e)
        e = ::semantic(e, sc);
    return e;
}

/************************************
 * Return alignment to use for this type.
 */

structalign_t Type::alignment()
{
    return STRUCTALIGN_DEFAULT;
}

/***************************************
 * Figures out what to do with an undefined member reference
 * for classes and structs.
 *
 * If flag & 1, don't report "not a property" error and just return NULL.
 */
Expression *Type::noMember(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    //printf("Type::noMember(e: %s ident: %s flag: %d)\n", e->toChars(), ident->toChars(), flag);

    static int nest;      // https://issues.dlang.org/show_bug.cgi?id=17380

    if (++nest > 500)
    {
      ::error(e->loc, "cannot resolve identifier `%s`", ident->toChars());
      --nest;
      return (flag & 1) ? NULL : new ErrorExp();
    }

    assert(ty == Tstruct || ty == Tclass);
    AggregateDeclaration *sym = toDsymbol(sc)->isAggregateDeclaration();
    assert(sym);

    if (ident != Id::__sizeof &&
        ident != Id::__xalignof &&
        ident != Id::_init &&
        ident != Id::_mangleof &&
        ident != Id::stringof &&
        ident != Id::offsetof &&
        // Bugzilla 15045: Don't forward special built-in member functions.
        ident != Id::ctor &&
        ident != Id::dtor &&
        ident != Id::__xdtor &&
        ident != Id::postblit &&
        ident != Id::__xpostblit)
    {
        /* Look for overloaded opDot() to see if we should forward request
         * to it.
         */
        if (Dsymbol *fd = search_function(sym, Id::opDot))
        {
            /* Rewrite e.ident as:
             *  e.opDot().ident
             */
            e = build_overload(e->loc, sc, e, NULL, fd);
            e = new DotIdExp(e->loc, e, ident);
            e = ::semantic(e, sc);
            --nest;
            return e;
        }

        /* Look for overloaded opDispatch to see if we should forward request
         * to it.
         */
        if (Dsymbol *fd = search_function(sym, Id::opDispatch))
        {
            /* Rewrite e.ident as:
             *  e.opDispatch!("ident")
             */
            TemplateDeclaration *td = fd->isTemplateDeclaration();
            if (!td)
            {
                fd->error("must be a template opDispatch(string s), not a %s", fd->kind());
                --nest;
                return new ErrorExp();
            }
            StringExp *se = new StringExp(e->loc, const_cast<char *>(ident->toChars()));
            Objects *tiargs = new Objects();
            tiargs->push(se);
            DotTemplateInstanceExp *dti = new DotTemplateInstanceExp(e->loc, e, Id::opDispatch, tiargs);
            dti->ti->tempdecl = td;

            /* opDispatch, which doesn't need IFTI,  may occur instantiate error.
             * It should be gagged if flag & 1.
             * e.g.
             *  template opDispatch(name) if (isValid!name) { ... }
             */
            unsigned errors = flag & 1 ? global.startGagging() : 0;
            e = semanticY(dti, sc, 0);
            if (flag & 1 && global.endGagging(errors))
                e = NULL;
            --nest;
            return e;
        }

        /* See if we should forward to the alias this.
         */
        if (sym->aliasthis)
        {   /* Rewrite e.ident as:
             *  e.aliasthis.ident
             */
            e = resolveAliasThis(sc, e);
            DotIdExp *die = new DotIdExp(e->loc, e, ident);
            e = semanticY(die, sc, flag & 1);
            --nest;
            return e;
        }
    }

    e = Type::dotExp(sc, e, ident, flag);
    --nest;
    return e;
}

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, ...)
{
    va_list ap;
    va_start(ap, format);
    ::vwarning(loc, format, ap);
    va_end( ap );
}

Identifier *Type::getTypeInfoIdent()
{
    // _init_10TypeInfo_%s
    OutBuffer buf;
    buf.reserve(32);
    mangleToBuffer(this, &buf);

    size_t len = buf.offset;
    buf.writeByte(0);

    // Allocate buffer on stack, fail over to using malloc()
    char namebuf[128];
    size_t namelen = 19 + sizeof(len) * 3 + len + 1;
    char *name = namelen <= sizeof(namebuf) ? namebuf : (char *)mem.xmalloc(namelen);

    int length = sprintf(name, "_D%lluTypeInfo_%s6__initZ", (unsigned long long) 9 + len, buf.data);
    //printf("%p, deco = %s, name = %s\n", this, deco, name);
    assert(0 < length && (size_t)length < namelen);     // don't overflow the buffer

    Identifier *id = Identifier::idPool(name, length);

    if (name != namebuf)
        free(name);
    return id;
}

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

TypeFunction *Type::toTypeFunction()
{
    if (ty != Tfunction)
        assert(0);
    return (TypeFunction *)this;
}

/***************************************
 * Resolve 'this' type to either type, symbol, or expression.
 * If errors happened, resolved to Type.terror.
 */
void Type::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool)
{
    //printf("Type::resolve() %s, %d\n", toChars(), ty);
    Type *t = semantic(loc, sc);
    *pt = t;
    *pe = NULL;
    *ps = NULL;
}

/***************************************
 * Normalize `e` as the result of Type::resolve() process.
 */
void Type::resolveExp(Expression *e, Type **pt, Expression **pe, Dsymbol **ps)
{
    *pt = NULL;
    *pe = NULL;
    *ps = NULL;

    Dsymbol *s;
    switch (e->op)
    {
        case TOKerror:
            *pt = Type::terror;
            return;

        case TOKtype:
            *pt = e->type;
            return;

        case TOKvar:
            s = ((VarExp *)e)->var;
            if (s->isVarDeclaration())
                goto Ldefault;
            //if (s->isOverDeclaration())
            //    todo;
            break;

        case TOKtemplate:
            // TemplateDeclaration
            s = ((TemplateExp *)e)->td;
            break;

        case TOKimport:
            s = ((ScopeExp *)e)->sds;
            // TemplateDeclaration, TemplateInstance, Import, Package, Module
            break;

        case TOKfunction:
            s = getDsymbol(e);
            break;

        //case TOKthis:
        //case TOKsuper:

        //case TOKtuple:

        //case TOKoverloadset:

        //case TOKdotvar:
        //case TOKdottd:
        //case TOKdotti:
        //case TOKdottype:
        //case TOKdot:

        default:
    Ldefault:
            *pe = e;
            return;
    }

    *ps = s;
}

/***************************************
 * Return !=0 if the type or any of its subtypes is wild.
 */

int Type::hasWild() const
{
    return mod & MODwild;
}

/***************************************
 * Return !=0 if type has pointers that need to
 * be scanned by the GC during a collection cycle.
 */
bool Type::hasPointers()
{
    //printf("Type::hasPointers() %s, %d\n", toChars(), ty);
    return false;
}

/*************************************
 * Detect if type has pointer fields that are initialized to void.
 * Local stack variables with such void fields can remain uninitialized,
 * leading to pointer bugs.
 * Returns:
 *  true if so
 */
bool Type::hasVoidInitPointers()
{
    return false;
}

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

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

/*************************************
 * If this is a type of static array, return its base element type.
 */

Type *Type::baseElemOf()
{
    Type *t = toBasetype();
    while (t->ty == Tsarray)
        t = ((TypeSArray *)t)->next->toBasetype();
    return t;
}

/*************************************
 * Bugzilla 14488: Check if the inner most base type is complex or imaginary.
 * Should only give alerts when set to emit transitional messages.
 */

void Type::checkComplexTransition(Loc loc)
{
    Type *t = baseElemOf();
    while (t->ty == Tpointer || t->ty == Tarray)
        t = t->nextOf()->baseElemOf();

    if (t->isimaginary() || t->iscomplex())
    {
        Type *rt;
        switch (t->ty)
        {
            case Tcomplex32:
            case Timaginary32:
                rt = Type::tfloat32; break;
            case Tcomplex64:
            case Timaginary64:
                rt = Type::tfloat64; break;
            case Tcomplex80:
            case Timaginary80:
                rt = Type::tfloat80; break;
            default:
                assert(0);
        }
        if (t->iscomplex())
        {
            message(loc, "use of complex type `%s` is scheduled for deprecation, "
                    "use `std.complex.Complex!(%s)` instead", toChars(), rt->toChars());
        }
        else
        {
            message(loc, "use of imaginary type `%s` is scheduled for deprecation, "
                    "use `%s` instead\n", toChars(), rt->toChars());
        }
    }
}

/*******************************************
 * Compute number of elements for a (possibly multidimensional) static array,
 * or 1 for other types.
 * Params:
 *  loc = for error message
 * Returns:
 *  number of elements, uint.max on overflow
 */
unsigned Type::numberOfElems(const Loc &loc)
{
  //printf("Type::numberOfElems()\n");
  uinteger_t n = 1;
  Type *tb = this;
  while ((tb = tb->toBasetype())->ty == Tsarray)
    {
      bool overflow = false;
      n = mulu(n, ((TypeSArray *)tb)->dim->toUInteger(), overflow);
      if (overflow || n >= UINT32_MAX)
      {
          error(loc, "static array `%s` size overflowed to %llu", toChars(), (unsigned long long)n);
          return UINT32_MAX;
      }
      tb = ((TypeSArray *)tb)->next;
    }
  return (unsigned)n;
}

/****************************************
 * 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;
}

/* ============================= TypeError =========================== */

TypeError::TypeError()
        : Type(Terror)
{
}

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

d_uns64 TypeError::size(Loc) { return SIZE_INVALID; }
Expression *TypeError::getProperty(Loc, Identifier *, int) { return new ErrorExp(); }
Expression *TypeError::dotExp(Scope *, Expression *, Identifier *, int) { return new ErrorExp(); }
Expression *TypeError::defaultInit(Loc) { return new ErrorExp(); }
Expression *TypeError::defaultInitLiteral(Loc) { return new ErrorExp(); }

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

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

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);
}

int TypeNext::hasWild() const
{
    if (ty == Tfunction)
        return 0;
    if (ty == Tdelegate)
        return Type::hasWild();
    return mod & MODwild || (next && next->hasWild());
}


/*******************************
 * 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 && next->ty != Tfunction &&
        !next->isImmutable())
    {
        if (next->isShared())
        {
            if (next->isWild())
                t->next = next->sharedWildConstOf();
            else
                t->next = next->sharedConstOf();
        }
        else
        {
            if (next->isWild())
                t->next = next->wildConstOf();
            else
                t->next = next->constOf();
        }
    }
    //printf("TypeNext::makeConst() returns %p, %s\n", t, t->toChars());
    return t;
}

Type *TypeNext::makeImmutable()
{
    //printf("TypeNext::makeImmutable() %s\n", toChars());
    if (ito)
    {
        assert(ito->isImmutable());
        return ito;
    }
    TypeNext *t = (TypeNext *)Type::makeImmutable();
    if (ty != Tfunction && next->ty != Tfunction &&
        !next->isImmutable())
    {
        t->next = next->immutableOf();
    }
    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 && next->ty != Tfunction &&
        !next->isImmutable())
    {
        if (next->isWild())
        {
            if (next->isConst())
                t->next = next->sharedWildConstOf();
            else
                t->next = next->sharedWildOf();
        }
        else
        {
            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 && next->ty != Tfunction &&
        !next->isImmutable())
    {
        if (next->isWild())
            t->next = next->sharedWildConstOf();
        else
            t->next = next->sharedConstOf();
    }
    //printf("TypeNext::makeSharedConst() returns %p, %s\n", t, t->toChars());
    return t;
}

Type *TypeNext::makeWild()
{
    //printf("TypeNext::makeWild() %s\n", toChars());
    if (wto)
    {
        assert(wto->mod == MODwild);
        return wto;
    }
    TypeNext *t = (TypeNext *)Type::makeWild();
    if (ty != Tfunction && next->ty != Tfunction &&
        !next->isImmutable())
    {
        if (next->isShared())
        {
            if (next->isConst())
                t->next = next->sharedWildConstOf();
            else
                t->next = next->sharedWildOf();
        }
        else
        {
            if (next->isConst())
                t->next = next->wildConstOf();
            else
                t->next = next->wildOf();
        }
    }
    //printf("TypeNext::makeWild() returns %p, %s\n", t, t->toChars());
    return t;
}

Type *TypeNext::makeWildConst()
{
    //printf("TypeNext::makeWildConst() %s\n", toChars());
    if (wcto)
    {
        assert(wcto->mod == MODwildconst);
        return wcto;
    }
    TypeNext *t = (TypeNext *)Type::makeWildConst();
    if (ty != Tfunction && next->ty != Tfunction &&
        !next->isImmutable())
    {
        if (next->isShared())
            t->next = next->sharedWildConstOf();
        else
            t->next = next->wildConstOf();
    }
    //printf("TypeNext::makeWildConst() returns %p, %s\n", t, t->toChars());
    return t;
}

Type *TypeNext::makeSharedWild()
{
    //printf("TypeNext::makeSharedWild() %s\n", toChars());
    if (swto)
    {
        assert(swto->isSharedWild());
        return swto;
    }
    TypeNext *t = (TypeNext *)Type::makeSharedWild();
    if (ty != Tfunction && next->ty != Tfunction &&
        !next->isImmutable())
    {
        if (next->isConst())
            t->next = next->sharedWildConstOf();
        else
            t->next = next->sharedWildOf();
    }
    //printf("TypeNext::makeSharedWild() returns %p, %s\n", t, t->toChars());
    return t;
}

Type *TypeNext::makeSharedWildConst()
{
    //printf("TypeNext::makeSharedWildConst() %s\n", toChars());
    if (swcto)
    {
        assert(swcto->mod == (MODshared | MODwildconst));
        return swcto;
    }
    TypeNext *t = (TypeNext *)Type::makeSharedWildConst();
    if (ty != Tfunction && next->ty != Tfunction &&
        !next->isImmutable())
    {
        t->next = next->sharedWildConstOf();
    }
    //printf("TypeNext::makeSharedWildConst() returns %p, %s\n", t, t->toChars());
    return t;
}

Type *TypeNext::makeMutable()
{
    //printf("TypeNext::makeMutable() %p, %s\n", this, toChars());
    TypeNext *t = (TypeNext *)Type::makeMutable();
    if (ty == Tsarray)
    {
        t->next = next->mutableOf();
    }
    //printf("TypeNext::makeMutable() returns %p, %s\n", t, t->toChars());
    return t;
}

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

    if (!(ty == to->ty && MODimplicitConv(mod, to->mod)))
        return MATCHnomatch;

    Type *tn = to->nextOf();
    if (!(tn && next->ty == tn->ty))
        return MATCHnomatch;

    MATCH m;
    if (to->isConst())  // whole tail const conversion
    {   // Recursive shared level check
        m = next->constConv(tn);
        if (m == MATCHexact)
            m = MATCHconst;
    }
    else
    {   //printf("\tnext => %s, to->next => %s\n", next->toChars(), tn->toChars());
        m = next->equals(tn) ? MATCHconst : MATCHnomatch;
    }
    return m;
}

unsigned char TypeNext::deduceWild(Type *t, bool isRef)
{
    if (ty == Tfunction)
        return 0;

    unsigned char wm;

    Type *tn = t->nextOf();
    if (!isRef && (ty == Tarray || ty == Tpointer) && tn)
    {
        wm = next->deduceWild(tn, true);
        if (!wm)
            wm = Type::deduceWild(t, true);
    }
    else
    {
        wm = Type::deduceWild(t, isRef);
        if (!wm && tn)
            wm = next->deduceWild(tn, true);
    }

    return wm;
}


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

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

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

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

    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 Tint128:   d = Token::toChars(TOKint128);
                        flags |= TFLAGSintegral;
                        break;

        case Tuns128:   d = Token::toChars(TOKuns128);
                        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 Tchar:     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();
}

const char *TypeBasic::kind()
{
    return dstring;
}

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

d_uns64 TypeBasic::size(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 = Target::realsize; break;
        case Tcomplex32:
                        size = 8;               break;
        case Tcomplex64:
        case Tint128:
        case Tuns128:
                        size = 16;              break;
        case Tcomplex80:
                        size = Target::realsize * 2; break;

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

        case Tbool:     size = 1;               break;
        case Tchar:     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()
{
    return Target::alignsize(this);
}


Expression *TypeBasic::getProperty(Loc loc, Identifier *ident, int flag)
{
    Expression *e;
    dinteger_t ivalue;
    real_t fvalue;

    //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 = Target::FloatProperties::max;
            goto Lfvalue;
        case Tcomplex64:
        case Timaginary64:
        case Tfloat64:
            fvalue = Target::DoubleProperties::max;
            goto Lfvalue;
        case Tcomplex80:
        case Timaginary80:
        case Tfloat80:
            fvalue = Target::RealProperties::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:
        case Tcomplex64:
        case Timaginary64:
        case Tfloat64:
        case Tcomplex80:
        case Timaginary80:
        case Tfloat80:
            error(loc, "use .min_normal property instead of .min");
            return new ErrorExp();
        }
    }
    else if (ident == Id::min_normal)
    {
        switch (ty)
        {
        case Tcomplex32:
        case Timaginary32:
        case Tfloat32:
            fvalue = Target::FloatProperties::min_normal;
            goto Lfvalue;
        case Tcomplex64:
        case Timaginary64:
        case Tfloat64:
            fvalue = Target::DoubleProperties::min_normal;
            goto Lfvalue;
        case Tcomplex80:
        case Timaginary80:
        case Tfloat80:
            fvalue = Target::RealProperties::min_normal;
            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 = Target::RealProperties::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 = Target::RealProperties::infinity;
            goto Lfvalue;
        }
    }
    else if (ident == Id::dig)
    {
        switch (ty)
        {
        case Tcomplex32:
        case Timaginary32:
        case Tfloat32:
            ivalue = Target::FloatProperties::dig;
            goto Lint;
        case Tcomplex64:
        case Timaginary64:
        case Tfloat64:
            ivalue = Target::DoubleProperties::dig;
            goto Lint;
        case Tcomplex80:
        case Timaginary80:
        case Tfloat80:
            ivalue = Target::RealProperties::dig;
            goto Lint;
        }
    }
    else if (ident == Id::epsilon)
    {
        switch (ty)
        {
        case Tcomplex32:
        case Timaginary32:
        case Tfloat32:
            fvalue = Target::FloatProperties::epsilon;
            goto Lfvalue;
        case Tcomplex64:
        case Timaginary64:
        case Tfloat64:
            fvalue = Target::DoubleProperties::epsilon;
            goto Lfvalue;
        case Tcomplex80:
        case Timaginary80:
        case Tfloat80:
            fvalue = Target::RealProperties::epsilon;
            goto Lfvalue;
        }
    }
    else if (ident == Id::mant_dig)
    {
        switch (ty)
        {
        case Tcomplex32:
        case Timaginary32:
        case Tfloat32:
            ivalue = Target::FloatProperties::mant_dig;
            goto Lint;
        case Tcomplex64:
        case Timaginary64:
        case Tfloat64:
            ivalue = Target::DoubleProperties::mant_dig;
            goto Lint;
        case Tcomplex80:
        case Timaginary80:
        case Tfloat80:
            ivalue = Target::RealProperties::mant_dig;
            goto Lint;
        }
    }
    else if (ident == Id::max_10_exp)
    {
        switch (ty)
        {
        case Tcomplex32:
        case Timaginary32:
        case Tfloat32:
            ivalue = Target::FloatProperties::max_10_exp;
            goto Lint;
        case Tcomplex64:
        case Timaginary64:
        case Tfloat64:
            ivalue = Target::DoubleProperties::max_10_exp;
            goto Lint;
        case Tcomplex80:
        case Timaginary80:
        case Tfloat80:
            ivalue = Target::RealProperties::max_10_exp;
            goto Lint;
        }
    }
    else if (ident == Id::max_exp)
    {
        switch (ty)
        {
        case Tcomplex32:
        case Timaginary32:
        case Tfloat32:
            ivalue = Target::FloatProperties::max_exp;
            goto Lint;
        case Tcomplex64:
        case Timaginary64:
        case Tfloat64:
            ivalue = Target::DoubleProperties::max_exp;
            goto Lint;
        case Tcomplex80:
        case Timaginary80:
        case Tfloat80:
            ivalue = Target::RealProperties::max_exp;
            goto Lint;
        }
    }
    else if (ident == Id::min_10_exp)
    {
        switch (ty)
        {
        case Tcomplex32:
        case Timaginary32:
        case Tfloat32:
            ivalue = Target::FloatProperties::min_10_exp;
            goto Lint;
        case Tcomplex64:
        case Timaginary64:
        case Tfloat64:
            ivalue = Target::DoubleProperties::min_10_exp;
            goto Lint;
        case Tcomplex80:
        case Timaginary80:
        case Tfloat80:
            ivalue = Target::RealProperties::min_10_exp;
            goto Lint;
        }
    }
    else if (ident == Id::min_exp)
    {
        switch (ty)
        {
        case Tcomplex32:
        case Timaginary32:
        case Tfloat32:
            ivalue = Target::FloatProperties::min_exp;
            goto Lint;
        case Tcomplex64:
        case Timaginary64:
        case Tfloat64:
            ivalue = Target::DoubleProperties::min_exp;
            goto Lint;
        case Tcomplex80:
        case Timaginary80:
        case Tfloat80:
            ivalue = Target::RealProperties::min_exp;
            goto Lint;
        }
    }

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

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

Lfvalue:
    if (isreal() || isimaginary())
        e = new RealExp(loc, fvalue, this);
    else
    {
        complex_t cvalue = complex_t(fvalue, fvalue);
        //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, int flag)
{
    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(e->loc, CTFloat::zero, t);
                break;

            default:
                e = Type::getProperty(e->loc, ident, flag);
                break;
        }
    }
    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(e->loc, CTFloat::zero, this);
                break;

            default:
                e = Type::getProperty(e->loc, ident, flag);
                break;
        }
    }
    else
    {
        return Type::dotExp(sc, e, ident, flag);
    }
    if (!(flag & 1) || e)
        e = ::semantic(e, sc);
    return e;
}

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

    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:
            return new RealExp(loc, Target::RealProperties::snan, this);

        case Tcomplex32:
        case Tcomplex64:
        case Tcomplex80:
        {   // Can't use fvalue + I*fvalue (the im part becomes a quiet NaN).
            complex_t cvalue = complex_t(Target::RealProperties::snan, Target::RealProperties::snan);
            return new ComplexExp(loc, cvalue, this);
        }

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

bool TypeBasic::isZeroInit(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 false;       // no
        default:
            return true;        // yes
    }
}

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

bool TypeBasic::isfloating()
{
    return (flags & TFLAGSfloating) != 0;
}

bool TypeBasic::isreal()
{
    return (flags & TFLAGSreal) != 0;
}

bool TypeBasic::isimaginary()
{
    return (flags & TFLAGSimaginary) != 0;
}

bool TypeBasic::iscomplex()
{
    return (flags & TFLAGScomplex) != 0;
}

bool TypeBasic::isunsigned()
{
    return (flags & TFLAGSunsigned) != 0;
}

bool TypeBasic::isscalar()
{
    return (flags & (TFLAGSintegral | TFLAGSfloating)) != 0;
}

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

    if (ty == to->ty)
    {
        if (mod == to->mod)
            return MATCHexact;
        else if (MODimplicitConv(mod, to->mod))
            return MATCHconst;
        else if (!((mod ^ to->mod) & MODshared))    // for wild matching
            return MATCHconst;
        else
            return MATCHconvert;
    }

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

    TypeBasic *tob;
    if (to->ty == Tvector && to->deco)
    {
        TypeVector *tv = (TypeVector *)to;
        tob = tv->elementType();
    }
    else if (to->ty == Tenum)
    {
        EnumDeclaration *ed = ((TypeEnum *)to)->sym;
        if (ed->isSpecial())
        {
            /* Special enums that allow implicit conversions to them.  */
            tob = to->toBasetype()->isTypeBasic();
            if (tob)
                return implicitConvTo(tob);
        }
        else
            return MATCHnomatch;
    }
    else
        tob = to->isTypeBasic();
    if (!tob)
        return MATCHnomatch;

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

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

            /* 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;*/
        }
    }
    else if (flags & TFLAGSfloating)
    {
        // Disallow implicit conversion of floating point to integer
        if (tob->flags & TFLAGSintegral)
            return MATCHnomatch;

        assert(tob->flags & TFLAGSfloating || to->ty == Tvector);

        // 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;
}

/* ============================= TypeVector =========================== */

/* The basetype must be one of:
 *   byte[16],ubyte[16],short[8],ushort[8],int[4],uint[4],long[2],ulong[2],float[4],double[2]
 * For AVX:
 *   byte[32],ubyte[32],short[16],ushort[16],int[8],uint[8],long[4],ulong[4],float[8],double[4]
 */
TypeVector::TypeVector(Type *basetype)
        : Type(Tvector)
{
    this->basetype = basetype;
}

TypeVector *TypeVector::create(Loc, Type *basetype)
{
    return new TypeVector(basetype);
}

const char *TypeVector::kind()
{
    return "vector";
}

Type *TypeVector::syntaxCopy()
{
    return new TypeVector(basetype->syntaxCopy());
}

Type *TypeVector::semantic(Loc loc, Scope *sc)
{
    unsigned int errors = global.errors;
    basetype = basetype->semantic(loc, sc);
    if (errors != global.errors)
        return terror;
    basetype = basetype->toBasetype()->mutableOf();
    if (basetype->ty != Tsarray)
    {
        error(loc, "T in __vector(T) must be a static array, not %s", basetype->toChars());
        return terror;
    }
    TypeSArray *t = (TypeSArray *)basetype;
    int sz = (int)t->size(loc);
    switch (Target::isVectorTypeSupported(sz, t->nextOf()))
    {
    case 0: // valid
        break;
    case 1: // no support at all
        error(loc, "SIMD vector types not supported on this platform");
        return terror;
    case 2: // invalid size
        error(loc, "%d byte vector type %s is not supported on this platform", sz, toChars());
        return terror;
    case 3: // invalid base type
        error(loc, "vector type %s is not supported on this platform", toChars());
        return terror;
    default:
        assert(0);
    }
    return merge();
}

TypeBasic *TypeVector::elementType()
{
    assert(basetype->ty == Tsarray);
    TypeSArray *t = (TypeSArray *)basetype;
    TypeBasic *tb = t->nextOf()->isTypeBasic();
    assert(tb);
    return tb;
}

bool TypeVector::isBoolean()
{
    return false;
}

d_uns64 TypeVector::size(Loc)
{
    return basetype->size();
}

unsigned TypeVector::alignsize()
{
    return (unsigned)basetype->size();
}

Expression *TypeVector::getProperty(Loc loc, Identifier *ident, int flag)
{
    return Type::getProperty(loc, ident, flag);
}

Expression *TypeVector::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    if (ident == Id::ptr && e->op == TOKcall)
    {
        /* The trouble with TOKcall is the return ABI for float[4] is different from
         * __vector(float[4]), and a type paint won't do.
         */
        e = new AddrExp(e->loc, e);
        e = ::semantic(e, sc);
        e = e->castTo(sc, basetype->nextOf()->pointerTo());
        return e;
    }
    if (ident == Id::array)
    {
        //e = e->castTo(sc, basetype);
        // Keep lvalue-ness
        e = new VectorArrayExp(e->loc, e);
        e = ::semantic(e, sc);
        return e;
    }
    if (ident == Id::_init || ident == Id::offsetof || ident == Id::stringof || ident == Id::__xalignof)
    {
        // init should return a new VectorExp (Bugzilla 12776)
        // offsetof does not work on a cast expression, so use e directly
        // stringof should not add a cast to the output
        return Type::dotExp(sc, e, ident, flag);
    }
    return basetype->dotExp(sc, e->castTo(sc, basetype), ident, flag);
}

Expression *TypeVector::defaultInit(Loc loc)
{
    //printf("TypeVector::defaultInit()\n");
    assert(basetype->ty == Tsarray);
    Expression *e = basetype->defaultInit(loc);
    VectorExp *ve = new VectorExp(loc, e, this);
    ve->type = this;
    ve->dim = (int)(basetype->size(loc) / elementType()->size(loc));
    return ve;
}

Expression *TypeVector::defaultInitLiteral(Loc loc)
{
    //printf("TypeVector::defaultInitLiteral()\n");
    assert(basetype->ty == Tsarray);
    Expression *e = basetype->defaultInitLiteral(loc);
    VectorExp *ve = new VectorExp(loc, e, this);
    ve->type = this;
    ve->dim = (int)(basetype->size(loc) / elementType()->size(loc));
    return ve;
}

bool TypeVector::isZeroInit(Loc loc)
{
    return basetype->isZeroInit(loc);
}

bool TypeVector::isintegral()
{
    //printf("TypeVector::isintegral('%s') x%x\n", toChars(), flags);
    return basetype->nextOf()->isintegral();
}

bool TypeVector::isfloating()
{
    return basetype->nextOf()->isfloating();
}

bool TypeVector::isunsigned()
{
    return basetype->nextOf()->isunsigned();
}

bool TypeVector::isscalar()
{
    return basetype->nextOf()->isscalar();
}

MATCH TypeVector::implicitConvTo(Type *to)
{
    //printf("TypeVector::implicitConvTo(%s) from %s\n", to->toChars(), toChars());
    if (this == to)
        return MATCHexact;
#ifdef IN_GCC
    if (to->ty == Tvector)
    {
        TypeVector *tv = (TypeVector *)to;
        assert(basetype->ty == Tsarray && tv->basetype->ty == Tsarray);

        // Can't convert to a vector which has different size.
        if (basetype->size() != tv->basetype->size())
            return MATCHnomatch;

        // Allow conversion to void[]
        if (tv->basetype->nextOf()->ty == Tvoid)
            return MATCHconvert;

        // Otherwise implicitly convertible only if basetypes are.
        return basetype->implicitConvTo(tv->basetype);
    }
#else
    if (ty == to->ty)
        return MATCHconvert;
#endif
    return MATCHnomatch;
}

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

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

Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    e = Type::dotExp(sc, e, ident, flag);

    if (!(flag & 1) || e)
        e = ::semantic(e, sc);
    return e;
}


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

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

const char *TypeSArray::kind()
{
    return "sarray";
}

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)
{
    //printf("TypeSArray::size()\n");
    uinteger_t n = numberOfElems(loc);
    uinteger_t elemsize = baseElemOf()->size();
    bool overflow = false;
    uinteger_t sz = mulu(n, elemsize, overflow);
    if (overflow || sz >= UINT32_MAX)
    {
        if (elemsize != SIZE_INVALID && n != UINT32_MAX)
            error(loc, "static array `%s` size overflowed to %lld", toChars(), (long long)sz);
        return SIZE_INVALID;
    }
    return sz;
}

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);

        sc = sc->startCTFE();
        exp = ::semantic(exp, sc);
        sc = sc->endCTFE();

        sc->pop();
    }
    else
    {
        sc = sc->startCTFE();
        exp = ::semantic(exp, sc);
        sc = sc->endCTFE();
    }

    return exp;
}

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

    sc = sc->startCTFE();
    exp = ::semantic(exp, sc);
    sc = sc->endCTFE();

    sc->pop();
    return exp;
}

void TypeSArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
{
    //printf("TypeSArray::resolve() %s\n", toChars());
    next->resolve(loc, sc, pe, pt, ps, intypeid);
    //printf("s = %p, e = %p, t = %p\n", *ps, *pe, *pt);
    if (*pe)
    {
        // It's really an index expression
        if (Dsymbol *s = getDsymbol(*pe))
            *pe = new DsymbolExp(loc, s);
        *pe = new ArrayExp(loc, *pe, dim);
    }
    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);
            sc = sc->startCTFE();
            dim = ::semantic(dim, sc);
            sc = sc->endCTFE();
            sc = sc->pop();

            dim = dim->ctfeInterpret();
            uinteger_t d = dim->toUInteger();

            if (d >= td->objects->dim)
            {
                error(loc, "tuple index %llu exceeds length %u", d, td->objects->dim);
                *ps = NULL;
                *pt = Type::terror;
                return;
            }
            RootObject *o = (*td->objects)[(size_t)d];
            if (o->dyncast() == DYNCAST_DSYMBOL)
            {
                *ps = (Dsymbol *)o;
                return;
            }
            if (o->dyncast() == DYNCAST_EXPRESSION)
            {
                Expression *e = (Expression *)o;
                if (e->op == TOKdsymbol)
                {
                    *ps = ((DsymbolExp *)e)->s;
                    *pe = NULL;
                }
                else
                {
                    *ps = NULL;
                    *pe = e;
                }
                return;
            }
            if (o->dyncast() == DYNCAST_TYPE)
            {
                *ps = NULL;
                *pt = ((Type *)o)->addMod(this->mod);
                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)[0] = o;

            TupleDeclaration *tds = new TupleDeclaration(loc, td->ident, objects);
            *ps = tds;
        }
        else
            goto Ldefault;
    }
    else
    {
        if ((*pt)->ty != Terror)
            next = *pt;     // prevent re-running semantic() on 'next'
     Ldefault:
        Type::resolve(loc, sc, pe, pt, ps, intypeid);
    }
}

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->ctfeInterpret();
        uinteger_t d = dim->toUInteger();

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

    Type *tn = next->semantic(loc, sc);
    if (tn->ty == Terror)
        return terror;

    Type *tbn = tn->toBasetype();

    if (dim)
    {
        unsigned int errors = global.errors;
        dim = semanticLength(sc, tbn, dim);
        if (errors != global.errors)
            goto Lerror;

        dim = dim->optimize(WANTvalue);
        dim = dim->ctfeInterpret();
        if (dim->op == TOKerror)
            goto Lerror;
        errors = global.errors;
        dinteger_t d1 = dim->toInteger();
        if (errors != global.errors)
            goto Lerror;

        dim = dim->implicitCastTo(sc, tsize_t);
        dim = dim->optimize(WANTvalue);
        if (dim->op == TOKerror)
            goto Lerror;
        errors = global.errors;
        dinteger_t d2 = dim->toInteger();
        if (errors != global.errors)
            goto Lerror;

        if (dim->op == TOKerror)
            goto Lerror;

        if (d1 != d2)
        {
        Loverflow:
            error(loc, "%s size %llu * %llu exceeds 0x%llx size limit for static array",
                toChars(), (unsigned long long)tbn->size(loc), (unsigned long long)d1, Target::maxStaticDataSize);
            goto Lerror;
        }

        Type *tbx = tbn->baseElemOf();
        if ((tbx->ty == Tstruct && !((TypeStruct *)tbx)->sym->members) ||
            (tbx->ty == Tenum && !((TypeEnum *)tbx)->sym->members))
        {
            /* To avoid meaningless error message, skip the total size limit check
             * when the bottom of element type is opaque.
             */
        }
        else if (tbn->isTypeBasic() ||
                 tbn->ty == Tpointer ||
                 tbn->ty == Tarray ||
                 tbn->ty == Tsarray ||
                 tbn->ty == Taarray ||
                 (tbn->ty == Tstruct && (((TypeStruct *)tbn)->sym->sizeok == SIZEOKdone)) ||
                 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.
             */
            bool overflow = false;
            if (mulu(tbn->size(loc), d2, overflow) >= Target::maxStaticDataSize || overflow)
                goto Loverflow;
        }
    }
    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 %llu exceeds %u", d, tt->arguments->dim);
                goto Lerror;
            }
            Type *telem = (*tt->arguments)[(size_t)d]->type;
            return telem->addMod(this->mod);
        }
        case Tfunction:
        case Tnone:
            error(loc, "can't have array of %s", tbn->toChars());
            goto Lerror;
        default:
            break;
    }
    if (tbn->isscope())
    {   error(loc, "cannot have array of scope %s", tbn->toChars());
        goto Lerror;
    }

    /* Ensure things like const(immutable(T)[3]) become immutable(T[3])
     * and const(T)[3] become const(T[3])
     */
    next = tn;
    transitive();
    t = addMod(tn->mod);

    return t->merge();

Lerror:
    return Type::terror;
}

Expression *TypeSArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    if (ident == Id::length)
    {
        Loc oldLoc = e->loc;
        e = dim->copy();
        e->loc = oldLoc;
    }
    else if (ident == Id::ptr)
    {
        if (e->op == TOKtype)
        {
            e->error("%s is not an expression", e->toChars());
            return new ErrorExp();
        }
        else if (!(flag & 2) && sc->func && !sc->intypeof && sc->func->setUnsafe())
        {
            e->deprecation("%s.ptr cannot be used in @safe code, use &%s[0] instead", e->toChars(), e->toChars());
            // return new ErrorExp();
        }
        e = e->castTo(sc, e->type->nextOf()->pointerTo());
    }
    else
    {
        e = TypeArray::dotExp(sc, e, ident, flag);
    }
    if (!(flag & 1) || e)
        e = ::semantic(e, sc);
    return e;
}

structalign_t TypeSArray::alignment()
{
    return next->alignment();
}

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

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());

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

        if (!MODimplicitConv(next->mod, ta->next->mod))
            return MATCHnomatch;

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

        MATCH m = next->constConv(ta->next);
        if (m > MATCHnomatch)
        {
            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 (next->ty == Tvoid)
        return tuns8->defaultInit(loc);
    else
        return next->defaultInit(loc);
}

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

bool TypeSArray::needsDestruction()
{
    return next->needsDestruction();
}

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

bool TypeSArray::needsNested()
{
    return next->needsNested();
}

Expression *TypeSArray::defaultInitLiteral(Loc loc)
{
    size_t d = (size_t)dim->toInteger();
    Expression *elementinit;
    if (next->ty == Tvoid)
        elementinit = tuns8->defaultInitLiteral(loc);
    else
        elementinit = next->defaultInitLiteral(loc);
    Expressions *elements = new Expressions();
    elements->setDim(d);
    for (size_t i = 0; i < d; i++)
        (*elements)[i] = NULL;
    ArrayLiteralExp *ae = new ArrayLiteralExp(Loc(), this, elementinit, elements);
    return ae;
}

bool TypeSArray::hasPointers()
{
    /* Don't want to do this, because:
     *    struct S { T* array[0]; }
     * may be a variable length struct.
     */
    //if (dim->toInteger() == 0)
    //    return false;

    if (next->ty == Tvoid)
    {
        // Arrays of void contain arbitrary data, which may include pointers
        return true;
    }
    else
        return next->hasPointers();
}

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

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

const char *TypeDArray::kind()
{
    return "darray";
}

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)
{
    //printf("TypeDArray::size()\n");
    return Target::ptrsize * 2;
}

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

Type *TypeDArray::semantic(Loc loc, Scope *sc)
{
    Type *tn = next->semantic(loc,sc);
    Type *tbn = tn->toBasetype();
    switch (tbn->ty)
    {
        case Ttuple:
            return tbn;
        case Tfunction:
        case Tnone:
            error(loc, "can't have array of %s", tbn->toChars());
            return Type::terror;
        case Terror:
            return Type::terror;
        default:
            break;
    }
    if (tn->isscope())
    {   error(loc, "cannot have array of scope %s", tn->toChars());
        return Type::terror;
    }
    next = tn;
    transitive();
    return merge();
}

void TypeDArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
{
    //printf("TypeDArray::resolve() %s\n", toChars());
    next->resolve(loc, sc, pe, pt, ps, intypeid);
    //printf("s = %p, e = %p, t = %p\n", *ps, *pe, *pt);
    if (*pe)
    {
        // It's really a slice expression
        if (Dsymbol *s = getDsymbol(*pe))
            *pe = new DsymbolExp(loc, s);
        *pe = new ArrayExp(loc, *pe);
    }
    else if (*ps)
    {
        TupleDeclaration *td = (*ps)->isTupleDeclaration();
        if (td)
            ;   // keep *ps
        else
            goto Ldefault;
    }
    else
    {
        if ((*pt)->ty != Terror)
            next = *pt;     // prevent re-running semantic() on 'next'
     Ldefault:
        Type::resolve(loc, sc, pe, pt, ps, intypeid);
    }
}

Expression *TypeDArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    if (e->op == TOKtype &&
        (ident == Id::length || ident == Id::ptr))
    {
        e->error("%s is not an expression", e->toChars());
        return new ErrorExp();
    }
    if (ident == Id::length)
    {
        if (e->op == TOKstring)
        {
            StringExp *se = (StringExp *)e;
            return new IntegerExp(se->loc, se->len, Type::tsize_t);
        }
        if (e->op == TOKnull)
            return new IntegerExp(e->loc, 0, Type::tsize_t);
        if (checkNonAssignmentArrayOp(e))
            return new ErrorExp();
        e = new ArrayLengthExp(e->loc, e);
        e->type = Type::tsize_t;
        return e;
    }
    else if (ident == Id::ptr)
    {
        if (!(flag & 2) && sc->func && !sc->intypeof && sc->func->setUnsafe())
        {
            e->deprecation("%s.ptr cannot be used in @safe code, use &%s[0] instead", e->toChars(), e->toChars());
            // return new ErrorExp();
        }
        e = e->castTo(sc, next->pointerTo());
        return e;
    }
    else
    {
        e = TypeArray::dotExp(sc, e, ident, flag);
    }
    return e;
}

bool 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;

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

        if (!MODimplicitConv(next->mod, ta->next->mod))
            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;
        }
    }
    return Type::implicitConvTo(to);
}

Expression *TypeDArray::defaultInit(Loc loc)
{
    return new NullExp(loc, this);
}

bool TypeDArray::isZeroInit(Loc)
{
    return true;
}

bool TypeDArray::isBoolean()
{
    return true;
}

bool TypeDArray::hasPointers()
{
    return true;
}


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

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

TypeAArray *TypeAArray::create(Type *t, Type *index)
{
    return new TypeAArray(t, index);
}

const char *TypeAArray::kind()
{
    return "aarray";
}

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)
{
    return Target::ptrsize;
}

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

    this->loc = loc;
    this->sc = sc;
    if (sc)
        sc->setNoFree();

    // 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 ||
        index->ty == Ttypeof || index->ty == Treturn)
    {
        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->semantic(loc, sc);
        }
        else if (t)
            index = t->semantic(loc, sc);
        else
        {
            index->error(loc, "index is not a type or an expression");
            return Type::terror;
        }
    }
    else
        index = index->semantic(loc,sc);
    index = index->merge2();

    if (index->nextOf() && !index->nextOf()->isImmutable())
    {
        index = index->constOf()->mutableOf();
    }

    switch (index->toBasetype()->ty)
    {
        case Tfunction:
        case Tvoid:
        case Tnone:
        case Ttuple:
            error(loc, "can't have associative array key of %s", index->toBasetype()->toChars());
            /* fall through */
        case Terror:
            return Type::terror;
        default:
            break;
    }
    Type *tbase = index->baseElemOf();
    while (tbase->ty == Tarray)
        tbase = tbase->nextOf()->baseElemOf();
    if (tbase->ty == Tstruct)
    {
        /* AA's need typeid(index).equals() and getHash(). Issue error if not correctly set up.
         */
        StructDeclaration *sd = ((TypeStruct *)tbase)->sym;
        if (sd->semanticRun < PASSsemanticdone)
            sd->semantic(NULL);

        // duplicate a part of StructDeclaration::semanticTypeInfoMembers
        //printf("AA = %s, key: xeq = %p, xerreq = %p xhash = %p\n", toChars(), sd->xeq, sd->xerreq, sd->xhash);
        if (sd->xeq &&
            sd->xeq->_scope &&
            sd->xeq->semanticRun < PASSsemantic3done)
        {
            unsigned errors = global.startGagging();
            sd->xeq->semantic3(sd->xeq->_scope);
            if (global.endGagging(errors))
                sd->xeq = sd->xerreq;
        }

        const char *s = (index->toBasetype()->ty != Tstruct) ? "bottom of " : "";
        if (!sd->xeq)
        {
            // If sd->xhash != NULL:
            //   sd or its fields have user-defined toHash.
            //   AA assumes that its result is consistent with bitwise equality.
            // else:
            //   bitwise equality & hashing
        }
        else if (sd->xeq == sd->xerreq)
        {
            if (search_function(sd, Id::eq))
            {
                error(loc, "%sAA key type %s does not have 'bool opEquals(ref const %s) const'",
                        s, sd->toChars(), sd->toChars());
            }
            else
            {
                error(loc, "%sAA key type %s does not support const equality",
                        s, sd->toChars());
            }
            return Type::terror;
        }
        else if (!sd->xhash)
        {
            if (search_function(sd, Id::eq))
            {
                error(loc, "%sAA key type %s should have 'size_t toHash() const nothrow @safe' if opEquals defined",
                        s, sd->toChars());
            }
            else
            {
                error(loc, "%sAA key type %s supports const equality but doesn't support const hashing",
                        s, sd->toChars());
            }
            return Type::terror;
        }
        else
        {
            // defined equality & hashing
            assert(sd->xeq && sd->xhash);

            /* xeq and xhash may be implicitly defined by compiler. For example:
             *   struct S { int[] arr; }
             * With 'arr' field equality and hashing, compiler will implicitly
             * generate functions for xopEquals and xtoHash in TypeInfo_Struct.
             */
        }
    }
    else if (tbase->ty == Tclass && !((TypeClass *)tbase)->sym->isInterfaceDeclaration())
    {
        ClassDeclaration *cd = ((TypeClass *)tbase)->sym;
        if (cd->semanticRun < PASSsemanticdone)
            cd->semantic(NULL);

        if (!ClassDeclaration::object)
        {
            error(Loc(), "missing or corrupt object.d");
            fatal();
        }

        static FuncDeclaration *feq   = NULL;
        static FuncDeclaration *fcmp  = NULL;
        static FuncDeclaration *fhash = NULL;
        if (!feq)   feq   = search_function(ClassDeclaration::object, Id::eq)->isFuncDeclaration();
        if (!fcmp)  fcmp  = search_function(ClassDeclaration::object, Id::cmp)->isFuncDeclaration();
        if (!fhash) fhash = search_function(ClassDeclaration::object, Id::tohash)->isFuncDeclaration();
        assert(fcmp && feq && fhash);

        if (feq->vtblIndex < (int)cd->vtbl.dim && cd->vtbl[feq ->vtblIndex] == feq)
        {
            if (fcmp->vtblIndex < (int)cd->vtbl.dim && cd->vtbl[fcmp->vtblIndex] != fcmp)
            {
                const char *s = (index->toBasetype()->ty != Tclass) ? "bottom of " : "";
                error(loc, "%sAA key type %s now requires equality rather than comparison",
                    s, cd->toChars());
                errorSupplemental(loc, "Please override Object.opEquals and toHash.");
            }
        }
    }
    next = next->semantic(loc,sc)->merge2();
    transitive();

    switch (next->toBasetype()->ty)
    {
        case Tfunction:
        case Tvoid:
        case Tnone:
        case Ttuple:
            error(loc, "can't have associative array of %s", next->toChars());
            /* fall through */
        case Terror:
            return Type::terror;
    }
    if (next->isscope())
    {   error(loc, "cannot have array of scope %s", next->toChars());
        return Type::terror;
    }
    return merge();
}

void TypeAArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
{
    //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, intypeid);
        if (e)
        {
            // It was an expression -
            // Rewrite as a static array
            TypeSArray *tsa = new TypeSArray(next, e);
            tsa->mod = this->mod;   // just copy mod field so tsa's semantic is not yet done
            return tsa->resolve(loc, sc, pe, pt, ps, intypeid);
        }
        else if (t)
            index = t;
        else
            index->error(loc, "index is not a type or an expression");
    }
    Type::resolve(loc, sc, pe, pt, ps, intypeid);
}


Expression *TypeAArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    if (ident == Id::length)
    {
        static FuncDeclaration *fd_aaLen = NULL;
        if (fd_aaLen == NULL)
        {
            Parameters *fparams = new Parameters();
            fparams->push(new Parameter(STCin, this, NULL, NULL));
            fd_aaLen = FuncDeclaration::genCfunc(fparams, Type::tsize_t, Id::aaLen);
            TypeFunction *tf = fd_aaLen->type->toTypeFunction();
            tf->purity = PUREconst;
            tf->isnothrow = true;
            tf->isnogc = false;
        }
        Expression *ev = new VarExp(e->loc, fd_aaLen, false);
        e = new CallExp(e->loc, ev, e);
        e->type = fd_aaLen->type->toTypeFunction()->next;
    }
    else
        e = Type::dotExp(sc, e, ident, flag);
    return e;
}

Expression *TypeAArray::defaultInit(Loc loc)
{
    return new NullExp(loc, this);
}

bool TypeAArray::isZeroInit(Loc)
{
    return true;
}

bool TypeAArray::isBoolean()
{
    return true;
}

bool 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 (!MODimplicitConv(next->mod, ta->next->mod))
            return MATCHnomatch;        // not const-compatible

        if (!MODimplicitConv(index->mod, ta->index->mod))
            return MATCHnomatch;        // not const-compatible

        MATCH m = next->constConv(ta->next);
        MATCH mi = index->constConv(ta->index);
        if (m > MATCHnomatch && mi > MATCHnomatch)
        {
            return MODimplicitConv(mod, to->mod) ? MATCHconst : MATCHnomatch;
        }
    }
    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;
    }
    return Type::constConv(to);
}

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

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

TypePointer *TypePointer::create(Type *t)
{
    return new TypePointer(t);
}

const char *TypePointer::kind()
{
    return "pointer";
}

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() %s\n", toChars());
    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());
            /* fall through */
        case Terror:
            return Type::terror;
        default:
            break;
    }
    if (n != next)
    {
        deco = NULL;
    }
    next = n;
    if (next->ty != Tfunction)
    {   transitive();
        return merge();
    }
    deco = merge()->deco;
    /* Don't return merge(), because arg identifiers and default args
     * can be different
     * even though the types match
     */
    return this;
}


d_uns64 TypePointer::size(Loc)
{
    return Target::ptrsize;
}

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

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

                if (next->covariant(tp->next) == 1)
                {
                    Type *tret = this->next->nextOf();
                    Type *toret = tp->next->nextOf();
                    if (tret->ty == Tclass && toret->ty == Tclass)
                    {
                        /* Bugzilla 10219: Check covariant interface return with offset tweaking.
                         * interface I {}
                         * class C : Object, I {}
                         * I function() dg = function C() {}    // should be error
                         */
                        int offset = 0;
                        if (toret->isBaseOf(tret, &offset) && offset != 0)
                            return MATCHnomatch;
                    }
                    return MATCHconvert;
                }
            }
            else if (tp->next->ty == Tvoid)
            {
                // Allow conversions to void*
                return MATCHconvert;
            }
        }
        return MATCHnomatch;
    }
    else if (to->ty == Tpointer)
    {
        TypePointer *tp = (TypePointer *)to;
        assert(tp->next);

        if (!MODimplicitConv(next->mod, tp->next->mod))
            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;
        }
    }
    return MATCHnomatch;
}

MATCH TypePointer::constConv(Type *to)
{
    if (next->ty == Tfunction)
    {
        if (to->nextOf() && next->equals(((TypeNext *)to)->next))
            return Type::constConv(to);
        else
            return MATCHnomatch;
    }
    return TypeNext::constConv(to);
}

bool TypePointer::isscalar()
{
    return true;
}

Expression *TypePointer::defaultInit(Loc loc)
{
    return new NullExp(loc, this);
}

bool TypePointer::isZeroInit(Loc)
{
    return true;
}

bool TypePointer::hasPointers()
{
    return true;
}


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

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

const char *TypeReference::kind()
{
    return "reference";
}

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)
{
    return Target::ptrsize;
}

Expression *TypeReference::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    // References just forward things along
    return next->dotExp(sc, e, ident, flag);
}

Expression *TypeReference::defaultInit(Loc loc)
{
    return new NullExp(loc, this);
}

bool TypeReference::isZeroInit(Loc)
{
    return true;
}


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

TypeFunction::TypeFunction(Parameters *parameters, Type *treturn, int varargs, LINK linkage, StorageClass stc)
    : 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->isnogc = false;
    this->purity = PUREimpure;
    this->isproperty = false;
    this->isref = false;
    this->isreturn = false;
    this->isscope = false;
    this->isscopeinferred = false;
    this->iswild = 0;
    this->fargs = NULL;

    if (stc & STCpure)
        this->purity = PUREfwdref;
    if (stc & STCnothrow)
        this->isnothrow = true;
    if (stc & STCnogc)
        this->isnogc = true;
    if (stc & STCproperty)
        this->isproperty = true;

    if (stc & STCref)
        this->isref = true;
    if (stc & STCreturn)
        this->isreturn = true;
    if (stc & STCscope)
        this->isscope = true;
    if (stc & STCscopeinferred)
        this->isscopeinferred = true;

    this->trust = TRUSTdefault;
    if (stc & STCsafe)
        this->trust = TRUSTsafe;
    if (stc & STCsystem)
        this->trust = TRUSTsystem;
    if (stc & STCtrusted)
        this->trust = TRUSTtrusted;
}

TypeFunction *TypeFunction::create(Parameters *parameters, Type *treturn, int varargs, LINK linkage, StorageClass stc)
{
    return new TypeFunction(parameters, treturn, varargs, linkage, stc);
}

const char *TypeFunction::kind()
{
    return "function";
}

Type *TypeFunction::syntaxCopy()
{
    Type *treturn = next ? next->syntaxCopy() : NULL;
    Parameters *params = Parameter::arraySyntaxCopy(parameters);
    TypeFunction *t = new TypeFunction(params, treturn, varargs, linkage);
    t->mod = mod;
    t->isnothrow = isnothrow;
    t->isnogc = isnogc;
    t->purity = purity;
    t->isproperty = isproperty;
    t->isref = isref;
    t->isreturn = isreturn;
    t->isscope = isscope;
    t->isscopeinferred = isscopeinferred;
    t->iswild = iswild;
    t->trust = trust;
    t->fargs = fargs;
    return t;
}

/*******************************
 * Covariant means that 'this' can substitute for 't',
 * i.e. a pure function is a match for an impure type.
 * Params:
 *      t = type 'this' is covariant with
 *      pstc = if not null, store STCxxxx which would make it covariant
 *      fix17349 = enable fix https://issues.dlang.org/show_bug.cgi?id=17349
 * 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
 *      *pstc   STCxxxx which would make it covariant
 */

int Type::covariant(Type *t, StorageClass *pstc, bool fix17349)
{
    if (pstc)
        *pstc = 0;
    StorageClass stc = 0;

    bool notcovariant = false;

    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 = Parameter::dim(t1->parameters);
        if (dim != Parameter::dim(t2->parameters))
            goto Ldistinct;

        for (size_t i = 0; i < dim; i++)
        {
            Parameter *fparam1 = Parameter::getNth(t1->parameters, i);
            Parameter *fparam2 = Parameter::getNth(t2->parameters, i);

            if (!fparam1->type->equals(fparam2->type))
            {
                if (!fix17349)
                    goto Ldistinct;
                Type *tp1 = fparam1->type;
                Type *tp2 = fparam2->type;
                if (tp1->ty == tp2->ty)
                {
                    if (tp1->ty == Tclass)
                    {
                        if (((TypeClass *)tp1)->sym == ((TypeClass *)tp2)->sym && MODimplicitConv(tp2->mod, tp1->mod))
                            goto Lcov;
                    }
                    else if (tp1->ty == Tstruct)
                    {
                        if (((TypeStruct *)tp1)->sym == ((TypeStruct *)tp2)->sym && MODimplicitConv(tp2->mod, tp1->mod))
                            goto Lcov;
                    }
                    else if (tp1->ty == Tpointer)
                    {
                        if (tp2->implicitConvTo(tp1))
                            goto Lcov;
                    }
                    else if (tp1->ty == Tarray)
                    {
                        if (tp2->implicitConvTo(tp1))
                            goto Lcov;
                    }
                    else if (tp1->ty == Tdelegate)
                    {
                        if (tp1->implicitConvTo(tp2))
                            goto Lcov;
                    }
                }
                goto Ldistinct;
            }
        Lcov:
            notcovariant |= !fparam1->isCovariant(t1->isref, fparam2);
        }
    }
    else if (t1->parameters != t2->parameters)
    {
        size_t dim1 = !t1->parameters ? 0 : t1->parameters->dim;
        size_t dim2 = !t2->parameters ? 0 : t2->parameters->dim;
        if (dim1 || dim2)
            goto Ldistinct;
    }

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

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

    if (!t1n || !t2n)           // happens with return type inference
        goto Lnotcovariant;

    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 &&
            MODimplicitConv(t1n->mod, t2n->mod))
            goto Lcovariant;

        // If t1n is forward referenced:
        ClassDeclaration *cd = ((TypeClass *)t1n)->sym;
        if (cd->semanticRun < PASSsemanticdone && !cd->isBaseInfoComplete())
            cd->semantic(NULL);
        if (!cd->isBaseInfoComplete())
        {
            return 3;   // forward references
        }
    }
    if (t1n->ty == Tstruct && t2n->ty == Tstruct)
    {
        if (((TypeStruct *)t1n)->sym == ((TypeStruct *)t2n)->sym &&
            MODimplicitConv(t1n->mod, t2n->mod))
            goto Lcovariant;
    }
    else if (t1n->ty == t2n->ty && t1n->implicitConvTo(t2n))
        goto Lcovariant;
    else if (t1n->ty == Tnull)
    {
        // NULL is covariant with any pointer type, but not with any
        // dynamic arrays, associative arrays or delegates.
        // https://issues.dlang.org/show_bug.cgi?id=8589
        // https://issues.dlang.org/show_bug.cgi?id=19618
        Type *t2bn = t2n->toBasetype();
        if (t2bn->ty == Tnull || t2bn->ty == Tpointer || t2bn->ty == Tclass)
            goto Lcovariant;
    }
  }
    goto Lnotcovariant;

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

    if (!t1->isref && (t1->isscope || t2->isscope))
    {
        StorageClass stc1 = t1->isscope ? STCscope : 0;
        StorageClass stc2 = t2->isscope ? STCscope : 0;
        if (t1->isreturn)
        {
            stc1 |= STCreturn;
            if (!t1->isscope)
                stc1 |= STCref;
        }
        if (t2->isreturn)
        {
            stc2 |= STCreturn;
            if (!t2->isscope)
                stc2 |= STCref;
        }
        if (!Parameter::isCovariantScope(t1->isref, stc1, stc2))
            goto Lnotcovariant;
    }

    // We can subtract 'return ref' from 'this', but cannot add it
    else if (t1->isreturn && !t2->isreturn)
        goto Lnotcovariant;

    /* Can convert mutable to const
     */
    if (!MODimplicitConv(t2->mod, t1->mod))
    {
        goto Ldistinct;
    }

    /* Can convert pure to impure, nothrow to throw, and nogc to gc
     */
    if (!t1->purity && t2->purity)
        stc |= STCpure;

    if (!t1->isnothrow && t2->isnothrow)
        stc |= STCnothrow;

    if (!t1->isnogc && t2->isnogc)
        stc |= STCnogc;

    /* Can convert safe/trusted to system
     */
    if (t1->trust <= TRUSTsystem && t2->trust >= TRUSTtrusted)
    {
        // Should we infer trusted or safe? Go with safe.
        stc |= STCsafe;
    }

    if (stc)
    {   if (pstc)
            *pstc = stc;
        goto Lnotcovariant;
    }

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

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

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

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 = %llx, fargs = %p\n", toChars(), sc->stc, fargs);

    bool errors = false;

    if (inuse > 500)
    {
        inuse = 0;
        ::error(loc, "recursive type");
        return Type::terror;
    }

    /* 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 = copy()->toTypeFunction();
    if (parameters)
    {
        tf->parameters = parameters->copy();
        for (size_t i = 0; i < parameters->dim; i++)
        {
            void *pp = mem.xmalloc(sizeof(Parameter));
            Parameter *p = (Parameter *)memcpy(pp, (void *)(*parameters)[i], sizeof(Parameter));
            (*tf->parameters)[i] = p;
        }
    }

    if (sc->stc & STCpure)
        tf->purity = PUREfwdref;
    if (sc->stc & STCnothrow)
        tf->isnothrow = true;
    if (sc->stc & STCnogc)
        tf->isnogc = true;
    if (sc->stc & STCref)
        tf->isref = true;
    if (sc->stc & STCreturn)
        tf->isreturn = true;
    if (sc->stc & STCscope)
        tf->isscope = true;
    if (sc->stc & STCscopeinferred)
        tf->isscopeinferred = true;

//    if ((sc->stc & (STCreturn | STCref)) == STCreturn)
//        tf->isscope = true;                                 // return by itself means 'return scope'

    if (tf->trust == TRUSTdefault)
    {
        if (sc->stc & STCsafe)
            tf->trust = TRUSTsafe;
        if (sc->stc & STCsystem)
            tf->trust = TRUSTsystem;
        if (sc->stc & STCtrusted)
            tf->trust = TRUSTtrusted;
    }

    if (sc->stc & STCproperty)
        tf->isproperty = true;

    tf->linkage = sc->linkage;
    bool wildreturn = false;
    if (tf->next)
    {
        sc = sc->push();
        sc->stc &= ~(STC_TYPECTOR | STC_FUNCATTR);
        tf->next = tf->next->semantic(loc, sc);
        sc = sc->pop();
        errors |= tf->checkRetType(loc);
        if (tf->next->isscope() && !(sc->flags & SCOPEctor))
        {
            error(loc, "functions cannot return scope %s", tf->next->toChars());
            errors = true;
        }
        if (tf->next->hasWild())
            wildreturn = true;

        if (tf->isreturn && !tf->isref && !tf->next->hasPointers())
        {
            error(loc, "function type '%s' has 'return' but does not return any indirections", tf->toChars());
        }
    }

    unsigned char wildparams = 0;
    if (tf->parameters)
    {
        /* Create a scope for evaluating the default arguments for the parameters
         */
        Scope *argsc = sc->push();
        argsc->stc = 0;                 // don't inherit storage class
        argsc->protection = Prot(PROTpublic);
        argsc->func = NULL;

        size_t dim = Parameter::dim(tf->parameters);
        for (size_t i = 0; i < dim; i++)
        {
            Parameter *fparam = Parameter::getNth(tf->parameters, i);
            inuse++;
            fparam->type = fparam->type->semantic(loc, argsc);
            inuse--;

            if (fparam->type->ty == Terror)
            {
                errors = true;
                continue;
            }

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

            if (fparam->storageClass & (STCauto | STCalias | STCstatic))
            {
                if (!fparam->type)
                    continue;
            }

            Type *t = fparam->type->toBasetype();

            if (t->ty == Tfunction)
            {
                error(loc, "cannot have parameter of function type %s", fparam->type->toChars());
                errors = true;
            }
            else if (!(fparam->storageClass & (STCref | STCout)) &&
                     (t->ty == Tstruct || t->ty == Tsarray || t->ty == Tenum))
            {
                Type *tb2 = t->baseElemOf();
                if ((tb2->ty == Tstruct && !((TypeStruct *)tb2)->sym->members) ||
                    (tb2->ty == Tenum && !((TypeEnum *)tb2)->sym->memtype))
                {
                    error(loc, "cannot have parameter of opaque type %s by value", fparam->type->toChars());
                    errors = true;
                }
            }
            else if (!(fparam->storageClass & STClazy) && t->ty == Tvoid)
            {
                error(loc, "cannot have parameter of type %s", fparam->type->toChars());
                errors = true;
            }

            if ((fparam->storageClass & (STCref | STCwild)) == (STCref | STCwild))
            {
                // 'ref inout' implies 'return'
                fparam->storageClass |= STCreturn;
            }

            if (fparam->storageClass & STCreturn)
            {
                if (fparam->storageClass & (STCref | STCout))
                {
                    // Disabled for the moment awaiting improvement to allow return by ref
                    // to be transformed into return by scope.
                    if (0 && !tf->isref)
                    {
                        StorageClass stc = fparam->storageClass & (STCref | STCout);
                        error(loc, "parameter %s is 'return %s' but function does not return by ref",
                            fparam->ident ? fparam->ident->toChars() : "",
                            stcToChars(stc));
                        errors = true;
                    }
                }
                else
                {
                    fparam->storageClass |= STCscope;        // 'return' implies 'scope'
                    if (tf->isref)
                    {
                    }
                    else if (!tf->isref && tf->next && !tf->next->hasPointers())
                    {
                        error(loc, "parameter %s is 'return' but function does not return any indirections",
                            fparam->ident ? fparam->ident->toChars() : "");
                        errors = true;
                    }
                }
            }

            if (fparam->storageClass & (STCref | STClazy))
            {
            }
            else if (fparam->storageClass & STCout)
            {
                if (unsigned char m = fparam->type->mod & (MODimmutable | MODconst | MODwild))
                {
                    error(loc, "cannot have %s out parameter of type %s", MODtoChars(m), t->toChars());
                    errors = true;
                }
                else
                {
                    Type *tv = t;
                    while (tv->ty == Tsarray)
                        tv = tv->nextOf()->toBasetype();
                    if (tv->ty == Tstruct && ((TypeStruct *)tv)->sym->noDefaultCtor)
                    {
                        error(loc, "cannot have out parameter of type %s because the default construction is disabled",
                            fparam->type->toChars());
                        errors = true;
                    }
                }
            }

            if (fparam->storageClass & STCscope && !fparam->type->hasPointers() && fparam->type->ty != Ttuple)
            {
                fparam->storageClass &= ~STCscope;
                if (!(fparam->storageClass & STCref))
                    fparam->storageClass &= ~STCreturn;
            }

            if (t->hasWild())
            {
                wildparams |= 1;
                //if (tf->next && !wildreturn)
                //    error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with 'ref')");
            }

            if (fparam->defaultArg)
            {
                Expression *e = fparam->defaultArg;
                if (fparam->storageClass & (STCref | STCout))
                {
                    e = ::semantic(e, argsc);
                    e = resolveProperties(argsc, e);
                }
                else
                {
                    e = inferType(e, fparam->type);
                    Initializer *iz = new ExpInitializer(e->loc, e);
                    iz = ::semantic(iz, argsc, fparam->type, INITnointerpret);
                    e = initializerToExpression(iz);
                }
                if (e->op == TOKfunction)               // see Bugzilla 4820
                {
                    FuncExp *fe = (FuncExp *)e;
                    // Replace function literal with a function symbol,
                    // since default arg expression must be copied when used
                    // and copying the literal itself is wrong.
                    e = new VarExp(e->loc, fe->fd, false);
                    e = new AddrExp(e->loc, e);
                    e = ::semantic(e, argsc);
                }
                e = e->implicitCastTo(argsc, fparam->type);

                // default arg must be an lvalue
                if (fparam->storageClass & (STCout | STCref))
                    e = e->toLvalue(argsc, e);

                fparam->defaultArg = e;
                if (e->op == TOKerror)
                    errors = true;
            }

            /* If fparam after semantic() turns out to be a tuple, the number of parameters may
             * change.
             */
            if (t->ty == Ttuple)
            {
                /* TypeFunction::parameter also is used as the storage of
                 * Parameter objects for FuncDeclaration. So we should copy
                 * the elements of TypeTuple::arguments to avoid unintended
                 * sharing of Parameter object among other functions.
                 */
                TypeTuple *tt = (TypeTuple *)t;
                if (tt->arguments && tt->arguments->dim)
                {
                    /* Propagate additional storage class from tuple parameters to their
                     * element-parameters.
                     * Make a copy, as original may be referenced elsewhere.
                     */
                    size_t tdim = tt->arguments->dim;
                    Parameters *newparams = new Parameters();
                    newparams->setDim(tdim);
                    for (size_t j = 0; j < tdim; j++)
                    {
                        Parameter *narg = (*tt->arguments)[j];

                        // Bugzilla 12744: If the storage classes of narg
                        // conflict with the ones in fparam, it's ignored.
                        StorageClass stc  = fparam->storageClass | narg->storageClass;
                        StorageClass stc1 = fparam->storageClass & (STCref | STCout | STClazy);
                        StorageClass stc2 =   narg->storageClass & (STCref | STCout | STClazy);
                        if (stc1 && stc2 && stc1 != stc2)
                        {
                            OutBuffer buf1;  stcToBuffer(&buf1, stc1 | ((stc1 & STCref) ? (fparam->storageClass & STCauto) : 0));
                            OutBuffer buf2;  stcToBuffer(&buf2, stc2);

                            error(loc, "incompatible parameter storage classes '%s' and '%s'",
                                      buf1.peekString(), buf2.peekString());
                            errors = true;
                            stc = stc1 | (stc & ~(STCref | STCout | STClazy));
                        }

                        (*newparams)[j] = new Parameter(
                                stc, narg->type, narg->ident, narg->defaultArg);
                    }
                    fparam->type = new TypeTuple(newparams);
                }
                fparam->storageClass = 0;

                /* Reset number of parameters, and back up one to do this fparam again,
                 * now that it is a tuple
                 */
                dim = Parameter::dim(tf->parameters);
                i--;
                continue;
            }

            /* Resolve "auto ref" storage class to be either ref or value,
             * based on the argument matching the parameter
             */
            if (fparam->storageClass & STCauto)
            {
                if (fargs && i < fargs->dim && (fparam->storageClass & STCref))
                {
                    Expression *farg = (*fargs)[i];
                    if (farg->isLvalue())
                        ;                               // ref parameter
                    else
                        fparam->storageClass &= ~STCref;        // value parameter
                    fparam->storageClass &= ~STCauto;    // Bugzilla 14656
                    fparam->storageClass |= STCautoref;
                }
                else
                {
                    error(loc, "'auto' can only be used as part of 'auto ref' for template function parameters");
                    errors = true;
                }
            }

            // Remove redundant storage classes for type, they are already applied
            fparam->storageClass &= ~(STC_TYPECTOR | STCin);
        }
        argsc->pop();
    }
    if (tf->isWild())
        wildparams |= 2;

    if (wildreturn && !wildparams)
    {
        error(loc, "inout on return means inout must be on a parameter as well for %s", toChars());
        errors = true;
    }
    tf->iswild = wildparams;

    if (tf->isproperty && (tf->varargs || Parameter::dim(tf->parameters) > 2))
    {
        error(loc, "properties can only have zero, one, or two parameter");
        errors = true;
    }

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

    if (errors)
        return terror;

    if (tf->next)
        tf->deco = tf->merge()->deco;

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

bool TypeFunction::checkRetType(Loc loc)
{
    Type *tb = next->toBasetype();
    if (tb->ty == Tfunction)
    {
        error(loc, "functions cannot return a function");
        next = Type::terror;
    }
    if (tb->ty == Ttuple)
    {
        error(loc, "functions cannot return a tuple");
        next = Type::terror;
    }
    if (!isref && (tb->ty == Tstruct || tb->ty == Tsarray))
    {
        Type *tb2 = tb->baseElemOf();
        if (tb2->ty == Tstruct && !((TypeStruct *)tb2)->sym->members)
        {
            error(loc, "functions cannot return opaque type %s by value", tb->toChars());
            next = Type::terror;
        }
    }
    if (tb->ty == Terror)
        return true;

    return false;
}

/* Determine purity level based on mutability of t
 * and whether it is a 'ref' type or not.
 */
static PURE purityOfType(bool isref, Type *t)
{
    if (isref)
    {
        if (t->mod & MODimmutable)
            return PUREstrong;
        if (t->mod & (MODconst | MODwild))
            return PUREconst;
        return PUREweak;
    }

    t = t->baseElemOf();

    if (!t->hasPointers() || t->mod & MODimmutable)
        return PUREstrong;

    /* Accept immutable(T)[] and immutable(T)* as being strongly pure
     */
    if (t->ty == Tarray || t->ty == Tpointer)
    {
        Type *tn = t->nextOf()->toBasetype();
        if (tn->mod & MODimmutable)
            return PUREstrong;
        if (tn->mod & (MODconst | MODwild))
            return PUREconst;
    }

    /* The rest of this is too strict; fix later.
     * For example, the only pointer members of a struct may be immutable,
     * which would maintain strong purity.
     * (Just like for dynamic arrays and pointers above.)
     */
    if (t->mod & (MODconst | MODwild))
        return PUREconst;

    /* Should catch delegates and function pointers, and fold in their purity
     */
    return PUREweak;
}

/********************************************
 * Set 'purity' field of 'this'.
 * Do this lazily, as the parameter types might be forward referenced.
 */
void TypeFunction::purityLevel()
{
    TypeFunction *tf = this;
    if (tf->purity != PUREfwdref)
        return;

    purity = PUREstrong; // assume strong until something weakens it

    /* Evaluate what kind of purity based on the modifiers for the parameters
     */
    const size_t dim = Parameter::dim(tf->parameters);
    for (size_t i = 0; i < dim; i++)
    {
        Parameter *fparam = Parameter::getNth(tf->parameters, i);
        Type *t = fparam->type;
        if (!t)
            continue;

        if (fparam->storageClass & (STClazy | STCout))
        {
            purity = PUREweak;
            break;
        }
        switch (purityOfType((fparam->storageClass & STCref) != 0, t))
        {
            case PUREweak:
                purity = PUREweak;
                break;

            case PUREconst:
                purity = PUREconst;
                continue;

            case PUREstrong:
                continue;

            default:
                assert(0);
        }
        break;              // since PUREweak, no need to check further
    }

    if (purity > PUREweak && tf->nextOf())
    {
        /* Adjust purity based on mutability of return type.
         * https://issues.dlang.org/show_bug.cgi?id=15862
         */
        const PURE purity2 = purityOfType(tf->isref, tf->nextOf());
        if (purity2 < purity)
            purity = purity2;
    }
    tf->purity = purity;
}

/********************************
 * 'args' are being matched to function 'this'
 * Determine match level.
 * Input:
 *      flag    1       performing a partial ordering match
 * Returns:
 *      MATCHxxxx
 */

MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag)
{
    //printf("TypeFunction::callMatch() %s\n", toChars());
    MATCH match = MATCHexact;           // assume exact match
    unsigned char wildmatch = 0;

    if (tthis)
    {
        Type *t = tthis;
        if (t->toBasetype()->ty == Tpointer)
            t = t->toBasetype()->nextOf();      // change struct* to struct
        if (t->mod != mod)
        {
            if (MODimplicitConv(t->mod, mod))
                match = MATCHconst;
            else if ((mod & MODwild) && MODimplicitConv(t->mod, (mod & ~MODwild) | MODconst))
            {
                match = MATCHconst;
            }
            else
                return MATCHnomatch;
        }
        if (isWild())
        {
            if (t->isWild())
                wildmatch |= MODwild;
            else if (t->isConst())
                wildmatch |= MODconst;
            else if (t->isImmutable())
                wildmatch |= MODimmutable;
            else
                wildmatch |= MODmutable;
        }
    }

    size_t nparams = Parameter::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 < nargs; u++)
    {
        if (u >= nparams)
            break;
        Parameter *p = Parameter::getNth(parameters, u);
        Expression *arg = (*args)[u];
        assert(arg);
        Type *tprm = p->type;
        Type *targ = arg->type;

        if (!(p->storageClass & STClazy && tprm->ty == Tvoid && targ->ty != Tvoid))
        {
            bool isRef = (p->storageClass & (STCref | STCout)) != 0;
            wildmatch |= targ->deduceWild(tprm, isRef);
        }
    }
    if (wildmatch)
    {
        /* Calculate wild matching modifier
         */
        if (wildmatch & MODconst || wildmatch & (wildmatch - 1))
            wildmatch = MODconst;
        else if (wildmatch & MODimmutable)
            wildmatch = MODimmutable;
        else if (wildmatch & MODwild)
            wildmatch = MODwild;
        else
        {
            assert(wildmatch & MODmutable);
            wildmatch = MODmutable;
        }
    }

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

        Parameter *p = Parameter::getNth(parameters, u);
        assert(p);
        if (u >= nargs)
        {
            if (p->defaultArg)
                continue;
            goto L1;        // try typesafe variadics
        }
        {
            Expression *arg = (*args)[u];
            assert(arg);
            //printf("arg: %s, type: %s\n", arg->toChars(), arg->type->toChars());

            Type *targ = arg->type;
            Type *tprm = wildmatch ? p->type->substWildTo(wildmatch) : p->type;

            if (p->storageClass & STClazy && tprm->ty == Tvoid && targ->ty != Tvoid)
                m = MATCHconvert;
            else
            {
                //printf("%s of type %s implicitConvTo %s\n", arg->toChars(), targ->toChars(), tprm->toChars());
                if (flag)
                {
                    // for partial ordering, value is an irrelevant mockup, just look at the type
                    m = targ->implicitConvTo(tprm);
                }
                else
                    m = arg->implicitConvTo(tprm);
                //printf("match %d\n", m);
            }

            // Non-lvalues do not match ref or out parameters
            if (p->storageClass & (STCref | STCout))
            {
                // Bugzilla 13783: Don't use toBasetype() to handle enum types.
                Type *ta = targ;
                Type *tp = tprm;
                //printf("fparam[%d] ta = %s, tp = %s\n", u, ta->toChars(), tp->toChars());

                if (m && !arg->isLvalue())
                {
                    if (p->storageClass & STCout)
                        goto Nomatch;

                    if (arg->op == TOKstring && tp->ty == Tsarray)
                    {
                        if (ta->ty != Tsarray)
                        {
                            Type *tn = tp->nextOf()->castMod(ta->nextOf()->mod);
                            dinteger_t dim = ((StringExp *)arg)->len;
                            ta = tn->sarrayOf(dim);
                        }
                    }
                    else if (arg->op == TOKslice && tp->ty == Tsarray)
                    {
                        // Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
                        if (ta->ty != Tsarray)
                        {
                            Type *tn = ta->nextOf();
                            dinteger_t dim = ((TypeSArray *)tp)->dim->toUInteger();
                            ta = tn->sarrayOf(dim);
                        }
                    }
                    else
                        goto Nomatch;
                }

                /* Find most derived alias this type being matched.
                 * Bugzilla 15674: Allow on both ref and out parameters.
                 */
                while (1)
                {
                    Type *tat = ta->toBasetype()->aliasthisOf();
                    if (!tat || !tat->implicitConvTo(tprm))
                        break;
                    ta = tat;
                }

                /* A ref variable should work like a head-const reference.
                 * e.g. disallows:
                 *  ref T      <- an lvalue of const(T) argument
                 *  ref T[dim] <- an lvalue of const(T[dim]) argument
                 */
                if (!ta->constConv(tp))
                    goto Nomatch;
            }
        }

        /* prefer matching the element type rather than the array
         * type when more arguments are present with T[]...
         */
        if (varargs == 2 && u + 1 == nparams && nargs > nparams)
            goto L1;

        //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;
                    /* fall through */
                case Tarray:
                    {
                        TypeArray *ta = (TypeArray *)tb;
                        for (; u < nargs; u++)
                        {
                            Expression *arg = (*args)[u];
                            assert(arg);

                            /* 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 if (tret->toBasetype()->ty == Tvoid)
                                    m = MATCHconvert;
                                else
                                {
                                    m = arg->implicitConvTo(tret);
                                    if (m == MATCHnomatch)
                                        m = arg->implicitConvTo(ta->next);
                                }
                            }
                            else
                                m = arg->implicitConvTo(ta->next);

                            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;
}

/********************************************
 * Return true if there are lazy parameters.
 */
bool TypeFunction::hasLazyParameters()
{
    size_t dim = Parameter::dim(parameters);
    for (size_t i = 0; i < dim; i++)
    {
        Parameter *fparam = Parameter::getNth(parameters, i);
        if (fparam->storageClass & STClazy)
            return true;
    }
    return false;
}

/***************************
 * Examine function signature for parameter p and see if
 * the value of p can 'escape' the scope of the function.
 * This is useful to minimize the needed annotations for the parameters.
 * Params:
 *  p = parameter to this function
 * Returns:
 *  true if escapes via assignment to global or through a parameter
 */

bool TypeFunction::parameterEscapes(Parameter *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 (parameterStorageClass(p) & (STCscope | STClazy))
        return false;
    return true;
}

/************************************
 * Take the specified storage class for p,
 * and use the function signature to infer whether
 * STCscope and STCreturn should be OR'd in.
 * (This will not affect the name mangling.)
 * Params:
 *  p = one of the parameters to 'this'
 * Returns:
 *  storage class with STCscope or STCreturn OR'd in
 */
StorageClass TypeFunction::parameterStorageClass(Parameter *p)
{
    StorageClass stc = p->storageClass;
    if (!global.params.vsafe)
        return stc;

    if (stc & (STCscope | STCreturn | STClazy) || purity == PUREimpure)
        return stc;

    /* If haven't inferred the return type yet, can't infer storage classes
     */
    if (!nextOf())
        return stc;

    purityLevel();

    // See if p can escape via any of the other parameters
    if (purity == PUREweak)
    {
        const size_t dim = Parameter::dim(parameters);
        for (size_t i = 0; i < dim; i++)
        {
            Parameter *fparam = Parameter::getNth(parameters, i);
            Type *t = fparam->type;
            if (!t)
                continue;
            t = t->baseElemOf();
            if (t->isMutable() && t->hasPointers())
            {
                if (fparam->storageClass & (STCref | STCout))
                {
                }
                else if (t->ty == Tarray || t->ty == Tpointer)
                {
                    Type *tn = t->nextOf()->toBasetype();
                    if (!(tn->isMutable() && tn->hasPointers()))
                        continue;
                }
                return stc;
            }
        }
    }

    stc |= STCscope;

    /* Inferring STCreturn here has false positives
     * for pure functions, producing spurious error messages
     * about escaping references.
     * Give up on it for now.
     */
    return stc;
}

Expression *TypeFunction::defaultInit(Loc loc)
{
    error(loc, "function does not have a default initializer");
    return new ErrorExp();
}

Type *TypeFunction::addStorageClass(StorageClass stc)
{
    //printf("addStorageClass(%llx) %d\n", stc, (stc & STCscope) != 0);
    TypeFunction *t = Type::addStorageClass(stc)->toTypeFunction();
    if ((stc & STCpure && !t->purity) ||
        (stc & STCnothrow && !t->isnothrow) ||
        (stc & STCnogc && !t->isnogc) ||
        (stc & STCscope && !t->isscope) ||
        (stc & STCsafe && t->trust < TRUSTtrusted))
    {
        // Klunky to change these
        TypeFunction *tf = new TypeFunction(t->parameters, t->next, t->varargs, t->linkage, 0);
        tf->mod = t->mod;
        tf->fargs = fargs;
        tf->purity = t->purity;
        tf->isnothrow = t->isnothrow;
        tf->isnogc = t->isnogc;
        tf->isproperty = t->isproperty;
        tf->isref = t->isref;
        tf->isreturn = t->isreturn;
        tf->isscope = t->isscope;
        tf->isscopeinferred = t->isscopeinferred;
        tf->trust = t->trust;
        tf->iswild = t->iswild;

        if (stc & STCpure)
            tf->purity = PUREfwdref;
        if (stc & STCnothrow)
            tf->isnothrow = true;
        if (stc & STCnogc)
            tf->isnogc = true;
        if (stc & STCsafe)
            tf->trust = TRUSTsafe;
        if (stc & STCscope)
        {
            tf->isscope = true;
            if (stc & STCscopeinferred)
                tf->isscopeinferred = true;
        }

        tf->deco = tf->merge()->deco;
        t = tf;
    }
    return t;
}

/** For each active attribute (ref/const/nogc/etc) call fp with a void* for the
work param and a string representation of the attribute. */
int TypeFunction::attributesApply(void *param, int (*fp)(void *, const char *), TRUSTformat trustFormat)
{
    int res = 0;

    if (purity) res = fp(param, "pure");
    if (res) return res;

    if (isnothrow) res = fp(param, "nothrow");
    if (res) return res;

    if (isnogc) res = fp(param, "@nogc");
    if (res) return res;

    if (isproperty) res = fp(param, "@property");
    if (res) return res;

    if (isref) res = fp(param, "ref");
    if (res) return res;

    if (isreturn) res = fp(param, "return");
    if (res) return res;

    if (isscope && !isscopeinferred) res = fp(param, "scope");
    if (res) return res;

    TRUST trustAttrib = trust;

    if (trustAttrib == TRUSTdefault)
    {
        // Print out "@system" when trust equals TRUSTdefault (if desired).
        if (trustFormat == TRUSTformatSystem)
            trustAttrib = TRUSTsystem;
        else
            return res;  // avoid calling with an empty string
    }

    return fp(param, trustToChars(trustAttrib));
}

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

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

TypeDelegate *TypeDelegate::create(Type *t)
{
    return new TypeDelegate(t);
}

const char *TypeDelegate::kind()
{
    return "delegate";
}

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)
{
    //printf("TypeDelegate::semantic() %s\n", toChars());
    if (deco)                   // if semantic() already run
    {
        //printf("already done\n");
        return this;
    }
    next = next->semantic(loc,sc);
    if (next->ty != Tfunction)
        return terror;

    /* In order to deal with Bugzilla 4028, perhaps default arguments should
     * be removed from next before the merge.
     */

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

Type *TypeDelegate::addStorageClass(StorageClass stc)
{
    TypeDelegate *t = (TypeDelegate*)Type::addStorageClass(stc);
    if (!global.params.vsafe)
        return t;

    /* The rest is meant to add 'scope' to a delegate declaration if it is of the form:
     *  alias dg_t = void* delegate();
     *  scope dg_t dg = ...;
     */
    if(stc & STCscope)
    {
        Type *n = t->next->addStorageClass(STCscope | STCscopeinferred);
        if (n != t->next)
        {
            t->next = n;
            t->deco = t->merge()->deco; // mangling supposed to not be changed due to STCscopeinferrred
        }
    }
    return t;
}

d_uns64 TypeDelegate::size(Loc)
{
    return Target::ptrsize * 2;
}

unsigned TypeDelegate::alignsize()
{
    return Target::ptrsize;
}

MATCH TypeDelegate::implicitConvTo(Type *to)
{
    //printf("TypeDelegate::implicitConvTo(this=%p, to=%p)\n", this, to);
    //printf("from: %s\n", toChars());
    //printf("to  : %s\n", to->toChars());
    if (this == to)
        return MATCHexact;
#if 1 // not allowing covariant conversions because it interferes with overriding
    if (to->ty == Tdelegate && this->nextOf()->covariant(to->nextOf()) == 1)
    {
        Type *tret = this->next->nextOf();
        Type *toret = ((TypeDelegate *)to)->next->nextOf();
        if (tret->ty == Tclass && toret->ty == Tclass)
        {
            /* Bugzilla 10219: Check covariant interface return with offset tweaking.
             * interface I {}
             * class C : Object, I {}
             * I delegate() dg = delegate C() {}    // should be error
             */
            int offset = 0;
            if (toret->isBaseOf(tret, &offset) && offset != 0)
                return MATCHnomatch;
        }
        return MATCHconvert;
    }
#endif
    return MATCHnomatch;
}

Expression *TypeDelegate::defaultInit(Loc loc)
{
    return new NullExp(loc, this);
}

bool TypeDelegate::isZeroInit(Loc)
{
    return true;
}

bool TypeDelegate::isBoolean()
{
    return true;
}

Expression *TypeDelegate::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    if (ident == Id::ptr)
    {
        e = new DelegatePtrExp(e->loc, e);
        e = ::semantic(e, sc);
    }
    else if (ident == Id::funcptr)
    {
        if (!(flag & 2) && sc->func && !sc->intypeof && sc->func->setUnsafe())
        {
            e->error("%s.funcptr cannot be used in @safe code", e->toChars());
            return new ErrorExp();
        }
        e = new DelegateFuncptrExp(e->loc, e);
        e = ::semantic(e, sc);
    }
    else
    {
        e = Type::dotExp(sc, e, ident, flag);
    }
    return e;
}

bool 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 (size_t i = 0; i < idents.dim; i++)
    {
        RootObject *id = t->idents[i];
        if (id->dyncast() == DYNCAST_DSYMBOL)
        {
            TemplateInstance *ti = (TemplateInstance *)id;

            ti = (TemplateInstance *)ti->syntaxCopy(NULL);
            id = ti;
        }
        else if (id->dyncast() == DYNCAST_EXPRESSION)
        {
            Expression *e = (Expression *)id;
            e = e->syntaxCopy();
            id = e;
        }
        else if (id->dyncast() == DYNCAST_TYPE)
        {
            Type *tx = (Type *)id;
            tx = tx->syntaxCopy();
            id = tx;
        }
        idents[i] = id;
    }
}

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

void TypeQualified::addInst(TemplateInstance *inst)
{
    idents.push(inst);
}

void TypeQualified::addIndex(RootObject *e)
{
    idents.push(e);
}

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

/*************************************
 * Resolve a tuple index.
 */
void TypeQualified::resolveTupleIndex(Loc loc, Scope *sc, Dsymbol *s,
        Expression **pe, Type **pt, Dsymbol **ps, RootObject *oindex)
{
    *pt = NULL;
    *ps = NULL;
    *pe = NULL;

    TupleDeclaration *td = s->isTupleDeclaration();

    Expression *eindex = isExpression(oindex);
    Type *tindex = isType(oindex);
    Dsymbol *sindex = isDsymbol(oindex);

    if (!td)
    {
        // It's really an index expression
        if (tindex)
            eindex = new TypeExp(loc, tindex);
        else if (sindex)
            eindex = ::resolve(loc, sc, sindex, false);
        Expression *e = new IndexExp(loc, ::resolve(loc, sc, s, false), eindex);
        e = ::semantic(e, sc);
        resolveExp(e, pt, pe, ps);
        return;
    }

    // Convert oindex to Expression, then try to resolve to constant.
    if (tindex)
        tindex->resolve(loc, sc, &eindex, &tindex, &sindex);
    if (sindex)
        eindex = ::resolve(loc, sc, sindex, false);
    if (!eindex)
    {
        ::error(loc, "index is %s not an expression", oindex->toChars());
        *pt = Type::terror;
        return;
    }
    sc = sc->startCTFE();
    eindex = ::semantic(eindex, sc);
    sc = sc->endCTFE();

    eindex = eindex->ctfeInterpret();
    if (eindex->op == TOKerror)
    {
        *pt = Type::terror;
        return;
    }

    const uinteger_t d = eindex->toUInteger();
    if (d >= td->objects->dim)
    {
        ::error(loc, "tuple index %llu exceeds length %u", (ulonglong)d, (unsigned)td->objects->dim);
        *pt = Type::terror;
        return;
    }

    RootObject *o = (*td->objects)[(size_t)d];
    *pt = isType(o);
    *ps = isDsymbol(o);
    *pe = isExpression(o);

    if (*pt)
        *pt = (*pt)->semantic(loc, sc);
    if (*pe)
        resolveExp(*pe, pt, pe, ps);
}

/*************************************
 * 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 *,
        Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
{
    *pe = NULL;
    *pt = NULL;
    *ps = NULL;
    if (s)
    {
        //printf("\t1: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind());
        Declaration *d = s->isDeclaration();
        if (d && (d->storage_class & STCtemplateparameter))
            s = s->toAlias();
        else
            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 (size_t i = 0; i < idents.dim; i++)
        {
            RootObject *id = idents[i];

            if (id->dyncast() == DYNCAST_EXPRESSION ||
                id->dyncast() == DYNCAST_TYPE)
            {
                Type *tx;
                Expression *ex;
                Dsymbol *sx;
                resolveTupleIndex(loc, sc, s, &ex, &tx, &sx, id);
                if (sx)
                {
                    s = sx->toAlias();
                    continue;
                }
                if (tx)
                    ex = new TypeExp(loc, tx);
                assert(ex);

                ex = typeToExpressionHelper(this, ex, i + 1);
                ex = ::semantic(ex, sc);
                resolveExp(ex, pt, pe, ps);
                return;
            }

            Type *t = s->getType();     // type symbol, type alias, or type tuple?
            unsigned errorsave = global.errors;
            Dsymbol *sm = s->searchX(loc, sc, id);
            if (sm && !(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, sm))
            {
                ::deprecation(loc, "%s is not visible from module %s", sm->toPrettyChars(), sc->_module->toChars());
                // sm = NULL;
            }
            if (global.errors != errorsave)
            {
                *pt = Type::terror;
                return;
            }
            //printf("\t3: s = %p %s %s, sm = %p\n", s, s->kind(), s->toChars(), sm);
            if (intypeid && !t && sm && sm->needThis())
                goto L3;
            if (VarDeclaration *v = s->isVarDeclaration())
            {
                // https://issues.dlang.org/show_bug.cgi?id=19913
                // v->type would be null if it is a forward referenced member.
                if (v->type == NULL)
                    v->semantic(sc);
                if (v->storage_class & (STCconst | STCimmutable | STCmanifest) ||
                    v->type->isConst() || v->type->isImmutable())
                {
                    // Bugzilla 13087: this.field is not constant always
                    if (!v->isThisDeclaration())
                        goto L3;
                }
            }
            if (!sm)
            {
                if (!t)
                {
                    if (s->isDeclaration())         // var, func, or tuple declaration?
                    {
                        t = s->isDeclaration()->type;
                        if (!t && s->isTupleDeclaration())  // expression tuple?
                            goto L3;
                    }
                    else if (s->isTemplateInstance() ||
                             s->isImport() || s->isPackage() || s->isModule())
                    {
                        goto L3;
                    }
                }
                if (t)
                {
                    sm = t->toDsymbol(sc);
                    if (sm && id->dyncast() == DYNCAST_IDENTIFIER)
                    {
                        sm = sm->search(loc, (Identifier *)id);
                        if (sm)
                            goto L2;
                    }
                L3:
                    Expression *e;
                    VarDeclaration *v = s->isVarDeclaration();
                    FuncDeclaration *f = s->isFuncDeclaration();
                    if (intypeid || (!v && !f))
                        e = ::resolve(loc, sc, s, true);
                    else
                        e = new VarExp(loc, s->isDeclaration(), true);

                    e = typeToExpressionHelper(this, e, i);
                    e = ::semantic(e, sc);
                    resolveExp(e, pt, pe, ps);
                    return;
                }
                else
                {
                    if (id->dyncast() == DYNCAST_DSYMBOL)
                    {
                        // searchX already handles errors for template instances
                        assert(global.errors);
                    }
                    else
                    {
                        assert(id->dyncast() == DYNCAST_IDENTIFIER);
                        sm = s->search_correct((Identifier *)id);
                        if (sm)
                            error(loc, "identifier '%s' of '%s' is not defined, did you mean %s '%s'?",
                                  id->toChars(), toChars(), sm->kind(), sm->toChars());
                        else
                            error(loc, "identifier '%s' of '%s' is not defined", id->toChars(), toChars());
                    }
                    *pe = new ErrorExp();
                }
                return;
            }
        L2:
            s = sm->toAlias();
        }

        if (EnumMember *em = s->isEnumMember())
        {
            // It's not a type, it's an expression
            *pe = em->getVarExp(loc, sc);
            return;
        }
        if (VarDeclaration *v = s->isVarDeclaration())
        {
            /* This is mostly same with DsymbolExp::semantic(), but we cannot use it
             * because some variables used in type context need to prevent lowering
             * to a literal or contextful expression. For example:
             *
             *  enum a = 1; alias b = a;
             *  template X(alias e){ alias v = e; }  alias x = X!(1);
             *  struct S { int v; alias w = v; }
             *      // TypeIdentifier 'a', 'e', and 'v' should be TOKvar,
             *      // because getDsymbol() need to work in AliasDeclaration::semantic().
             */
            if (!v->type ||
                (!v->type->deco && v->inuse))
            {
                if (v->inuse)   // Bugzilla 9494
                    error(loc, "circular reference to %s '%s'", v->kind(), v->toPrettyChars());
                else
                    error(loc, "forward reference to %s '%s'", v->kind(), v->toPrettyChars());
                *pt = Type::terror;
                return;
            }
            if (v->type->ty == Terror)
                *pt = Type::terror;
            else
                *pe = new VarExp(loc, v);
            return;
        }
        if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration())
        {
            //printf("'%s' is a function literal\n", fld->toChars());
            *pe = new FuncExp(loc, fld);
            *pe = ::semantic(*pe, sc);
            return;
        }
L1:
        Type *t = s->getType();
        if (!t)
        {
            // If the symbol is an import, try looking inside the import
            if (Import *si = s->isImport())
            {
                s = si->search(loc, s->ident);
                if (s && s != si)
                    goto L1;
                s = si;
            }
            *ps = s;
            return;
        }
        if (t->ty == Tinstance && t != this && !t->deco)
        {
            if (!((TypeInstance *)t)->tempinst->errors)
                error(loc, "forward reference to '%s'", t->toChars());
            *pt = Type::terror;
            return;
        }

        if (t->ty == Ttuple)
            *pt = t;
        else
            *pt = t->merge();
    }
    if (!s)
    {
        /* Look for what user might have intended
         */
        const char *p = mutableOf()->unSharedOf()->toChars();
        Identifier *id = Identifier::idPool(p, strlen(p));
        if (const char *n = importHint(p))
            error(loc, "`%s` is not defined, perhaps `import %s;` ?", p, n);
        else if (Dsymbol *s2 = sc->search_correct(id))
            error(loc, "undefined identifier `%s`, did you mean %s `%s`?", p, s2->kind(), s2->toChars());
        else if (const char *q = Scope::search_correct_C(id))
            error(loc, "undefined identifier `%s`, did you mean `%s`?", p, q);
        else
            error(loc, "undefined identifier `%s`", p);

        *pt = Type::terror;
    }
}

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

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

const char *TypeIdentifier::kind()
{
    return "identifier";
}

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

/*************************************
 * 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, bool intypeid)
{
    //printf("TypeIdentifier::resolve(sc = %p, idents = '%s')\n", sc, toChars());

    if ((ident->equals(Id::_super) || ident->equals(Id::This)) && !hasThis(sc))
    {
        AggregateDeclaration *ad = sc->getStructClassScope();
        if (ad)
        {
            ClassDeclaration *cd = ad->isClassDeclaration();
            if (cd)
            {
                if (ident->equals(Id::This))
                    ident = cd->ident;
                else if (cd->baseClass && ident->equals(Id::_super))
                    ident = cd->baseClass->ident;
            }
            else
            {
                StructDeclaration *sd = ad->isStructDeclaration();
                if (sd && ident->equals(Id::This))
                    ident = sd->ident;
            }
        }
    }
    if (ident == Id::ctfe)
    {
        error(loc, "variable __ctfe cannot be read at compile time");
        *pe = NULL;
        *ps = NULL;
        *pt = Type::terror;
        return;
    }

    Dsymbol *scopesym;
    Dsymbol *s = sc->search(loc, ident, &scopesym);
    resolveHelper(loc, sc, s, scopesym, pe, pt, ps, intypeid);
    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;

    Type *t;
    Expression *e;
    Dsymbol *s;

    resolve(loc, sc, &e, &t, &s);
    if (t && t->ty != Tident)
        s = t->toDsymbol(sc);
    if (e)
        s = getDsymbol(e);

    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);
        t = t->addMod(mod);
    }
    else
    {
        if (s)
        {
            s->error(loc, "is used as a type");
            //halt();
        }
        else
            error(loc, "%s is used as a type", toChars());
        t = terror;
    }
    //t->print();
    return t;
}

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

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

const char *TypeInstance::kind()
{
    return "instance";
}

Type *TypeInstance::syntaxCopy()
{
    //printf("TypeInstance::syntaxCopy() %s, %d\n", toChars(), idents.dim);
    TypeInstance *t = new TypeInstance(loc, (TemplateInstance *)tempinst->syntaxCopy(NULL));
    t->syntaxCopyHelper(this);
    t->mod = mod;
    return t;
}

void TypeInstance::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
{
    // Note close similarity to TypeIdentifier::resolve()
    *pe = NULL;
    *pt = NULL;
    *ps = NULL;
    //printf("TypeInstance::resolve(sc = %p, tempinst = '%s')\n", sc, tempinst->toChars());
    tempinst->semantic(sc);
    if (!global.gag && tempinst->errors)
    {
        *pt = terror;
        return;
    }

    resolveHelper(loc, sc, tempinst, NULL, pe, pt, ps, intypeid);
    if (*pt)
        *pt = (*pt)->addMod(mod);
    //if (*pt) printf("pt = '%s'\n", (*pt)->toChars());
}

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

    //printf("TypeInstance::semantic(%p, %s)\n", this, toChars());
    {
        unsigned errors = global.errors;
        resolve(loc, sc, &e, &t, &s);
        // if we had an error evaluating the symbol, suppress further errors
        if (!t && errors != global.errors)
            return terror;
    }

    if (!t)
    {
        if (!e && s && s->errors)
        {
            // if there was an error evaluating the symbol, it might actually
            // be a type. Avoid misleading error messages.
            error(loc, "%s had previous errors", toChars());
        }
        else
            error(loc, "%s is used as a type", toChars());
        t = terror;
    }
    return t;
}

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

    //printf("TypeInstance::semantic(%s)\n", toChars());
    resolve(loc, sc, &e, &t, &s);
    if (t && t->ty != Tinstance)
        s = t->toDsymbol(sc);

    return s;
}


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

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

const char *TypeTypeof::kind()
{
    return "typeof";
}

Type *TypeTypeof::syntaxCopy()
{
    //printf("TypeTypeof::syntaxCopy() %s\n", toChars());
    TypeTypeof *t = new TypeTypeof(loc, exp->syntaxCopy());
    t->syntaxCopyHelper(this);
    t->mod = mod;
    return t;
}

Dsymbol *TypeTypeof::toDsymbol(Scope *sc)
{
    //printf("TypeTypeof::toDsymbol('%s')\n", toChars());
    Expression *e;
    Type *t;
    Dsymbol *s;
    resolve(loc, sc, &e, &t, &s);

    return s;
}

void TypeTypeof::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
{
    *pe = NULL;
    *pt = NULL;
    *ps = NULL;

    //printf("TypeTypeof::resolve(sc = %p, idents = '%s')\n", sc, toChars());
    //static int nest; if (++nest == 50) *(char*)0=0;
    if (inuse)
    {
        inuse = 2;
        error(loc, "circular typeof definition");
    Lerr:
        *pt = Type::terror;
        inuse--;
        return;
    }
    inuse++;

    Type *t;
    {
        /* Currently we cannot evalute 'exp' in speculative context, because
         * the type implementation may leak to the final execution. Consider:
         *
         * struct S(T) {
         *   string toString() const { return "x"; }
         * }
         * void main() {
         *   alias X = typeof(S!int());
         *   assert(typeid(X).xtoString(null) == "x");
         * }
         */
        Scope *sc2 = sc->push();
        sc2->intypeof = 1;
        Expression *exp2 = ::semantic(exp, sc2);
        exp2 = resolvePropertiesOnly(sc2, exp2);
        sc2->pop();

        if (exp2->op == TOKerror)
        {
            if (!global.gag)
                exp = exp2;
            goto Lerr;
        }
        exp = exp2;

        if (exp->op == TOKtype ||
            exp->op == TOKscope)
        {
            if (exp->checkType())
                goto Lerr;

            /* Today, 'typeof(func)' returns void if func is a
             * function template (TemplateExp), or
             * template lambda (FuncExp).
             * It's actually used in Phobos as an idiom, to branch code for
             * template functions.
             */
        }
        if (FuncDeclaration *f = exp->op == TOKvar    ? ((   VarExp *)exp)->var->isFuncDeclaration()
                               : exp->op == TOKdotvar ? ((DotVarExp *)exp)->var->isFuncDeclaration() : NULL)
        {
            if (f->checkForwardRef(loc))
                goto Lerr;
        }
        if (FuncDeclaration *f = isFuncAddress(exp))
        {
            if (f->checkForwardRef(loc))
                goto Lerr;
        }

        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());
            goto Lerr;
        }
    }
    if (idents.dim == 0)
        *pt = t;
    else
    {
        if (Dsymbol *s = t->toDsymbol(sc))
            resolveHelper(loc, sc, s, NULL, pe, pt, ps, intypeid);
        else
        {
            Expression *e = typeToExpressionHelper(this, new TypeExp(loc, t));
            e = ::semantic(e, sc);
            resolveExp(e, pt, pe, ps);
        }
    }
    if (*pt)
        (*pt) = (*pt)->addMod(mod);
    inuse--;
    return;
}

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

    Expression *e;
    Type *t;
    Dsymbol *s;
    resolve(loc, sc, &e, &t, &s);
    if (s && (t = s->getType()) != NULL)
        t = t->addMod(mod);
    if (!t)
    {
        error(loc, "%s is used as a type", toChars());
        t = Type::terror;
    }
    return t;
}

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)
{
}

const char *TypeReturn::kind()
{
    return "return";
}

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

Dsymbol *TypeReturn::toDsymbol(Scope *sc)
{
    Expression *e;
    Type *t;
    Dsymbol *s;
    resolve(loc, sc, &e, &t, &s);

    return s;
}

void TypeReturn::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
{
    *pe = NULL;
    *pt = NULL;
    *ps = NULL;

    //printf("TypeReturn::resolve(sc = %p, idents = '%s')\n", sc, toChars());
    Type *t;
    {
        FuncDeclaration *func = sc->func;
        if (!func)
        {
            error(loc, "typeof(return) must be inside function");
            goto Lerr;
        }
        if (func->fes)
            func = func->fes->func;

        t = func->type->nextOf();
        if (!t)
        {
            error(loc, "cannot use typeof(return) inside function %s with inferred return type", sc->func->toChars());
            goto Lerr;
        }
    }
    if (idents.dim == 0)
        *pt = t;
    else
    {
        if (Dsymbol *s = t->toDsymbol(sc))
            resolveHelper(loc, sc, s, NULL, pe, pt, ps, intypeid);
        else
        {
            Expression *e = typeToExpressionHelper(this, new TypeExp(loc, t));
            e = ::semantic(e, sc);
            resolveExp(e, pt, pe, ps);
        }
    }
    if (*pt)
        (*pt) = (*pt)->addMod(mod);
    return;

Lerr:
    *pt = Type::terror;
    return;
}

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

    Expression *e;
    Type *t;
    Dsymbol *s;
    resolve(loc, sc, &e, &t, &s);
    if (s && (t = s->getType()) != NULL)
        t = t->addMod(mod);
    if (!t)
    {
        error(loc, "%s is used as a type", toChars());
        t = Type::terror;
    }
    return t;
}

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

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

const char *TypeEnum::kind()
{
    return "enum";
}

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

Type *TypeEnum::semantic(Loc, Scope *)
{
    //printf("TypeEnum::semantic() %s\n", toChars());
    if (deco)
        return this;
    return merge();
}

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

unsigned TypeEnum::alignsize()
{
    Type *t = sym->getMemtype(Loc());
    if (t->ty == Terror)
        return 4;
    return t->alignsize();
}

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

Type *TypeEnum::toBasetype()
{
    if (!sym->members && !sym->memtype)
        return this;
    Type *tb = sym->getMemtype(Loc())->toBasetype();
    return tb->castMod(mod);         // retain modifier bits from 'this'
}

Expression *TypeEnum::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    // Bugzilla 14010
    if (ident == Id::_mangleof)
        return getProperty(e->loc, ident, flag & 1);

    if (sym->semanticRun < PASSsemanticdone)
        sym->semantic(NULL);
    if (!sym->members)
    {
        if (sym->isSpecial())
        {
            /* Special enums forward to the base type
             */
            e = sym->memtype->dotExp(sc, e, ident, flag);
        }
        else if (!(flag & 1))
        {
            sym->error("is forward referenced when looking for '%s'", ident->toChars());
            e = new ErrorExp();
        }
        else
            e = NULL;
        return e;
    }

    Dsymbol *s = sym->search(e->loc, ident);
    if (!s)
    {
        if (ident == Id::max ||
            ident == Id::min ||
            ident == Id::_init)
        {
            return getProperty(e->loc, ident, flag & 1);
        }
        Expression *res = sym->getMemtype(Loc())->dotExp(sc, e, ident, 1);
        if (!(flag & 1) && !res)
        {
            if (Dsymbol *ns = sym->search_correct(ident))
                e->error("no property '%s' for type '%s'. Did you mean '%s.%s' ?",
                    ident->toChars(), toChars(), toChars(), ns->toChars());
            else
                e->error("no property '%s' for type '%s'",
                    ident->toChars(), toChars());

            return new ErrorExp();
        }
        return res;
    }
    EnumMember *m = s->isEnumMember();
    return m->getVarExp(e->loc, sc);
}

Expression *TypeEnum::getProperty(Loc loc, Identifier *ident, int flag)
{
    Expression *e;
    if (ident == Id::max || ident == Id::min)
    {
        return sym->getMaxMinValue(loc, ident);
    }
    else if (ident == Id::_init)
    {
        e = defaultInitLiteral(loc);
    }
    else if (ident == Id::stringof)
    {
        const char *s = toChars();
        e = new StringExp(loc, const_cast<char *>(s), strlen(s));
        Scope sc;
        e = ::semantic(e, &sc);
    }
    else if (ident == Id::_mangleof)
    {
        e = Type::getProperty(loc, ident, flag);
    }
    else
    {
        e = toBasetype()->getProperty(loc, ident, flag);
    }
    return e;
}

bool TypeEnum::isintegral()
{
    return sym->getMemtype(Loc())->isintegral();
}

bool TypeEnum::isfloating()
{
    return sym->getMemtype(Loc())->isfloating();
}

bool TypeEnum::isreal()
{
    return sym->getMemtype(Loc())->isreal();
}

bool TypeEnum::isimaginary()
{
    return sym->getMemtype(Loc())->isimaginary();
}

bool TypeEnum::iscomplex()
{
    return sym->getMemtype(Loc())->iscomplex();
}

bool TypeEnum::isunsigned()
{
    return sym->getMemtype(Loc())->isunsigned();
}

bool TypeEnum::isscalar()
{
    return sym->getMemtype(Loc())->isscalar();
}

bool TypeEnum::isString()
{
    return sym->getMemtype(Loc())->isString();
}

bool TypeEnum::isAssignable()
{
    return sym->getMemtype(Loc())->isAssignable();
}

bool TypeEnum::isBoolean()
{
    return sym->getMemtype(Loc())->isBoolean();
}

bool TypeEnum::needsDestruction()
{
    return sym->getMemtype(Loc())->needsDestruction();
}

bool TypeEnum::needsNested()
{
    return sym->getMemtype(Loc())->needsNested();
}

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->getMemtype(Loc())->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 &&
        MODimplicitConv(mod, to->mod))
        return MATCHconst;
    return MATCHnomatch;
}


Expression *TypeEnum::defaultInit(Loc loc)
{
    // Initialize to first member of enum
    Expression *e = sym->getDefaultValue(loc);
    e = e->copy();
    e->loc = loc;
    e->type = this;     // to deal with const, immutable, etc., variants
    return e;
}

bool TypeEnum::isZeroInit(Loc loc)
{
    return sym->getDefaultValue(loc)->isBool(false);
}

bool TypeEnum::hasPointers()
{
    return sym->getMemtype(Loc())->hasPointers();
}

bool TypeEnum::hasVoidInitPointers()
{
    return sym->getMemtype(Loc())->hasVoidInitPointers();
}

Type *TypeEnum::nextOf()
{
    return sym->getMemtype(Loc())->nextOf();
}

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

TypeStruct::TypeStruct(StructDeclaration *sym)
        : Type(Tstruct)
{
    this->sym = sym;
    this->att = RECfwdref;
    this->cppmangle = CPPMANGLEdefault;
}

TypeStruct *TypeStruct::create(StructDeclaration *sym)
{
    return new TypeStruct(sym);
}

const char *TypeStruct::kind()
{
    return "struct";
}

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

Type *TypeStruct::semantic(Loc, Scope *sc)
{
    //printf("TypeStruct::semantic('%s')\n", sym->toChars());
    if (deco)
    {
        if (sc && sc->cppmangle != CPPMANGLEdefault)
        {
            if (this->cppmangle == CPPMANGLEdefault)
                this->cppmangle = sc->cppmangle;
            else
                assert(this->cppmangle == sc->cppmangle);
        }
        return this;
    }

    /* Don't semantic for sym because it should be deferred until
     * sizeof needed or its members accessed.
     */
    // instead, parent should be set correctly
    assert(sym->parent);

    if (sym->type->ty == Terror)
        return Type::terror;
    if (sc)
        this->cppmangle = sc->cppmangle;
    return merge();
}

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

unsigned TypeStruct::alignsize()
{
    sym->size(Loc());               // give error for forward references
    return sym->alignsize;
}

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

static Dsymbol *searchSymStruct(Scope *sc, Dsymbol *sym, Expression *e, Identifier *ident)
{
    int flags = sc->flags & SCOPEignoresymbolvisibility ? IgnoreSymbolVisibility : 0;
    Dsymbol *sold = NULL;
    if (global.params.bug10378 || global.params.check10378)
    {
        sold = sym->search(e->loc, ident, flags);
        if (!global.params.check10378)
            return sold;
    }

    Dsymbol *s = sym->search(e->loc, ident, flags | SearchLocalsOnly);
    if (global.params.check10378)
    {
        Dsymbol *snew = s;
        if (sold != snew)
            Scope::deprecation10378(e->loc, sold, snew);
        if (global.params.bug10378)
            s = sold;
    }
    return s;
}

Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    Dsymbol *s;

    assert(e->op != TOKdot);

    // Bugzilla 14010
    if (ident == Id::_mangleof)
        return getProperty(e->loc, ident, flag & 1);

    /* 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 = ::semantic(e, sc);  // do this before turning on noaccesscheck

        sym->size(e->loc);      // do semantic of type

        Expression *e0 = NULL;
        Expression *ev = e->op == TOKtype ? NULL : e;
        if (ev)
            ev = extractSideEffect(sc, "__tup", &e0, ev);

        Expressions *exps = new Expressions;
        exps->reserve(sym->fields.dim);
        for (size_t i = 0; i < sym->fields.dim; i++)
        {
            VarDeclaration *v = sym->fields[i];
            Expression *ex;
            if (ev)
                ex = new DotVarExp(e->loc, ev, v);
            else
            {
                ex = new VarExp(e->loc, v);
                ex->type = ex->type->addMod(e->type->mod);
            }
            exps->push(ex);
        }

        e = new TupleExp(e->loc, e0, exps);
        Scope *sc2 = sc->push();
        sc2->flags = sc->flags | SCOPEnoaccesscheck;
        e = ::semantic(e, sc2);
        sc2->pop();
        return e;
    }

    s = searchSymStruct(sc, sym, e, ident);
L1:
    if (!s)
    {
        return noMember(sc, e, ident, flag);
    }
    if (!(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, s))
    {
        ::deprecation(e->loc, "%s is not visible from module %s", s->toPrettyChars(), sc->_module->toPrettyChars());
        // return noMember(sc, e, ident, flag);
    }
    if (!s->isFuncDeclaration())        // because of overloading
        s->checkDeprecated(e->loc, sc);
    s = s->toAlias();

    EnumMember *em = s->isEnumMember();
    if (em)
    {
        return em->getVarExp(e->loc, sc);
    }

    if (VarDeclaration *v = s->isVarDeclaration())
    {
        if (!v->type ||
            (!v->type->deco && v->inuse))
        {
            if (v->inuse) // Bugzilla 9494
                e->error("circular reference to %s '%s'", v->kind(), v->toPrettyChars());
            else
                e->error("forward reference to %s '%s'", v->kind(), v->toPrettyChars());
            return new ErrorExp();
        }
        if (v->type->ty == Terror)
            return new ErrorExp();

        if ((v->storage_class & STCmanifest) && v->_init)
        {
            if (v->inuse)
            {
                e->error("circular initialization of %s '%s'", v->kind(), v->toPrettyChars());
                return new ErrorExp();
            }
            checkAccess(e->loc, sc, NULL, v);
            Expression *ve = new VarExp(e->loc, v);
            ve = ::semantic(ve, sc);
            return ve;
        }
    }

    if (Type *t = s->getType())
    {
        return ::semantic(new TypeExp(e->loc, t), sc);
    }

    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)
    {
        if (e->op == TOKtype)
            e = new TemplateExp(e->loc, td);
        else
            e = new DotTemplateExp(e->loc, e, td);
        e = ::semantic(e, sc);
        return e;
    }

    TemplateInstance *ti = s->isTemplateInstance();
    if (ti)
    {
        if (!ti->semanticRun)
        {
            ti->semantic(sc);
            if (!ti->inst || ti->errors)    // if template failed to expand
                return new ErrorExp();
        }
        s = ti->inst->toAlias();
        if (!s->isTemplateInstance())
            goto L1;
        if (e->op == TOKtype)
            e = new ScopeExp(e->loc, ti);
        else
            e = new DotExp(e->loc, e, new ScopeExp(e->loc, ti));
        return ::semantic(e, sc);
    }

    if (s->isImport() || s->isModule() || s->isPackage())
    {
        e = ::resolve(e->loc, sc, s, false);
        return e;
    }

    OverloadSet *o = s->isOverloadSet();
    if (o)
    {
        OverExp *oe = new OverExp(e->loc, o);
        if (e->op == TOKtype)
            return oe;
        return new DotExp(e->loc, e, oe);
    }

    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:
         *    Struct.d
         */
        if (TupleDeclaration *tup = d->isTupleDeclaration())
        {
            e = new TupleExp(e->loc, tup);
            e = ::semantic(e, sc);
            return e;
        }
        if (d->needThis() && sc->intypeof != 1)
        {
            /* Rewrite as:
             *  this.d
             */
            if (hasThis(sc))
            {
                e = new DotVarExp(e->loc, new ThisExp(e->loc), d);
                e = ::semantic(e, sc);
                return e;
            }
        }
        if (d->semanticRun == PASSinit)
            d->semantic(NULL);
        checkAccess(e->loc, sc, e, d);
        VarExp *ve = new VarExp(e->loc, d);
        if (d->isVarDeclaration() && d->needThis())
            ve->type = d->type->addMod(e->type->mod);
        return ve;
    }

    bool unreal = e->op == TOKvar && ((VarExp *)e)->var->isField();
    if (d->isDataseg() || (unreal && d->isField()))
    {
        // (e, d)
        checkAccess(e->loc, sc, e, d);
        Expression *ve = new VarExp(e->loc, d);
        e = unreal ? ve : new CommaExp(e->loc, e, ve);
        e = ::semantic(e, sc);
        return e;
    }

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

structalign_t TypeStruct::alignment()
{
    if (sym->alignment == 0)
        sym->size(sym->loc);
    return sym->alignment;
}

Expression *TypeStruct::defaultInit(Loc)
{
    Declaration *d = new SymbolDeclaration(sym->loc, sym);
    assert(d);
    d->type = this;
    d->storage_class |= STCrvalue;      // Bugzilla 14398
    return new VarExp(sym->loc, d);
}

/***************************************
 * Use when we prefer the default initializer to be a literal,
 * rather than a global immutable variable.
 */
Expression *TypeStruct::defaultInitLiteral(Loc loc)
{
    sym->size(loc);
    if (sym->sizeok != SIZEOKdone)
        return new ErrorExp();
    Expressions *structelems = new Expressions();
    structelems->setDim(sym->fields.dim - sym->isNested());
    unsigned offset = 0;
    for (size_t j = 0; j < structelems->dim; j++)
    {
        VarDeclaration *vd = sym->fields[j];
        Expression *e;
        if (vd->inuse)
        {
            error(loc, "circular reference to '%s'", vd->toPrettyChars());
            return new ErrorExp();
        }
        if (vd->offset < offset || vd->type->size() == 0)
            e = NULL;
        else if (vd->_init)
        {
            if (vd->_init->isVoidInitializer())
                e = NULL;
            else
                e = vd->getConstInitializer(false);
        }
        else
            e = vd->type->defaultInitLiteral(loc);
        if (e && e->op == TOKerror)
            return e;
        if (e)
            offset = vd->offset + (unsigned)vd->type->size();
        (*structelems)[j] = e;
    }
    StructLiteralExp *structinit = new StructLiteralExp(loc, (StructDeclaration *)sym, structelems);

    /* Copy from the initializer symbol for larger symbols,
     * otherwise the literals expressed as code get excessively large.
     */
    if (size(loc) > Target::ptrsize * 4U && !needsNested())
        structinit->useStaticInit = true;

    structinit->type = this;
    return structinit;
}


bool TypeStruct::isZeroInit(Loc)
{
    return sym->zeroInit != 0;
}

bool TypeStruct::isBoolean()
{
    return false;
}

bool TypeStruct::needsDestruction()
{
    return sym->dtor != NULL;
}

bool TypeStruct::needsNested()
{
    if (sym->isNested())
        return true;

    for (size_t i = 0; i < sym->fields.dim; i++)
    {
        VarDeclaration *v = sym->fields[i];
        if (!v->isDataseg() && v->type->needsNested())
            return true;
    }
    return false;
}

bool TypeStruct::isAssignable()
{
    bool assignable = true;
    unsigned offset = ~0;  // dead-store initialize to prevent spurious warning

    /* If any of the fields are const or immutable,
     * then one cannot assign this struct.
     */
    for (size_t i = 0; i < sym->fields.dim; i++)
    {
        VarDeclaration *v = sym->fields[i];
        //printf("%s [%d] v = (%s) %s, v->offset = %d, v->parent = %s", sym->toChars(), i, v->kind(), v->toChars(), v->offset, v->parent->kind());
        if (i == 0)
            ;
        else if (v->offset == offset)
        {
            /* If any fields of anonymous union are assignable,
             * then regard union as assignable.
             * This is to support unsafe things like Rebindable templates.
             */
            if (assignable)
                continue;
        }
        else
        {
            if (!assignable)
                return false;
        }
        assignable = v->type->isMutable() && v->type->isAssignable();
        offset = v->offset;
        //printf(" -> assignable = %d\n", assignable);
    }

    return assignable;
}

bool TypeStruct::hasPointers()
{
    // Probably should cache this information in sym rather than recompute
    StructDeclaration *s = sym;

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

bool TypeStruct::hasVoidInitPointers()
{
    // Probably should cache this information in sym rather than recompute
    StructDeclaration *s = sym;

    sym->size(Loc()); // give error for forward references
    for (size_t i = 0; i < s->fields.dim; i++)
    {
        VarDeclaration *v = s->fields[i];
        if (v->_init && v->_init->isVoidInitializer() && v->type->hasPointers())
            return true;
        if (!v->_init && v->type->hasVoidInitPointers())
            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)
        {
            m = MATCHconst;
            if (MODimplicitConv(mod, to->mod))
                ;
            else
            {
                /* Check all the fields. If they can all be converted,
                 * allow the conversion.
                 */
                unsigned offset = ~0;   // dead-store to prevent spurious warning
                for (size_t i = 0; i < sym->fields.dim; i++)
                {
                    VarDeclaration *v = sym->fields[i];
                    if (i == 0)
                        ;
                    else if (v->offset == offset)
                    {
                        if (m > MATCHnomatch)
                            continue;
                    }
                    else
                    {
                        if (m <= MATCHnomatch)
                            return m;
                    }

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

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

                    // field match
                    MATCH mf = tvf->implicitConvTo(tv);
                    //printf("\t%s => %s, match = %d\n", v->type->toChars(), tv->toChars(), mf);

                    if (mf <= MATCHnomatch)
                        return mf;
                    if (mf < m)         // if field match is worse
                        m = mf;
                    offset = v->offset;
                }
            }
        }
    }
    else if (sym->aliasthis && !(att & RECtracing))
    {
        att = (AliasThisRec)(att | RECtracing);
        m = aliasthisOf()->implicitConvTo(to);
        att = (AliasThisRec)(att & ~RECtracing);
    }
    else
        m = MATCHnomatch;       // no match
    return m;
}

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

unsigned char TypeStruct::deduceWild(Type *t, bool isRef)
{
    if (ty == t->ty && sym == ((TypeStruct *)t)->sym)
        return Type::deduceWild(t, isRef);

    unsigned char wm = 0;

    if (t->hasWild() && sym->aliasthis && !(att & RECtracing))
    {
        att = (AliasThisRec)(att | RECtracing);
        wm = aliasthisOf()->deduceWild(t, isRef);
        att = (AliasThisRec)(att & ~RECtracing);
    }

    return wm;
}

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


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

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

const char *TypeClass::kind()
{
    return "class";
}

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

Type *TypeClass::semantic(Loc, Scope *sc)
{
    //printf("TypeClass::semantic(%s)\n", sym->toChars());
    if (deco)
    {
        if (sc && sc->cppmangle != CPPMANGLEdefault)
        {
            if (this->cppmangle == CPPMANGLEdefault)
                this->cppmangle = sc->cppmangle;
            else
                assert(this->cppmangle == sc->cppmangle);
        }
        return this;
    }

    /* Don't semantic for sym because it should be deferred until
     * sizeof needed or its members accessed.
     */
    // instead, parent should be set correctly
    assert(sym->parent);

    if (sym->type->ty == Terror)
        return Type::terror;
    if (sc)
        this->cppmangle = sc->cppmangle;
    return merge();
}

d_uns64 TypeClass::size(Loc)
{
    return Target::ptrsize;
}

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

static Dsymbol *searchSymClass(Scope *sc, Dsymbol *sym, Expression *e, Identifier *ident)
{
    int flags = sc->flags & SCOPEignoresymbolvisibility ? IgnoreSymbolVisibility : 0;
    Dsymbol *sold = NULL;
    if (global.params.bug10378 || global.params.check10378)
    {
        sold = sym->search(e->loc, ident, flags | IgnoreSymbolVisibility);
        if (!global.params.check10378)
            return sold;
    }

    Dsymbol *s = sym->search(e->loc, ident, flags | SearchLocalsOnly);
    if (!s && !(flags & IgnoreSymbolVisibility))
    {
        s = sym->search(e->loc, ident, flags | SearchLocalsOnly | IgnoreSymbolVisibility);
        if (s && !(flags & IgnoreErrors))
            ::deprecation(e->loc, "%s is not visible from class %s", s->toPrettyChars(), sym->toChars());
    }
    if (global.params.check10378)
    {
        Dsymbol *snew = s;
        if (sold != snew)
            Scope::deprecation10378(e->loc, sold, snew);
        if (global.params.bug10378)
            s = sold;
    }
    return s;
}

Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag)
{
    Dsymbol *s;
    assert(e->op != TOKdot);

    // Bugzilla 12543
    if (ident == Id::__sizeof || ident == Id::__xalignof || ident == Id::_mangleof)
    {
        return Type::getProperty(e->loc, ident, 0);
    }

    /* If e.tupleof
     */
    if (ident == Id::_tupleof)
    {
        /* Create a TupleExp
         */
        e = ::semantic(e, sc);  // do this before turning on noaccesscheck

        sym->size(e->loc); // do semantic of type

        Expression *e0 = NULL;
        Expression *ev = e->op == TOKtype ? NULL : e;
        if (ev)
            ev = extractSideEffect(sc, "__tup", &e0, ev);

        Expressions *exps = new Expressions;
        exps->reserve(sym->fields.dim);
        for (size_t i = 0; i < sym->fields.dim; i++)
        {
            VarDeclaration *v = sym->fields[i];
            // Don't include hidden 'this' pointer
            if (v->isThisDeclaration())
                continue;
            Expression *ex;
            if (ev)
                ex = new DotVarExp(e->loc, ev, v);
            else
            {
                ex = new VarExp(e->loc, v);
                ex->type = ex->type->addMod(e->type->mod);
            }
            exps->push(ex);
        }

        e = new TupleExp(e->loc, e0, exps);
        Scope *sc2 = sc->push();
        sc2->flags = sc->flags | SCOPEnoaccesscheck;
        e = ::semantic(e, sc2);
        sc2->pop();
        return e;
    }

    s = searchSymClass(sc, sym, e, ident);
L1:
    if (!s)
    {
        // See if it's 'this' class or a base class
        if (sym->ident == ident)
        {
            if (e->op == TOKtype)
                return Type::getProperty(e->loc, ident, 0);
            e = new DotTypeExp(e->loc, e, sym);
            e = ::semantic(e, sc);
            return e;
        }
        if (ClassDeclaration *cbase = sym->searchBase(ident))
        {
            if (e->op == TOKtype)
                return Type::getProperty(e->loc, ident, 0);
            if (InterfaceDeclaration *ifbase = cbase->isInterfaceDeclaration())
                e = new CastExp(e->loc, e, ifbase->type);
            else
                e = new DotTypeExp(e->loc, e, cbase);
            e = ::semantic(e, sc);
            return e;
        }

        if (ident == Id::classinfo)
        {
            if (!Type::typeinfoclass)
            {
                error(e->loc, "`object.TypeInfo_Class` could not be found, but is implicitly used");
                return new ErrorExp();
            }

            Type *t = Type::typeinfoclass->type;
            if (e->op == TOKtype || e->op == TOKdottype)
            {
                /* For type.classinfo, we know the classinfo
                 * at compile time.
                 */
                if (!sym->vclassinfo)
                    sym->vclassinfo = new TypeInfoClassDeclaration(sym->type);
                e = new VarExp(e->loc, sym->vclassinfo);
                e = e->addressOf();
                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[]
                 */
                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);
            }
            return e;
        }

        if (ident == Id::__vptr)
        {
            /* The pointer to the vtbl[]
             * *cast(immutable(void*)**)e
             */
            e = e->castTo(sc, tvoidptr->immutableOf()->pointerTo()->pointerTo());
            e = new PtrExp(e->loc, e);
            e = ::semantic(e, 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 = ::semantic(e, sc);
            return e;
        }

        if (ident == Id::outer && sym->vthis)
        {
            if (sym->vthis->semanticRun == PASSinit)
                sym->vthis->semantic(NULL);

            if (ClassDeclaration *cdp = sym->toParent2()->isClassDeclaration())
            {
                DotVarExp *dve = new DotVarExp(e->loc, e, sym->vthis);
                dve->type = cdp->type->addMod(e->type->mod);
                return dve;
            }

            /* Bugzilla 15839: Find closest parent class through nested functions.
             */
            for (Dsymbol *p = sym->toParent2(); p; p = p->toParent2())
            {
                FuncDeclaration *fd = p->isFuncDeclaration();
                if (!fd)
                    break;
                if (fd->isNested())
                    continue;
                AggregateDeclaration *ad = fd->isThis();
                if (!ad)
                    break;
                if (ad->isClassDeclaration())
                {
                    ThisExp *ve = new ThisExp(e->loc);

                    ve->var = fd->vthis;
                    const bool nestedError = fd->vthis->checkNestedReference(sc, e->loc);
                    assert(!nestedError);

                    ve->type = fd->vthis->type->addMod(e->type->mod);
                    return ve;
                }
                break;
            }

            // Continue to show enclosing function's frame (stack or closure).
            DotVarExp *dve = new DotVarExp(e->loc, e, sym->vthis);
            dve->type = sym->vthis->type->addMod(e->type->mod);
            return dve;
        }

        return noMember(sc, e, ident, flag & 1);
    }
    if (!(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, s))
    {
        ::deprecation(e->loc, "%s is not visible from module %s", s->toPrettyChars(), sc->_module->toPrettyChars());
        // return noMember(sc, e, ident, flag);
    }
    if (!s->isFuncDeclaration())        // because of overloading
        s->checkDeprecated(e->loc, sc);
    s = s->toAlias();

    EnumMember *em = s->isEnumMember();
    if (em)
    {
        return em->getVarExp(e->loc, sc);
    }

    if (VarDeclaration *v = s->isVarDeclaration())
    {
        if (!v->type ||
            (!v->type->deco && v->inuse))
        {
            if (v->inuse) // Bugzilla 9494
                e->error("circular reference to %s '%s'", v->kind(), v->toPrettyChars());
            else
                e->error("forward reference to %s '%s'", v->kind(), v->toPrettyChars());
            return new ErrorExp();
        }
        if (v->type->ty == Terror)
            return new ErrorExp();

        if ((v->storage_class & STCmanifest) && v->_init)
        {
            if (v->inuse)
            {
                e->error("circular initialization of %s '%s'", v->kind(), v->toPrettyChars());
                return new ErrorExp();
            }
            checkAccess(e->loc, sc, NULL, v);
            Expression *ve = new VarExp(e->loc, v);
            ve = ::semantic(ve, sc);
            return ve;
        }
    }

    if (Type *t = s->getType())
    {
        return ::semantic(new TypeExp(e->loc, t), sc);
    }

    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)
    {
        if (e->op == TOKtype)
            e = new TemplateExp(e->loc, td);
        else
            e = new DotTemplateExp(e->loc, e, td);
        e = ::semantic(e, sc);
        return e;
    }

    TemplateInstance *ti = s->isTemplateInstance();
    if (ti)
    {
        if (!ti->semanticRun)
        {
            ti->semantic(sc);
            if (!ti->inst || ti->errors)    // if template failed to expand
                return new ErrorExp();
        }
        s = ti->inst->toAlias();
        if (!s->isTemplateInstance())
            goto L1;
        if (e->op == TOKtype)
            e = new ScopeExp(e->loc, ti);
        else
            e = new DotExp(e->loc, e, new ScopeExp(e->loc, ti));
        return ::semantic(e, sc);
    }

    if (s->isImport() || s->isModule() || s->isPackage())
    {
        e = ::resolve(e->loc, sc, s, false);
        return e;
    }

    OverloadSet *o = s->isOverloadSet();
    if (o)
    {
        OverExp *oe = new OverExp(e->loc, o);
        if (e->op == TOKtype)
            return oe;
        return new DotExp(e->loc, e, oe);
    }

    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 (TupleDeclaration *tup = d->isTupleDeclaration())
        {
            e = new TupleExp(e->loc, tup);
            e = ::semantic(e, sc);
            return e;
        }
        if (d->needThis() && sc->intypeof != 1)
        {
            /* Rewrite as:
             *  this.d
             */
            if (hasThis(sc))
            {
                // This is almost same as getRightThis() in expression.c
                Expression *e1 = new ThisExp(e->loc);
                e1 = ::semantic(e1, sc);
            L2:
                Type *t = e1->type->toBasetype();
                ClassDeclaration *cd = e->type->isClassHandle();
                ClassDeclaration *tcd = t->isClassHandle();
                if (cd && tcd && (tcd == cd || cd->isBaseOf(tcd, NULL)))
                {
                    e = new DotTypeExp(e1->loc, e1, cd);
                    e = new DotVarExp(e->loc, e, d);
                    e = ::semantic(e, sc);
                    return e;
                }
                if (tcd && tcd->isNested())
                {   /* e1 is the 'this' pointer for an inner class: tcd.
                     * Rewrite it as the 'this' pointer for the outer class.
                     */

                    e1 = new DotVarExp(e->loc, e1, tcd->vthis);
                    e1->type = tcd->vthis->type;
                    e1->type = e1->type->addMod(t->mod);
                    // Do not call checkNestedRef()
                    //e1 = ::semantic(e1, sc);

                    // Skip up over nested functions, and get the enclosing
                    // class type.
                    int n = 0;
                    for (s = tcd->toParent();
                         s && s->isFuncDeclaration();
                         s = s->toParent())
                    {   FuncDeclaration *f = s->isFuncDeclaration();
                        if (f->vthis)
                        {
                            //printf("rewriting e1 to %s's this\n", f->toChars());
                            n++;
                            e1 = new VarExp(e->loc, f->vthis);
                        }
                        else
                        {
                            e = new VarExp(e->loc, d);
                            return e;
                        }
                    }
                    if (s && s->isClassDeclaration())
                    {   e1->type = s->isClassDeclaration()->type;
                        e1->type = e1->type->addMod(t->mod);
                        if (n > 1)
                            e1 = ::semantic(e1, sc);
                    }
                    else
                        e1 = ::semantic(e1, sc);
                    goto L2;
                }
            }
        }
        //printf("e = %s, d = %s\n", e->toChars(), d->toChars());
        if (d->semanticRun == PASSinit)
            d->semantic(NULL);
        checkAccess(e->loc, sc, e, d);
        VarExp *ve = new VarExp(e->loc, d);
        if (d->isVarDeclaration() && d->needThis())
            ve->type = d->type->addMod(e->type->mod);
        return ve;
    }

    bool unreal = e->op == TOKvar && ((VarExp *)e)->var->isField();
    if (d->isDataseg() || (unreal && d->isField()))
    {
        // (e, d)
        checkAccess(e->loc, sc, e, d);
        Expression *ve = new VarExp(e->loc, d);
        e = unreal ? ve : new CommaExp(e->loc, e, ve);
        e = ::semantic(e, sc);
        return e;
    }

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

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

bool TypeClass::isscope()
{
    return sym->isscope;
}

bool TypeClass::isBaseOf(Type *t, int *poffset)
{
    if (t && t->ty == Tclass)
    {
        ClassDeclaration *cd = ((TypeClass *)t)->sym;
        if (sym->isBaseOf(cd, poffset))
            return true;
    }
    return false;
}

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)
    {
        //printf("TypeClass::implicitConvTo(to = '%s') %s, isbase = %d %d\n", to->toChars(), toChars(), cdto->isBaseInfoComplete(), sym->isBaseInfoComplete());
        if (cdto->semanticRun < PASSsemanticdone && !cdto->isBaseInfoComplete())
            cdto->semantic(NULL);
        if (sym->semanticRun < PASSsemanticdone && !sym->isBaseInfoComplete())
            sym->semantic(NULL);
        if (cdto->isBaseOf(sym, NULL) && MODimplicitConv(mod, to->mod))
        {
            //printf("'to' is base\n");
            return MATCHconvert;
        }
    }

    m = MATCHnomatch;
    if (sym->aliasthis && !(att & RECtracing))
    {
        att = (AliasThisRec)(att | RECtracing);
        m = aliasthisOf()->implicitConvTo(to);
        att = (AliasThisRec)(att & ~RECtracing);
    }

    return m;
}

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

    /* Conversion derived to const(base)
     */
    int offset = 0;
    if (to->isBaseOf(this, &offset) && offset == 0 &&
        MODimplicitConv(mod, to->mod))
    {
        // Disallow:
        //  derived to base
        //  inout(derived) to inout(base)
        if (!to->isMutable() && !to->isWild())
            return MATCHconvert;
    }

    return MATCHnomatch;
}

unsigned char TypeClass::deduceWild(Type *t, bool isRef)
{
    ClassDeclaration *cd = t->isClassHandle();
    if (cd && (sym == cd || cd->isBaseOf(sym, NULL)))
        return Type::deduceWild(t, isRef);

    unsigned char wm = 0;

    if (t->hasWild() && sym->aliasthis && !(att & RECtracing))
    {
        att = (AliasThisRec)(att | RECtracing);
        wm = aliasthisOf()->deduceWild(t, isRef);
        att = (AliasThisRec)(att & ~RECtracing);
    }

    return wm;
}

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

Expression *TypeClass::defaultInit(Loc loc)
{
    return new NullExp(loc, this);
}

bool TypeClass::isZeroInit(Loc)
{
    return true;
}

bool TypeClass::isBoolean()
{
    return true;
}

bool TypeClass::hasPointers()
{
    return true;
}

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

TypeTuple::TypeTuple(Parameters *arguments)
    : Type(Ttuple)
{
    //printf("TypeTuple(this = %p)\n", this);
    this->arguments = arguments;
    //printf("TypeTuple() %p, %s\n", this, toChars());
}

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

TypeTuple::TypeTuple(Expressions *exps)
    : Type(Ttuple)
{
    Parameters *arguments = new Parameters;
    if (exps)
    {
        arguments->setDim(exps->dim);
        for (size_t i = 0; i < exps->dim; i++)
        {   Expression *e = (*exps)[i];
            if (e->type->ty == Ttuple)
                e->error("cannot form tuple of tuples");
            Parameter *arg = new Parameter(STCundefined, e->type, NULL, NULL);
            (*arguments)[i] = arg;
        }
    }
    this->arguments = arguments;
    //printf("TypeTuple() %p, %s\n", this, toChars());
}

TypeTuple *TypeTuple::create(Parameters *arguments)
{
    return new TypeTuple(arguments);
}

/*******************************************
 * Type tuple with 0, 1 or 2 types in it.
 */
TypeTuple::TypeTuple()
    : Type(Ttuple)
{
    arguments = new Parameters();
}

TypeTuple::TypeTuple(Type *t1)
    : Type(Ttuple)
{
    arguments = new Parameters();
    arguments->push(new Parameter(0, t1, NULL, NULL));
}

TypeTuple::TypeTuple(Type *t1, Type *t2)
    : Type(Ttuple)
{
    arguments = new Parameters();
    arguments->push(new Parameter(0, t1, NULL, NULL));
    arguments->push(new Parameter(0, t2, NULL, NULL));
}

const char *TypeTuple::kind()
{
    return "tuple";
}

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

Type *TypeTuple::semantic(Loc, Scope *)
{
    //printf("TypeTuple::semantic(this = %p)\n", this);
    //printf("TypeTuple::semantic() %p, %s\n", this, 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;
}

bool TypeTuple::equals(RootObject *o)
{
    Type *t = (Type *)o;
    //printf("TypeTuple::equals(%s, %s)\n", toChars(), t->toChars());
    if (this == t)
        return true;
    if (t->ty == Ttuple)
    {
        TypeTuple *tt = (TypeTuple *)t;
        if (arguments->dim == tt->arguments->dim)
        {
            for (size_t i = 0; i < tt->arguments->dim; i++)
            {
                Parameter *arg1 = (*arguments)[i];
                Parameter *arg2 = (*tt->arguments)[i];
                if (!arg1->type->equals(arg2->type))
                    return false;
            }
            return true;
        }
    }
    return false;
}

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

    if (ident == Id::length)
    {
        e = new IntegerExp(loc, arguments->dim, Type::tsize_t);
    }
    else if (ident == Id::_init)
    {
        e = defaultInitLiteral(loc);
    }
    else if (flag)
    {
        e = NULL;
    }
    else
    {
        error(loc, "no property '%s' for tuple '%s'", ident->toChars(), toChars());
        e = new ErrorExp();
    }
    return e;
}

Expression *TypeTuple::defaultInit(Loc loc)
{
    Expressions *exps = new Expressions();
    exps->setDim(arguments->dim);
    for (size_t i = 0; i < arguments->dim; i++)
    {
        Parameter *p = (*arguments)[i];
        assert(p->type);
        Expression *e = p->type->defaultInitLiteral(loc);
        if (e->op == TOKerror)
            return e;
        (*exps)[i] = e;
    }
    return new TupleExp(loc, exps);
}

/***************************** 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;
}

const char *TypeSlice::kind()
{
    return "slice";
}

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());
    Type *tn = next->semantic(loc, sc);
    //printf("next: %s\n", tn->toChars());

    Type *tbn = tn->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->ctfeInterpret();
    uinteger_t i1 = lwr->toUInteger();

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

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

    next = tn;
    transitive();

    Parameters *args = new Parameters;
    args->reserve((size_t)(i2 - i1));
    for (size_t i = (size_t)i1; i < (size_t)i2; i++)
    {
        Parameter *arg = (*tt->arguments)[i];
        args->push(arg);
    }
    Type *t = new TypeTuple(args);
    return t->semantic(loc, sc);
}

void TypeSlice::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid)
{
    next->resolve(loc, sc, pe, pt, ps, intypeid);
    if (*pe)
    {
        // It's really a slice expression
        if (Dsymbol *s = getDsymbol(*pe))
            *pe = new DsymbolExp(loc, s);
        *pe = new ArrayExp(loc, *pe, new IntervalExp(loc, lwr, upr));
    }
    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);
            sc = sc->startCTFE();
            lwr = ::semantic(lwr, sc);
            upr = ::semantic(upr, sc);
            sc = sc->endCTFE();
            sc = sc->pop();

            lwr = lwr->ctfeInterpret();
            upr = upr->ctfeInterpret();
            uinteger_t i1 = lwr->toUInteger();
            uinteger_t i2 = upr->toUInteger();

            if (!(i1 <= i2 && i2 <= td->objects->dim))
            {
                error(loc, "slice [%llu..%llu] is out of range of [0..%u]", i1, i2, td->objects->dim);
                *ps = NULL;
                *pt = Type::terror;
                return;
            }

            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((size_t)(i2 - i1));
            for (size_t i = 0; i < objects->dim; i++)
            {
                (*objects)[i] = (*td->objects)[(size_t)i1 + i];
            }

            TupleDeclaration *tds = new TupleDeclaration(loc, td->ident, objects);
            *ps = tds;
        }
        else
            goto Ldefault;
    }
    else
    {
        if ((*pt)->ty != Terror)
            next = *pt;     // prevent re-running semantic() on 'next'
     Ldefault:
        Type::resolve(loc, sc, pe, pt, ps, intypeid);
    }
}

/***************************** TypeNull *****************************/

TypeNull::TypeNull()
        : Type(Tnull)
{
}

const char *TypeNull::kind()
{
    return "null";
}

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

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

    // NULL implicitly converts to any pointer type or dynamic array
    //if (type->ty == Tpointer && type->nextOf()->ty == Tvoid)
    {
        Type *tb = to->toBasetype();
        if (tb->ty == Tnull ||
            tb->ty == Tpointer || tb->ty == Tarray ||
            tb->ty == Taarray  || tb->ty == Tclass ||
            tb->ty == Tdelegate)
            return MATCHconst;
    }

    return MATCHnomatch;
}

bool TypeNull::isBoolean()
{
    return true;
}

d_uns64 TypeNull::size(Loc loc)
{
    return tvoidptr->size(loc);
}

Expression *TypeNull::defaultInit(Loc)
{
    return new NullExp(Loc(), Type::tnull);
}

/***************************** Parameter *****************************/

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

Parameter *Parameter::create(StorageClass storageClass, Type *type, Identifier *ident, Expression *defaultArg)
{
    return new Parameter(storageClass, type, ident, defaultArg);
}

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

Parameters *Parameter::arraySyntaxCopy(Parameters *parameters)
{
    Parameters *params = NULL;
    if (parameters)
    {
        params = new Parameters();
        params->setDim(parameters->dim);
        for (size_t i = 0; i < params->dim; i++)
            (*params)[i] = (*parameters)[i]->syntaxCopy();
    }
    return params;
}

/****************************************************
 * Determine if parameter is a lazy array of delegates.
 * If so, return the return type of those delegates.
 * If not, return NULL.
 *
 * Returns T if the type is one of the following forms:
 *      T delegate()[]
 *      T delegate()[dim]
 */

Type *Parameter::isLazyArray()
{
    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 = td->next->toTypeFunction();

            if (!tf->varargs && Parameter::dim(tf->parameters) == 0)
            {
                return tf->next;    // return type of delegate
            }
        }
    }
    return NULL;
}

/***************************************
 * Determine number of arguments, folding in tuples.
 */

static int dimDg(void *ctx, size_t, Parameter *)
{
    ++*(size_t *)ctx;
    return 0;
}

size_t Parameter::dim(Parameters *parameters)
{
    size_t n = 0;
    Parameter_foreach(parameters, &dimDg, &n);
    return n;
}

/***************************************
 * Get nth Parameter, folding in tuples.
 * Returns:
 *      Parameter*      nth Parameter
 *      NULL            not found, *pn gets incremented by the number
 *                      of Parameters
 */

struct GetNthParamCtx
{
    size_t nth;
    Parameter *param;
};

static int getNthParamDg(void *ctx, size_t n, Parameter *p)
{
    GetNthParamCtx *c = (GetNthParamCtx *)ctx;
    if (n == c->nth)
    {
        c->param = p;
        return 1;
    }
    return 0;
}

Parameter *Parameter::getNth(Parameters *parameters, size_t nth, size_t *)
{
    GetNthParamCtx ctx = { nth, NULL };
    int res = Parameter_foreach(parameters, &getNthParamDg, &ctx);
    return res ? ctx.param : NULL;
}

/***************************************
 * Expands tuples in args in depth first order. Calls
 * dg(void *ctx, size_t argidx, Parameter *arg) for each Parameter.
 * If dg returns !=0, stops and returns that value else returns 0.
 * Use this function to avoid the O(N + N^2/2) complexity of
 * calculating dim and calling N times getNth.
 */

int Parameter_foreach(Parameters *parameters, ForeachDg dg, void *ctx, size_t *pn)
{
    assert(dg);
    if (!parameters)
        return 0;

    size_t n = pn ? *pn : 0; // take over index
    int result = 0;
    for (size_t i = 0; i < parameters->dim; i++)
    {
        Parameter *p = (*parameters)[i];
        Type *t = p->type->toBasetype();

        if (t->ty == Ttuple)
        {
            TypeTuple *tu = (TypeTuple *)t;
            result = Parameter_foreach(tu->arguments, dg, ctx, &n);
        }
        else
            result = dg(ctx, n++, p);

        if (result)
            break;
    }

    if (pn)
        *pn = n; // update index
    return result;
}


const char *Parameter::toChars()
{
    return ident ? ident->toChars() : "__anonymous_param";
}

/*********************************
 * Compute covariance of parameters `this` and `p`
 * as determined by the storage classes of both.
 * Params:
 *  p = Parameter to compare with
 * Returns:
 *  true = `this` can be used in place of `p`
 *  false = nope
 */
bool Parameter::isCovariant(bool returnByRef, const Parameter *p) const
{
    const StorageClass stc = STCref | STCin | STCout | STClazy;
    if ((this->storageClass & stc) != (p->storageClass & stc))
        return false;

    return isCovariantScope(returnByRef, this->storageClass, p->storageClass);
}

bool Parameter::isCovariantScope(bool returnByRef, StorageClass from, StorageClass to)
{
    if (from == to)
        return true;

    struct SR
    {
        /* Classification of 'scope-return-ref' possibilities
        */
        enum
        {
            SRNone,
            SRScope,
            SRReturnScope,
            SRRef,
            SRReturnRef,
            SRRefScope,
            SRReturnRef_Scope,
            SRRef_ReturnScope,
            SRMAX,
        };

        /* Shrinking the representation is necessary because StorageClass is so wide
         * Params:
         *   returnByRef = true if the function returns by ref
         *   stc = storage class of parameter
         */
        static unsigned buildSR(bool returnByRef, StorageClass stc)
        {
            unsigned result;
            StorageClass stc2 = stc & (STCref | STCscope | STCreturn);
            if (stc2 == 0)
                result = SRNone;
            else if (stc2 == STCref)
                result = SRRef;
            else if (stc2 == STCscope)
                result = SRScope;
            else if (stc2 == (STCscope | STCreturn))
                result = SRReturnScope;
            else if (stc2 == (STCref | STCreturn))
                result = SRReturnRef;
            else if (stc2 == (STCscope | STCref))
                result = SRRefScope;
            else if (stc2 == (STCscope | STCref | STCreturn))
                result = returnByRef ? SRReturnRef_Scope : SRRef_ReturnScope;
            else
                assert(0);
            return result;
        }

        static void covariantInit(bool covariant[SRMAX][SRMAX])
        {
            /* Initialize covariant[][] with this:

                 From\To           n   rs  s
                 None              X
                 ReturnScope       X   X
                 Scope             X   X   X

                 From\To           r   rr  rs  rr-s r-rs
                 Ref               X   X
                 ReturnRef             X
                 RefScope          X   X   X   X    X
                 ReturnRef-Scope       X       X
                 Ref-ReturnScope   X   X            X
            */
            for (int i = 0; i < SRMAX; i++)
            {
                covariant[i][i] = true;
                covariant[SRRefScope][i] = true;
            }
            covariant[SRReturnScope][SRNone]        = true;
            covariant[SRScope      ][SRNone]        = true;
            covariant[SRScope      ][SRReturnScope] = true;

            covariant[SRRef            ][SRReturnRef] = true;
            covariant[SRReturnRef_Scope][SRReturnRef] = true;
            covariant[SRRef_ReturnScope][SRRef      ] = true;
            covariant[SRRef_ReturnScope][SRReturnRef] = true;
        }
    };

    /* result is true if the 'from' can be used as a 'to'
     */

    if ((from ^ to) & STCref)               // differing in 'ref' means no covariance
        return false;

    static bool covariant[SR::SRMAX][SR::SRMAX];
    static bool init = false;
    if (!init)
    {
        SR::covariantInit(covariant);
        init = true;
    }

    return covariant[SR::buildSR(returnByRef, from)][SR::buildSR(returnByRef, to)];
}