| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2021 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 *typeToExpression(Type *t); |
| Expression *typeToExpressionHelper(TypeQualified *t, Expression *e, size_t i = 0); |
| RootObject *compileTypeMixin(TypeMixin *tm, Loc loc, Scope *sc); |
| |
| /***************************** 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::tnoreturn; |
| |
| 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::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); |
| sizeTy[Ttraits] = sizeof(TypeTraits); |
| sizeTy[Tmixin] = sizeof(TypeMixin); |
| sizeTy[Tnoreturn] = sizeof(TypeNoreturn); |
| |
| 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(); |
| |
| tnoreturn = new TypeNoreturn(); |
| tnoreturn->deco = tnoreturn->merge()->deco; |
| basic[Tnoreturn] = tnoreturn; |
| |
| 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]; |
| tnoreturn = basic[Tnoreturn]; |
| tnull = new TypeNull(); |
| tnull->deco = tnull->merge()->deco; |
| |
| tvoidptr = tvoid->pointerTo(); |
| tstring = tchar->immutableOf()->arrayOf(); |
| twstring = twchar->immutableOf()->arrayOf(); |
| tdstring = tdchar->immutableOf()->arrayOf(); |
| |
| const bool isLP64 = global.params.isLP64; |
| |
| tsize_t = basic[isLP64 ? Tuns64 : Tuns32]; |
| tptrdiff_t = basic[isLP64 ? Tint64 : Tint32]; |
| 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::trySemantic(Loc loc, Scope *sc) |
| { |
| //printf("+trySemantic(%s) %d\n", toChars(), global.errors); |
| unsigned errors = global.startGagging(); |
| Type *t = typeSemantic(this, 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.extractChars(); |
| } |
| |
| /******************************** |
| * 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.extractChars(); |
| } |
| |
| char *Type::toPrettyChars(bool QualifyTypes) |
| { |
| OutBuffer buf; |
| buf.reserve(16); |
| HdrGenState hgs; |
| hgs.fullQual = QualifyTypes; |
| |
| ::toCBuffer(this, &buf, NULL, &hgs); |
| return buf.extractChars(); |
| } |
| |
| /********************************* |
| * 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.extractChars(); |
| } |
| |
| /** 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->length > 0) |
| { |
| for (size_t i = 0; i < params->length; i++) |
| { |
| Parameter *p = (*params)[i]; |
| Type *ta = stripDefaultArgs(p->type); |
| if (ta != p->type || p->defaultArg || p->ident || p->userAttribDecl) |
| { |
| if (params == parameters) |
| { |
| params = new Parameters(); |
| params->setDim(parameters->length); |
| for (size_t j = 0; j < params->length; j++) |
| (*params)[j] = (*parameters)[j]; |
| } |
| (*params)[i] = new Parameter(p->storageClass, ta, NULL, 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->parameterList.parameters); |
| if (tret == tf->next && params == tf->parameterList.parameters) |
| goto Lnot; |
| tf = (TypeFunction *)tf->copy(); |
| tf->parameterList.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.slice().ptr, buf.length()); |
| 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) |
| { |
| if (Dsymbol *s = toDsymbol(sc)) |
| { |
| 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 = parameterList.parameters; |
| if (mod & MODwild) |
| params = parameterList.parameters->copy(); |
| for (size_t i = 0; i < params->length; i++) |
| { |
| Parameter *p = (*params)[i]; |
| Type *t = p->type->substWildTo(m); |
| if (t == p->type) |
| continue; |
| if (params == parameterList.parameters) |
| params = parameterList.parameters->copy(); |
| (*params)[i] = new Parameter(p->storageClass, t, NULL, NULL, NULL); |
| } |
| if (next == tret && params == parameterList.parameters) |
| return this; |
| |
| // Similar to TypeFunction::syntaxCopy; |
| TypeFunction *t = new TypeFunction(ParameterList(params, parameterList.varargs), |
| tret, 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 = expressionSemantic(e, &sc); |
| } |
| } |
| else if (ident == Id::stringof) |
| { |
| const char *s = toChars(); |
| e = new StringExp(loc, const_cast<char *>(s), strlen(s)); |
| Scope sc; |
| e = expressionSemantic(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->toPrettyChars()); |
| 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 = expressionSemantic(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 > global.recursionLimit) |
| { |
| ::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 = expressionSemantic(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.length(); |
| 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.slice().ptr); |
| //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; |
| } |
| |
| TypeError *Type::isTypeError() |
| { |
| return ty == Terror ? (TypeError *)this : NULL; |
| } |
| |
| TypeVector *Type::isTypeVector() |
| { |
| return ty == Tvector ? (TypeVector *)this : NULL; |
| } |
| |
| TypeSArray *Type::isTypeSArray() |
| { |
| return ty == Tsarray ? (TypeSArray *)this : NULL; |
| } |
| |
| TypeDArray *Type::isTypeDArray() |
| { |
| return ty == Tarray ? (TypeDArray *)this : NULL; |
| } |
| |
| TypeAArray *Type::isTypeAArray() |
| { |
| return ty == Taarray ? (TypeAArray *)this : NULL; |
| } |
| |
| TypePointer *Type::isTypePointer() |
| { |
| return ty == Tpointer ? (TypePointer *)this : NULL; |
| } |
| |
| TypeReference *Type::isTypeReference() |
| { |
| return ty == Treference ? (TypeReference *)this : NULL; |
| } |
| |
| TypeFunction *Type::isTypeFunction() |
| { |
| return ty == Tfunction ? (TypeFunction *)this : NULL; |
| } |
| |
| TypeDelegate *Type::isTypeDelegate() |
| { |
| return ty == Tdelegate ? (TypeDelegate *)this : NULL; |
| } |
| |
| TypeIdentifier *Type::isTypeIdentifier() |
| { |
| return ty == Tident ? (TypeIdentifier *)this : NULL; |
| } |
| |
| TypeInstance *Type::isTypeInstance() |
| { |
| return ty == Tinstance ? (TypeInstance *)this : NULL; |
| } |
| |
| TypeTypeof *Type::isTypeTypeof() |
| { |
| return ty == Ttypeof ? (TypeTypeof *)this : NULL; |
| } |
| |
| TypeReturn *Type::isTypeReturn() |
| { |
| return ty == Treturn ? (TypeReturn *)this : NULL; |
| } |
| |
| TypeStruct *Type::isTypeStruct() |
| { |
| return ty == Tstruct ? (TypeStruct *)this : NULL; |
| } |
| |
| TypeEnum *Type::isTypeEnum() |
| { |
| return ty == Tenum ? (TypeEnum *)this : NULL; |
| } |
| |
| TypeClass *Type::isTypeClass() |
| { |
| return ty == Tclass ? (TypeClass *)this : NULL; |
| } |
| |
| TypeTuple *Type::isTypeTuple() |
| { |
| return ty == Ttuple ? (TypeTuple *)this : NULL; |
| } |
| |
| TypeSlice *Type::isTypeSlice() |
| { |
| return ty == Tslice ? (TypeSlice *)this : NULL; |
| } |
| |
| TypeNull *Type::isTypeNull() |
| { |
| return ty == Tnull ? (TypeNull *)this : NULL; |
| } |
| |
| TypeTraits *Type::isTypeTraits() |
| { |
| return ty == Ttraits ? (TypeTraits *)this : NULL; |
| } |
| |
| TypeMixin *Type::isTypeMixin() |
| { |
| return ty == Tmixin ? (TypeMixin *)this : NULL; |
| } |
| |
| TypeNoreturn *Type::isTypeNoreturn() |
| { |
| return ty == Tnoreturn ? (TypeNoreturn *)this : 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 = typeSemantic(this, 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 = expressionSemantic(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(Type *basetype) |
| { |
| return new TypeVector(basetype); |
| } |
| |
| const char *TypeVector::kind() |
| { |
| return "vector"; |
| } |
| |
| Type *TypeVector::syntaxCopy() |
| { |
| return new TypeVector(basetype->syntaxCopy()); |
| } |
| |
| 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 = expressionSemantic(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 = expressionSemantic(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 = expressionSemantic(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(); |
| } |
| |
| 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 = expressionSemantic(dim, sc); |
| sc = sc->endCTFE(); |
| sc = sc->pop(); |
| |
| dim = dim->ctfeInterpret(); |
| uinteger_t d = dim->toUInteger(); |
| |
| if (d >= td->objects->length) |
| { |
| error(loc, "tuple index %llu exceeds length %u", d, td->objects->length); |
| *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); |
| } |
| } |
| |
| 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 = expressionSemantic(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; |
| } |
| |
| 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; |
| } |
| |
| 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, 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; |
| } |
| |
| 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; |
| } |
| |
| 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(const ParameterList &pl, Type *treturn, LINK linkage, StorageClass stc) |
| : TypeNext(Tfunction, treturn) |
| { |
| //if (!treturn) *(char*)0=0; |
| // assert(treturn); |
| assert(VARARGnone <= pl.varargs && pl.varargs <= VARARGtypesafe); |
| this->parameterList = pl; |
| 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, VarArg varargs, LINK linkage, StorageClass stc) |
| { |
| return new TypeFunction(ParameterList(parameters, varargs), treturn, linkage, stc); |
| } |
| |
| const char *TypeFunction::kind() |
| { |
| return "function"; |
| } |
| |
| Type *TypeFunction::syntaxCopy() |
| { |
| Type *treturn = next ? next->syntaxCopy() : NULL; |
| Parameters *parameters = Parameter::arraySyntaxCopy(parameterList.parameters); |
| TypeFunction *t = new TypeFunction(ParameterList(parameters, parameterList.varargs), |
| treturn, 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->parameterList.varargs != t2->parameterList.varargs) |
| goto Ldistinct; |
| |
| if (t1->parameterList.parameters && t2->parameterList.parameters) |
| { |
| size_t dim = t1->parameterList.length(); |
| if (dim != t2->parameterList.length()) |
| goto Ldistinct; |
| |
| for (size_t i = 0; i < dim; i++) |
| { |
| Parameter *fparam1 = t1->parameterList[i]; |
| Parameter *fparam2 = t2->parameterList[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->parameterList.parameters != t2->parameterList.parameters) |
| { |
| size_t dim1 = t1->parameterList.length(); |
| size_t dim2 = t2->parameterList.length(); |
| 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()) |
| dsymbolSemantic(cd, 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; |
| } |
| |
| 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 = tf->parameterList.length(); |
| for (size_t i = 0; i < dim; i++) |
| { |
| Parameter *fparam = tf->parameterList[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; |
| } |
| |
| // arguments get specially formatted |
| static const char *getParamError(TypeFunction *tf, Expression *arg, Parameter *par) |
| { |
| if (global.gag && !global.params.showGaggedErrors) |
| return NULL; |
| // show qualification when toChars() is the same but types are different |
| const char *at = arg->type->toChars(); |
| bool qual = !arg->type->equals(par->type) && strcmp(at, par->type->toChars()) == 0; |
| if (qual) |
| at = arg->type->toPrettyChars(true); |
| OutBuffer buf; |
| // only mention rvalue if it's relevant |
| const bool rv = !arg->isLvalue() && (par->storageClass & (STCref | STCout)) != 0; |
| buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`", |
| rv ? "rvalue " : "", arg->toChars(), at, |
| parameterToChars(par, tf, qual)); |
| return buf.extractChars(); |
| } |
| |
| static const char *getMatchError(const char *format, ...) |
| { |
| if (global.gag && !global.params.showGaggedErrors) |
| return NULL; |
| OutBuffer buf; |
| va_list ap; |
| va_start(ap, format); |
| buf.vprintf(format, ap); |
| va_end(ap); |
| return buf.extractChars(); |
| } |
| |
| /******************************** |
| * 'args' are being matched to function 'this' |
| * Determine match level. |
| * Input: |
| * flag 1 performing a partial ordering match |
| * pMessage address to store error message, or null |
| * Returns: |
| * MATCHxxxx |
| */ |
| |
| MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag, const char **pMessage) |
| { |
| //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 = parameterList.length(); |
| size_t nargs = args ? args->length : 0; |
| if (nargs > nparams) |
| { |
| if (parameterList.varargs == VARARGnone) |
| { |
| // suppress early exit if an error message is wanted, |
| // so we can check any matching args are valid |
| if (!pMessage) |
| 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 = parameterList[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 = parameterList[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) |
| { |
| if (pMessage) *pMessage = getParamError(this, arg, p); |
| 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 |
| { |
| if (pMessage) *pMessage = getParamError(this, arg, p); |
| 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)) |
| { |
| if (pMessage) *pMessage = getParamError(this, arg, p); |
| goto Nomatch; |
| } |
| } |
| } |
| |
| /* prefer matching the element type rather than the array |
| * type when more arguments are present with T[]... |
| */ |
| if (parameterList.varargs == VARARGtypesafe && u + 1 == nparams && nargs > nparams) |
| goto L1; |
| |
| //printf("\tm = %d\n", m); |
| if (m == MATCHnomatch) // if no match |
| { |
| L1: |
| if (parameterList.varargs == VARARGtypesafe && 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) |
| { |
| if (pMessage) |
| *pMessage = getMatchError("expected %llu variadic argument(s), not %zu", 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) |
| { |
| if (pMessage) *pMessage = getParamError(this, arg, p); |
| 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: |
| break; |
| } |
| } |
| if (pMessage && u < nargs) |
| *pMessage = getParamError(this, (*args)[u], p); |
| else if (pMessage) |
| *pMessage = getMatchError("missing argument for parameter #%d: `%s`", |
| u + 1, parameterToChars(p, this, false)); |
| goto Nomatch; |
| } |
| if (m < match) |
| match = m; // pick worst match |
| } |
| |
| Ldone: |
| if (pMessage && !parameterList.varargs && nargs > nparams) |
| { |
| // all parameters had a match, but there are surplus args |
| *pMessage = getMatchError("expected %d argument(s), not %d", nparams, nargs); |
| goto Nomatch; |
| } |
| //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 = parameterList.length(); |
| for (size_t i = 0; i < dim; i++) |
| { |
| Parameter *fparam = parameterList[i]; |
| if (fparam->storageClass & STClazy) |
| return true; |
| } |
| return false; |
| } |
| |
| /******************************* |
| * Check for `extern (D) U func(T t, ...)` variadic function type, |
| * which has `_arguments[]` added as the first argument. |
| * Returns: |
| * true if D-style variadic |
| */ |
| bool TypeFunction::isDstyleVariadic() const |
| { |
| return linkage == LINKd && parameterList.varargs == VARARGvariadic; |
| } |
| |
| /*************************** |
| * 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 = parameterList.length(); |
| for (size_t i = 0; i < dim; i++) |
| { |
| Parameter *fparam = parameterList[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->parameterList, t->next, 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::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 = expressionSemantic(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 = expressionSemantic(e, sc); |
| } |
| else |
| { |
| e = Type::dotExp(sc, e, ident, flag); |
| } |
| return e; |
| } |
| |
| bool TypeDelegate::hasPointers() |
| { |
| return true; |
| } |
| |
| /***************************** TypeTraits ********************************/ |
| |
| TypeTraits::TypeTraits(const Loc &loc, TraitsExp *exp) |
| : Type(Ttraits) |
| { |
| this->loc = loc; |
| this->exp = exp; |
| this->sym = NULL; |
| } |
| |
| Type *TypeTraits::syntaxCopy() |
| { |
| TraitsExp *te = (TraitsExp *) exp->syntaxCopy(); |
| TypeTraits *tt = new TypeTraits(loc, te); |
| tt->mod = mod; |
| return tt; |
| } |
| |
| Dsymbol *TypeTraits::toDsymbol(Scope *sc) |
| { |
| Type *t = NULL; |
| Expression *e = NULL; |
| Dsymbol *s = NULL; |
| resolve(loc, sc, &e, &t, &s); |
| if (t && t->ty != Terror) |
| s = t->toDsymbol(sc); |
| else if (e) |
| s = getDsymbol(e); |
| |
| return s; |
| } |
| |
| void TypeTraits::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool) |
| { |
| *pt = NULL; |
| *pe = NULL; |
| *ps = NULL; |
| |
| if (Type *t = typeSemantic(this, loc, sc)) |
| *pt = t; |
| else if (sym) |
| *ps = sym; |
| else |
| *pt = Type::terror; |
| } |
| |
| d_uns64 TypeTraits::size(Loc) |
| { |
| return SIZE_INVALID; |
| } |
| |
| /***************************** TypeMixin *****************************/ |
| |
| /****** |
| * Implements mixin types. |
| * |
| * Semantic analysis will convert it to a real type. |
| */ |
| TypeMixin::TypeMixin(const Loc &loc, Expressions *exps) |
| : Type(Tmixin) |
| { |
| this->loc = loc; |
| this->exps = exps; |
| this->obj = NULL; // cached result of semantic analysis. |
| } |
| |
| const char *TypeMixin::kind() |
| { |
| return "mixin"; |
| } |
| |
| Type *TypeMixin::syntaxCopy() |
| { |
| return new TypeMixin(loc, Expression::arraySyntaxCopy(exps)); |
| } |
| |
| Dsymbol *TypeMixin::toDsymbol(Scope *sc) |
| { |
| Type *t = NULL; |
| Expression *e = NULL; |
| Dsymbol *s = NULL; |
| resolve(loc, sc, &e, &t, &s); |
| if (t) |
| s = t->toDsymbol(sc); |
| else if (e) |
| s = getDsymbol(e); |
| |
| return s; |
| } |
| |
| void TypeMixin::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid) |
| { |
| // if already resolved just set pe/pt/ps and return. |
| if (obj) |
| { |
| *pe = isExpression(obj); |
| *pt = isType(obj); |
| *ps = isDsymbol(obj); |
| return; |
| } |
| |
| RootObject *o = compileTypeMixin(this, loc, sc); |
| if (Type *t = isType(o)) |
| { |
| t->resolve(loc, sc, pe, pt, ps, intypeid); |
| if (*pt) |
| (*pt) = (*pt)->addMod(mod); |
| } |
| else if (Expression *e = isExpression(o)) |
| { |
| e = expressionSemantic(e, sc); |
| if (TypeExp *et = e->isTypeExp()) |
| { |
| *pe = NULL; |
| *pt = et->type->addMod(mod); |
| *ps = NULL; |
| } |
| else |
| { |
| *pe = e; |
| *pt = NULL; |
| *ps = NULL; |
| } |
| } |
| else |
| { |
| *pe = NULL; |
| *pt = Type::terror; |
| *ps = NULL; |
| } |
| |
| // save the result |
| obj = *pe ? (RootObject *)*pe : (*pt ? (RootObject *)*pt : (RootObject *)*ps); |
| } |
| |
| /***************************** 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.length); |
| for (size_t i = 0; i < idents.length; 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 = expressionSemantic(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 = expressionSemantic(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->length) |
| { |
| ::error(loc, "tuple index %llu exceeds length %u", (ulonglong)d, (unsigned)td->objects->length); |
| *pt = Type::terror; |
| return; |
| } |
| |
| RootObject *o = (*td->objects)[(size_t)d]; |
| *pt = isType(o); |
| *ps = isDsymbol(o); |
| *pe = isExpression(o); |
| |
| if (*pt) |
| *pt = typeSemantic(*pt, 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 |
| { |
| // check for deprecated aliases |
| s->checkDeprecated(loc, sc); |
| if (d) |
| d->checkDisabled(loc, sc, true); |
| } |
| |
| s = s->toAlias(); |
| //printf("\t2: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind()); |
| for (size_t i = 0; i < idents.length; 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 = expressionSemantic(ex, sc); |
| resolveExp(ex, pt, pe, ps); |
| return; |
| } |
| |
| Type *t = s->getType(); // type symbol, type alias, or type tuple? |
| unsigned errorsave = global.errors; |
| int flags = t == NULL ? SearchLocalsOnly : IgnorePrivateImports; |
| Dsymbol *sm = s->searchX(loc, sc, id, flags); |
| if (sm) |
| { |
| if (!(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, sm)) |
| { |
| ::error(loc, "`%s` is not visible from module `%s`", sm->toPrettyChars(), sc->_module->toChars()); |
| sm = NULL; |
| } |
| // Same check as in Expression::semanticY(DotIdExp) |
| else if (sm->isPackage() && checkAccess(sc, (Package *)sm)) |
| { |
| // @@@DEPRECATED_2.096@@@ |
| // Should be an error in 2.106. Just remove the deprecation call |
| // and uncomment the null assignment |
| ::deprecation(loc, "%s %s is not accessible here, perhaps add 'static import %s;'", |
| sm->kind(), sm->toPrettyChars(), sm->toPrettyChars()); |
| //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) |
| dsymbolSemantic(v, 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, IgnorePrivateImports); |
| 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 = expressionSemantic(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 = expressionSemantic(*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; |
| } |
| |
| /***************************** 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.length); |
| 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()); |
| dsymbolSemantic(tempinst, 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()); |
| } |
| |
| 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 (sc == NULL) |
| { |
| *pt = Type::terror; |
| error(loc, "Invalid scope."); |
| return; |
| } |
| 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 = expressionSemantic(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.length == 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 = expressionSemantic(e, sc); |
| resolveExp(e, pt, pe, ps); |
| } |
| } |
| if (*pt) |
| (*pt) = (*pt)->addMod(mod); |
| inuse--; |
| return; |
| } |
| |
| 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.length == 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 = expressionSemantic(e, sc); |
| resolveExp(e, pt, pe, ps); |
| } |
| } |
| if (*pt) |
| (*pt) = (*pt)->addMod(mod); |
| return; |
| |
| Lerr: |
| *pt = Type::terror; |
| return; |
| } |
| |
| /***************************** TypeEnum *****************************/ |
| |
| TypeEnum::TypeEnum(EnumDeclaration *sym) |
| : Type(Tenum) |
| { |
| this->sym = sym; |
| } |
| |
| const char *TypeEnum::kind() |
| { |
| return "enum"; |
| } |
| |
| Type *TypeEnum::syntaxCopy() |
| { |
| return this; |
| } |
| |
| 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) |
| dsymbolSemantic(sym, NULL); |
| |
| 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 = expressionSemantic(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; |
| } |
| |
| 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; |
| } |
| |
| 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 = expressionSemantic(e, sc); // do this before turning on noaccesscheck |
| |
| if (!sym->determineFields()) |
| { |
| error(e->loc, "unable to determine fields of `%s` because of forward references", toChars()); |
| } |
| |
| 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.length); |
| for (size_t i = 0; i < sym->fields.length; 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 = expressionSemantic(e, sc2); |
| sc2->pop(); |
| return e; |
| } |
| |
| const int flags = sc->flags & SCOPEignoresymbolvisibility ? IgnoreSymbolVisibility : 0; |
| s = sym->search(e->loc, ident, flags | IgnorePrivateImports); |
| L1: |
| if (!s) |
| { |
| return noMember(sc, e, ident, flag); |
| } |
| if (!(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, s)) |
| { |
| return noMember(sc, e, ident, flag); |
| } |
| if (!s->isFuncDeclaration()) // because of overloading |
| { |
| s->checkDeprecated(e->loc, sc); |
| if (Declaration *d = s->isDeclaration()) |
| d->checkDisabled(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 = expressionSemantic(ve, sc); |
| return ve; |
| } |
| } |
| |
| if (Type *t = s->getType()) |
| { |
| return expressionSemantic(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 = expressionSemantic(e, sc); |
| return e; |
| } |
| |
| TemplateInstance *ti = s->isTemplateInstance(); |
| if (ti) |
| { |
| if (!ti->semanticRun) |
| { |
| dsymbolSemantic(ti, 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 expressionSemantic(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 = expressionSemantic(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 = expressionSemantic(e, sc); |
| return e; |
| } |
| } |
| if (d->semanticRun == PASSinit) |
| dsymbolSemantic(d, 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 = expressionSemantic(e, sc); |
| return e; |
| } |
| |
| e = new DotVarExp(e->loc, e, d); |
| e = expressionSemantic(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.length - sym->isNested()); |
| unsigned offset = 0; |
| for (size_t j = 0; j < structelems->length; 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.length; 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.length; 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; |
| |
| if (sym->members && !sym->determineFields() && sym->type != Type::terror) |
| error(sym->loc, "no size because of forward references"); |
| |
| for (size_t i = 0; i < s->fields.length; 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.length; 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.length; 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; |
| } |
| |
| d_uns64 TypeClass::size(Loc) |
| { |
| return target.ptrsize; |
| } |
| |
| Dsymbol *TypeClass::toDsymbol(Scope *) |
| { |
| return sym; |
| } |
| |
| 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 = expressionSemantic(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.length); |
| for (size_t i = 0; i < sym->fields.length; 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 = expressionSemantic(e, sc2); |
| sc2->pop(); |
| return e; |
| } |
| |
| int flags = sc->flags & SCOPEignoresymbolvisibility ? IgnoreSymbolVisibility : 0; |
| s = sym->search(e->loc, ident, flags | IgnorePrivateImports); |
| |
| 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 = expressionSemantic(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 = expressionSemantic(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 = expressionSemantic(e, sc); |
| return e; |
| } |
| |
| if (ident == Id::__monitor && sym->hasMonitor()) |
| { |
| /* 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 = expressionSemantic(e, sc); |
| return e; |
| } |
| |
| if (ident == Id::outer && sym->vthis) |
| { |
| if (sym->vthis->semanticRun == PASSinit) |
| dsymbolSemantic(sym->vthis, 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)) |
| { |
| return noMember(sc, e, ident, flag); |
| } |
| if (!s->isFuncDeclaration()) // because of overloading |
| { |
| s->checkDeprecated(e->loc, sc); |
| if (Declaration *d = s->isDeclaration()) |
| d->checkDisabled(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 = expressionSemantic(ve, sc); |
| return ve; |
| } |
| } |
| |
| if (Type *t = s->getType()) |
| { |
| return expressionSemantic(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 = expressionSemantic(e, sc); |
| return e; |
| } |
| |
| TemplateInstance *ti = s->isTemplateInstance(); |
| if (ti) |
| { |
| if (!ti->semanticRun) |
| { |
| dsymbolSemantic(ti, 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 expressionSemantic(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 = expressionSemantic(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 = expressionSemantic(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 = expressionSemantic(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 = expressionSemantic(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 = expressionSemantic(e1, sc); |
| } |
| else |
| e1 = expressionSemantic(e1, sc); |
| goto L2; |
| } |
| } |
| } |
| //printf("e = %s, d = %s\n", e->toChars(), d->toChars()); |
| if (d->semanticRun == PASSinit) |
| dsymbolSemantic(d, 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 = expressionSemantic(e, sc); |
| return e; |
| } |
| |
| e = new DotVarExp(e->loc, e, d); |
| e = expressionSemantic(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()) |
| dsymbolSemantic(cdto, NULL); |
| if (sym->semanticRun < PASSsemanticdone && !sym->isBaseInfoComplete()) |
| dsymbolSemantic(sym, 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->length); |
| for (size_t i = 0; i < exps->length; 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, 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, NULL)); |
| } |
| |
| TypeTuple::TypeTuple(Type *t1, Type *t2) |
| : Type(Ttuple) |
| { |
| arguments = new Parameters(); |
| arguments->push(new Parameter(0, t1, NULL, NULL, NULL)); |
| arguments->push(new Parameter(0, t2, NULL, 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; |
| } |
| |
| 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->length == tt->arguments->length) |
| { |
| for (size_t i = 0; i < tt->arguments->length; 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->length, 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->length); |
| for (size_t i = 0; i < arguments->length; 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; |
| } |
| |
| 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 = expressionSemantic(lwr, sc); |
| upr = expressionSemantic(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->length)) |
| { |
| error(loc, "slice [%llu..%llu] is out of range of [0..%u]", i1, i2, td->objects->length); |
| *ps = NULL; |
| *pt = Type::terror; |
| return; |
| } |
| |
| if (i1 == 0 && i2 == td->objects->length) |
| { |
| *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->length; 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); |
| } |
| |
| /***************************** TypeNoreturn *****************************/ |
| |
| TypeNoreturn::TypeNoreturn() |
| : Type(Tnoreturn) |
| { |
| //printf("TypeNoreturn %p\n", this); |
| } |
| |
| const char *TypeNoreturn::kind() |
| { |
| return "noreturn"; |
| } |
| |
| Type *TypeNoreturn::syntaxCopy() |
| { |
| // No semantic analysis done, no need to copy |
| return this; |
| } |
| |
| MATCH TypeNoreturn::implicitConvTo(Type *to) |
| { |
| //printf("TypeNoreturn::implicitConvTo(this=%p, to=%p)\n", this, to); |
| //printf("from: %s\n", toChars()); |
| //printf("to : %s\n", to.toChars()); |
| MATCH m = Type::implicitConvTo(to); |
| return (m == MATCHexact) ? MATCHexact : MATCHconvert; |
| } |
| |
| bool TypeNoreturn::isBoolean() |
| { |
| return true; // bottom type can be implicitly converted to any other type |
| } |
| |
| d_uns64 TypeNoreturn::size(Loc) |
| { |
| return 0; |
| } |
| |
| unsigned TypeNoreturn::alignsize() |
| { |
| return 0; |
| } |
| |
| /*********************************************************** |
| * Encapsulate Parameters* so .length and [i] can be used on it. |
| * https://dlang.org/spec/function.html#ParameterList |
| */ |
| |
| ParameterList::ParameterList(Parameters *parameters, VarArg varargs) |
| { |
| this->parameters = parameters; |
| this->varargs = varargs; |
| } |
| |
| size_t ParameterList::length() |
| { |
| return Parameter::dim(parameters); |
| } |
| |
| /***************************** Parameter *****************************/ |
| |
| Parameter::Parameter(StorageClass storageClass, Type *type, Identifier *ident, |
| Expression *defaultArg, UserAttributeDeclaration *userAttribDecl) |
| { |
| this->type = type; |
| this->ident = ident; |
| this->storageClass = storageClass; |
| this->defaultArg = defaultArg; |
| this->userAttribDecl = userAttribDecl; |
| } |
| |
| Parameter *Parameter::create(StorageClass storageClass, Type *type, Identifier *ident, |
| Expression *defaultArg, UserAttributeDeclaration *userAttribDecl) |
| { |
| return new Parameter(storageClass, type, ident, defaultArg, userAttribDecl); |
| } |
| |
| Parameter *Parameter::syntaxCopy() |
| { |
| return new Parameter(storageClass, |
| type ? type->syntaxCopy() : NULL, |
| ident, |
| defaultArg ? defaultArg->syntaxCopy() : NULL, |
| userAttribDecl ? (UserAttributeDeclaration *) userAttribDecl->syntaxCopy(NULL) : NULL); |
| } |
| |
| Parameters *Parameter::arraySyntaxCopy(Parameters *parameters) |
| { |
| Parameters *params = NULL; |
| if (parameters) |
| { |
| params = new Parameters(); |
| params->setDim(parameters->length); |
| for (size_t i = 0; i < params->length; 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->parameterList.varargs == VARARGnone && tf->parameterList.length() == 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->length; 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)]; |
| } |
| |
| /** |
| * For printing two types with qualification when necessary. |
| * Params: |
| * t1 = The first type to receive the type name for |
| * t2 = The second type to receive the type name for |
| * Returns: |
| * The fully-qualified names of both types if the two type names are not the same, |
| * or the unqualified names of both types if the two type names are the same. |
| */ |
| void toAutoQualChars(const char **result, Type *t1, Type *t2) |
| { |
| const char *s1 = t1->toChars(); |
| const char *s2 = t2->toChars(); |
| if (strcmp(s1, s2) == 0) |
| { |
| s1 = t1->toPrettyChars(true); |
| s2 = t2->toPrettyChars(true); |
| } |
| result[0] = s1; |
| result[1] = s2; |
| } |