/** * Compiler implementation of the * $(LINK2 http://www.dlang.org, D programming language). * * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Source: $(DMDSRC _mtype.d) */ module ddmd.mtype; import core.checkedint; import core.stdc.stdarg; import core.stdc.stdio; import core.stdc.stdlib; import core.stdc.string; import ddmd.access; import ddmd.aggregate; import ddmd.aliasthis; import ddmd.arrayop; import ddmd.arraytypes; import ddmd.gluelayer; import ddmd.complex; import ddmd.dcast; import ddmd.dclass; import ddmd.declaration; import ddmd.denum; import ddmd.dimport; import ddmd.dmangle; import ddmd.dscope; import ddmd.dstruct; import ddmd.dsymbol; import ddmd.dtemplate; import ddmd.errors; import ddmd.expression; import ddmd.func; import ddmd.globals; import ddmd.hdrgen; import ddmd.id; import ddmd.identifier; import ddmd.imphint; import ddmd.init; import ddmd.opover; import ddmd.root.ctfloat; import ddmd.root.outbuffer; import ddmd.root.rmem; import ddmd.root.rootobject; import ddmd.root.stringtable; import ddmd.sideeffect; import ddmd.target; import ddmd.tokens; import ddmd.visitor; enum LOGDOTEXP = 0; // log ::dotExp() enum LOGDEFAULTINIT = 0; // log ::defaultInit() extern (C++) __gshared int Tsize_t = Tuns32; extern (C++) __gshared int Tptrdiff_t = Tint32; enum SIZE_INVALID = (~cast(d_uns64)0); // error return from size() functions /*************************** * Return !=0 if modfrom can be implicitly converted to modto */ bool MODimplicitConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe { if (modfrom == modto) return true; //printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto); auto X(T, U)(T m, U n) { return ((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; } } /*************************** * Return MATCHexact or MATCHconst if a method of type '() modfrom' can call a method of type '() modto'. */ MATCH MODmethodConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe { if (modfrom == modto) return MATCHexact; if (MODimplicitConv(modfrom, modto)) return MATCHconst; auto X(T, U)(T m, U n) { return ((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; } } /*************************** * Merge mod bits to form common mod. */ MOD MODmerge(MOD mod1, MOD mod2) pure nothrow @nogc @safe { 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.toString(TOKimmutable)); break; case MODshared: buf.writestring(Token.toString(TOKshared)); break; case MODshared | MODconst: buf.writestring(Token.toString(TOKshared)); buf.writeByte(' '); goto case; /+ fall through +/ case MODconst: buf.writestring(Token.toString(TOKconst)); break; case MODshared | MODwild: buf.writestring(Token.toString(TOKshared)); buf.writeByte(' '); goto case; /+ fall through +/ case MODwild: buf.writestring(Token.toString(TOKwild)); break; case MODshared | MODwildconst: buf.writestring(Token.toString(TOKshared)); buf.writeByte(' '); goto case; /+ fall through +/ case MODwildconst: buf.writestring(Token.toString(TOKwild)); buf.writeByte(' '); buf.writestring(Token.toString(TOKconst)); break; default: assert(0); } } /********************************* * Return modifier name. */ char* MODtoChars(MOD mod) { OutBuffer buf; buf.reserve(16); MODtoBuffer(&buf, mod); return buf.extractString(); } /************************************ * Convert MODxxxx to STCxxx */ StorageClass ModToStc(uint mod) pure nothrow @nogc @safe { 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; } /************************************ * 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. */ private Type stripDefaultArgs(Type t) { static Parameters* stripParams(Parameters* parameters) { Parameters* params = parameters; if (params && params.dim > 0) { foreach (i; 0 .. params.dim) { Parameter p = (*params)[i]; Type ta = stripDefaultArgs(p.type); if (ta != p.type || p.defaultArg || p.ident) { if (params == parameters) { params = new Parameters(); params.setDim(parameters.dim); foreach (j; 0 .. params.dim) (*params)[j] = (*parameters)[j]; } (*params)[i] = new Parameter(p.storageClass, ta, null, null); } } } return params; } if (t is null) return t; if (t.ty == Tfunction) { TypeFunction tf = cast(TypeFunction)t; Type tret = stripDefaultArgs(tf.next); Parameters* params = stripParams(tf.parameters); if (tret == tf.next && params == tf.parameters) goto Lnot; tf = cast(TypeFunction)tf.copy(); tf.parameters = params; tf.next = tret; //printf("strip %s\n <- %s\n", tf.toChars(), t.toChars()); t = tf; } else if (t.ty == Ttuple) { TypeTuple tt = cast(TypeTuple)t; Parameters* args = stripParams(tt.arguments); if (args == tt.arguments) goto Lnot; t = t.copy(); (cast(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(); (cast(TypeNext)t).next = n; } //printf("strip %s\n", t.toChars()); Lnot: return t; } enum TFLAGSintegral = 1; enum TFLAGSfloating = 2; enum TFLAGSunsigned = 4; enum TFLAGSreal = 8; enum TFLAGSimaginary = 0x10; enum TFLAGScomplex = 0x20; private Expression semanticLength(Scope* sc, TupleDeclaration tup, Expression exp) { ScopeDsymbol sym = new ArrayScopeSymbol(sc, tup); sym.parent = sc.scopesym; sc = sc.push(sym); sc = sc.startCTFE(); exp = exp.semantic(sc); sc = sc.endCTFE(); sc.pop(); return exp; } /************************** * This evaluates exp while setting length to be the number * of elements in the tuple t. */ private Expression semanticLength(Scope* sc, Type t, Expression exp) { if (t.ty == Ttuple) { ScopeDsymbol sym = new ArrayScopeSymbol(sc, cast(TypeTuple)t); sym.parent = sc.scopesym; sc = sc.push(sym); sc = sc.startCTFE(); exp = exp.semantic(sc); sc = sc.endCTFE(); sc.pop(); } else { sc = sc.startCTFE(); exp = exp.semantic(sc); sc = sc.endCTFE(); } return exp; } enum ENUMTY : int { Tarray, // slice array, aka T[] Tsarray, // static array, aka T[dimension] Taarray, // associative array, aka T[type] Tpointer, Treference, Tfunction, Tident, Tclass, Tstruct, Tenum, Tdelegate, Tnone, Tvoid, Tint8, Tuns8, Tint16, Tuns16, Tint32, Tuns32, Tint64, Tuns64, Tfloat32, Tfloat64, Tfloat80, Timaginary32, Timaginary64, Timaginary80, Tcomplex32, Tcomplex64, Tcomplex80, Tbool, Tchar, Twchar, Tdchar, Terror, Tinstance, Ttypeof, Ttuple, Tslice, Treturn, Tnull, Tvector, Tint128, Tuns128, TMAX, } alias Tarray = ENUMTY.Tarray; alias Tsarray = ENUMTY.Tsarray; alias Taarray = ENUMTY.Taarray; alias Tpointer = ENUMTY.Tpointer; alias Treference = ENUMTY.Treference; alias Tfunction = ENUMTY.Tfunction; alias Tident = ENUMTY.Tident; alias Tclass = ENUMTY.Tclass; alias Tstruct = ENUMTY.Tstruct; alias Tenum = ENUMTY.Tenum; alias Tdelegate = ENUMTY.Tdelegate; alias Tnone = ENUMTY.Tnone; alias Tvoid = ENUMTY.Tvoid; alias Tint8 = ENUMTY.Tint8; alias Tuns8 = ENUMTY.Tuns8; alias Tint16 = ENUMTY.Tint16; alias Tuns16 = ENUMTY.Tuns16; alias Tint32 = ENUMTY.Tint32; alias Tuns32 = ENUMTY.Tuns32; alias Tint64 = ENUMTY.Tint64; alias Tuns64 = ENUMTY.Tuns64; alias Tfloat32 = ENUMTY.Tfloat32; alias Tfloat64 = ENUMTY.Tfloat64; alias Tfloat80 = ENUMTY.Tfloat80; alias Timaginary32 = ENUMTY.Timaginary32; alias Timaginary64 = ENUMTY.Timaginary64; alias Timaginary80 = ENUMTY.Timaginary80; alias Tcomplex32 = ENUMTY.Tcomplex32; alias Tcomplex64 = ENUMTY.Tcomplex64; alias Tcomplex80 = ENUMTY.Tcomplex80; alias Tbool = ENUMTY.Tbool; alias Tchar = ENUMTY.Tchar; alias Twchar = ENUMTY.Twchar; alias Tdchar = ENUMTY.Tdchar; alias Terror = ENUMTY.Terror; alias Tinstance = ENUMTY.Tinstance; alias Ttypeof = ENUMTY.Ttypeof; alias Ttuple = ENUMTY.Ttuple; alias Tslice = ENUMTY.Tslice; alias Treturn = ENUMTY.Treturn; alias Tnull = ENUMTY.Tnull; alias Tvector = ENUMTY.Tvector; alias Tint128 = ENUMTY.Tint128; alias Tuns128 = ENUMTY.Tuns128; alias TMAX = ENUMTY.TMAX; alias TY = ubyte; enum MODFlags : int { MODconst = 1, // type is const MODimmutable = 4, // type is immutable MODshared = 2, // type is shared MODwild = 8, // type is wild MODwildconst = (MODwild | MODconst), // type is wild const MODmutable = 0x10, // type is mutable (only used in wildcard matching) } alias MODconst = MODFlags.MODconst; alias MODimmutable = MODFlags.MODimmutable; alias MODshared = MODFlags.MODshared; alias MODwild = MODFlags.MODwild; alias MODwildconst = MODFlags.MODwildconst; alias MODmutable = MODFlags.MODmutable; alias MOD = ubyte; /*********************************************************** */ extern (C++) abstract class Type : RootObject { TY ty; MOD mod; // modifiers MODxxxx char* deco; /* These are cached values that are lazily evaluated by constOf(), immutableOf(), etc. * They should not be referenced by anybody but mtype.c. * They can be NULL if not lazily evaluated yet. * Note that there is no "shared immutable", because that is just immutable * Naked == no MOD bits */ Type cto; // MODconst ? naked version of this type : const version Type ito; // MODimmutable ? naked version of this type : immutable version Type sto; // MODshared ? naked version of this type : shared mutable version Type scto; // MODshared | MODconst ? naked version of this type : shared const version Type wto; // MODwild ? naked version of this type : wild version Type wcto; // MODwildconst ? naked version of this type : wild const version Type swto; // MODshared | MODwild ? naked version of this type : shared wild version Type swcto; // MODshared | MODwildconst ? naked version of this type : shared wild const version Type pto; // merged pointer to this type Type rto; // reference to this type Type arrayof; // array of this type TypeInfoDeclaration vtinfo; // TypeInfo object for this Type type* ctype; // for back end extern (C++) static __gshared Type tvoid; extern (C++) static __gshared Type tint8; extern (C++) static __gshared Type tuns8; extern (C++) static __gshared Type tint16; extern (C++) static __gshared Type tuns16; extern (C++) static __gshared Type tint32; extern (C++) static __gshared Type tuns32; extern (C++) static __gshared Type tint64; extern (C++) static __gshared Type tuns64; extern (C++) static __gshared Type tint128; extern (C++) static __gshared Type tuns128; extern (C++) static __gshared Type tfloat32; extern (C++) static __gshared Type tfloat64; extern (C++) static __gshared Type tfloat80; extern (C++) static __gshared Type timaginary32; extern (C++) static __gshared Type timaginary64; extern (C++) static __gshared Type timaginary80; extern (C++) static __gshared Type tcomplex32; extern (C++) static __gshared Type tcomplex64; extern (C++) static __gshared Type tcomplex80; extern (C++) static __gshared Type tbool; extern (C++) static __gshared Type tchar; extern (C++) static __gshared Type twchar; extern (C++) static __gshared Type tdchar; // Some special types extern (C++) static __gshared Type tshiftcnt; extern (C++) static __gshared Type tvoidptr; // void* extern (C++) static __gshared Type tstring; // immutable(char)[] extern (C++) static __gshared Type twstring; // immutable(wchar)[] extern (C++) static __gshared Type tdstring; // immutable(dchar)[] extern (C++) static __gshared Type tvalist; // va_list alias extern (C++) static __gshared Type terror; // for error recovery extern (C++) static __gshared Type tnull; // for null type extern (C++) static __gshared Type tsize_t; // matches size_t alias extern (C++) static __gshared Type tptrdiff_t; // matches ptrdiff_t alias extern (C++) static __gshared Type thash_t; // matches hash_t alias extern (C++) static __gshared ClassDeclaration dtypeinfo; extern (C++) static __gshared ClassDeclaration typeinfoclass; extern (C++) static __gshared ClassDeclaration typeinfointerface; extern (C++) static __gshared ClassDeclaration typeinfostruct; extern (C++) static __gshared ClassDeclaration typeinfopointer; extern (C++) static __gshared ClassDeclaration typeinfoarray; extern (C++) static __gshared ClassDeclaration typeinfostaticarray; extern (C++) static __gshared ClassDeclaration typeinfoassociativearray; extern (C++) static __gshared ClassDeclaration typeinfovector; extern (C++) static __gshared ClassDeclaration typeinfoenum; extern (C++) static __gshared ClassDeclaration typeinfofunction; extern (C++) static __gshared ClassDeclaration typeinfodelegate; extern (C++) static __gshared ClassDeclaration typeinfotypelist; extern (C++) static __gshared ClassDeclaration typeinfoconst; extern (C++) static __gshared ClassDeclaration typeinfoinvariant; extern (C++) static __gshared ClassDeclaration typeinfoshared; extern (C++) static __gshared ClassDeclaration typeinfowild; extern (C++) static __gshared TemplateDeclaration rtinfo; extern (C++) static __gshared Type[TMAX] basic; extern (C++) static __gshared StringTable stringtable; extern (C++) static __gshared ubyte[TMAX] sizeTy = () { ubyte[TMAX] sizeTy = __traits(classInstanceSize, TypeBasic); sizeTy[Tsarray] = __traits(classInstanceSize, TypeSArray); sizeTy[Tarray] = __traits(classInstanceSize, TypeDArray); sizeTy[Taarray] = __traits(classInstanceSize, TypeAArray); sizeTy[Tpointer] = __traits(classInstanceSize, TypePointer); sizeTy[Treference] = __traits(classInstanceSize, TypeReference); sizeTy[Tfunction] = __traits(classInstanceSize, TypeFunction); sizeTy[Tdelegate] = __traits(classInstanceSize, TypeDelegate); sizeTy[Tident] = __traits(classInstanceSize, TypeIdentifier); sizeTy[Tinstance] = __traits(classInstanceSize, TypeInstance); sizeTy[Ttypeof] = __traits(classInstanceSize, TypeTypeof); sizeTy[Tenum] = __traits(classInstanceSize, TypeEnum); sizeTy[Tstruct] = __traits(classInstanceSize, TypeStruct); sizeTy[Tclass] = __traits(classInstanceSize, TypeClass); sizeTy[Ttuple] = __traits(classInstanceSize, TypeTuple); sizeTy[Tslice] = __traits(classInstanceSize, TypeSlice); sizeTy[Treturn] = __traits(classInstanceSize, TypeReturn); sizeTy[Terror] = __traits(classInstanceSize, TypeError); sizeTy[Tnull] = __traits(classInstanceSize, TypeNull); sizeTy[Tvector] = __traits(classInstanceSize, TypeVector); return sizeTy; }(); final extern (D) this(TY ty) { this.ty = ty; } const(char)* kind() const { assert(false); // should be overridden } final Type copy() { Type t = cast(Type)mem.xmalloc(sizeTy[ty]); memcpy(cast(void*)t, cast(void*)this, sizeTy[ty]); return t; } Type syntaxCopy() { print(); fprintf(stderr, "ty = %d\n", ty); assert(0); } override bool equals(RootObject o) { Type t = cast(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 !is 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; } final bool equivalent(Type t) { return immutableOf().equals(t.immutableOf()); } // kludge for template.isType() override final int dyncast() { return DYNCAST_TYPE; } /******************************* * Covariant means that 'this' can substitute for 't', * i.e. a pure function is a match for an impure type. * 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 */ final int covariant(Type t, StorageClass* pstc = null) { version (none) { printf("Type::covariant(t = %s) %s\n", t.toChars(), toChars()); printf("deco = %p, %p\n", deco, t.deco); // printf("ty = %d\n", next.ty); printf("mod = %x, %x\n", mod, t.mod); } if (pstc) *pstc = 0; StorageClass stc = 0; int inoutmismatch = 0; TypeFunction t1; TypeFunction t2; if (equals(t)) return 1; // covariant if (ty != Tfunction || t.ty != Tfunction) goto Ldistinct; t1 = cast(TypeFunction)this; t2 = cast(TypeFunction)t; if (t1.varargs != t2.varargs) goto Ldistinct; if (t1.parameters && t2.parameters) { size_t dim = Parameter.dim(t1.parameters); if (dim != Parameter.dim(t2.parameters)) goto Ldistinct; for (size_t i = 0; i < dim; i++) { Parameter fparam1 = Parameter.getNth(t1.parameters, i); Parameter fparam2 = Parameter.getNth(t2.parameters, i); if (!fparam1.type.equals(fparam2.type)) { goto Ldistinct; } inoutmismatch = !fparam1.isCovariant(fparam2); } } else if (t1.parameters != t2.parameters) { size_t dim1 = !t1.parameters ? 0 : t1.parameters.dim; size_t dim2 = !t2.parameters ? 0 : t2.parameters.dim; if (dim1 || dim2) goto Ldistinct; } // The argument lists match if (inoutmismatch) 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 ((cast(TypeClass)t1n).sym == (cast(TypeClass)t2n).sym && MODimplicitConv(t1n.mod, t2n.mod)) goto Lcovariant; // If t1n is forward referenced: ClassDeclaration cd = (cast(TypeClass)t1n).sym; if (cd._scope) cd.semantic(null); if (!cd.isBaseInfoComplete()) { return 3; // forward references } } if (t1n.ty == Tstruct && t2n.ty == Tstruct) { if ((cast(TypeStruct)t1n).sym == (cast(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 && t1n.implicitConvTo(t2n) && t1n.size() == t2n.size()) goto Lcovariant; } goto Lnotcovariant; Lcovariant: if (t1.isref != t2.isref) goto Lnotcovariant; // We can subtract 'return' from 'this', but cannot add it if (t1.isreturn && !t2.isreturn) goto Lnotcovariant; /* Can convert mutable to const */ if (!MODimplicitConv(t2.mod, t1.mod)) { version (none) { //stop attribute inference with const // If adding 'const' will make it covariant if (MODimplicitConv(t2.mod, MODmerge(t1.mod, MODconst))) stc |= STCconst; else goto Lnotcovariant; } else { 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; } /******************************** * For pretty-printing a type. */ final override const(char)* toChars() { OutBuffer buf; buf.reserve(16); HdrGenState hgs; hgs.fullQual = (ty == Tclass && !mod); .toCBuffer(this, &buf, null, &hgs); return buf.extractString(); } final char* toPrettyChars(bool QualifyTypes = false) { OutBuffer buf; buf.reserve(16); HdrGenState hgs; hgs.fullQual = QualifyTypes; .toCBuffer(this, &buf, null, &hgs); return buf.extractString(); } static void _init() { stringtable._init(14000); // Set basic types static __gshared TY* basetab = [ Tvoid, Tint8, Tuns8, Tint16, Tuns16, Tint32, Tuns32, Tint64, Tuns64, Tint128, Tuns128, Tfloat32, Tfloat64, Tfloat80, Timaginary32, Timaginary64, Timaginary80, Tcomplex32, Tcomplex64, Tcomplex80, Tbool, Tchar, Twchar, Tdchar, Terror ]; for (size_t i = 0; basetab[i] != Terror; i++) { Type t = new TypeBasic(basetab[i]); t = t.merge(); basic[basetab[i]] = t; } basic[Terror] = new TypeError(); tvoid = basic[Tvoid]; tint8 = basic[Tint8]; tuns8 = basic[Tuns8]; tint16 = basic[Tint16]; tuns16 = basic[Tuns16]; tint32 = basic[Tint32]; tuns32 = basic[Tuns32]; tint64 = basic[Tint64]; tuns64 = basic[Tuns64]; tint128 = basic[Tint128]; tuns128 = basic[Tuns128]; tfloat32 = basic[Tfloat32]; tfloat64 = basic[Tfloat64]; tfloat80 = basic[Tfloat80]; timaginary32 = basic[Timaginary32]; timaginary64 = basic[Timaginary64]; timaginary80 = basic[Timaginary80]; tcomplex32 = basic[Tcomplex32]; tcomplex64 = basic[Tcomplex64]; tcomplex80 = basic[Tcomplex80]; tbool = basic[Tbool]; tchar = basic[Tchar]; twchar = basic[Twchar]; tdchar = basic[Tdchar]; tshiftcnt = tint32; terror = basic[Terror]; tnull = basic[Tnull]; tnull = new TypeNull(); tnull.deco = tnull.merge().deco; tvoidptr = tvoid.pointerTo(); tstring = tchar.immutableOf().arrayOf(); twstring = twchar.immutableOf().arrayOf(); tdstring = tdchar.immutableOf().arrayOf(); tvalist = Target.va_listType(); if (global.params.isLP64) { Tsize_t = Tuns64; Tptrdiff_t = Tint64; } else { Tsize_t = Tuns32; Tptrdiff_t = Tint32; } tsize_t = basic[Tsize_t]; tptrdiff_t = basic[Tptrdiff_t]; thash_t = tsize_t; } final d_uns64 size() { return size(Loc()); } d_uns64 size(Loc loc) { error(loc, "no size for type %s", toChars()); return SIZE_INVALID; } uint alignsize() { return cast(uint)size(Loc()); } Type semantic(Loc loc, Scope* sc) { if (ty == Tint128 || ty == Tuns128) { error(loc, "cent and ucent types not implemented"); return terror; } return merge(); } final Type trySemantic(Loc loc, Scope* sc) { //printf("+trySemantic(%s) %d\n", toChars(), global.errors); uint errors = global.startGagging(); Type t = semantic(loc, sc); if (global.endGagging(errors) || t.ty == Terror) // if any errors happened { t = null; } //printf("-trySemantic(%s) %d\n", toChars(), global.errors); return t; } /************************************ */ final 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 && !(cast(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(cast(char*)buf.data, buf.offset); if (sv.ptrvalue) { t = cast(Type)sv.ptrvalue; debug { if (!t.deco) printf("t = %s\n", t.toChars()); } assert(t.deco); //printf("old value, deco = '%s' %p\n", t.deco, t.deco); } else { sv.ptrvalue = cast(char*)(t = stripDefaultArgs(t)); deco = t.deco = 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. */ final Type merge2() { //printf("merge2(%s)\n", toChars()); Type t = this; assert(t); if (!t.deco) return t.merge(); StringValue* sv = stringtable.lookup(t.deco, strlen(t.deco)); if (sv && sv.ptrvalue) { t = cast(Type)sv.ptrvalue; assert(t.deco); } else assert(0); return t; } /********************************* * Store this type's modifier name into buf. */ final void modToBuffer(OutBuffer* buf) { if (mod) { buf.writeByte(' '); MODtoBuffer(buf, mod); } } /********************************* * Return this type's modifier name. */ final char* modToChars() { OutBuffer buf; buf.reserve(16); modToBuffer(&buf); return buf.extractString(); } /** For each active modifier (MODconst, MODimmutable, etc) call fp with a void* for the work param and a string representation of the attribute. */ final int modifiersApply(void* param, int function(void*, const(char)*) fp) { immutable ubyte[4] modsArr = [MODconst, MODimmutable, MODwild, MODshared]; foreach (modsarr; modsArr) { if (mod & modsarr) { if (int res = fp(param, MODtoChars(modsarr))) return res; } } return 0; } bool isintegral() { return false; } // real, imaginary, or complex bool isfloating() { return false; } bool isreal() { return false; } bool isimaginary() { return false; } bool iscomplex() { return false; } bool isscalar() { return false; } bool isunsigned() { return false; } bool isscope() { return false; } bool isString() { return false; } /************************** * When T is mutable, * Given: * T a, b; * Can we bitwise assign: * a = b; * ? */ bool isAssignable() { return true; } /************************** * Returns true if T can be converted to boolean value. */ bool isBoolean() { return isscalar(); } /********************************* * Check type to see if it is based on a deprecated symbol. */ void checkDeprecated(Loc loc, Scope* sc) { Dsymbol s = toDsymbol(sc); if (s) s.checkDeprecated(loc, sc); } final bool isConst() const { return (mod & MODconst) != 0; } final bool isImmutable() const { return (mod & MODimmutable) != 0; } final bool isMutable() const { return (mod & (MODconst | MODimmutable | MODwild)) == 0; } final bool isShared() const { return (mod & MODshared) != 0; } final bool isSharedConst() const { return (mod & (MODshared | MODconst)) == (MODshared | MODconst); } final bool isWild() const { return (mod & MODwild) != 0; } final bool isWildConst() const { return (mod & MODwildconst) == MODwildconst; } final bool isSharedWild() const { return (mod & (MODshared | MODwild)) == (MODshared | MODwild); } final bool isNaked() const { return mod == 0; } /******************************** * Return a copy of this type with all attributes null-initialized. * Useful for creating a type with different modifiers. */ final Type nullAttributes() { uint sz = sizeTy[ty]; Type t = cast(Type)mem.xmalloc(sz); memcpy(cast(void*)t, cast(void*)this, sz); // t.mod = NULL; // leave mod unchanged 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) (cast(TypeStruct)t).att = RECfwdref; if (t.ty == Tclass) (cast(TypeClass)t).att = RECfwdref; return t; } /******************************** * Convert to 'const'. */ final 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'. */ final 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. */ final 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; } final 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; } final 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 */ final 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'. */ final 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; } final 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; } final 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; } final 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. */ final void 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; default: break; } } assert(mod != t.mod); auto X(T, U)(T m, U n) { return ((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); } check(); t.check(); //printf("fixTo: %s, %s\n", toChars(), t.toChars()); } /*************************** * Look for bugs in constructing types. */ final void 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(); } } /************************************* * Apply STCxxxx bits to existing type. * Use *before* semantic analysis is run. */ final 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; } /************************************ * Apply MODxxxx bits to existing type. */ final 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" */ final 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 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); } final 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; } final Type referenceTo() { if (ty == Terror) return this; if (!rto) { Type t = new TypeReference(this); rto = t.merge(); } return rto; } final 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 final 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; } final Type aliasthisOf() { auto ad = isAggregate(this); if (!ad || !ad.aliasthis) return null; auto s = ad.aliasthis; if (s.isAliasDeclaration()) s = s.toAlias(); if (s.isTupleDeclaration()) return null; if (auto vd = s.isVarDeclaration()) { auto t = vd.type; if (vd.needThis()) t = t.addMod(this.mod); return t; } if (auto fd = s.isFuncDeclaration()) { fd = resolveFuncCall(Loc(), null, fd, null, this, null, 1); if (!fd || fd.errors || !fd.functionSemantic()) return Type.terror; auto t = fd.type.nextOf(); if (!t) // issue 14185 return Type.terror; t = t.substWildTo(mod == 0 ? MODmutable : mod); return t; } if (auto d = s.isDeclaration()) { assert(d.type); return d.type; } if (auto ed = s.isEnumDeclaration()) { return ed.type; } if (auto td = s.isTemplateDeclaration()) { assert(td._scope); auto fd = resolveFuncCall(Loc(), null, td, null, this, null, 1); if (!fd || fd.errors || !fd.functionSemantic()) return Type.terror; auto t = fd.type.nextOf(); if (!t) return Type.terror; t = t.substWildTo(mod == 0 ? MODmutable : mod); return t; } //printf("%s\n", s.kind()); return null; } final bool checkAliasThisRec() { Type tb = toBasetype(); AliasThisRec* pflag; if (tb.ty == Tstruct) pflag = &(cast(TypeStruct)tb).att; else if (tb.ty == Tclass) pflag = &(cast(TypeClass)tb).att; else return false; AliasThisRec flag = cast(AliasThisRec)(*pflag & RECtypeMask); if (flag == RECfwdref) { Type att = aliasthisOf(); flag = att && att.implicitConvTo(this) ? RECyes : RECno; } *pflag = cast(AliasThisRec)(flag | (*pflag & ~RECtypeMask)); return flag == RECyes; } 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 makeImmutable() { if (ito) return ito; Type t = this.nullAttributes(); t.mod = MODimmutable; return t; } Type makeShared() { if (sto) return sto; Type t = this.nullAttributes(); t.mod = MODshared; return t; } Type makeSharedConst() { if (scto) return scto; Type t = this.nullAttributes(); t.mod = MODshared | MODconst; return t; } Type makeWild() { if (wto) return wto; Type t = this.nullAttributes(); t.mod = MODwild; return t; } Type makeWildConst() { if (wcto) return wcto; Type t = this.nullAttributes(); t.mod = MODwildconst; return t; } Type makeSharedWild() { if (swto) return swto; Type t = this.nullAttributes(); t.mod = MODshared | MODwild; return t; } Type makeSharedWildConst() { if (swcto) return swcto; Type t = this.nullAttributes(); t.mod = MODshared | MODwildconst; return t; } Type makeMutable() { Type t = this.nullAttributes(); t.mod = mod & MODshared; return t; } Dsymbol toDsymbol(Scope* sc) { return null; } /******************************* * If this is a shell around another type, * get that other type. */ Type toBasetype() { return this; } bool isBaseOf(Type t, int* poffset) { return 0; // assume not } /******************************** * Determine if 'this' can be implicitly converted * to type 'to'. * Returns: * MATCHnomatch, MATCHconvert, MATCHconst, MATCHexact */ MATCH 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 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). */ ubyte deduceWild(Type t, bool isRef) { //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 substWildTo(uint 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, (cast(TypeSArray)this).dim.syntaxCopy()); else if (ty == Taarray) { t = new TypeAArray(t, (cast(TypeAArray)this).index.syntaxCopy()); (cast(TypeAArray)t).sc = (cast(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; } final Type unqualify(uint 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, (cast(TypeSArray)this).dim); else if (ty == Taarray) { t = new TypeAArray(utn, (cast(TypeAArray)this).index); (cast(TypeAArray)t).sc = (cast(TypeAArray)this).sc; // duplicate scope } else assert(0); t = t.merge(); } } t = t.addMod(mod & ~m); return t; } /************************** * Return type with the top level of it being mutable. */ Type toHeadMutable() { if (!mod) return this; return mutableOf(); } ClassDeclaration isClassHandle() { return null; } /*************************************** * 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 getProperty(Loc loc, Identifier ident, int flag) { Expression e; static if (LOGDOTEXP) { printf("Type::getProperty(type = '%s', ident = '%s')\n", toChars(), ident.toChars()); } if (ident == Id.__sizeof) { d_uns64 sz = size(loc); if (sz == SIZE_INVALID) return new ErrorExp(); e = new IntegerExp(loc, sz, Type.tsize_t); } else if (ident == Id.__xalignof) { e = new IntegerExp(loc, alignsize(), Type.tsize_t); } else if (ident == Id._init) { Type tb = toBasetype(); e = defaultInitLiteral(loc); if (tb.ty == Tstruct && tb.needsNested()) { StructLiteralExp se = cast(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, deco); Scope sc; e = e.semantic(&sc); } } else if (ident == Id.stringof) { const s = toChars(); e = new StringExp(loc, cast(char*)s); Scope sc; e = e.semantic(&sc); } else if (flag && this != Type.terror) { return null; } else { Dsymbol s = null; if (ty == Tstruct || ty == Tclass || ty == Tenum) s = toDsymbol(null); if (s) s = s.search_correct(ident); if (this != Type.terror) { if (s) error(loc, "no property '%s' for type '%s', did you mean '%s'?", ident.toChars(), toChars(), s.toChars()); else error(loc, "no property '%s' for type '%s'", ident.toChars(), toChars()); } e = new ErrorExp(); } return e; } /**************** * dotExp() bit flags */ enum DotExpFlag { gag = 1, // don't report "not a property" error and just return null noDeref = 2, // the use of the expression will not attempt a dereference } /*************************************** * Access the members of the object e. This type is same as e.type. * Params: * flag = DotExpFlag bit flags * Returns: * resulting expression with e.ident resolved */ Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { VarDeclaration v = null; static if (LOGDOTEXP) { printf("Type::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } Expression ex = e; while (ex.op == TOKcomma) ex = (cast(CommaExp)ex).e2; if (ex.op == TOKdotvar) { DotVarExp dv = cast(DotVarExp)ex; v = dv.var.isVarDeclaration(); } else if (ex.op == TOKvar) { VarExp ve = cast(VarExp)ex; v = ve.var.isVarDeclaration(); } if (v) { if (ident == Id.offsetof) { if (v.isField()) { auto 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 = cast(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 s = e.toChars(); e = new StringExp(e.loc, cast(char*)s); } else e = getProperty(e.loc, ident, flag & DotExpFlag.gag); Lreturn: if (!(flag & DotExpFlag.gag) || e) e = e.semantic(sc); return e; } /************************************ * Return alignment to use for this type. */ structalign_t 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. */ final Expression noMember(Scope* sc, Expression e, Identifier ident, int flag) { assert(ty == Tstruct || ty == Tclass); auto 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 (auto 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); return e.semantic(sc); } /* Look for overloaded opDispatch to see if we should forward request * to it. */ if (auto 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()); return new ErrorExp(); } auto se = new StringExp(e.loc, cast(char*)ident.toChars()); auto tiargs = new Objects(); tiargs.push(se); auto 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) { ... } */ uint errors = flag & 1 ? global.startGagging() : 0; e = dti.semanticY(sc, 0); if (flag & 1 && global.endGagging(errors)) e = null; 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); auto die = new DotIdExp(e.loc, e, ident); return die.semanticY(sc, flag & 1); } } return Type.dotExp(sc, e, ident, flag); } Expression defaultInit(Loc loc = Loc()) { static if (LOGDEFAULTINIT) { printf("Type::defaultInit() '%s'\n", toChars()); } return null; } /*************************************** * Use when we prefer the default initializer to be a literal, * rather than a global immutable variable. */ Expression defaultInitLiteral(Loc loc) { static if (LOGDEFAULTINIT) { printf("Type::defaultInitLiteral() '%s'\n", toChars()); } return defaultInit(loc); } // if initializer is 0 bool isZeroInit(Loc loc = Loc()) { return false; // assume not } final Identifier getTypeInfoIdent() { // _init_10TypeInfo_%s OutBuffer buf; buf.reserve(32); mangleToBuffer(this, &buf); const slice = buf.peekSlice(); // Allocate buffer on stack, fail over to using malloc() char[128] namebuf; const namelen = 19 + size_t.sizeof * 3 + slice.length + 1; auto name = namelen <= namebuf.length ? namebuf.ptr : cast(char*)malloc(namelen); assert(name); const length = sprintf(name, "_D%lluTypeInfo_%.*s6__initZ", cast(ulong)(9 + slice.length), cast(int)slice.length, slice.ptr); //printf("%p %s, deco = %s, name = %s\n", this, toChars(), deco, name); assert(0 < length && length < namelen); // don't overflow the buffer int off = 0; static if (!IN_GCC) { if (global.params.isOSX || global.params.isWindows && !global.params.is64bit) ++off; // C mangling will add '_' back in } auto id = Identifier.idPool(name + off, length - off); if (name != namebuf.ptr) free(name); return id; } /*************************************** * Resolve 'this' type to either type, symbol, or expression. * If errors happened, resolved to Type.terror. */ void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) { //printf("Type::resolve() %s, %d\n", toChars(), ty); Type t = semantic(loc, sc); *pt = t; *pe = null; *ps = null; } /*************************************** * Normalize `e` as the result of Type.resolve() process. */ final void 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 = (cast(VarExp)e).var; if (s.isVarDeclaration()) goto default; //if (s.isOverDeclaration()) // todo; break; case TOKtemplate: // TemplateDeclaration s = (cast(TemplateExp)e).td; break; case TOKscope: s = (cast(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 TOKdotid: default: *pe = e; return; } *ps = s; } /*************************************** * Return !=0 if the type or any of its subtypes is wild. */ int hasWild() const { return mod & MODwild; } /******************************** * We've mistakenly parsed this as a type. * Redo it as an Expression. * NULL if cannot. */ Expression toExpression() { return null; } /*************************************** * Return !=0 if type has pointers that need to * be scanned by the GC during a collection cycle. */ bool 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 hasVoidInitPointers() { return false; } /************************************* * If this is a type of something, return that something. */ Type nextOf() { return null; } /************************************* * If this is a type of static array, return its base element type. */ final Type baseElemOf() { Type t = toBasetype(); while (t.ty == Tsarray) t = (cast(TypeSArray)t).next.toBasetype(); return t; } /**************************************** * Return the mask that an integral type will * fit into. */ final uinteger_t 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 = 0xFFFFU; break; case Tdchar: case Tint32: case Tuns32: m = 0xFFFFFFFFU; break; case Tint64: case Tuns64: m = 0xFFFFFFFFFFFFFFFFUL; break; default: assert(0); } return m; } /******************************** * true if when type goes out of scope, it needs a destructor applied. * Only applies to value types, not ref types. */ bool needsDestruction() { return false; } /********************************* * */ bool needsNested() { return false; } /************************************* * Bugzilla 14488: Check if the inner most base type is complex or imaginary. * Should only give alerts when set to emit transitional messages. */ final void checkComplexTransition(Loc loc) { Type t = baseElemOf(); while (t.ty == Tpointer || t.ty == Tarray) t = t.nextOf().baseElemOf(); if (t.isimaginary() || t.iscomplex()) { const(char)* p = loc.toChars(); 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()) { fprintf(global.stdmsg, "%s: use of complex type '%s' is scheduled for deprecation, use 'std.complex.Complex!(%s)' instead\n", p ? p : "", toChars(), rt.toChars()); } else { fprintf(global.stdmsg, "%s: use of imaginary type '%s' is scheduled for deprecation, use '%s' instead\n", p ? p : "", toChars(), rt.toChars()); } } } static void error(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); .verror(loc, format, ap); va_end(ap); } static void warning(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); .vwarning(loc, format, ap); va_end(ap); } // For eliminating dynamic_cast TypeBasic isTypeBasic() { return null; } void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeError : Type { extern (D) this() { super(Terror); } override Type syntaxCopy() { // No semantic analysis done, no need to copy return this; } override d_uns64 size(Loc loc) { return SIZE_INVALID; } override Expression getProperty(Loc loc, Identifier ident, int flag) { return new ErrorExp(); } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { return new ErrorExp(); } override Expression defaultInit(Loc loc) { return new ErrorExp(); } override Expression defaultInitLiteral(Loc loc) { return new ErrorExp(); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) abstract class TypeNext : Type { Type next; final extern (D) this(TY ty, Type next) { super(ty); this.next = next; } override final void 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); } override final int 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. */ override final Type nextOf() { return next; } override final Type makeConst() { //printf("TypeNext::makeConst() %p, %s\n", this, toChars()); if (cto) { assert(cto.mod == MODconst); return cto; } TypeNext t = cast(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; } override final Type makeImmutable() { //printf("TypeNext::makeImmutable() %s\n", toChars()); if (ito) { assert(ito.isImmutable()); return ito; } TypeNext t = cast(TypeNext)Type.makeImmutable(); if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable()) { t.next = next.immutableOf(); } return t; } override final Type makeShared() { //printf("TypeNext::makeShared() %s\n", toChars()); if (sto) { assert(sto.mod == MODshared); return sto; } TypeNext t = cast(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; } override final Type makeSharedConst() { //printf("TypeNext::makeSharedConst() %s\n", toChars()); if (scto) { assert(scto.mod == (MODshared | MODconst)); return scto; } TypeNext t = cast(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; } override final Type makeWild() { //printf("TypeNext::makeWild() %s\n", toChars()); if (wto) { assert(wto.mod == MODwild); return wto; } TypeNext t = cast(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; } override final Type makeWildConst() { //printf("TypeNext::makeWildConst() %s\n", toChars()); if (wcto) { assert(wcto.mod == MODwildconst); return wcto; } TypeNext t = cast(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; } override final Type makeSharedWild() { //printf("TypeNext::makeSharedWild() %s\n", toChars()); if (swto) { assert(swto.isSharedWild()); return swto; } TypeNext t = cast(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; } override final Type makeSharedWildConst() { //printf("TypeNext::makeSharedWildConst() %s\n", toChars()); if (swcto) { assert(swcto.mod == (MODshared | MODwildconst)); return swcto; } TypeNext t = cast(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; } override final Type makeMutable() { //printf("TypeNext::makeMutable() %p, %s\n", this, toChars()); TypeNext t = cast(TypeNext)Type.makeMutable(); if (ty == Tsarray) { t.next = next.mutableOf(); } //printf("TypeNext::makeMutable() returns %p, %s\n", t, t.toChars()); return t; } override MATCH 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; } override final ubyte deduceWild(Type t, bool isRef) { if (ty == Tfunction) return 0; ubyte 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; } final void transitive() { /* Invoke transitivity of type attributes */ next = next.addMod(mod); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeBasic : Type { const(char)* dstring; uint flags; extern (D) this(TY ty) { super(ty); const(char)* d; uint 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(); } override const(char)* kind() const { return dstring; } override Type syntaxCopy() { // No semantic analysis done on basic types, no need to copy return this; } override d_uns64 size(Loc loc) const { uint 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); } //printf("TypeBasic::size() = %d\n", size); return size; } override uint alignsize() { return Target.alignsize(this); } override Expression getProperty(Loc loc, Identifier ident, int flag) { Expression e; dinteger_t ivalue; real_t fvalue = 0; //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 = 0x7FFFU; goto Livalue; case Tuns16: ivalue = 0xFFFFU; goto Livalue; case Tint32: ivalue = 0x7FFFFFFFU; goto Livalue; case Tuns32: ivalue = 0xFFFFFFFFU; goto Livalue; case Tint64: ivalue = 0x7FFFFFFFFFFFFFFFL; goto Livalue; case Tuns64: ivalue = 0xFFFFFFFFFFFFFFFFUL; goto Livalue; case Tbool: ivalue = 1; goto Livalue; case Tchar: ivalue = 0xFF; goto Livalue; case Twchar: ivalue = 0xFFFFU; goto Livalue; case Tdchar: ivalue = 0x10FFFFU; 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; default: break; } } 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 = -2147483647 - 1; goto Livalue; case Tuns32: ivalue = 0; goto Livalue; case Tint64: ivalue = (-9223372036854775807L - 1L); 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; default: break; } } else if (ident == Id.min_normal) { Lmin_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; default: break; } } 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; default: break; } } 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; default: break; } } 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; default: break; } } 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; default: break; } } 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; default: break; } } 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; default: break; } } 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; default: break; } } 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; default: break; } } 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; default: break; } } 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 { const 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; } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { static if (LOGDOTEXP) { printf("TypeBasic::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } 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 = e.semantic(sc); return e; } override bool isintegral() { //printf("TypeBasic::isintegral('%s') x%x\n", toChars(), flags); return (flags & TFLAGSintegral) != 0; } override bool isfloating() const { return (flags & TFLAGSfloating) != 0; } override bool isreal() const { return (flags & TFLAGSreal) != 0; } override bool isimaginary() const { return (flags & TFLAGSimaginary) != 0; } override bool iscomplex() const { return (flags & TFLAGScomplex) != 0; } override bool isscalar() const { return (flags & (TFLAGSintegral | TFLAGSfloating)) != 0; } override bool isunsigned() const { return (flags & TFLAGSunsigned) != 0; } override MATCH 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 = cast(TypeVector)to; tob = tv.elementType(); } 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; } override Expression defaultInit(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypeBasic::defaultInit() '%s'\n", toChars()); } 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). const 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(); default: break; } return new IntegerExp(loc, value, this); } override bool isZeroInit(Loc loc) const { 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 } } // For eliminating dynamic_cast override TypeBasic isTypeBasic() { return this; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * 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] */ extern (C++) final class TypeVector : Type { Type basetype; extern (D) this(Loc loc, Type basetype) { super(Tvector); this.basetype = basetype; } override const(char)* kind() const { return "vector"; } override Type syntaxCopy() { return new TypeVector(Loc(), basetype.syntaxCopy()); } override Type semantic(Loc loc, Scope* sc) { uint errors = global.errors; basetype = basetype.semantic(loc, sc); if (errors != global.errors) return terror; basetype = basetype.toBasetype().mutableOf(); if (basetype.ty != Tsarray) { error(loc, "T in __vector(T) must be a static array, not %s", basetype.toChars()); return terror; } TypeSArray t = cast(TypeSArray)basetype; int sz = cast(int)t.size(loc); switch (Target.checkVectorType(sz, t.nextOf())) { case 0: // valid break; case 1: // no support at all error(loc, "SIMD vector types not supported on this platform"); return terror; case 2: // invalid size error(loc, "%d byte vector type %s is not supported on this platform", sz, toChars()); return terror; case 3: // invalid base type error(loc, "vector type %s is not supported on this platform", toChars()); return terror; default: assert(0); } return merge(); } override d_uns64 size(Loc loc) { return basetype.size(); } override uint alignsize() { return cast(uint)basetype.size(); } override Expression getProperty(Loc loc, Identifier ident, int flag) { return Type.getProperty(loc, ident, flag); } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { static if (LOGDOTEXP) { printf("TypeVector::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } 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 = e.semantic(sc); e = e.castTo(sc, basetype.nextOf().pointerTo()); return e; } if (ident == Id.array) { //e = e.castTo(sc, basetype); // Keep lvalue-ness e = e.copy(); e.type = basetype; 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); } override bool isintegral() { //printf("TypeVector::isintegral('%s') x%x\n", toChars(), flags); return basetype.nextOf().isintegral(); } override bool isfloating() { return basetype.nextOf().isfloating(); } override bool isscalar() { return basetype.nextOf().isscalar(); } override bool isunsigned() { return basetype.nextOf().isunsigned(); } override bool isBoolean() const { return false; } override MATCH implicitConvTo(Type to) { //printf("TypeVector::implicitConvTo(%s) from %s\n", to.toChars(), toChars()); if (this == to) return MATCHexact; if (ty == to.ty) return MATCHconvert; return MATCHnomatch; } override Expression defaultInit(Loc loc) { //printf("TypeVector::defaultInit()\n"); assert(basetype.ty == Tsarray); Expression e = basetype.defaultInit(loc); auto ve = new VectorExp(loc, e, this); ve.type = this; ve.dim = cast(int)(basetype.size(loc) / elementType().size(loc)); return ve; } override Expression defaultInitLiteral(Loc loc) { //printf("TypeVector::defaultInitLiteral()\n"); assert(basetype.ty == Tsarray); Expression e = basetype.defaultInitLiteral(loc); auto ve = new VectorExp(loc, e, this); ve.type = this; ve.dim = cast(int)(basetype.size(loc) / elementType().size(loc)); return ve; } TypeBasic elementType() { assert(basetype.ty == Tsarray); TypeSArray t = cast(TypeSArray)basetype; TypeBasic tb = t.nextOf().isTypeBasic(); assert(tb); return tb; } override bool isZeroInit(Loc loc) { return basetype.isZeroInit(loc); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) class TypeArray : TypeNext { final extern (D) this(TY ty, Type next) { super(ty, next); } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { Type n = this.next.toBasetype(); // uncover any typedef's static if (LOGDOTEXP) { printf("TypeArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } if (e.op == TOKtype) { if (ident == Id.sort || ident == Id.reverse) { e.error("%s is not an expression", e.toChars()); return new ErrorExp(); } } if (!n.isMutable()) { if (ident == Id.sort || ident == Id.reverse) { error(e.loc, "can only %s a mutable array", ident.toChars()); goto Lerror; } } if (ident == Id.reverse && (n.ty == Tchar || n.ty == Twchar)) { static __gshared const(char)** reverseName = ["_adReverseChar", "_adReverseWchar"]; static __gshared FuncDeclaration* reverseFd = [null, null]; deprecation(e.loc, "use std.algorithm.reverse instead of .reverse property"); int i = n.ty == Twchar; if (!reverseFd[i]) { auto params = new Parameters(); Type next = n.ty == Twchar ? Type.twchar : Type.tchar; Type arrty = next.arrayOf(); params.push(new Parameter(0, arrty, null, null)); reverseFd[i] = FuncDeclaration.genCfunc(params, arrty, reverseName[i]); } Expression ec = new VarExp(Loc(), reverseFd[i], false); e = e.castTo(sc, n.arrayOf()); // convert to dynamic array auto arguments = new Expressions(); arguments.push(e); e = new CallExp(e.loc, ec, arguments); e.type = next.arrayOf(); } else if (ident == Id.sort && (n.ty == Tchar || n.ty == Twchar)) { static __gshared const(char)** sortName = ["_adSortChar", "_adSortWchar"]; static __gshared FuncDeclaration* sortFd = [null, null]; deprecation(e.loc, "use std.algorithm.sort instead of .sort property"); int i = n.ty == Twchar; if (!sortFd[i]) { auto params = new Parameters(); Type next = n.ty == Twchar ? Type.twchar : Type.tchar; Type arrty = next.arrayOf(); params.push(new Parameter(0, arrty, null, null)); sortFd[i] = FuncDeclaration.genCfunc(params, arrty, sortName[i]); } Expression ec = new VarExp(Loc(), sortFd[i], false); e = e.castTo(sc, n.arrayOf()); // convert to dynamic array auto arguments = new Expressions(); arguments.push(e); e = new CallExp(e.loc, ec, arguments); e.type = next.arrayOf(); } else if (ident == Id.reverse) { Expression ec; FuncDeclaration fd; Expressions* arguments; dinteger_t size = next.size(e.loc); deprecation(e.loc, "use std.algorithm.reverse instead of .reverse property"); assert(size); static __gshared FuncDeclaration adReverse_fd = null; if (!adReverse_fd) { auto params = new Parameters(); params.push(new Parameter(0, Type.tvoid.arrayOf(), null, null)); params.push(new Parameter(0, Type.tsize_t, null, null)); adReverse_fd = FuncDeclaration.genCfunc(params, Type.tvoid.arrayOf(), Id.adReverse); } fd = adReverse_fd; ec = new VarExp(Loc(), fd, false); e = e.castTo(sc, n.arrayOf()); // convert to dynamic array arguments = new Expressions(); arguments.push(e); arguments.push(new IntegerExp(Loc(), size, Type.tsize_t)); e = new CallExp(e.loc, ec, arguments); e.type = next.mutableOf().arrayOf(); } else if (ident == Id.sort) { static __gshared FuncDeclaration fd = null; Expression ec; Expressions* arguments; deprecation(e.loc, "use std.algorithm.sort instead of .sort property"); if (!fd) { auto params = new Parameters(); params.push(new Parameter(0, Type.tvoid.arrayOf(), null, null)); params.push(new Parameter(0, Type.dtypeinfo.type, null, null)); fd = FuncDeclaration.genCfunc(params, Type.tvoid.arrayOf(), "_adSort"); } ec = new VarExp(Loc(), fd, false); e = e.castTo(sc, n.arrayOf()); // convert to dynamic array arguments = new Expressions(); arguments.push(e); // don't convert to dynamic array Expression tid = new TypeidExp(e.loc, n); tid = tid.semantic(sc); arguments.push(tid); e = new CallExp(e.loc, ec, arguments); e.type = next.arrayOf(); } else { e = Type.dotExp(sc, e, ident, flag); } if (!(flag & 1) || e) e = e.semantic(sc); return e; Lerror: return new ErrorExp(); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * Static array, one with a fixed dimension */ extern (C++) final class TypeSArray : TypeArray { Expression dim; extern (D) this(Type t, Expression dim) { super(Tsarray, t); //printf("TypeSArray(%s)\n", dim.toChars()); this.dim = dim; } override const(char)* kind() const { return "sarray"; } override Type syntaxCopy() { Type t = next.syntaxCopy(); Expression e = dim.syntaxCopy(); t = new TypeSArray(t, e); t.mod = mod; return t; } override d_uns64 size(Loc loc) { //printf("TypeSArray::size()\n"); dinteger_t sz; if (!dim) return Type.size(loc); sz = dim.toInteger(); { bool overflow = false; sz = mulu(next.size(), sz, overflow); if (overflow) goto Loverflow; } if (sz > uint.max) goto Loverflow; return sz; Loverflow: error(loc, "static array %s size overflowed to %lld", toChars(), cast(long)sz); return SIZE_INVALID; } override uint alignsize() { return next.alignsize(); } override Type semantic(Loc loc, Scope* sc) { //printf("TypeSArray::semantic() %s\n", toChars()); Type t; Expression e; Dsymbol s; next.resolve(loc, sc, &e, &t, &s); if (auto tup = s ? s.isTupleDeclaration() : null) { dim = semanticLength(sc, tup, dim); dim = dim.ctfeInterpret(); if (dim.op == TOKerror) return Type.terror; uinteger_t d = dim.toUInteger(); if (d >= tup.objects.dim) { error(loc, "tuple index %llu exceeds %u", d, tup.objects.dim); return Type.terror; } RootObject o = (*tup.objects)[cast(size_t)d]; if (o.dyncast() != DYNCAST_TYPE) { error(loc, "%s is not a type", toChars()); return Type.terror; } t = (cast(Type)o).addMod(this.mod); return t; } Type tn = next.semantic(loc, sc); if (tn.ty == Terror) return terror; Type tbn = tn.toBasetype(); if (dim) { uint errors = global.errors; dim = semanticLength(sc, tbn, dim); if (errors != global.errors) goto Lerror; dim = dim.optimize(WANTvalue); dim = dim.ctfeInterpret(); if (dim.op == TOKerror) goto Lerror; errors = global.errors; dinteger_t d1 = dim.toInteger(); if (errors != global.errors) goto Lerror; dim = dim.implicitCastTo(sc, tsize_t); dim = dim.optimize(WANTvalue); if (dim.op == TOKerror) goto Lerror; errors = global.errors; dinteger_t d2 = dim.toInteger(); if (errors != global.errors) goto Lerror; if (dim.op == TOKerror) goto Lerror; if (d1 != d2) { Loverflow: error(loc, "%s size %llu * %llu exceeds 16MiB size limit for static array", toChars(), cast(ulong)tbn.size(loc), cast(ulong)d1); goto Lerror; } Type tbx = tbn.baseElemOf(); if (tbx.ty == Tstruct && !(cast(TypeStruct)tbx).sym.members || tbx.ty == Tenum && !(cast(TypeEnum)tbx).sym.members) { /* To avoid meaningess error message, skip the total size limit check * when the bottom of element type is opaque. */ } else if (tbn.isintegral() || tbn.isfloating() || tbn.ty == Tpointer || tbn.ty == Tarray || tbn.ty == Tsarray || tbn.ty == Taarray || (tbn.ty == Tstruct && ((cast(TypeStruct)tbn).sym.sizeok == SIZEOKdone)) || tbn.ty == Tclass) { /* Only do this for types that don't need to have semantic() * run on them for the size, since they may be forward referenced. */ bool overflow = false; if (mulu(tbn.size(loc), d2, overflow) >= 0x100_0000 || overflow) // put a 'reasonable' limit on it goto Loverflow; } } switch (tbn.ty) { case Ttuple: { // Index the tuple to get the type assert(dim); TypeTuple tt = cast(TypeTuple)tbn; uinteger_t d = dim.toUInteger(); if (d >= tt.arguments.dim) { error(loc, "tuple index %llu exceeds %u", d, tt.arguments.dim); goto Lerror; } Type telem = (*tt.arguments)[cast(size_t)d].type; return telem.addMod(this.mod); } case Tfunction: case Tnone: error(loc, "can't have array of %s", tbn.toChars()); goto Lerror; default: break; } if (tbn.isscope()) { error(loc, "cannot have array of scope %s", tbn.toChars()); goto Lerror; } /* Ensure things like const(immutable(T)[3]) become immutable(T[3]) * and const(T)[3] become const(T[3]) */ next = tn; transitive(); t = addMod(tn.mod); return t.merge(); Lerror: return Type.terror; } override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) { //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; if (auto tup = s.isTupleDeclaration()) { dim = semanticLength(sc, tup, dim); dim = dim.ctfeInterpret(); if (dim.op == TOKerror) { *ps = null; *pt = Type.terror; return; } uinteger_t d = dim.toUInteger(); if (d >= tup.objects.dim) { error(loc, "tuple index %llu exceeds length %u", d, tup.objects.dim); *ps = null; *pt = Type.terror; return; } RootObject o = (*tup.objects)[cast(size_t)d]; if (o.dyncast() == DYNCAST_DSYMBOL) { *ps = cast(Dsymbol)o; return; } if (o.dyncast() == DYNCAST_EXPRESSION) { Expression e = cast(Expression)o; if (e.op == TOKdsymbol) { *ps = (cast(DsymbolExp)e).s; *pe = null; } else { *ps = null; *pe = e; } return; } if (o.dyncast() == DYNCAST_TYPE) { *ps = null; *pt = (cast(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. */ auto objects = new Objects(); objects.setDim(1); (*objects)[0] = o; *ps = new TupleDeclaration(loc, tup.ident, objects); } 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); } } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { static if (LOGDOTEXP) { printf("TypeSArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } 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 & DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe()) { // MAINTENANCE: turn into error in 2.073 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 = e.semantic(sc); return e; } override bool isString() { TY nty = next.toBasetype().ty; return nty == Tchar || nty == Twchar || nty == Tdchar; } override bool isZeroInit(Loc loc) { return next.isZeroInit(loc); } override structalign_t alignment() { return next.alignment(); } override MATCH constConv(Type to) { if (to.ty == Tsarray) { TypeSArray tsa = cast(TypeSArray)to; if (!dim.equals(tsa.dim)) return MATCHnomatch; } return TypeNext.constConv(to); } override MATCH implicitConvTo(Type to) { //printf("TypeSArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), toChars()); if (to.ty == Tarray) { TypeDArray ta = cast(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 = cast(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; } override Expression defaultInit(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypeSArray::defaultInit() '%s'\n", toChars()); } if (next.ty == Tvoid) return tuns8.defaultInit(loc); else return next.defaultInit(loc); } override Expression defaultInitLiteral(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypeSArray::defaultInitLiteral() '%s'\n", toChars()); } size_t d = cast(size_t)dim.toInteger(); Expression elementinit; if (next.ty == Tvoid) elementinit = tuns8.defaultInitLiteral(loc); else elementinit = next.defaultInitLiteral(loc); auto elements = new Expressions(); elements.setDim(d); for (size_t i = 0; i < d; i++) (*elements)[i] = null; auto ae = new ArrayLiteralExp(Loc(), elementinit, elements); ae.type = this; return ae; } override Expression toExpression() { Expression e = next.toExpression(); if (e) e = new ArrayExp(dim.loc, e, dim); return e; } override bool 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(); } override bool needsDestruction() { return next.needsDestruction(); } /********************************* * */ override bool needsNested() { return next.needsNested(); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * Dynamic array, no dimension */ extern (C++) final class TypeDArray : TypeArray { extern (D) this(Type t) { super(Tarray, t); //printf("TypeDArray(t = %p)\n", t); } override const(char)* kind() const { return "darray"; } override Type syntaxCopy() { Type t = next.syntaxCopy(); if (t == next) t = this; else { t = new TypeDArray(t); t.mod = mod; } return t; } override d_uns64 size(Loc loc) const { //printf("TypeDArray::size()\n"); return Target.ptrsize * 2; } override uint alignsize() const { // A DArray consists of two ptr-sized values, so align it on pointer size // boundary return Target.ptrsize; } override Type semantic(Loc loc, Scope* sc) { Type tn = next.semantic(loc, sc); Type tbn = tn.toBasetype(); switch (tbn.ty) { case Ttuple: return tbn; case Tfunction: case Tnone: error(loc, "can't have array of %s", tbn.toChars()); return Type.terror; case Terror: return Type.terror; default: break; } if (tn.isscope()) { error(loc, "cannot have array of scope %s", tn.toChars()); return Type.terror; } next = tn; transitive(); return merge(); } override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) { //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) { if (auto tup = (*ps).isTupleDeclaration()) { // 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); } } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { static if (LOGDOTEXP) { printf("TypeDArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } 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 = cast(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 & DotExpFlag.noDeref) && sc.func && !sc.intypeof && sc.func.setUnsafe()) { // MAINTENANCE: turn into error in 2.073 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; } override bool isString() { TY nty = next.toBasetype().ty; return nty == Tchar || nty == Twchar || nty == Tdchar; } override bool isZeroInit(Loc loc) const { return true; } override bool isBoolean() const { return true; } override MATCH 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 = cast(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); } override Expression defaultInit(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypeDArray::defaultInit() '%s'\n", toChars()); } return new NullExp(loc, this); } override bool hasPointers() const { return true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeAArray : TypeArray { Type index; // key type Loc loc; Scope* sc; extern (D) this(Type t, Type index) { super(Taarray, t); this.index = index; } static TypeAArray create(Type t, Type index) { return new TypeAArray(t, index); } override const(char)* kind() const { return "aarray"; } override Type 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; } override d_uns64 size(Loc loc) { return Target.ptrsize; } override Type semantic(Loc loc, Scope* sc) { //printf("TypeAArray::semantic() %s index.ty = %d\n", toChars(), index.ty); if (deco) return this; this.loc = loc; this.sc = sc; if (sc) sc.setNoFree(); // Deal with the case where we thought the index was a type, but // in reality it was an expression. if (index.ty == Tident || index.ty == Tinstance || index.ty == Tsarray || index.ty == Ttypeof || index.ty == Treturn) { Expression e; Type t; Dsymbol s; index.resolve(loc, sc, &e, &t, &s); if (e) { // It was an expression - // Rewrite as a static array auto tsa = new TypeSArray(next, e); return tsa.semantic(loc, sc); } else if (t) index = t.semantic(loc, sc); else { index.error(loc, "index is not a type or an expression"); return Type.terror; } } else index = index.semantic(loc, sc); index = index.merge2(); if (index.nextOf() && !index.nextOf().isImmutable()) { index = index.constOf().mutableOf(); version (none) { printf("index is %p %s\n", index, index.toChars()); index.check(); printf("index.mod = x%x\n", index.mod); printf("index.ito = x%x\n", index.ito); if (index.ito) { printf("index.ito.mod = x%x\n", index.ito.mod); printf("index.ito.ito = x%x\n", index.ito.ito); } } } switch (index.toBasetype().ty) { case Tfunction: case Tvoid: case Tnone: case Ttuple: error(loc, "can't have associative array key of %s", index.toBasetype().toChars()); goto case Terror; case Terror: return Type.terror; default: break; } Type tbase = index.baseElemOf(); while (tbase.ty == Tarray) tbase = tbase.nextOf().baseElemOf(); if (tbase.ty == Tstruct) { /* AA's need typeid(index).equals() and getHash(). Issue error if not correctly set up. */ StructDeclaration sd = (cast(TypeStruct)tbase).sym; if (sd._scope) sd.semantic(null); // duplicate a part of StructDeclaration::semanticTypeInfoMembers //printf("AA = %s, key: xeq = %p, xerreq = %p xhash = %p\n", toChars(), sd.xeq, sd.xerreq, sd.xhash); if (sd.xeq && sd.xeq._scope && sd.xeq.semanticRun < PASSsemantic3done) { uint errors = global.startGagging(); sd.xeq.semantic3(sd.xeq._scope); if (global.endGagging(errors)) sd.xeq = sd.xerreq; } //printf("AA = %s, key: xeq = %p, xhash = %p\n", toChars(), sd.xeq, sd.xhash); const(char)* s = (index.toBasetype().ty != Tstruct) ? "bottom of " : ""; if (!sd.xeq) { // If sd.xhash != NULL: // sd or its fields have user-defined toHash. // AA assumes that its result is consistent with bitwise equality. // else: // bitwise equality & hashing } else if (sd.xeq == sd.xerreq) { if (search_function(sd, Id.eq)) { error(loc, "%sAA key type %s does not have 'bool opEquals(ref const %s) const'", s, sd.toChars(), sd.toChars()); } else { error(loc, "%sAA key type %s does not support const equality", s, sd.toChars()); } return Type.terror; } else if (!sd.xhash) { if (search_function(sd, Id.eq)) { error(loc, "%sAA key type %s should have 'size_t toHash() const nothrow @safe' if opEquals defined", s, sd.toChars()); } else { error(loc, "%sAA key type %s supports const equality but doesn't support const hashing", s, sd.toChars()); } return Type.terror; } else { // defined equality & hashing assert(sd.xeq && sd.xhash); /* xeq and xhash may be implicitly defined by compiler. For example: * struct S { int[] arr; } * With 'arr' field equality and hashing, compiler will implicitly * generate functions for xopEquals and xtoHash in TypeInfo_Struct. */ } } else if (tbase.ty == Tclass && !(cast(TypeClass)tbase).sym.isInterfaceDeclaration()) { ClassDeclaration cd = (cast(TypeClass)tbase).sym; if (cd._scope) cd.semantic(null); if (!ClassDeclaration.object) { error(Loc(), "missing or corrupt object.d"); fatal(); } static __gshared FuncDeclaration feq = null; static __gshared FuncDeclaration fcmp = null; static __gshared FuncDeclaration fhash = null; if (!feq) feq = search_function(ClassDeclaration.object, Id.eq).isFuncDeclaration(); if (!fcmp) fcmp = search_function(ClassDeclaration.object, Id.cmp).isFuncDeclaration(); if (!fhash) fhash = search_function(ClassDeclaration.object, Id.tohash).isFuncDeclaration(); assert(fcmp && feq && fhash); if (feq.vtblIndex < cd.vtbl.dim && cd.vtbl[feq.vtblIndex] == feq) { version (all) { if (fcmp.vtblIndex < cd.vtbl.dim && cd.vtbl[fcmp.vtblIndex] != fcmp) { const(char)* s = (index.toBasetype().ty != Tclass) ? "bottom of " : ""; error(loc, "%sAA key type %s now requires equality rather than comparison", s, cd.toChars()); errorSupplemental(loc, "Please override Object.opEquals and toHash."); } } } } next = next.semantic(loc, sc).merge2(); transitive(); switch (next.toBasetype().ty) { case Tfunction: case Tvoid: case Tnone: case Ttuple: error(loc, "can't have associative array of %s", next.toChars()); goto case Terror; case Terror: return Type.terror; default: break; } if (next.isscope()) { error(loc, "cannot have array of scope %s", next.toChars()); return Type.terror; } return merge(); } override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) { //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 auto 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); } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { static if (LOGDOTEXP) { printf("TypeAArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } if (ident == Id.length) { static __gshared FuncDeclaration fd_aaLen = null; if (fd_aaLen is null) { auto fparams = new Parameters(); fparams.push(new Parameter(STCin, this, null, null)); fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen); TypeFunction tf = cast(TypeFunction)fd_aaLen.type; 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 = (cast(TypeFunction)fd_aaLen.type).next; } else e = Type.dotExp(sc, e, ident, flag); return e; } override Expression defaultInit(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypeAArray::defaultInit() '%s'\n", toChars()); } return new NullExp(loc, this); } override bool isZeroInit(Loc loc) const { return true; } override bool isBoolean() const { return true; } override Expression toExpression() { Expression e = next.toExpression(); if (e) { Expression ei = index.toExpression(); if (ei) return new ArrayExp(loc, e, ei); } return null; } override bool hasPointers() const { return true; } override MATCH 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 = cast(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); } override MATCH constConv(Type to) { if (to.ty == Taarray) { TypeAArray taa = cast(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); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypePointer : TypeNext { extern (D) this(Type t) { super(Tpointer, t); } override const(char)* kind() const { return "pointer"; } override Type syntaxCopy() { Type t = next.syntaxCopy(); if (t == next) t = this; else { t = new TypePointer(t); t.mod = mod; } return t; } override Type semantic(Loc loc, Scope* sc) { //printf("TypePointer::semantic() %s\n", toChars()); if (deco) return this; Type n = next.semantic(loc, sc); switch (n.toBasetype().ty) { case Ttuple: error(loc, "can't have pointer to %s", n.toChars()); goto case Terror; case Terror: return Type.terror; default: break; } if (n != next) { deco = null; } next = n; if (next.ty != Tfunction) { transitive(); return merge(); } version (none) { return merge(); } else { deco = merge().deco; /* Don't return merge(), because arg identifiers and default args * can be different * even though the types match */ return this; } } override d_uns64 size(Loc loc) const { return Target.ptrsize; } override MATCH 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 = cast(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 = cast(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; } override MATCH constConv(Type to) { if (next.ty == Tfunction) { if (to.nextOf() && next.equals((cast(TypeNext)to).next)) return Type.constConv(to); else return MATCHnomatch; } return TypeNext.constConv(to); } override bool isscalar() const { return true; } override Expression defaultInit(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypePointer::defaultInit() '%s'\n", toChars()); } return new NullExp(loc, this); } override bool isZeroInit(Loc loc) const { return true; } override bool hasPointers() const { return true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeReference : TypeNext { extern (D) this(Type t) { super(Treference, t); // BUG: what about references to static arrays? } override const(char)* kind() const { return "reference"; } override Type syntaxCopy() { Type t = next.syntaxCopy(); if (t == next) t = this; else { t = new TypeReference(t); t.mod = mod; } return t; } override Type semantic(Loc loc, Scope* sc) { //printf("TypeReference::semantic()\n"); Type n = next.semantic(loc, sc); if (n != next) deco = null; next = n; transitive(); return merge(); } override d_uns64 size(Loc loc) const { return Target.ptrsize; } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { static if (LOGDOTEXP) { printf("TypeReference::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } // References just forward things along return next.dotExp(sc, e, ident, flag); } override Expression defaultInit(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypeReference::defaultInit() '%s'\n", toChars()); } return new NullExp(loc, this); } override bool isZeroInit(Loc loc) const { return true; } override void accept(Visitor v) { v.visit(this); } } enum RET : int { RETregs = 1, // returned in registers RETstack = 2, // returned on stack } alias RETregs = RET.RETregs; alias RETstack = RET.RETstack; enum TRUST : int { TRUSTdefault = 0, TRUSTsystem = 1, // @system (same as TRUSTdefault) TRUSTtrusted = 2, // @trusted TRUSTsafe = 3, // @safe } alias TRUSTdefault = TRUST.TRUSTdefault; alias TRUSTsystem = TRUST.TRUSTsystem; alias TRUSTtrusted = TRUST.TRUSTtrusted; alias TRUSTsafe = TRUST.TRUSTsafe; enum TRUSTformat : int { TRUSTformatDefault, // do not emit @system when trust == TRUSTdefault TRUSTformatSystem, // emit @system when trust == TRUSTdefault } alias TRUSTformatDefault = TRUSTformat.TRUSTformatDefault; alias TRUSTformatSystem = TRUSTformat.TRUSTformatSystem; enum PURE : int { PUREimpure = 0, // not pure at all PUREfwdref = 1, // it's pure, but not known which level yet PUREweak = 2, // no mutable globals are read or written PUREconst = 3, // parameters are values or const PUREstrong = 4, // parameters are values or immutable } alias PUREimpure = PURE.PUREimpure; alias PUREfwdref = PURE.PUREfwdref; alias PUREweak = PURE.PUREweak; alias PUREconst = PURE.PUREconst; alias PUREstrong = PURE.PUREstrong; /*********************************************************** */ extern (C++) final class TypeFunction : TypeNext { // .next is the return type Parameters* parameters; // function parameters int varargs; // 1: T t, ...) style for variable number of arguments // 2: T t ...) style for variable number of arguments bool isnothrow; // true: nothrow bool isnogc; // true: is @nogc bool isproperty; // can be called without parentheses bool isref; // true: returns a reference bool isreturn; // true: 'this' is returned by ref bool isscope; // true: 'this' is scope LINK linkage; // calling convention TRUST trust; // level of trust PURE purity = PUREimpure; ubyte iswild; // bit0: inout on params, bit1: inout on qualifier Expressions* fargs; // function arguments int inuse; extern (D) this(Parameters* parameters, Type treturn, int varargs, LINK linkage, StorageClass stc = 0) { super(Tfunction, treturn); //if (!treturn) *(char*)0=0; // assert(treturn); assert(0 <= varargs && varargs <= 2); this.parameters = parameters; this.varargs = varargs; this.linkage = linkage; 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; this.trust = TRUSTdefault; if (stc & STCsafe) this.trust = TRUSTsafe; if (stc & STCsystem) this.trust = TRUSTsystem; if (stc & STCtrusted) this.trust = TRUSTtrusted; } static TypeFunction create(Parameters* parameters, Type treturn, int varargs, LINK linkage, StorageClass stc = 0) { return new TypeFunction(parameters, treturn, varargs, linkage, stc); } override const(char)* kind() const { return "function"; } override Type syntaxCopy() { Type treturn = next ? next.syntaxCopy() : null; Parameters* params = Parameter.arraySyntaxCopy(parameters); auto t = new TypeFunction(params, treturn, varargs, linkage); t.mod = mod; t.isnothrow = isnothrow; t.isnogc = isnogc; t.purity = purity; t.isproperty = isproperty; t.isref = isref; t.isreturn = isreturn; t.isscope = isscope; t.iswild = iswild; t.trust = trust; t.fargs = fargs; return t; } override Type semantic(Loc loc, Scope* sc) { if (deco) // if semantic() already run { //printf("already done\n"); return this; } //printf("TypeFunction::semantic() this = %p\n", this); //printf("TypeFunction::semantic() %s, sc.stc = %llx, fargs = %p\n", toChars(), sc.stc, fargs); bool errors = false; /* Copy in order to not mess up original. * This can produce redundant copies if inferring return type, * as semantic() will get called again on this. */ TypeFunction tf = cast(TypeFunction)copy(); if (parameters) { tf.parameters = parameters.copy(); for (size_t i = 0; i < parameters.dim; i++) { Parameter p = cast(Parameter)mem.xmalloc(__traits(classInstanceSize, Parameter)); memcpy(cast(void*)p, cast(void*)(*parameters)[i], __traits(classInstanceSize, Parameter)); (*tf.parameters)[i] = p; } } if (sc.stc & STCpure) tf.purity = PUREfwdref; if (sc.stc & STCnothrow) tf.isnothrow = true; if (sc.stc & STCnogc) tf.isnogc = true; if (sc.stc & STCref) tf.isref = true; if (sc.stc & STCreturn) tf.isreturn = true; if (sc.stc & STCscope) tf.isscope = true; if ((sc.stc & (STCreturn | STCref)) == STCreturn) tf.isscope = true; // return by itself means 'return scope' if (tf.trust == TRUSTdefault) { if (sc.stc & STCsafe) tf.trust = TRUSTsafe; else if (sc.stc & STCsystem) tf.trust = TRUSTsystem; else if (sc.stc & STCtrusted) tf.trust = TRUSTtrusted; } if (sc.stc & STCproperty) tf.isproperty = true; tf.linkage = sc.linkage; version (none) { /* If the parent is @safe, then this function defaults to safe * too. * If the parent's @safe-ty is inferred, then this function's @safe-ty needs * to be inferred first. */ if (tf.trust == TRUSTdefault) for (Dsymbol p = sc.func; p; p = p.toParent2()) { FuncDeclaration fd = p.isFuncDeclaration(); if (fd) { if (fd.isSafeBypassingInference()) tf.trust = TRUSTsafe; // default to @safe break; } } } bool wildreturn = false; if (tf.next) { sc = sc.push(); sc.stc &= ~(STC_TYPECTOR | STC_FUNCATTR); tf.next = tf.next.semantic(loc, sc); sc = sc.pop(); errors |= tf.checkRetType(loc); if (tf.next.isscope() && !(sc.flags & SCOPEctor)) { error(loc, "functions cannot return scope %s", tf.next.toChars()); errors = true; } if (tf.next.hasWild()) wildreturn = true; if (tf.isreturn && !tf.isref && !tf.next.hasPointers()) { error(loc, "function has 'return' but does not return any indirections"); } } ubyte wildparams = 0; if (tf.parameters) { /* Create a scope for evaluating the default arguments for the parameters */ Scope* argsc = sc.push(); argsc.stc = 0; // don't inherit storage class argsc.protection = Prot(PROTpublic); argsc.func = null; size_t dim = Parameter.dim(tf.parameters); for (size_t i = 0; i < dim; i++) { Parameter fparam = Parameter.getNth(tf.parameters, i); tf.inuse++; fparam.type = fparam.type.semantic(loc, argsc); if (tf.inuse == 1) tf.inuse--; if (fparam.type.ty == Terror) { errors = true; continue; } fparam.type = fparam.type.addStorageClass(fparam.storageClass); if (fparam.storageClass & (STCauto | STCalias | STCstatic)) { if (!fparam.type) continue; } Type t = fparam.type.toBasetype(); if (t.ty == Tfunction) { error(loc, "cannot have parameter of function type %s", fparam.type.toChars()); errors = true; } else if (!(fparam.storageClass & (STCref | STCout)) && (t.ty == Tstruct || t.ty == Tsarray || t.ty == Tenum)) { Type tb2 = t.baseElemOf(); if (tb2.ty == Tstruct && !(cast(TypeStruct)tb2).sym.members || tb2.ty == Tenum && !(cast(TypeEnum)tb2).sym.memtype) { error(loc, "cannot have parameter of opaque type %s by value", fparam.type.toChars()); errors = true; } } else if (!(fparam.storageClass & STClazy) && t.ty == Tvoid) { error(loc, "cannot have parameter of type %s", fparam.type.toChars()); errors = true; } if ((fparam.storageClass & (STCref | STCwild)) == (STCref | STCwild)) { // 'ref inout' implies 'return' fparam.storageClass |= STCreturn; } if (fparam.storageClass & STCreturn) { if (fparam.storageClass & (STCref | STCout)) { // Disabled for the moment awaiting improvement to allow return by ref // to be transformed into return by scope. if (0 && !tf.isref) { auto stc = fparam.storageClass & (STCref | STCout); error(loc, "parameter %s is 'return %s' but function does not return by ref", fparam.ident ? fparam.ident.toChars() : "", stcToChars(stc)); errors = true; } } else { fparam.storageClass |= STCscope; // 'return' implies 'scope' if (tf.isref) { } else if (tf.next && !tf.next.hasPointers()) { error(loc, "parameter %s is 'return' but function does not return any indirections", fparam.ident ? fparam.ident.toChars() : ""); errors = true; } } } if (fparam.storageClass & (STCref | STClazy)) { } else if (fparam.storageClass & STCout) { if (ubyte m = fparam.type.mod & (MODimmutable | MODconst | MODwild)) { error(loc, "cannot have %s out parameter of type %s", MODtoChars(m), t.toChars()); errors = true; } else { Type tv = t; while (tv.ty == Tsarray) tv = tv.nextOf().toBasetype(); if (tv.ty == Tstruct && (cast(TypeStruct)tv).sym.noDefaultCtor) { error(loc, "cannot have out parameter of type %s because the default construction is disabled", fparam.type.toChars()); errors = true; } } } if (fparam.storageClass & STCscope && !fparam.type.hasPointers()) { fparam.storageClass &= ~STCscope; if (!(fparam.storageClass & STCref)) fparam.storageClass &= ~STCreturn; } if (t.hasWild()) { wildparams |= 1; //if (tf.next && !wildreturn) // error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with 'ref')"); } if (fparam.defaultArg) { Expression e = fparam.defaultArg; if (fparam.storageClass & (STCref | STCout)) { e = e.semantic(argsc); e = resolveProperties(argsc, e); } else { e = inferType(e, fparam.type); Initializer iz = new ExpInitializer(e.loc, e); iz = iz.semantic(argsc, fparam.type, INITnointerpret); e = iz.toExpression(); } if (e.op == TOKfunction) // see Bugzilla 4820 { FuncExp fe = cast(FuncExp)e; // Replace function literal with a function symbol, // since default arg expression must be copied when used // and copying the literal itself is wrong. e = new VarExp(e.loc, fe.fd, false); e = new AddrExp(e.loc, e); e = e.semantic(argsc); } e = e.implicitCastTo(argsc, fparam.type); // default arg must be an lvalue if (fparam.storageClass & (STCout | STCref)) e = e.toLvalue(argsc, e); fparam.defaultArg = e; if (e.op == TOKerror) errors = true; } /* If fparam after semantic() turns out to be a tuple, the number of parameters may * change. */ if (t.ty == Ttuple) { /* TypeFunction::parameter also is used as the storage of * Parameter objects for FuncDeclaration. So we should copy * the elements of TypeTuple::arguments to avoid unintended * sharing of Parameter object among other functions. */ TypeTuple tt = cast(TypeTuple)t; if (tt.arguments && tt.arguments.dim) { /* Propagate additional storage class from tuple parameters to their * element-parameters. * Make a copy, as original may be referenced elsewhere. */ size_t tdim = tt.arguments.dim; auto newparams = new Parameters(); newparams.setDim(tdim); for (size_t j = 0; j < tdim; j++) { Parameter narg = (*tt.arguments)[j]; // Bugzilla 12744: If the storage classes of narg // conflict with the ones in fparam, it's ignored. StorageClass stc = fparam.storageClass | narg.storageClass; StorageClass stc1 = fparam.storageClass & (STCref | STCout | STClazy); StorageClass stc2 = narg.storageClass & (STCref | STCout | STClazy); if (stc1 && stc2 && stc1 != stc2) { OutBuffer buf1; stcToBuffer(&buf1, stc1 | ((stc1 & STCref) ? (fparam.storageClass & STCauto) : 0)); OutBuffer buf2; stcToBuffer(&buf2, stc2); error(loc, "incompatible parameter storage classes '%s' and '%s'", buf1.peekString(), buf2.peekString()); errors = true; stc = stc1 | (stc & ~(STCref | STCout | STClazy)); } (*newparams)[j] = new Parameter( stc, narg.type, narg.ident, narg.defaultArg); } fparam.type = new TypeTuple(newparams); } fparam.storageClass = 0; /* Reset number of parameters, and back up one to do this fparam again, * now that it is a tuple */ dim = Parameter.dim(tf.parameters); i--; continue; } /* Resolve "auto ref" storage class to be either ref or value, * based on the argument matching the parameter */ if (fparam.storageClass & STCauto) { if (fargs && i < fargs.dim && (fparam.storageClass & STCref)) { Expression farg = (*fargs)[i]; if (farg.isLvalue()) { // ref parameter } else fparam.storageClass &= ~STCref; // value parameter fparam.storageClass &= ~STCauto; // Bugzilla 14656 fparam.storageClass |= STCautoref; } else { error(loc, "'auto' can only be used as part of 'auto ref' for template function parameters"); errors = true; } } // Remove redundant storage classes for type, they are already applied fparam.storageClass &= ~(STC_TYPECTOR | STCin); } argsc.pop(); } if (tf.isWild()) wildparams |= 2; if (wildreturn && !wildparams) { error(loc, "inout on return means inout must be on a parameter as well for %s", toChars()); errors = true; } tf.iswild = wildparams; if (tf.inuse) { error(loc, "recursive type"); tf.inuse = 0; errors = true; } if (tf.isproperty && (tf.varargs || Parameter.dim(tf.parameters) > 2)) { error(loc, "properties can only have zero, one, or two parameter"); errors = true; } if (tf.varargs == 1 && tf.linkage != LINKd && Parameter.dim(tf.parameters) == 0) { error(loc, "variadic functions with non-D linkage must have at least one parameter"); errors = true; } if (errors) return terror; if (tf.next) tf.deco = tf.merge().deco; /* Don't return merge(), because arg identifiers and default args * can be different * even though the types match */ return tf; } /******************************************** * Set 'purity' field of 'this'. * Do this lazily, as the parameter types might be forward referenced. */ void purityLevel() { TypeFunction tf = this; if (tf.purity != PUREfwdref) return; /* 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; } purity = PUREstrong; // assume strong until something weakens it /* Evaluate what kind of purity based on the modifiers for the parameters */ const dim = Parameter.dim(tf.parameters); Lloop: foreach (i; 0 .. dim) { Parameter fparam = Parameter.getNth(tf.parameters, i); Type t = fparam.type; if (!t) continue; if (fparam.storageClass & (STClazy | STCout)) { purity = PUREweak; break; } switch (purityOfType((fparam.storageClass & STCref) != 0, t)) { case PUREweak: purity = PUREweak; break Lloop; // since PUREweak, no need to check further case PUREconst: purity = PUREconst; continue; case PUREstrong: continue; default: assert(0); } } if (purity > PUREweak && tf.nextOf()) { /* Adjust purity based on mutability of return type. * https://issues.dlang.org/show_bug.cgi?id=15862 */ const purity2 = purityOfType(tf.isref, tf.nextOf()); if (purity2 < purity) purity = purity2; } tf.purity = purity; } /******************************************** * Return true if there are lazy parameters. */ bool hasLazyParameters() { size_t dim = Parameter.dim(parameters); for (size_t i = 0; i < dim; i++) { Parameter fparam = Parameter.getNth(parameters, i); if (fparam.storageClass & STClazy) return true; } return false; } /*************************** * Examine function signature for parameter p and see if * the value of p can 'escape' the scope of the function. * This is useful to minimize the needed annotations for the parameters. * Params: * p = parameter to this function * Returns: * true if escapes via assignment to global or through a parameter */ bool 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 */ final StorageClass parameterStorageClass(Parameter p) { auto 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 dim = Parameter.dim(parameters); foreach (const i; 0 .. dim) { Parameter fparam = Parameter.getNth(parameters, i); Type t = fparam.type; if (!t) continue; t = t.baseElemOf(); if (t.isMutable() && t.hasPointers()) { if (fparam.storageClass & (STCref | STCout)) { } else if (t.ty == Tarray || t.ty == Tpointer) { Type tn = t.nextOf().toBasetype(); if (!(tn.isMutable() && tn.hasPointers())) continue; } return stc; } } } stc |= STCscope; Type tret = nextOf().toBasetype(); if (isref || tret.hasPointers()) { /* The result has references, so p could be escaping * that way. */ stc |= STCreturn; } return stc; } override Type addStorageClass(StorageClass stc) { TypeFunction t = cast(TypeFunction)Type.addStorageClass(stc); if ((stc & STCpure && !t.purity) || (stc & STCnothrow && !t.isnothrow) || (stc & STCnogc && !t.isnogc) || (stc & STCsafe && t.trust < TRUSTtrusted)) { // Klunky to change these auto tf = new TypeFunction(t.parameters, t.next, t.varargs, t.linkage, 0); tf.mod = t.mod; tf.fargs = fargs; tf.purity = t.purity; tf.isnothrow = t.isnothrow; tf.isnogc = t.isnogc; tf.isproperty = t.isproperty; tf.isref = t.isref; tf.isreturn = t.isreturn; tf.isscope = t.isscope; tf.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; 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 attributesApply(void* param, int function(void*, const(char)*) fp, TRUSTformat trustFormat = TRUSTformatDefault) { 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) 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)); } override Type substWildTo(uint) { 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. uint m = MODconst; assert(next); Type tret = next.substWildTo(m); Parameters* params = parameters; if (mod & MODwild) params = parameters.copy(); for (size_t i = 0; i < params.dim; i++) { Parameter p = (*params)[i]; Type t = p.type.substWildTo(m); if (t == p.type) continue; if (params == parameters) params = parameters.copy(); (*params)[i] = new Parameter(p.storageClass, t, null, null); } if (next == tret && params == parameters) return this; // Similar to TypeFunction::syntaxCopy; auto t = new TypeFunction(params, tret, varargs, linkage); t.mod = ((mod & MODwild) ? (mod & ~MODwild) | MODconst : mod); t.isnothrow = isnothrow; t.isnogc = isnogc; t.purity = purity; t.isproperty = isproperty; t.isref = isref; t.isreturn = isreturn; t.isscope = isscope; t.iswild = 0; t.trust = trust; t.fargs = fargs; return t.merge(); } /******************************** * 'args' are being matched to function 'this' * Determine match level. * Input: * flag 1 performing a partial ordering match * Returns: * MATCHxxxx */ MATCH callMatch(Type tthis, Expressions* args, int flag = 0) { //printf("TypeFunction::callMatch() %s\n", toChars()); MATCH match = MATCHexact; // assume exact match ubyte wildmatch = 0; if (tthis) { Type t = tthis; if (t.toBasetype().ty == Tpointer) t = t.toBasetype().nextOf(); // change struct* to struct if (t.mod != mod) { if (MODimplicitConv(t.mod, mod)) match = MATCHconst; else if ((mod & MODwild) && MODimplicitConv(t.mod, (mod & ~MODwild) | MODconst)) { match = MATCHconst; } else return MATCHnomatch; } if (isWild()) { if (t.isWild()) wildmatch |= MODwild; else if (t.isConst()) wildmatch |= MODconst; else if (t.isImmutable()) wildmatch |= MODimmutable; else wildmatch |= MODmutable; } } size_t nparams = Parameter.dim(parameters); size_t nargs = args ? args.dim : 0; if (nparams == nargs) { } else if (nargs > nparams) { if (varargs == 0) goto Nomatch; // too many args; no match match = MATCHconvert; // match ... with a "conversion" match level } for (size_t u = 0; u < nargs; u++) { if (u >= nparams) break; Parameter p = Parameter.getNth(parameters, u); Expression arg = (*args)[u]; assert(arg); Type tprm = p.type; Type targ = arg.type; if (!(p.storageClass & STClazy && tprm.ty == Tvoid && targ.ty != Tvoid)) { bool isRef = (p.storageClass & (STCref | STCout)) != 0; wildmatch |= targ.deduceWild(tprm, isRef); } } if (wildmatch) { /* Calculate wild matching modifier */ if (wildmatch & MODconst || wildmatch & (wildmatch - 1)) wildmatch = MODconst; else if (wildmatch & MODimmutable) wildmatch = MODimmutable; else if (wildmatch & MODwild) wildmatch = MODwild; else { assert(wildmatch & MODmutable); wildmatch = MODmutable; } } for (size_t u = 0; u < nparams; u++) { MATCH m; Parameter p = Parameter.getNth(parameters, u); assert(p); if (u >= nargs) { if (p.defaultArg) continue; goto L1; // try typesafe variadics } { Expression arg = (*args)[u]; assert(arg); //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars()); Type targ = arg.type; Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type; if (p.storageClass & STClazy && tprm.ty == Tvoid && targ.ty != Tvoid) m = MATCHconvert; else { //printf("%s of type %s implicitConvTo %s\n", arg.toChars(), targ.toChars(), tprm.toChars()); if (flag) { // for partial ordering, value is an irrelevant mockup, just look at the type m = targ.implicitConvTo(tprm); } else m = arg.implicitConvTo(tprm); //printf("match %d\n", m); } // Non-lvalues do not match ref or out parameters if (p.storageClass & (STCref | STCout)) { // Bugzilla 13783: Don't use toBasetype() to handle enum types. Type ta = targ; Type tp = tprm; //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars()); if (m && !arg.isLvalue()) { if (p.storageClass & STCout) goto Nomatch; if (arg.op == TOKstring && tp.ty == Tsarray) { if (ta.ty != Tsarray) { Type tn = tp.nextOf().castMod(ta.nextOf().mod); dinteger_t dim = (cast(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 = (cast(TypeSArray)tp).dim.toUInteger(); ta = tn.sarrayOf(dim); } } else goto Nomatch; } /* Find most derived alias this type being matched. * Bugzilla 15674: Allow on both ref and out parameters. */ while (1) { Type tat = ta.toBasetype().aliasthisOf(); if (!tat || !tat.implicitConvTo(tprm)) break; ta = tat; } /* A ref variable should work like a head-const reference. * e.g. disallows: * ref T <- an lvalue of const(T) argument * ref T[dim] <- an lvalue of const(T[dim]) argument */ if (!ta.constConv(tp)) goto Nomatch; } } /* prefer matching the element type rather than the array * type when more arguments are present with T[]... */ if (varargs == 2 && u + 1 == nparams && nargs > nparams) goto L1; //printf("\tm = %d\n", m); if (m == MATCHnomatch) // if no match { L1: if (varargs == 2 && u + 1 == nparams) // if last varargs param { Type tb = p.type.toBasetype(); TypeSArray tsa; dinteger_t sz; switch (tb.ty) { case Tsarray: tsa = cast(TypeSArray)tb; sz = tsa.dim.toInteger(); if (sz != nargs - u) goto Nomatch; goto case Tarray; case Tarray: { TypeArray ta = cast(TypeArray)tb; for (; u < nargs; u++) { Expression arg = (*args)[u]; assert(arg); /* If lazy array of delegates, * convert arg(s) to delegate(s) */ Type tret = p.isLazyArray(); if (tret) { if (ta.next.equals(arg.type)) m = MATCHexact; else if (tret.toBasetype().ty == Tvoid) m = MATCHconvert; else { m = arg.implicitConvTo(tret); if (m == MATCHnomatch) m = arg.implicitConvTo(ta.next); } } else m = arg.implicitConvTo(ta.next); if (m == MATCHnomatch) goto Nomatch; if (m < match) match = m; } goto Ldone; } case Tclass: // Should see if there's a constructor match? // Or just leave it ambiguous? goto Ldone; default: goto Nomatch; } } goto Nomatch; } if (m < match) match = m; // pick worst match } Ldone: //printf("match = %d\n", match); return match; Nomatch: //printf("no match\n"); return MATCHnomatch; } bool 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 && !(cast(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; } override Expression defaultInit(Loc loc) const { error(loc, "function does not have a default initializer"); return new ErrorExp(); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeDelegate : TypeNext { // .next is a TypeFunction extern (D) this(Type t) { super(Tfunction, t); ty = Tdelegate; } override const(char)* kind() const { return "delegate"; } override Type syntaxCopy() { Type t = next.syntaxCopy(); if (t == next) t = this; else { t = new TypeDelegate(t); t.mod = mod; } return t; } override Type semantic(Loc loc, Scope* sc) { //printf("TypeDelegate::semantic() %s\n", toChars()); if (deco) // if semantic() already run { //printf("already done\n"); return this; } next = next.semantic(loc, sc); if (next.ty != Tfunction) return terror; /* In order to deal with Bugzilla 4028, perhaps default arguments should * be removed from next before the merge. */ version (none) { return merge(); } else { /* Don't return merge(), because arg identifiers and default args * can be different * even though the types match */ deco = merge().deco; return this; } } override d_uns64 size(Loc loc) const { return Target.ptrsize * 2; } override uint alignsize() const { return Target.ptrsize; } override MATCH 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; version (all) { // 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 = (cast(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; } } return MATCHnomatch; } override Expression defaultInit(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypeDelegate::defaultInit() '%s'\n", toChars()); } return new NullExp(loc, this); } override bool isZeroInit(Loc loc) const { return true; } override bool isBoolean() const { return true; } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { static if (LOGDOTEXP) { printf("TypeDelegate::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } if (ident == Id.ptr) { e = new DelegatePtrExp(e.loc, e); e = e.semantic(sc); } else if (ident == Id.funcptr) { if (!(flag & DotExpFlag.noDeref) && 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 = e.semantic(sc); } else { e = Type.dotExp(sc, e, ident, flag); } return e; } override bool hasPointers() const { return true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) abstract class TypeQualified : Type { Loc loc; // array of Identifier and TypeInstance, // representing ident.ident!tiargs.ident. ... etc. Objects idents; final extern (D) this(TY ty, Loc loc) { super(ty); this.loc = loc; } final void syntaxCopyHelper(TypeQualified t) { //printf("TypeQualified::syntaxCopyHelper(%s) %s\n", t.toChars(), toChars()); idents.setDim(t.idents.dim); for (size_t i = 0; i < idents.dim; i++) { RootObject id = t.idents[i]; if (id.dyncast() == DYNCAST_DSYMBOL) { TemplateInstance ti = cast(TemplateInstance)id; ti = cast(TemplateInstance)ti.syntaxCopy(null); id = ti; } else if (id.dyncast() == DYNCAST_EXPRESSION) { Expression e = cast(Expression)id; e = e.syntaxCopy(); id = e; } else if (id.dyncast() == DYNCAST_TYPE) { Type tx = cast(Type)id; tx = tx.syntaxCopy(); id = tx; } idents[i] = id; } } final void addIdent(Identifier ident) { idents.push(ident); } final void addInst(TemplateInstance inst) { idents.push(inst); } final void addIndex(RootObject e) { idents.push(e); } override d_uns64 size(Loc loc) { error(this.loc, "size of type %s is not known", toChars()); return SIZE_INVALID; } /************************************* * Resolve a tuple index. */ final void resolveTupleIndex(Loc loc, Scope* sc, Dsymbol s, Expression* pe, Type* pt, Dsymbol* ps, RootObject oindex) { *pt = null; *ps = null; *pe = null; auto tup = s.isTupleDeclaration(); auto eindex = isExpression(oindex); auto tindex = isType(oindex); auto sindex = isDsymbol(oindex); if (!tup) { // It's really an index expression if (tindex) eindex = new TypeExp(loc, tindex); else if (sindex) eindex = DsymbolExp.resolve(loc, sc, sindex, false); Expression e = new IndexExp(loc, DsymbolExp.resolve(loc, sc, s, false), eindex); e = e.semantic(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 = DsymbolExp.resolve(loc, sc, sindex, false); if (!eindex) { .error(loc, "index is %s not an expression", oindex.toChars()); *pt = Type.terror; return; } eindex = semanticLength(sc, tup, eindex); eindex = eindex.ctfeInterpret(); if (eindex.op == TOKerror) { *pt = Type.terror; return; } const(uinteger_t) d = eindex.toUInteger(); if (d >= tup.objects.dim) { .error(loc, "tuple index %llu exceeds length %u", d, tup.objects.dim); *pt = Type.terror; return; } RootObject o = (*tup.objects)[cast(size_t)d]; *pt = isType(o); *ps = isDsymbol(o); *pe = isExpression(o); if (*pt) *pt = (*pt).semantic(loc, sc); if (*pe) resolveExp(*pe, pt, pe, ps); } final Expression toExpressionHelper(Expression e, size_t i = 0) { //printf("toExpressionHelper(e = %s %s)\n", Token.toChars(e.op), e.toChars()); for (; i < idents.dim; i++) { RootObject id = idents[i]; //printf("\t[%d] e: '%s', id: '%s'\n", i, e.toChars(), id.toChars()); switch (id.dyncast()) { // ... '. ident' case DYNCAST_IDENTIFIER: e = new DotIdExp(e.loc, e, cast(Identifier)id); break; // ... '. name!(tiargs)' case DYNCAST_DSYMBOL: auto ti = (cast(Dsymbol)id).isTemplateInstance(); assert(ti); e = new DotTemplateInstanceExp(e.loc, e, ti.name, ti.tiargs); break; // ... '[type]' case DYNCAST_TYPE: // Bugzilla 1215 e = new ArrayExp(loc, e, new TypeExp(loc, cast(Type)id)); break; // ... '[expr]' case DYNCAST_EXPRESSION: // Bugzilla 1215 e = new ArrayExp(loc, e, cast(Expression)id); break; default: assert(0); } } return e; } /************************************* * 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 */ final void resolveHelper(Loc loc, Scope* sc, Dsymbol s, Dsymbol scopesym, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) { version (none) { printf("TypeQualified::resolveHelper(sc = %p, idents = '%s')\n", sc, toChars()); if (scopesym) printf("\tscopesym = '%s'\n", scopesym.toChars()); } *pe = null; *pt = null; *ps = null; if (s) { //printf("\t1: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind()); Declaration d = s.isDeclaration(); if (d && (d.storage_class & STCtemplateparameter)) s = s.toAlias(); else s.checkDeprecated(loc, sc); // check for deprecated aliases s = s.toAlias(); //printf("\t2: s = '%s' %p, kind = '%s'\n",s.toChars(), s, s.kind()); for (size_t i = 0; i < idents.dim; i++) { RootObject id = idents[i]; if (id.dyncast() == DYNCAST_EXPRESSION || id.dyncast() == DYNCAST_TYPE) { Type tx; Expression ex; Dsymbol sx; resolveTupleIndex(loc, sc, s, &ex, &tx, &sx, id); if (sx) { s = sx.toAlias(); continue; } if (tx) ex = new TypeExp(loc, tx); assert(ex); ex = toExpressionHelper(ex, i + 1); ex = ex.semantic(sc); resolveExp(ex, pt, pe, ps); return; } Type t = s.getType(); // type symbol, type alias, or type tuple? uint errorsave = global.errors; Dsymbol sm = s.searchX(loc, sc, id); if (sm && !(sc.flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, sm)) { .deprecation(loc, "%s is not visible from module %s", sm.toPrettyChars(), sc._module.toChars()); // sm = null; } if (global.errors != errorsave) { *pt = Type.terror; return; } //printf("\t3: s = %p %s %s, sm = %p\n", s, s.kind(), s.toChars(), sm); if (intypeid && !t && sm && sm.needThis()) goto L3; if (VarDeclaration v = s.isVarDeclaration()) { 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, cast(Identifier)id); if (sm) goto L2; } L3: Expression e; VarDeclaration v = s.isVarDeclaration(); FuncDeclaration f = s.isFuncDeclaration(); if (intypeid || !v && !f) e = DsymbolExp.resolve(loc, sc, s, true); else e = new VarExp(loc, s.isDeclaration(), true); e = toExpressionHelper(e, i); e = e.semantic(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(cast(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 (auto em = s.isEnumMember()) { // It's not a type, it's an expression *pe = em.getVarExp(loc, sc); return; } if (auto 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 (auto fld = s.isFuncLiteralDeclaration()) { //printf("'%s' is a function literal\n", fld.toChars()); *pe = new FuncExp(loc, fld); *pe = (*pe).semantic(sc); return; } version (none) { if (FuncDeclaration fd = s.isFuncDeclaration()) { *pe = new DsymbolExp(loc, fd); 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 (!(cast(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) { const(char)* p = mutableOf().unSharedOf().toChars(); const(char)* n = importHint(p); if (n) error(loc, "'%s' is not defined, perhaps you need to import %s; ?", p, n); else { auto id = new Identifier(p); s = sc.search_correct(id); if (s) error(loc, "undefined identifier '%s', did you mean %s '%s'?", p, s.kind(), s.toChars()); else error(loc, "undefined identifier '%s'", p); } *pt = Type.terror; } } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeIdentifier : TypeQualified { Identifier ident; // The symbol representing this identifier, before alias resolution Dsymbol originalSymbol; extern (D) this(Loc loc, Identifier ident) { super(Tident, loc); this.ident = ident; } override const(char)* kind() const { return "identifier"; } override Type syntaxCopy() { auto 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 */ override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) { //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. */ override Dsymbol 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; } override Type semantic(Loc loc, Scope* sc) { Type t; Expression e; Dsymbol s; //printf("TypeIdentifier::semantic(%s)\n", toChars()); resolve(loc, sc, &e, &t, &s); if (t) { //printf("\tit's a type %d, %s, %s\n", t.ty, t.toChars(), t.deco); t = t.addMod(mod); } else { if (s) { s.error(loc, "is used as a type"); //assert(0); } else error(loc, "%s is used as a type", toChars()); t = terror; } //t.print(); return t; } override Expression toExpression() { return toExpressionHelper(new IdentifierExp(loc, ident)); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * Similar to TypeIdentifier, but with a TemplateInstance as the root */ extern (C++) final class TypeInstance : TypeQualified { TemplateInstance tempinst; extern (D) this(Loc loc, TemplateInstance tempinst) { super(Tinstance, loc); this.tempinst = tempinst; } override const(char)* kind() const { return "instance"; } override Type syntaxCopy() { //printf("TypeInstance::syntaxCopy() %s, %d\n", toChars(), idents.dim); auto t = new TypeInstance(loc, cast(TemplateInstance)tempinst.syntaxCopy(null)); t.syntaxCopyHelper(this); t.mod = mod; return t; } override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) { // Note close similarity to TypeIdentifier::resolve() *pe = null; *pt = null; *ps = null; //printf("TypeInstance::resolve(sc = %p, tempinst = '%s')\n", sc, tempinst.toChars()); tempinst.semantic(sc); if (!global.gag && tempinst.errors) { *pt = terror; return; } resolveHelper(loc, sc, tempinst, null, pe, pt, ps, intypeid); if (*pt) *pt = (*pt).addMod(mod); //if (*pt) printf("*pt = %d '%s'\n", (*pt).ty, (*pt).toChars()); } override Type semantic(Loc loc, Scope* sc) { Type t; Expression e; Dsymbol s; //printf("TypeInstance::semantic(%p, %s)\n", this, toChars()); { uint errors = global.errors; resolve(loc, sc, &e, &t, &s); // if we had an error evaluating the symbol, suppress further errors if (!t && errors != global.errors) return terror; } if (!t) { if (!e && s && s.errors) { // if there was an error evaluating the symbol, it might actually // be a type. Avoid misleading error messages. error(loc, "%s had previous errors", toChars()); } else error(loc, "%s is used as a type", toChars()); t = terror; } return t; } override Dsymbol 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; } override Expression toExpression() { return toExpressionHelper(new ScopeExp(loc, tempinst)); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeTypeof : TypeQualified { Expression exp; int inuse; extern (D) this(Loc loc, Expression exp) { super(Ttypeof, loc); this.exp = exp; } override const(char)* kind() const { return "typeof"; } override Type syntaxCopy() { //printf("TypeTypeof::syntaxCopy() %s\n", toChars()); auto t = new TypeTypeof(loc, exp.syntaxCopy()); t.syntaxCopyHelper(this); t.mod = mod; return t; } override Dsymbol toDsymbol(Scope* sc) { //printf("TypeTypeof::toDsymbol('%s')\n", toChars()); Expression e; Type t; Dsymbol s; resolve(loc, sc, &e, &t, &s); return s; } override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) { *pe = null; *pt = null; *ps = null; //printf("TypeTypeof::resolve(sc = %p, idents = '%s')\n", sc, toChars()); //static int nest; if (++nest == 50) *(char*)0=0; if (inuse) { inuse = 2; error(loc, "circular typeof definition"); Lerr: *pt = Type.terror; inuse--; return; } inuse++; /* 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; auto exp2 = exp.semantic(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 (auto f = exp.op == TOKvar ? (cast( VarExp)exp).var.isFuncDeclaration() : exp.op == TOKdotvar ? (cast(DotVarExp)exp).var.isFuncDeclaration() : null) { if (f.checkForwardRef(loc)) goto Lerr; } if (auto f = isFuncAddress(exp)) { if (f.checkForwardRef(loc)) goto Lerr; } Type t = exp.type; if (!t) { error(loc, "expression (%s) has no type", exp.toChars()); goto Lerr; } if (t.ty == Ttypeof) { error(loc, "forward reference to %s", toChars()); goto Lerr; } if (idents.dim == 0) *pt = t; else { if (Dsymbol s = t.toDsymbol(sc)) resolveHelper(loc, sc, s, null, pe, pt, ps, intypeid); else { auto e = toExpressionHelper(new TypeExp(loc, t)); e = e.semantic(sc); resolveExp(e, pt, pe, ps); } } if (*pt) (*pt) = (*pt).addMod(mod); inuse--; return; } override Type semantic(Loc loc, Scope* sc) { //printf("TypeTypeof::semantic() %s\n", toChars()); Expression e; Type t; Dsymbol s; resolve(loc, sc, &e, &t, &s); if (s && (t = s.getType()) !is null) t = t.addMod(mod); if (!t) { error(loc, "%s is used as a type", toChars()); t = Type.terror; } return t; } override d_uns64 size(Loc loc) { if (exp.type) return exp.type.size(loc); else return TypeQualified.size(loc); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeReturn : TypeQualified { extern (D) this(Loc loc) { super(Treturn, loc); } override const(char)* kind() const { return "return"; } override Type syntaxCopy() { auto t = new TypeReturn(loc); t.syntaxCopyHelper(this); t.mod = mod; return t; } override Dsymbol toDsymbol(Scope* sc) { Expression e; Type t; Dsymbol s; resolve(loc, sc, &e, &t, &s); return s; } override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) { *pe = null; *pt = null; *ps = null; //printf("TypeReturn::resolve(sc = %p, idents = '%s')\n", sc, toChars()); Type t; { FuncDeclaration func = sc.func; if (!func) { error(loc, "typeof(return) must be inside function"); goto Lerr; } if (func.fes) func = func.fes.func; t = func.type.nextOf(); if (!t) { error(loc, "cannot use typeof(return) inside function %s with inferred return type", sc.func.toChars()); goto Lerr; } } if (idents.dim == 0) *pt = t; else { if (Dsymbol s = t.toDsymbol(sc)) resolveHelper(loc, sc, s, null, pe, pt, ps, intypeid); else { auto e = toExpressionHelper(new TypeExp(loc, t)); e = e.semantic(sc); resolveExp(e, pt, pe, ps); } } if (*pt) (*pt) = (*pt).addMod(mod); return; Lerr: *pt = Type.terror; return; } override Type semantic(Loc loc, Scope* sc) { //printf("TypeReturn::semantic() %s\n", toChars()); Expression e; Type t; Dsymbol s; resolve(loc, sc, &e, &t, &s); if (s && (t = s.getType()) !is null) t = t.addMod(mod); if (!t) { error(loc, "%s is used as a type", toChars()); t = Type.terror; } return t; } override void accept(Visitor v) { v.visit(this); } } // Whether alias this dependency is recursive or not. enum AliasThisRec : int { RECno = 0, // no alias this recursion RECyes = 1, // alias this has recursive dependency RECfwdref = 2, // not yet known RECtypeMask = 3, // mask to read no/yes/fwdref RECtracing = 0x4, // mark in progress of implicitConvTo/deduceWild RECtracingDT = 0x8, // mark in progress of deduceType } alias RECno = AliasThisRec.RECno; alias RECyes = AliasThisRec.RECyes; alias RECfwdref = AliasThisRec.RECfwdref; alias RECtypeMask = AliasThisRec.RECtypeMask; alias RECtracing = AliasThisRec.RECtracing; alias RECtracingDT = AliasThisRec.RECtracingDT; /*********************************************************** */ extern (C++) final class TypeStruct : Type { StructDeclaration sym; AliasThisRec att = RECfwdref; CPPMANGLE cppmangle = CPPMANGLE.def; extern (D) this(StructDeclaration sym) { super(Tstruct); this.sym = sym; } override const(char)* kind() const { return "struct"; } override d_uns64 size(Loc loc) { return sym.size(loc); } override uint alignsize() { sym.size(Loc()); // give error for forward references return sym.alignsize; } override Type syntaxCopy() { return this; } override Type semantic(Loc loc, Scope* sc) { //printf("TypeStruct::semantic('%s')\n", sym.toChars()); if (deco) { if (sc && sc.cppmangle != CPPMANGLE.def) { if (this.cppmangle == CPPMANGLE.def) this.cppmangle = sc.cppmangle; else assert(this.cppmangle == sc.cppmangle); } return this; } /* Don't semantic for sym because it should be deferred until * sizeof needed or its members accessed. */ // instead, parent should be set correctly assert(sym.parent); if (sym.type.ty == Terror) return Type.terror; if (sc) this.cppmangle = sc.cppmangle; return merge(); } override Dsymbol toDsymbol(Scope* sc) { return sym; } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { Dsymbol s; static if (LOGDOTEXP) { printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } 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 = e.semantic(sc); // do this before turning on noaccesscheck sym.size(e.loc); // do semantic of type Expression e0; Expression ev = e.op == TOKtype ? null : e; if (ev) ev = extractSideEffect(sc, "__tup", e0, ev); auto exps = new Expressions(); exps.reserve(sym.fields.dim); for (size_t i = 0; i < sym.fields.dim; i++) { VarDeclaration v = sym.fields[i]; Expression ex; if (ev) ex = new DotVarExp(e.loc, ev, v); else { ex = new VarExp(e.loc, v); ex.type = ex.type.addMod(e.type.mod); } exps.push(ex); } e = new TupleExp(e.loc, e0, exps); Scope* sc2 = sc.push(); sc2.flags = sc.flags | SCOPEnoaccesscheck; e = e.semantic(sc2); sc2.pop(); return e; } Dsymbol searchSym() { int flags = sc.flags & SCOPEignoresymbolvisibility ? IgnoreSymbolVisibility : 0; Dsymbol sold = void; if (global.params.bug10378 || global.params.check10378) { sold = sym.search(e.loc, ident, flags); if (!global.params.check10378) return sold; } auto s = sym.search(e.loc, ident, flags | SearchLocalsOnly); if (global.params.check10378) { alias snew = s; if (sold !is snew) Scope.deprecation10378(e.loc, sold, snew); if (global.params.bug10378) s = sold; } return s; } s = searchSym(); L1: if (!s) { return noMember(sc, e, ident, flag); } if (!(sc.flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, s)) { .deprecation(e.loc, "%s is not visible from module %s", s.toPrettyChars(), sc._module.toPrettyChars()); // return noMember(sc, e, ident, flag); } if (!s.isFuncDeclaration()) // because of overloading s.checkDeprecated(e.loc, sc); s = s.toAlias(); if (auto em = s.isEnumMember()) { return em.getVarExp(e.loc, sc); } if (auto 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 = ve.semantic(sc); return ve; } } if (auto t = s.getType()) { return (new TypeExp(e.loc, t)).semantic(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 = e.semantic(sc); return e; } TemplateInstance ti = s.isTemplateInstance(); if (ti) { if (!ti.semanticRun) { ti.semantic(sc); if (!ti.inst || ti.errors) // if template failed to expand return new ErrorExp(); } s = ti.inst.toAlias(); if (!s.isTemplateInstance()) goto L1; if (e.op == TOKtype) e = new ScopeExp(e.loc, ti); else e = new DotExp(e.loc, e, new ScopeExp(e.loc, ti)); return e.semantic(sc); } if (s.isImport() || s.isModule() || s.isPackage()) { e = DsymbolExp.resolve(e.loc, sc, s, false); return e; } OverloadSet o = s.isOverloadSet(); if (o) { auto 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 = e.semantic(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 = e.semantic(sc); return e; } } if (d.semanticRun == PASSinit && d._scope) d.semantic(d._scope); checkAccess(e.loc, sc, e, d); auto 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 && (cast(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 = e.semantic(sc); return e; } e = new DotVarExp(e.loc, e, d); e = e.semantic(sc); return e; } override structalign_t alignment() { if (sym.alignment == 0) sym.size(sym.loc); return sym.alignment; } override Expression defaultInit(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypeStruct::defaultInit() '%s'\n", toChars()); } 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. */ override Expression defaultInitLiteral(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars()); } sym.size(loc); if (sym.sizeok != SIZEOKdone) return new ErrorExp(); auto structelems = new Expressions(); structelems.setDim(sym.fields.dim - sym.isNested()); uint offset = 0; for (size_t j = 0; j < structelems.dim; j++) { VarDeclaration vd = sym.fields[j]; Expression e; if (vd.inuse) { error(loc, "circular reference to '%s'", vd.toPrettyChars()); return new ErrorExp(); } if (vd.offset < offset || vd.type.size() == 0) e = null; else if (vd._init) { if (vd._init.isVoidInitializer()) e = null; else e = vd.getConstInitializer(false); } else e = vd.type.defaultInitLiteral(loc); if (e && e.op == TOKerror) return e; if (e) offset = vd.offset + cast(uint)vd.type.size(); (*structelems)[j] = e; } auto structinit = new StructLiteralExp(loc, sym, structelems); /* Copy from the initializer symbol for larger symbols, * otherwise the literals expressed as code get excessively large. */ if (size(loc) > Target.ptrsize * 4 && !needsNested()) structinit.useStaticInit = true; structinit.type = this; return structinit; } override bool isZeroInit(Loc loc) const { return sym.zeroInit != 0; } override bool isAssignable() { bool assignable = true; uint offset = ~0; // dead-store initialize to prevent spurious warning /* If any of the fields are const or immutable, * then one cannot assign this struct. */ for (size_t i = 0; i < sym.fields.dim; i++) { VarDeclaration v = sym.fields[i]; //printf("%s [%d] v = (%s) %s, v.offset = %d, v.parent = %s", sym.toChars(), i, v.kind(), v.toChars(), v.offset, v.parent.kind()); if (i == 0) { } else if (v.offset == offset) { /* If any fields of anonymous union are assignable, * then regard union as assignable. * This is to support unsafe things like Rebindable templates. */ if (assignable) continue; } else { if (!assignable) return false; } assignable = v.type.isMutable() && v.type.isAssignable(); offset = v.offset; //printf(" -> assignable = %d\n", assignable); } return assignable; } override bool isBoolean() const { return false; } override bool needsDestruction() const { return sym.dtor !is null; } override bool needsNested() { if (sym.isNested()) return true; for (size_t i = 0; i < sym.fields.dim; i++) { VarDeclaration v = sym.fields[i]; if (!v.isDataseg() && v.type.needsNested()) return true; } return false; } override bool hasPointers() { // Probably should cache this information in sym rather than recompute StructDeclaration s = sym; sym.size(Loc()); // give error for forward references foreach (VarDeclaration v; s.fields) { if (v.storage_class & STCref || v.hasPointers()) return true; } return false; } override bool hasVoidInitPointers() { // Probably should cache this information in sym rather than recompute StructDeclaration s = sym; sym.size(Loc()); // give error for forward references foreach (VarDeclaration v; s.fields) { if (v._init && v._init.isVoidInitializer() && v.type.hasPointers()) return true; if (!v._init && v.type.hasVoidInitPointers()) return true; } return false; } override MATCH implicitConvTo(Type to) { MATCH m; //printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to.toChars()); if (ty == to.ty && sym == (cast(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. */ uint offset = ~0; // dead-store to prevent spurious warning for (size_t i = 0; i < sym.fields.dim; i++) { VarDeclaration v = sym.fields[i]; if (i == 0) { } else if (v.offset == offset) { if (m > MATCHnomatch) continue; } else { if (m <= MATCHnomatch) return m; } // 'from' type Type tvf = v.type.addMod(mod); // 'to' type Type tv = v.type.addMod(to.mod); // field match MATCH mf = tvf.implicitConvTo(tv); //printf("\t%s => %s, match = %d\n", v.type.toChars(), tv.toChars(), mf); if (mf <= MATCHnomatch) return mf; if (mf < m) // if field match is worse m = mf; offset = v.offset; } } } } else if (sym.aliasthis && !(att & RECtracing)) { att = cast(AliasThisRec)(att | RECtracing); m = aliasthisOf().implicitConvTo(to); att = cast(AliasThisRec)(att & ~RECtracing); } else m = MATCHnomatch; // no match return m; } override MATCH constConv(Type to) { if (equals(to)) return MATCHexact; if (ty == to.ty && sym == (cast(TypeStruct)to).sym && MODimplicitConv(mod, to.mod)) return MATCHconst; return MATCHnomatch; } override ubyte deduceWild(Type t, bool isRef) { if (ty == t.ty && sym == (cast(TypeStruct)t).sym) return Type.deduceWild(t, isRef); ubyte wm = 0; if (t.hasWild() && sym.aliasthis && !(att & RECtracing)) { att = cast(AliasThisRec)(att | RECtracing); wm = aliasthisOf().deduceWild(t, isRef); att = cast(AliasThisRec)(att & ~RECtracing); } return wm; } override Type toHeadMutable() { return this; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeEnum : Type { EnumDeclaration sym; extern (D) this(EnumDeclaration sym) { super(Tenum); this.sym = sym; } override const(char)* kind() const { return "enum"; } override Type syntaxCopy() { return this; } override d_uns64 size(Loc loc) { return sym.getMemtype(loc).size(loc); } override uint alignsize() { Type t = sym.getMemtype(Loc()); if (t.ty == Terror) return 4; return t.alignsize(); } override Type semantic(Loc loc, Scope* sc) { //printf("TypeEnum::semantic() %s\n", toChars()); if (deco) return this; return merge(); } override Dsymbol toDsymbol(Scope* sc) { return sym; } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { static if (LOGDOTEXP) { printf("TypeEnum::dotExp(e = '%s', ident = '%s') '%s'\n", e.toChars(), ident.toChars(), toChars()); } // Bugzilla 14010 if (ident == Id._mangleof) return getProperty(e.loc, ident, flag & 1); if (sym._scope) sym.semantic(sym._scope); if (!sym.members) { if (!(flag & 1)) { sym.error("is forward referenced when looking for '%s'", ident.toChars()); e = new ErrorExp(); } else e = null; return e; } Dsymbol s = sym.search(e.loc, ident); if (!s) { if (ident == Id.max || ident == Id.min || ident == Id._init) { return getProperty(e.loc, ident, flag & 1); } Expression res = sym.getMemtype(Loc()).dotExp(sc, e, ident, 1); if (!(flag & 1) && !res) { if (auto 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); } override Expression 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 s = toChars(); e = new StringExp(loc, cast(char*)s); Scope sc; e = e.semantic(&sc); } else if (ident == Id._mangleof) { e = Type.getProperty(loc, ident, flag); } else { e = toBasetype().getProperty(loc, ident, flag); } return e; } override bool isintegral() { return sym.getMemtype(Loc()).isintegral(); } override bool isfloating() { return sym.getMemtype(Loc()).isfloating(); } override bool isreal() { return sym.getMemtype(Loc()).isreal(); } override bool isimaginary() { return sym.getMemtype(Loc()).isimaginary(); } override bool iscomplex() { return sym.getMemtype(Loc()).iscomplex(); } override bool isscalar() { return sym.getMemtype(Loc()).isscalar(); } override bool isunsigned() { return sym.getMemtype(Loc()).isunsigned(); } override bool isBoolean() { return sym.getMemtype(Loc()).isBoolean(); } override bool isString() { return sym.getMemtype(Loc()).isString(); } override bool isAssignable() { return sym.getMemtype(Loc()).isAssignable(); } override bool needsDestruction() { return sym.getMemtype(Loc()).needsDestruction(); } override bool needsNested() { return sym.getMemtype(Loc()).needsNested(); } override MATCH implicitConvTo(Type to) { MATCH m; //printf("TypeEnum::implicitConvTo()\n"); if (ty == to.ty && sym == (cast(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; } override MATCH constConv(Type to) { if (equals(to)) return MATCHexact; if (ty == to.ty && sym == (cast(TypeEnum)to).sym && MODimplicitConv(mod, to.mod)) return MATCHconst; return MATCHnomatch; } override Type toBasetype() { if (!sym.members && !sym.memtype) return this; auto tb = sym.getMemtype(Loc()).toBasetype(); return tb.castMod(mod); // retain modifier bits from 'this' } override Expression defaultInit(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypeEnum::defaultInit() '%s'\n", toChars()); } // 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; } override bool isZeroInit(Loc loc) { return sym.getDefaultValue(loc).isBool(false); } override bool hasPointers() { return sym.getMemtype(Loc()).hasPointers(); } override bool hasVoidInitPointers() { return sym.getMemtype(Loc()).hasVoidInitPointers(); } override Type nextOf() { return sym.getMemtype(Loc()).nextOf(); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeClass : Type { ClassDeclaration sym; AliasThisRec att = RECfwdref; CPPMANGLE cppmangle = CPPMANGLE.def; extern (D) this(ClassDeclaration sym) { super(Tclass); this.sym = sym; } override const(char)* kind() const { return "class"; } override d_uns64 size(Loc loc) const { return Target.ptrsize; } override Type syntaxCopy() { return this; } override Type semantic(Loc loc, Scope* sc) { //printf("TypeClass::semantic(%s)\n", sym.toChars()); if (deco) { if (sc && sc.cppmangle != CPPMANGLE.def) { if (this.cppmangle == CPPMANGLE.def) this.cppmangle = sc.cppmangle; else assert(this.cppmangle == sc.cppmangle); } return this; } /* Don't semantic for sym because it should be deferred until * sizeof needed or its members accessed. */ // instead, parent should be set correctly assert(sym.parent); if (sym.type.ty == Terror) return Type.terror; if (sc) this.cppmangle = sc.cppmangle; return merge(); } override Dsymbol toDsymbol(Scope* sc) { return sym; } override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag) { Dsymbol s; static if (LOGDOTEXP) { printf("TypeClass::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); } 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 = e.semantic(sc); // do this before turning on noaccesscheck sym.size(e.loc); // do semantic of type Expression e0; Expression ev = e.op == TOKtype ? null : e; if (ev) ev = extractSideEffect(sc, "__tup", e0, ev); auto exps = new Expressions(); exps.reserve(sym.fields.dim); for (size_t i = 0; i < sym.fields.dim; i++) { VarDeclaration v = sym.fields[i]; // Don't include hidden 'this' pointer if (v.isThisDeclaration()) continue; Expression ex; if (ev) ex = new DotVarExp(e.loc, ev, v); else { ex = new VarExp(e.loc, v); ex.type = ex.type.addMod(e.type.mod); } exps.push(ex); } e = new TupleExp(e.loc, e0, exps); Scope* sc2 = sc.push(); sc2.flags = sc.flags | SCOPEnoaccesscheck; e = e.semantic(sc2); sc2.pop(); return e; } Dsymbol searchSym() { int flags = sc.flags & SCOPEignoresymbolvisibility ? IgnoreSymbolVisibility : 0; Dsymbol sold = void; if (global.params.bug10378 || global.params.check10378) { sold = sym.search(e.loc, ident, flags | IgnoreSymbolVisibility); if (!global.params.check10378) return sold; } auto s = sym.search(e.loc, ident, flags | SearchLocalsOnly); if (!s && !(flags & IgnoreSymbolVisibility)) { s = sym.search(e.loc, ident, flags | SearchLocalsOnly | IgnoreSymbolVisibility); if (s && !(flags & IgnoreErrors)) .deprecation(e.loc, "%s is not visible from class %s", s.toPrettyChars(), sym.toChars()); } if (global.params.check10378) { alias snew = s; if (sold !is snew) Scope.deprecation10378(e.loc, sold, snew); if (global.params.bug10378) s = sold; } return s; } s = searchSym(); 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 = e.semantic(sc); return e; } if (auto cbase = sym.searchBase(ident)) { if (e.op == TOKtype) return Type.getProperty(e.loc, ident, 0); if (auto ifbase = cbase.isInterfaceDeclaration()) e = new CastExp(e.loc, e, ifbase.type); else e = new DotTypeExp(e.loc, e, cbase); e = e.semantic(sc); return e; } if (ident == Id.classinfo) { assert(Type.typeinfoclass); 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 = e.semantic(sc); return e; } if (ident == Id.__monitor) { /* The handle to the monitor (call it a void*) * *(cast(void**)e + 1) */ e = e.castTo(sc, tvoidptr.pointerTo()); e = new AddExp(e.loc, e, new IntegerExp(1)); e = new PtrExp(e.loc, e); e = e.semantic(sc); return e; } if (ident == Id.outer && sym.vthis) { if (sym.vthis._scope) sym.vthis.semantic(null); if (auto cdp = sym.toParent2().isClassDeclaration()) { auto 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 (auto p = sym.toParent2(); p; p = p.toParent2()) { auto fd = p.isFuncDeclaration(); if (!fd) break; if (fd.isNested()) continue; auto ad = fd.isThis(); if (!ad) break; if (auto cdp = ad.isClassDeclaration()) { auto ve = new ThisExp(e.loc); ve.var = fd.vthis; const 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). auto dve = new DotVarExp(e.loc, e, sym.vthis); dve.type = sym.vthis.type.addMod(e.type.mod); return dve; } return noMember(sc, e, ident, flag & 1); } if (!(sc.flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, s)) { .deprecation(e.loc, "%s is not visible from module %s", s.toPrettyChars(), sc._module.toPrettyChars()); // return noMember(sc, e, ident, flag); } if (!s.isFuncDeclaration()) // because of overloading s.checkDeprecated(e.loc, sc); s = s.toAlias(); if (auto em = s.isEnumMember()) { return em.getVarExp(e.loc, sc); } if (auto 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 = ve.semantic(sc); return ve; } } if (auto t = s.getType()) { return (new TypeExp(e.loc, t)).semantic(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 = e.semantic(sc); return e; } TemplateInstance ti = s.isTemplateInstance(); if (ti) { if (!ti.semanticRun) { ti.semantic(sc); if (!ti.inst || ti.errors) // if template failed to expand return new ErrorExp(); } s = ti.inst.toAlias(); if (!s.isTemplateInstance()) goto L1; if (e.op == TOKtype) e = new ScopeExp(e.loc, ti); else e = new DotExp(e.loc, e, new ScopeExp(e.loc, ti)); return e.semantic(sc); } if (s.isImport() || s.isModule() || s.isPackage()) { e = DsymbolExp.resolve(e.loc, sc, s, false); return e; } OverloadSet o = s.isOverloadSet(); if (o) { auto 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 = e.semantic(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 = e1.semantic(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 = e.semantic(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 = e1.semantic(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 = e1.semantic(sc); } else e1 = e1.semantic(sc); goto L2; } } } //printf("e = %s, d = %s\n", e.toChars(), d.toChars()); if (d.semanticRun == PASSinit && d._scope) d.semantic(d._scope); checkAccess(e.loc, sc, e, d); auto 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 && (cast(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 = e.semantic(sc); return e; } e = new DotVarExp(e.loc, e, d); e = e.semantic(sc); return e; } override ClassDeclaration isClassHandle() { return sym; } override bool isBaseOf(Type t, int* poffset) { if (t && t.ty == Tclass) { ClassDeclaration cd = (cast(TypeClass)t).sym; if (sym.isBaseOf(cd, poffset)) return true; } return false; } override MATCH 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._scope && !cdto.isBaseInfoComplete()) cdto.semantic(null); if (sym._scope && !sym.isBaseInfoComplete()) sym.semantic(null); if (cdto.isBaseOf(sym, null) && MODimplicitConv(mod, to.mod)) { //printf("'to' is base\n"); return MATCHconvert; } } m = MATCHnomatch; if (sym.aliasthis && !(att & RECtracing)) { att = cast(AliasThisRec)(att | RECtracing); m = aliasthisOf().implicitConvTo(to); att = cast(AliasThisRec)(att & ~RECtracing); } return m; } override MATCH constConv(Type to) { if (equals(to)) return MATCHexact; if (ty == to.ty && sym == (cast(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; } override ubyte deduceWild(Type t, bool isRef) { ClassDeclaration cd = t.isClassHandle(); if (cd && (sym == cd || cd.isBaseOf(sym, null))) return Type.deduceWild(t, isRef); ubyte wm = 0; if (t.hasWild() && sym.aliasthis && !(att & RECtracing)) { att = cast(AliasThisRec)(att | RECtracing); wm = aliasthisOf().deduceWild(t, isRef); att = cast(AliasThisRec)(att & ~RECtracing); } return wm; } override Type toHeadMutable() { return this; } override Expression defaultInit(Loc loc) { static if (LOGDEFAULTINIT) { printf("TypeClass::defaultInit() '%s'\n", toChars()); } return new NullExp(loc, this); } override bool isZeroInit(Loc loc) const { return true; } override bool isscope() const { return sym.isscope; } override bool isBoolean() const { return true; } override bool hasPointers() const { return true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeTuple : Type { Parameters* arguments; // types making up the tuple extern (D) this(Parameters* arguments) { super(Ttuple); //printf("TypeTuple(this = %p)\n", this); this.arguments = arguments; //printf("TypeTuple() %p, %s\n", this, toChars()); debug { if (arguments) { for (size_t i = 0; i < arguments.dim; i++) { Parameter arg = (*arguments)[i]; assert(arg && arg.type); } } } } /**************** * Form TypeTuple from the types of the expressions. * Assume exps[] is already tuple expanded. */ extern (D) this(Expressions* exps) { super(Ttuple); auto arguments = new Parameters(); if (exps) { arguments.setDim(exps.dim); for (size_t i = 0; i < exps.dim; i++) { Expression e = (*exps)[i]; if (e.type.ty == Ttuple) e.error("cannot form tuple of tuples"); auto arg = new Parameter(STCundefined, e.type, null, null); (*arguments)[i] = arg; } } this.arguments = arguments; //printf("TypeTuple() %p, %s\n", this, toChars()); } static TypeTuple create(Parameters* arguments) { return new TypeTuple(arguments); } /******************************************* * Type tuple with 0, 1 or 2 types in it. */ extern (D) this() { super(Ttuple); arguments = new Parameters(); } extern (D) this(Type t1) { super(Ttuple); arguments = new Parameters(); arguments.push(new Parameter(0, t1, null, null)); } extern (D) this(Type t1, Type t2) { super(Ttuple); arguments = new Parameters(); arguments.push(new Parameter(0, t1, null, null)); arguments.push(new Parameter(0, t2, null, null)); } override const(char)* kind() const { return "tuple"; } override Type syntaxCopy() { Parameters* args = Parameter.arraySyntaxCopy(arguments); Type t = new TypeTuple(args); t.mod = mod; return t; } override Type semantic(Loc loc, Scope* sc) { //printf("TypeTuple::semantic(this = %p)\n", this); //printf("TypeTuple::semantic() %p, %s\n", this, toChars()); if (!deco) deco = merge().deco; /* Don't return merge(), because a tuple with one type has the * same deco as that type. */ return this; } override bool equals(RootObject o) { Type t = cast(Type)o; //printf("TypeTuple::equals(%s, %s)\n", toChars(), t.toChars()); if (this == t) return true; if (t.ty == Ttuple) { TypeTuple tt = cast(TypeTuple)t; if (arguments.dim == tt.arguments.dim) { for (size_t i = 0; i < tt.arguments.dim; i++) { Parameter arg1 = (*arguments)[i]; Parameter arg2 = (*tt.arguments)[i]; if (!arg1.type.equals(arg2.type)) return false; } return true; } } return false; } override Expression getProperty(Loc loc, Identifier ident, int flag) { Expression e; static if (LOGDOTEXP) { printf("TypeTuple::getProperty(type = '%s', ident = '%s')\n", toChars(), ident.toChars()); } if (ident == Id.length) { e = new IntegerExp(loc, arguments.dim, Type.tsize_t); } else if (ident == Id._init) { e = defaultInitLiteral(loc); } else if (flag) { e = null; } else { error(loc, "no property '%s' for tuple '%s'", ident.toChars(), toChars()); e = new ErrorExp(); } return e; } override Expression defaultInit(Loc loc) { auto exps = new Expressions(); exps.setDim(arguments.dim); for (size_t i = 0; i < arguments.dim; i++) { Parameter p = (*arguments)[i]; assert(p.type); Expression e = p.type.defaultInitLiteral(loc); if (e.op == TOKerror) return e; (*exps)[i] = e; } return new TupleExp(loc, exps); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * This is so we can slice a TypeTuple */ extern (C++) final class TypeSlice : TypeNext { Expression lwr; Expression upr; extern (D) this(Type next, Expression lwr, Expression upr) { super(Tslice, next); //printf("TypeSlice[%s .. %s]\n", lwr.toChars(), upr.toChars()); this.lwr = lwr; this.upr = upr; } override const(char)* kind() const { return "slice"; } override Type syntaxCopy() { Type t = new TypeSlice(next.syntaxCopy(), lwr.syntaxCopy(), upr.syntaxCopy()); t.mod = mod; return t; } override Type semantic(Loc loc, Scope* sc) { //printf("TypeSlice::semantic() %s\n", toChars()); Type tn = next.semantic(loc, sc); //printf("next: %s\n", tn.toChars()); Type tbn = tn.toBasetype(); if (tbn.ty != Ttuple) { error(loc, "can only slice tuple types, not %s", tbn.toChars()); return Type.terror; } TypeTuple tt = cast(TypeTuple)tbn; lwr = semanticLength(sc, tbn, lwr); upr = semanticLength(sc, tbn, upr); lwr = lwr.ctfeInterpret(); upr = upr.ctfeInterpret(); if (lwr.op == TOKerror || upr.op == TOKerror) return Type.terror; uinteger_t i1 = lwr.toUInteger(); uinteger_t i2 = upr.toUInteger(); if (!(i1 <= i2 && i2 <= tt.arguments.dim)) { error(loc, "slice [%llu..%llu] is out of range of [0..%u]", i1, i2, tt.arguments.dim); return Type.terror; } next = tn; transitive(); auto args = new Parameters(); args.reserve(cast(size_t)(i2 - i1)); for (size_t i = cast(size_t)i1; i < cast(size_t)i2; i++) { Parameter arg = (*tt.arguments)[i]; args.push(arg); } Type t = new TypeTuple(args); return t.semantic(loc, sc); } override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false) { 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 = lwr.semantic(sc); upr = upr.semantic(sc); sc = sc.endCTFE(); sc = sc.pop(); lwr = lwr.ctfeInterpret(); upr = upr.ctfeInterpret(); uinteger_t i1 = lwr.toUInteger(); uinteger_t i2 = upr.toUInteger(); if (!(i1 <= i2 && i2 <= td.objects.dim)) { error(loc, "slice [%llu..%llu] is out of range of [0..%u]", i1, i2, td.objects.dim); *ps = null; *pt = Type.terror; return; } if (i1 == 0 && i2 == td.objects.dim) { *ps = td; return; } /* Create a new TupleDeclaration which * is a slice [i1..i2] out of the old one. */ auto objects = new Objects(); objects.setDim(cast(size_t)(i2 - i1)); for (size_t i = 0; i < objects.dim; i++) { (*objects)[i] = (*td.objects)[cast(size_t)i1 + i]; } auto 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); } } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class TypeNull : Type { extern (D) this() { super(Tnull); } override const(char)* kind() const { return "null"; } override Type syntaxCopy() { // No semantic analysis done, no need to copy return this; } override MATCH 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; } override bool isBoolean() const { return true; } override d_uns64 size(Loc loc) const { return tvoidptr.size(loc); } override Expression defaultInit(Loc loc) const { return new NullExp(Loc(), Type.tnull); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class Parameter : RootObject { StorageClass storageClass; Type type; Identifier ident; Expression defaultArg; extern (D) this(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg) { this.type = type; this.ident = ident; this.storageClass = storageClass; this.defaultArg = defaultArg; } static Parameter create(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg) { return new Parameter(storageClass, type, ident, defaultArg); } Parameter syntaxCopy() { return new Parameter(storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null); } /**************************************************** * 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 isLazyArray() { Type tb = type.toBasetype(); if (tb.ty == Tsarray || tb.ty == Tarray) { Type tel = (cast(TypeArray)tb).next.toBasetype(); if (tel.ty == Tdelegate) { TypeDelegate td = cast(TypeDelegate)tel; TypeFunction tf = cast(TypeFunction)td.next; if (!tf.varargs && Parameter.dim(tf.parameters) == 0) { return tf.next; // return type of delegate } } } return null; } // kludge for template.isType() override int dyncast() { return DYNCAST_PARAMETER; } void accept(Visitor v) { v.visit(this); } static Parameters* arraySyntaxCopy(Parameters* parameters) { Parameters* params = null; if (parameters) { params = new Parameters(); params.setDim(parameters.dim); for (size_t i = 0; i < params.dim; i++) (*params)[i] = (*parameters)[i].syntaxCopy(); } return params; } /**************************************** * Determine if parameter list is really a template parameter list * (i.e. it has auto or alias parameters) */ extern (D) static int isTPL(Parameters* parameters) { //printf("Parameter::isTPL()\n"); int isTPLDg(size_t n, Parameter p) { if (p.storageClass & (STCalias | STCauto | STCstatic)) return 1; return 0; } return _foreach(parameters, &isTPLDg); } /*************************************** * Determine number of arguments, folding in tuples. */ static size_t dim(Parameters* parameters) { size_t nargs = 0; int dimDg(size_t n, Parameter p) { ++nargs; return 0; } _foreach(parameters, &dimDg); return nargs; } /*************************************** * Get nth Parameter, folding in tuples. * Returns: * Parameter* nth Parameter * NULL not found, *pn gets incremented by the number * of Parameters */ static Parameter getNth(Parameters* parameters, size_t nth, size_t* pn = null) { Parameter param; int getNthParamDg(size_t n, Parameter p) { if (n == nth) { param = p; return 1; } return 0; } int res = _foreach(parameters, &getNthParamDg); return res ? param : null; } alias ForeachDg = extern (D) int delegate(size_t paramidx, Parameter param); /*************************************** * 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. */ extern (D) static int _foreach(Parameters* parameters, scope ForeachDg dg, size_t* pn = null) { assert(dg); if (!parameters) return 0; size_t n = pn ? *pn : 0; // take over index int result = 0; foreach (i; 0 .. parameters.dim) { Parameter p = (*parameters)[i]; Type t = p.type.toBasetype(); if (t.ty == Ttuple) { TypeTuple tu = cast(TypeTuple)t; result = _foreach(tu.arguments, dg, &n); } else result = dg(n++, p); if (result) break; } if (pn) *pn = n; // update index return result; } override const(char)* toChars() const { 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 */ final bool isCovariant(const Parameter p) const pure nothrow @nogc @safe { enum stc = STCref | STCin | STCout | STClazy; return !((this.storageClass & stc) != (p.storageClass & stc) || // We can add scope, but not subtract it (!(this.storageClass & STCscope) && (p.storageClass & STCscope)) || // We can subtract return, but not add it ((this.storageClass & STCreturn) && !(p.storageClass & STCreturn))); } }