| |
| /* 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) |
| { |
| unsigned explicitAlignment = alignment(); |
| unsigned naturalAlignment = alignsize(); |
| unsigned actualAlignment = (explicitAlignment == STRUCTALIGN_DEFAULT ? naturalAlignment : explicitAlignment); |
| e = new IntegerExp(loc, actualAlignment, 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()); |
| } |
| } |
| } |
| |
| /**************************************** |
| * 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: |
| |