| /** |
| * Defines a D type. |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/mtype.d, _mtype.d) |
| * Documentation: https://dlang.org/phobos/dmd_mtype.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mtype.d |
| */ |
| |
| module dmd.mtype; |
| |
| import core.checkedint; |
| import core.stdc.stdarg; |
| import core.stdc.stdio; |
| import core.stdc.stdlib; |
| import core.stdc.string; |
| |
| import dmd.aggregate; |
| import dmd.arraytypes; |
| import dmd.attrib; |
| import dmd.astenums; |
| import dmd.ast_node; |
| import dmd.gluelayer; |
| import dmd.dclass; |
| import dmd.declaration; |
| import dmd.denum; |
| import dmd.dmangle; |
| import dmd.dscope; |
| import dmd.dstruct; |
| import dmd.dsymbol; |
| import dmd.dsymbolsem; |
| import dmd.dtemplate; |
| import dmd.errors; |
| import dmd.expression; |
| import dmd.expressionsem; |
| import dmd.func; |
| import dmd.globals; |
| import dmd.hdrgen; |
| import dmd.id; |
| import dmd.identifier; |
| import dmd.init; |
| import dmd.opover; |
| import dmd.root.ctfloat; |
| import dmd.common.outbuffer; |
| import dmd.root.rmem; |
| import dmd.root.rootobject; |
| import dmd.root.stringtable; |
| import dmd.target; |
| import dmd.tokens; |
| import dmd.typesem; |
| import dmd.visitor; |
| |
| enum LOGDOTEXP = 0; // log ::dotExp() |
| enum LOGDEFAULTINIT = 0; // log ::defaultInit() |
| |
| enum SIZE_INVALID = (~cast(uinteger_t)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 & ~MODFlags.shared_, modto & ~MODFlags.shared_)) |
| { |
| case X(0, MODFlags.const_): |
| case X(MODFlags.wild, MODFlags.const_): |
| case X(MODFlags.wild, MODFlags.wildconst): |
| case X(MODFlags.wildconst, MODFlags.const_): |
| return (modfrom & MODFlags.shared_) == (modto & MODFlags.shared_); |
| |
| case X(MODFlags.immutable_, MODFlags.const_): |
| case X(MODFlags.immutable_, MODFlags.wildconst): |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| /*************************** |
| * Return MATCH.exact or MATCH.constant 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 MATCH.exact; |
| if (MODimplicitConv(modfrom, modto)) |
| return MATCH.constant; |
| |
| auto X(T, U)(T m, U n) |
| { |
| return ((m << 4) | n); |
| } |
| |
| switch (X(modfrom, modto)) |
| { |
| case X(0, MODFlags.wild): |
| case X(MODFlags.immutable_, MODFlags.wild): |
| case X(MODFlags.const_, MODFlags.wild): |
| case X(MODFlags.wildconst, MODFlags.wild): |
| case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wild): |
| case X(MODFlags.shared_ | MODFlags.immutable_, MODFlags.shared_ | MODFlags.wild): |
| case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wild): |
| case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild): |
| return MATCH.constant; |
| |
| default: |
| return MATCH.nomatch; |
| } |
| } |
| |
| /*************************** |
| * 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) & MODFlags.shared_) |
| { |
| // If either type is shared, the result will be shared |
| result |= MODFlags.shared_; |
| mod1 &= ~MODFlags.shared_; |
| mod2 &= ~MODFlags.shared_; |
| } |
| if (mod1 == 0 || mod1 == MODFlags.mutable || mod1 == MODFlags.const_ || mod2 == 0 || mod2 == MODFlags.mutable || mod2 == MODFlags.const_) |
| { |
| // If either type is mutable or const, the result will be const. |
| result |= MODFlags.const_; |
| } |
| else |
| { |
| // MODFlags.immutable_ vs MODFlags.wild |
| // MODFlags.immutable_ vs MODFlags.wildconst |
| // MODFlags.wild vs MODFlags.wildconst |
| assert(mod1 & MODFlags.wild || mod2 & MODFlags.wild); |
| result |= MODFlags.wildconst; |
| } |
| return result; |
| } |
| |
| /********************************* |
| * Store modifier name into buf. |
| */ |
| void MODtoBuffer(OutBuffer* buf, MOD mod) nothrow |
| { |
| buf.writestring(MODtoString(mod)); |
| } |
| |
| /********************************* |
| * Returns: |
| * a human readable representation of `mod`, |
| * which is the token `mod` corresponds to |
| */ |
| const(char)* MODtoChars(MOD mod) nothrow pure |
| { |
| /// Works because we return a literal |
| return MODtoString(mod).ptr; |
| } |
| |
| /// Ditto |
| string MODtoString(MOD mod) nothrow pure |
| { |
| final switch (mod) |
| { |
| case 0: |
| return ""; |
| |
| case MODFlags.immutable_: |
| return "immutable"; |
| |
| case MODFlags.shared_: |
| return "shared"; |
| |
| case MODFlags.shared_ | MODFlags.const_: |
| return "shared const"; |
| |
| case MODFlags.const_: |
| return "const"; |
| |
| case MODFlags.shared_ | MODFlags.wild: |
| return "shared inout"; |
| |
| case MODFlags.wild: |
| return "inout"; |
| |
| case MODFlags.shared_ | MODFlags.wildconst: |
| return "shared inout const"; |
| |
| case MODFlags.wildconst: |
| return "inout const"; |
| } |
| } |
| |
| /************************************************* |
| * Pick off one of the trust flags from trust, |
| * and return a string representation of it. |
| */ |
| string trustToString(TRUST trust) pure nothrow @nogc @safe |
| { |
| final switch (trust) |
| { |
| case TRUST.default_: |
| return null; |
| case TRUST.system: |
| return "@system"; |
| case TRUST.trusted: |
| return "@trusted"; |
| case TRUST.safe: |
| return "@safe"; |
| } |
| } |
| |
| unittest |
| { |
| assert(trustToString(TRUST.default_) == ""); |
| assert(trustToString(TRUST.system) == "@system"); |
| assert(trustToString(TRUST.trusted) == "@trusted"); |
| assert(trustToString(TRUST.safe) == "@safe"); |
| } |
| |
| /************************************ |
| * Convert MODxxxx to STCxxx |
| */ |
| StorageClass ModToStc(uint mod) pure nothrow @nogc @safe |
| { |
| StorageClass stc = 0; |
| if (mod & MODFlags.immutable_) |
| stc |= STC.immutable_; |
| if (mod & MODFlags.const_) |
| stc |= STC.const_; |
| if (mod & MODFlags.wild) |
| stc |= STC.wild; |
| if (mod & MODFlags.shared_) |
| stc |= STC.shared_; |
| return stc; |
| } |
| |
| ///Returns true if ty is char, wchar, or dchar |
| bool isSomeChar(TY ty) pure nothrow @nogc @safe |
| { |
| return ty == Tchar || ty == Twchar || ty == Tdchar; |
| } |
| |
| /************************************ |
| * Determine mutability of indirections in (ref) t. |
| * |
| * Returns: When the type has any mutable indirections, returns 0. |
| * When all indirections are immutable, returns 2. |
| * Otherwise, when the type has const/inout indirections, returns 1. |
| * |
| * Params: |
| * isref = if true, check `ref t`; otherwise, check just `t` |
| * t = the type that is being checked |
| */ |
| int mutabilityOfType(bool isref, Type t) |
| { |
| if (isref) |
| { |
| if (t.mod & MODFlags.immutable_) |
| return 2; |
| if (t.mod & (MODFlags.const_ | MODFlags.wild)) |
| return 1; |
| return 0; |
| } |
| |
| t = t.baseElemOf(); |
| |
| if (!t.hasPointers() || t.mod & MODFlags.immutable_) |
| return 2; |
| |
| /* 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 & MODFlags.immutable_) |
| return 2; |
| if (tn.mod & (MODFlags.const_ | MODFlags.wild)) |
| return 1; |
| } |
| |
| /* 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 & (MODFlags.const_ | MODFlags.wild)) |
| return 1; |
| |
| /* Should catch delegates and function pointers, and fold in their purity |
| */ |
| return 0; |
| } |
| |
| /**************** |
| * 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 |
| noAliasThis = 4, // don't do 'alias this' resolution |
| } |
| |
| /// Result of a check whether two types are covariant |
| enum Covariant |
| { |
| distinct = 0, /// types are distinct |
| yes = 1, /// types are covariant |
| no = 2, /// arguments match as far as overloading goes, but types are not covariant |
| fwdref = 3, /// cannot determine covariance because of forward references |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) abstract class Type : ASTNode |
| { |
| TY ty; |
| MOD mod; // modifiers MODxxxx |
| char* deco; |
| |
| static struct Mcache |
| { |
| /* These are cached values that are lazily evaluated by constOf(), immutableOf(), etc. |
| * They should not be referenced by anybody but mtype.d. |
| * They can be null if not lazily evaluated yet. |
| * Note that there is no "shared immutable", because that is just immutable |
| * The point of this is to reduce the size of each Type instance as |
| * we bank on the idea that usually only one of variants exist. |
| * It will also speed up code because these are rarely referenced and |
| * so need not be in the cache. |
| */ |
| Type cto; // MODFlags.const_ |
| Type ito; // MODFlags.immutable_ |
| Type sto; // MODFlags.shared_ |
| Type scto; // MODFlags.shared_ | MODFlags.const_ |
| Type wto; // MODFlags.wild |
| Type wcto; // MODFlags.wildconst |
| Type swto; // MODFlags.shared_ | MODFlags.wild |
| Type swcto; // MODFlags.shared_ | MODFlags.wildconst |
| } |
| private Mcache* mcache; |
| |
| 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++) __gshared Type tvoid; |
| extern (C++) __gshared Type tint8; |
| extern (C++) __gshared Type tuns8; |
| extern (C++) __gshared Type tint16; |
| extern (C++) __gshared Type tuns16; |
| extern (C++) __gshared Type tint32; |
| extern (C++) __gshared Type tuns32; |
| extern (C++) __gshared Type tint64; |
| extern (C++) __gshared Type tuns64; |
| extern (C++) __gshared Type tint128; |
| extern (C++) __gshared Type tuns128; |
| extern (C++) __gshared Type tfloat32; |
| extern (C++) __gshared Type tfloat64; |
| extern (C++) __gshared Type tfloat80; |
| extern (C++) __gshared Type timaginary32; |
| extern (C++) __gshared Type timaginary64; |
| extern (C++) __gshared Type timaginary80; |
| extern (C++) __gshared Type tcomplex32; |
| extern (C++) __gshared Type tcomplex64; |
| extern (C++) __gshared Type tcomplex80; |
| extern (C++) __gshared Type tbool; |
| extern (C++) __gshared Type tchar; |
| extern (C++) __gshared Type twchar; |
| extern (C++) __gshared Type tdchar; |
| |
| // Some special types |
| extern (C++) __gshared Type tshiftcnt; |
| extern (C++) __gshared Type tvoidptr; // void* |
| extern (C++) __gshared Type tstring; // immutable(char)[] |
| extern (C++) __gshared Type twstring; // immutable(wchar)[] |
| extern (C++) __gshared Type tdstring; // immutable(dchar)[] |
| extern (C++) __gshared Type terror; // for error recovery |
| extern (C++) __gshared Type tnull; // for null type |
| extern (C++) __gshared Type tnoreturn; // for bottom type typeof(*null) |
| |
| extern (C++) __gshared Type tsize_t; // matches size_t alias |
| extern (C++) __gshared Type tptrdiff_t; // matches ptrdiff_t alias |
| extern (C++) __gshared Type thash_t; // matches hash_t alias |
| |
| extern (C++) __gshared ClassDeclaration dtypeinfo; |
| extern (C++) __gshared ClassDeclaration typeinfoclass; |
| extern (C++) __gshared ClassDeclaration typeinfointerface; |
| extern (C++) __gshared ClassDeclaration typeinfostruct; |
| extern (C++) __gshared ClassDeclaration typeinfopointer; |
| extern (C++) __gshared ClassDeclaration typeinfoarray; |
| extern (C++) __gshared ClassDeclaration typeinfostaticarray; |
| extern (C++) __gshared ClassDeclaration typeinfoassociativearray; |
| extern (C++) __gshared ClassDeclaration typeinfovector; |
| extern (C++) __gshared ClassDeclaration typeinfoenum; |
| extern (C++) __gshared ClassDeclaration typeinfofunction; |
| extern (C++) __gshared ClassDeclaration typeinfodelegate; |
| extern (C++) __gshared ClassDeclaration typeinfotypelist; |
| extern (C++) __gshared ClassDeclaration typeinfoconst; |
| extern (C++) __gshared ClassDeclaration typeinfoinvariant; |
| extern (C++) __gshared ClassDeclaration typeinfoshared; |
| extern (C++) __gshared ClassDeclaration typeinfowild; |
| |
| extern (C++) __gshared TemplateDeclaration rtinfo; |
| |
| extern (C++) __gshared Type[TMAX] basic; |
| |
| extern (D) __gshared StringTable!Type stringtable; |
| extern (D) private static immutable 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); |
| sizeTy[Ttraits] = __traits(classInstanceSize, TypeTraits); |
| sizeTy[Tmixin] = __traits(classInstanceSize, TypeMixin); |
| sizeTy[Tnoreturn] = __traits(classInstanceSize, TypeNoreturn); |
| sizeTy[Ttag] = __traits(classInstanceSize, TypeTag); |
| return sizeTy; |
| }(); |
| |
| final extern (D) this(TY ty) |
| { |
| this.ty = ty; |
| } |
| |
| const(char)* kind() const nothrow pure @nogc @safe |
| { |
| assert(false); // should be overridden |
| } |
| |
| final Type copy() nothrow const |
| { |
| Type t = cast(Type)mem.xmalloc(sizeTy[ty]); |
| memcpy(cast(void*)t, cast(void*)this, sizeTy[ty]); |
| return t; |
| } |
| |
| Type syntaxCopy() |
| { |
| fprintf(stderr, "this = %s, ty = %d\n", toChars(), ty); |
| assert(0); |
| } |
| |
| override bool equals(const RootObject o) const |
| { |
| 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 DYNCAST dyncast() const |
| { |
| return DYNCAST.type; |
| } |
| |
| /// Returns a non-zero unique ID for this Type, or returns 0 if the Type does not (yet) have a unique ID. |
| /// If `semantic()` has not been run, 0 is returned. |
| final size_t getUniqueID() const |
| { |
| return cast(size_t) deco; |
| } |
| |
| extern (D) |
| final Mcache* getMcache() |
| { |
| if (!mcache) |
| mcache = cast(Mcache*) mem.xcalloc(Mcache.sizeof, 1); |
| return mcache; |
| } |
| |
| /******************************* |
| * Covariant means that 'this' can substitute for 't', |
| * i.e. a pure function is a match for an impure type. |
| * Params: |
| * t = type 'this' is covariant with |
| * pstc = if not null, store STCxxxx which would make it covariant |
| * cppCovariant = true if extern(C++) function types should follow C++ covariant rules |
| * Returns: |
| * An enum value of either `Covariant.yes` or a reason it's not covariant. |
| */ |
| final Covariant covariant(Type t, StorageClass* pstc = null, bool cppCovariant = false) |
| { |
| 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; |
| |
| bool notcovariant = false; |
| |
| if (equals(t)) |
| return Covariant.yes; |
| |
| TypeFunction t1 = this.isTypeFunction(); |
| TypeFunction t2 = t.isTypeFunction(); |
| |
| if (!t1 || !t2) |
| goto Ldistinct; |
| |
| if (t1.parameterList.varargs != t2.parameterList.varargs) |
| goto Ldistinct; |
| |
| if (t1.parameterList.parameters && t2.parameterList.parameters) |
| { |
| if (t1.parameterList.length != t2.parameterList.length) |
| goto Ldistinct; |
| |
| foreach (i, fparam1; t1.parameterList) |
| { |
| Parameter fparam2 = t2.parameterList[i]; |
| Type tp1 = fparam1.type; |
| Type tp2 = fparam2.type; |
| |
| if (!tp1.equals(tp2)) |
| { |
| if (tp1.ty == tp2.ty) |
| { |
| if (auto tc1 = tp1.isTypeClass()) |
| { |
| if (tc1.sym == (cast(TypeClass)tp2).sym && MODimplicitConv(tp2.mod, tp1.mod)) |
| goto Lcov; |
| } |
| else if (auto ts1 = tp1.isTypeStruct()) |
| { |
| if (ts1.sym == (cast(TypeStruct)tp2).sym && MODimplicitConv(tp2.mod, tp1.mod)) |
| goto Lcov; |
| } |
| else if (tp1.ty == Tpointer) |
| { |
| if (tp2.implicitConvTo(tp1)) |
| goto Lcov; |
| } |
| else if (tp1.ty == Tarray) |
| { |
| if (tp2.implicitConvTo(tp1)) |
| goto Lcov; |
| } |
| else if (tp1.ty == Tdelegate) |
| { |
| if (tp2.implicitConvTo(tp1)) |
| goto Lcov; |
| } |
| } |
| goto Ldistinct; |
| } |
| Lcov: |
| notcovariant |= !fparam1.isCovariant(t1.isref, fparam2); |
| |
| /* https://issues.dlang.org/show_bug.cgi?id=23135 |
| * extern(C++) mutable parameters are not covariant with const. |
| */ |
| if (t1.linkage == LINK.cpp && cppCovariant) |
| { |
| notcovariant |= tp1.isNaked() != tp2.isNaked(); |
| if (auto tpn1 = tp1.nextOf()) |
| notcovariant |= tpn1.isNaked() != tp2.nextOf().isNaked(); |
| } |
| } |
| } |
| else if (t1.parameterList.parameters != t2.parameterList.parameters) |
| { |
| if (t1.parameterList.length || t2.parameterList.length) |
| goto Ldistinct; |
| } |
| |
| // The argument lists match |
| if (notcovariant) |
| goto Lnotcovariant; |
| if (t1.linkage != t2.linkage) |
| goto Lnotcovariant; |
| |
| { |
| // Return types |
| Type t1n = t1.next; |
| Type t2n = t2.next; |
| |
| if (!t1n || !t2n) // happens with return type inference |
| goto Lnotcovariant; |
| |
| if (t1n.equals(t2n)) |
| goto Lcovariant; |
| if (t1n.ty == Tclass && t2n.ty == Tclass) |
| { |
| /* If same class type, but t2n is const, then it's |
| * covariant. Do this test first because it can work on |
| * forward references. |
| */ |
| if ((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.semanticRun < PASS.semanticdone && !cd.isBaseInfoComplete()) |
| cd.dsymbolSemantic(null); |
| if (!cd.isBaseInfoComplete()) |
| { |
| return Covariant.fwdref; |
| } |
| } |
| 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)) |
| { |
| if (t1.isref && t2.isref) |
| { |
| // Treat like pointers to t1n and t2n |
| if (t1n.constConv(t2n) < MATCH.constant) |
| goto Lnotcovariant; |
| } |
| goto Lcovariant; |
| } |
| else if (t1n.ty == Tnull) |
| { |
| // NULL is covariant with any pointer type, but not with any |
| // dynamic arrays, associative arrays or delegates. |
| // https://issues.dlang.org/show_bug.cgi?id=8589 |
| // https://issues.dlang.org/show_bug.cgi?id=19618 |
| Type t2bn = t2n.toBasetype(); |
| if (t2bn.ty == Tnull || t2bn.ty == Tpointer || t2bn.ty == Tclass) |
| goto Lcovariant; |
| } |
| // bottom type is covariant to any type |
| else if (t1n.ty == Tnoreturn) |
| goto Lcovariant; |
| } |
| goto Lnotcovariant; |
| |
| Lcovariant: |
| if (t1.isref != t2.isref) |
| goto Lnotcovariant; |
| |
| if (!t1.isref && (t1.isScopeQual || t2.isScopeQual)) |
| { |
| StorageClass stc1 = t1.isScopeQual ? STC.scope_ : 0; |
| StorageClass stc2 = t2.isScopeQual ? STC.scope_ : 0; |
| if (t1.isreturn) |
| { |
| stc1 |= STC.return_; |
| if (!t1.isScopeQual) |
| stc1 |= STC.ref_; |
| } |
| if (t2.isreturn) |
| { |
| stc2 |= STC.return_; |
| if (!t2.isScopeQual) |
| stc2 |= STC.ref_; |
| } |
| if (!Parameter.isCovariantScope(t1.isref, stc1, stc2)) |
| goto Lnotcovariant; |
| } |
| |
| // We can subtract 'return ref' from 'this', but cannot add it |
| else if (t1.isreturn && !t2.isreturn) |
| goto Lnotcovariant; |
| |
| /* https://issues.dlang.org/show_bug.cgi?id=23135 |
| * extern(C++) mutable member functions are not covariant with const. |
| */ |
| if (t1.linkage == LINK.cpp && cppCovariant && t1.isNaked() != t2.isNaked()) |
| 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, MODFlags.const_))) |
| stc |= STC.const_; |
| else |
| goto Lnotcovariant; |
| } |
| else |
| { |
| goto Ldistinct; |
| } |
| } |
| |
| /* Can convert pure to impure, nothrow to throw, and nogc to gc |
| */ |
| if (!t1.purity && t2.purity) |
| stc |= STC.pure_; |
| |
| if (!t1.isnothrow && t2.isnothrow) |
| stc |= STC.nothrow_; |
| |
| if (!t1.isnogc && t2.isnogc) |
| stc |= STC.nogc; |
| |
| /* Can convert safe/trusted to system |
| */ |
| if (t1.trust <= TRUST.system && t2.trust >= TRUST.trusted) |
| { |
| // Should we infer trusted or safe? Go with safe. |
| stc |= STC.safe; |
| } |
| |
| if (stc) |
| { |
| if (pstc) |
| *pstc = stc; |
| goto Lnotcovariant; |
| } |
| |
| //printf("\tcovaraint: 1\n"); |
| return Covariant.yes; |
| |
| Ldistinct: |
| //printf("\tcovaraint: 0\n"); |
| return Covariant.distinct; |
| |
| Lnotcovariant: |
| //printf("\tcovaraint: 2\n"); |
| return Covariant.no; |
| } |
| |
| /******************************** |
| * For pretty-printing a type. |
| */ |
| final override const(char)* toChars() const |
| { |
| OutBuffer buf; |
| buf.reserve(16); |
| HdrGenState hgs; |
| hgs.fullQual = (ty == Tclass && !mod); |
| |
| .toCBuffer(this, &buf, null, &hgs); |
| return buf.extractChars(); |
| } |
| |
| /// ditto |
| final char* toPrettyChars(bool QualifyTypes = false) |
| { |
| OutBuffer buf; |
| buf.reserve(16); |
| HdrGenState hgs; |
| hgs.fullQual = QualifyTypes; |
| |
| .toCBuffer(this, &buf, null, &hgs); |
| return buf.extractChars(); |
| } |
| |
| static void _init() |
| { |
| stringtable._init(14_000); |
| |
| // Set basic types |
| __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(); |
| |
| tnoreturn = new TypeNoreturn(); |
| tnoreturn.deco = tnoreturn.merge().deco; |
| basic[Tnoreturn] = tnoreturn; |
| |
| tvoid = basic[Tvoid]; |
| tint8 = basic[Tint8]; |
| tuns8 = basic[Tuns8]; |
| tint16 = basic[Tint16]; |
| tuns16 = basic[Tuns16]; |
| tint32 = basic[Tint32]; |
| tuns32 = basic[Tuns32]; |
| tint64 = basic[Tint64]; |
| tuns64 = basic[Tuns64]; |
| tint128 = basic[Tint128]; |
| tuns128 = basic[Tuns128]; |
| tfloat32 = basic[Tfloat32]; |
| tfloat64 = basic[Tfloat64]; |
| tfloat80 = basic[Tfloat80]; |
| |
| timaginary32 = basic[Timaginary32]; |
| timaginary64 = basic[Timaginary64]; |
| timaginary80 = basic[Timaginary80]; |
| |
| tcomplex32 = basic[Tcomplex32]; |
| tcomplex64 = basic[Tcomplex64]; |
| tcomplex80 = basic[Tcomplex80]; |
| |
| tbool = basic[Tbool]; |
| tchar = basic[Tchar]; |
| twchar = basic[Twchar]; |
| tdchar = basic[Tdchar]; |
| |
| tshiftcnt = tint32; |
| terror = basic[Terror]; |
| tnoreturn = basic[Tnoreturn]; |
| tnull = new TypeNull(); |
| tnull.deco = tnull.merge().deco; |
| |
| tvoidptr = tvoid.pointerTo(); |
| tstring = tchar.immutableOf().arrayOf(); |
| twstring = twchar.immutableOf().arrayOf(); |
| tdstring = tdchar.immutableOf().arrayOf(); |
| |
| const isLP64 = target.isLP64; |
| |
| tsize_t = basic[isLP64 ? Tuns64 : Tuns32]; |
| tptrdiff_t = basic[isLP64 ? Tint64 : Tint32]; |
| thash_t = tsize_t; |
| } |
| |
| /** |
| * Deinitializes the global state of the compiler. |
| * |
| * This can be used to restore the state set by `_init` to its original |
| * state. |
| */ |
| static void deinitialize() |
| { |
| stringtable = stringtable.init; |
| } |
| |
| final uinteger_t size() |
| { |
| return size(Loc.initial); |
| } |
| |
| uinteger_t size(const ref Loc loc) |
| { |
| error(loc, "no size for type `%s`", toChars()); |
| return SIZE_INVALID; |
| } |
| |
| uint alignsize() |
| { |
| return cast(uint)size(Loc.initial); |
| } |
| |
| final Type trySemantic(const ref Loc loc, Scope* sc) |
| { |
| //printf("+trySemantic(%s) %d\n", toChars(), global.errors); |
| |
| // Needed to display any deprecations that were gagged |
| auto tcopy = this.syntaxCopy(); |
| |
| const errors = global.startGagging(); |
| Type t = typeSemantic(this, loc, sc); |
| if (global.endGagging(errors) || t.ty == Terror) // if any errors happened |
| { |
| t = null; |
| } |
| else |
| { |
| // If `typeSemantic` succeeded, there may have been deprecations that |
| // were gagged due the `startGagging` above. Run again to display |
| // those deprecations. https://issues.dlang.org/show_bug.cgi?id=19107 |
| if (global.gaggedWarnings > 0) |
| typeSemantic(tcopy, loc, sc); |
| } |
| //printf("-trySemantic(%s) %d\n", toChars(), global.errors); |
| 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(); |
| |
| auto sv = stringtable.lookup(t.deco, strlen(t.deco)); |
| if (sv && sv.value) |
| { |
| t = sv.value; |
| assert(t.deco); |
| } |
| else |
| assert(0); |
| return t; |
| } |
| |
| /********************************* |
| * Store this type's modifier name into buf. |
| */ |
| final void modToBuffer(OutBuffer* buf) nothrow const |
| { |
| if (mod) |
| { |
| buf.writeByte(' '); |
| MODtoBuffer(buf, mod); |
| } |
| } |
| |
| /********************************* |
| * Return this type's modifier name. |
| */ |
| final char* modToChars() nothrow const |
| { |
| OutBuffer buf; |
| buf.reserve(16); |
| modToBuffer(&buf); |
| return buf.extractChars(); |
| } |
| |
| 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(const ref Loc loc, Scope* sc) |
| { |
| if (Dsymbol s = toDsymbol(sc)) |
| { |
| s.checkDeprecated(loc, sc); |
| } |
| } |
| |
| final bool isConst() const nothrow pure @nogc @safe |
| { |
| return (mod & MODFlags.const_) != 0; |
| } |
| |
| final bool isImmutable() const nothrow pure @nogc @safe |
| { |
| return (mod & MODFlags.immutable_) != 0; |
| } |
| |
| final bool isMutable() const nothrow pure @nogc @safe |
| { |
| return (mod & (MODFlags.const_ | MODFlags.immutable_ | MODFlags.wild)) == 0; |
| } |
| |
| final bool isShared() const nothrow pure @nogc @safe |
| { |
| return (mod & MODFlags.shared_) != 0; |
| } |
| |
| final bool isSharedConst() const nothrow pure @nogc @safe |
| { |
| return (mod & (MODFlags.shared_ | MODFlags.const_)) == (MODFlags.shared_ | MODFlags.const_); |
| } |
| |
| final bool isWild() const nothrow pure @nogc @safe |
| { |
| return (mod & MODFlags.wild) != 0; |
| } |
| |
| final bool isWildConst() const nothrow pure @nogc @safe |
| { |
| return (mod & MODFlags.wildconst) == MODFlags.wildconst; |
| } |
| |
| final bool isSharedWild() const nothrow pure @nogc @safe |
| { |
| return (mod & (MODFlags.shared_ | MODFlags.wild)) == (MODFlags.shared_ | MODFlags.wild); |
| } |
| |
| final bool isNaked() const nothrow pure @nogc @safe |
| { |
| 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() nothrow const |
| { |
| 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.vtinfo = null; |
| t.ctype = null; |
| t.mcache = null; |
| if (t.ty == Tstruct) |
| (cast(TypeStruct)t).att = AliasThisRec.fwdref; |
| if (t.ty == Tclass) |
| (cast(TypeClass)t).att = AliasThisRec.fwdref; |
| return t; |
| } |
| |
| /******************************** |
| * Convert to 'const'. |
| */ |
| final Type constOf() |
| { |
| //printf("Type::constOf() %p %s\n", this, toChars()); |
| if (mod == MODFlags.const_) |
| return this; |
| if (mcache && mcache.cto) |
| { |
| assert(mcache.cto.mod == MODFlags.const_); |
| return mcache.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 (mcache && mcache.ito) |
| { |
| assert(mcache.ito.isImmutable()); |
| return mcache.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()) |
| { |
| getMcache(); |
| t = mcache.ito; // immutable => naked |
| assert(!t || (t.isMutable() && !t.isShared())); |
| } |
| else if (isConst()) |
| { |
| getMcache(); |
| if (isShared()) |
| { |
| if (isWild()) |
| t = mcache.swcto; // shared wild const -> shared |
| else |
| t = mcache.sto; // shared const => shared |
| } |
| else |
| { |
| if (isWild()) |
| t = mcache.wcto; // wild const -> naked |
| else |
| t = mcache.cto; // const => naked |
| } |
| assert(!t || t.isMutable()); |
| } |
| else if (isWild()) |
| { |
| getMcache(); |
| if (isShared()) |
| t = mcache.sto; // shared wild => shared |
| else |
| t = mcache.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 == MODFlags.shared_) |
| return this; |
| if (mcache && mcache.sto) |
| { |
| assert(mcache.sto.mod == MODFlags.shared_); |
| return mcache.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 == (MODFlags.shared_ | MODFlags.const_)) |
| return this; |
| if (mcache && mcache.scto) |
| { |
| assert(mcache.scto.mod == (MODFlags.shared_ | MODFlags.const_)); |
| return mcache.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()) |
| { |
| getMcache(); |
| if (isWild()) |
| { |
| if (isConst()) |
| t = mcache.wcto; // shared wild const => wild const |
| else |
| t = mcache.wto; // shared wild => wild |
| } |
| else |
| { |
| if (isConst()) |
| t = mcache.cto; // shared const => const |
| else |
| t = mcache.sto; // shared => naked |
| } |
| assert(!t || !t.isShared()); |
| } |
| |
| if (!t) |
| { |
| t = this.nullAttributes(); |
| t.mod = mod & ~MODFlags.shared_; |
| 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 == MODFlags.wild) |
| return this; |
| if (mcache && mcache.wto) |
| { |
| assert(mcache.wto.mod == MODFlags.wild); |
| return mcache.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 == MODFlags.wildconst) |
| return this; |
| if (mcache && mcache.wcto) |
| { |
| assert(mcache.wcto.mod == MODFlags.wildconst); |
| return mcache.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 == (MODFlags.shared_ | MODFlags.wild)) |
| return this; |
| if (mcache && mcache.swto) |
| { |
| assert(mcache.swto.mod == (MODFlags.shared_ | MODFlags.wild)); |
| return mcache.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 == (MODFlags.shared_ | MODFlags.wildconst)) |
| return this; |
| if (mcache && mcache.swcto) |
| { |
| assert(mcache.swcto.mod == (MODFlags.shared_ | MODFlags.wildconst)); |
| return mcache.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 MODFlags.const_: |
| getMcache(); |
| mcache.cto = t; |
| break; |
| |
| case MODFlags.wild: |
| getMcache(); |
| mcache.wto = t; |
| break; |
| |
| case MODFlags.wildconst: |
| getMcache(); |
| mcache.wcto = t; |
| break; |
| |
| case MODFlags.shared_: |
| getMcache(); |
| mcache.sto = t; |
| break; |
| |
| case MODFlags.shared_ | MODFlags.const_: |
| getMcache(); |
| mcache.scto = t; |
| break; |
| |
| case MODFlags.shared_ | MODFlags.wild: |
| getMcache(); |
| mcache.swto = t; |
| break; |
| |
| case MODFlags.shared_ | MODFlags.wildconst: |
| getMcache(); |
| mcache.swcto = t; |
| break; |
| |
| case MODFlags.immutable_: |
| getMcache(); |
| mcache.ito = t; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| assert(mod != t.mod); |
| |
| if (mod) |
| { |
| getMcache(); |
| t.getMcache(); |
| } |
| switch (mod) |
| { |
| case 0: |
| break; |
| |
| case MODFlags.const_: |
| mcache.cto = mto; |
| t.mcache.cto = this; |
| break; |
| |
| case MODFlags.wild: |
| mcache.wto = mto; |
| t.mcache.wto = this; |
| break; |
| |
| case MODFlags.wildconst: |
| mcache.wcto = mto; |
| t.mcache.wcto = this; |
| break; |
| |
| case MODFlags.shared_: |
| mcache.sto = mto; |
| t.mcache.sto = this; |
| break; |
| |
| case MODFlags.shared_ | MODFlags.const_: |
| mcache.scto = mto; |
| t.mcache.scto = this; |
| break; |
| |
| case MODFlags.shared_ | MODFlags.wild: |
| mcache.swto = mto; |
| t.mcache.swto = this; |
| break; |
| |
| case MODFlags.shared_ | MODFlags.wildconst: |
| mcache.swcto = mto; |
| t.mcache.swcto = this; |
| break; |
| |
| case MODFlags.immutable_: |
| t.mcache.ito = this; |
| if (t.mcache.cto) |
| t.mcache.cto.getMcache().ito = this; |
| if (t.mcache.sto) |
| t.mcache.sto.getMcache().ito = this; |
| if (t.mcache.scto) |
| t.mcache.scto.getMcache().ito = this; |
| if (t.mcache.wto) |
| t.mcache.wto.getMcache().ito = this; |
| if (t.mcache.wcto) |
| t.mcache.wcto.getMcache().ito = this; |
| if (t.mcache.swto) |
| t.mcache.swto.getMcache().ito = this; |
| if (t.mcache.swcto) |
| t.mcache.swcto.getMcache().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() |
| { |
| if (mcache) |
| with (mcache) |
| switch (mod) |
| { |
| case 0: |
| if (cto) |
| assert(cto.mod == MODFlags.const_); |
| if (ito) |
| assert(ito.mod == MODFlags.immutable_); |
| if (sto) |
| assert(sto.mod == MODFlags.shared_); |
| if (scto) |
| assert(scto.mod == (MODFlags.shared_ | MODFlags.const_)); |
| if (wto) |
| assert(wto.mod == MODFlags.wild); |
| if (wcto) |
| assert(wcto.mod == MODFlags.wildconst); |
| if (swto) |
| assert(swto.mod == (MODFlags.shared_ | MODFlags.wild)); |
| if (swcto) |
| assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst)); |
| break; |
| |
| case MODFlags.const_: |
| if (cto) |
| assert(cto.mod == 0); |
| if (ito) |
| assert(ito.mod == MODFlags.immutable_); |
| if (sto) |
| assert(sto.mod == MODFlags.shared_); |
| if (scto) |
| assert(scto.mod == (MODFlags.shared_ | MODFlags.const_)); |
| if (wto) |
| assert(wto.mod == MODFlags.wild); |
| if (wcto) |
| assert(wcto.mod == MODFlags.wildconst); |
| if (swto) |
| assert(swto.mod == (MODFlags.shared_ | MODFlags.wild)); |
| if (swcto) |
| assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst)); |
| break; |
| |
| case MODFlags.wild: |
| if (cto) |
| assert(cto.mod == MODFlags.const_); |
| if (ito) |
| assert(ito.mod == MODFlags.immutable_); |
| if (sto) |
| assert(sto.mod == MODFlags.shared_); |
| if (scto) |
| assert(scto.mod == (MODFlags.shared_ | MODFlags.const_)); |
| if (wto) |
| assert(wto.mod == 0); |
| if (wcto) |
| assert(wcto.mod == MODFlags.wildconst); |
| if (swto) |
| assert(swto.mod == (MODFlags.shared_ | MODFlags.wild)); |
| if (swcto) |
| assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst)); |
| break; |
| |
| case MODFlags.wildconst: |
| assert(!cto || cto.mod == MODFlags.const_); |
| assert(!ito || ito.mod == MODFlags.immutable_); |
| assert(!sto || sto.mod == MODFlags.shared_); |
| assert(!scto || scto.mod == (MODFlags.shared_ | MODFlags.const_)); |
| assert(!wto || wto.mod == MODFlags.wild); |
| assert(!wcto || wcto.mod == 0); |
| assert(!swto || swto.mod == (MODFlags.shared_ | MODFlags.wild)); |
| assert(!swcto || swcto.mod == (MODFlags.shared_ | MODFlags.wildconst)); |
| break; |
| |
| case MODFlags.shared_: |
| if (cto) |
| assert(cto.mod == MODFlags.const_); |
| if (ito) |
| assert(ito.mod == MODFlags.immutable_); |
| if (sto) |
| assert(sto.mod == 0); |
| if (scto) |
| assert(scto.mod == (MODFlags.shared_ | MODFlags.const_)); |
| if (wto) |
| assert(wto.mod == MODFlags.wild); |
| if (wcto) |
| assert(wcto.mod == MODFlags.wildconst); |
| if (swto) |
| assert(swto.mod == (MODFlags.shared_ | MODFlags.wild)); |
| if (swcto) |
| assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst)); |
| break; |
| |
| case MODFlags.shared_ | MODFlags.const_: |
| if (cto) |
| assert(cto.mod == MODFlags.const_); |
| if (ito) |
| assert(ito.mod == MODFlags.immutable_); |
| if (sto) |
| assert(sto.mod == MODFlags.shared_); |
| if (scto) |
| assert(scto.mod == 0); |
| if (wto) |
| assert(wto.mod == MODFlags.wild); |
| if (wcto) |
| assert(wcto.mod == MODFlags.wildconst); |
| if (swto) |
| assert(swto.mod == (MODFlags.shared_ | MODFlags.wild)); |
| if (swcto) |
| assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst)); |
| break; |
| |
| case MODFlags.shared_ | MODFlags.wild: |
| if (cto) |
| assert(cto.mod == MODFlags.const_); |
| if (ito) |
| assert(ito.mod == MODFlags.immutable_); |
| if (sto) |
| assert(sto.mod == MODFlags.shared_); |
| if (scto) |
| assert(scto.mod == (MODFlags.shared_ | MODFlags.const_)); |
| if (wto) |
| assert(wto.mod == MODFlags.wild); |
| if (wcto) |
| assert(wcto.mod == MODFlags.wildconst); |
| if (swto) |
| assert(swto.mod == 0); |
| if (swcto) |
| assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst)); |
| break; |
| |
| case MODFlags.shared_ | MODFlags.wildconst: |
| assert(!cto || cto.mod == MODFlags.const_); |
| assert(!ito || ito.mod == MODFlags.immutable_); |
| assert(!sto || sto.mod == MODFlags.shared_); |
| assert(!scto || scto.mod == (MODFlags.shared_ | MODFlags.const_)); |
| assert(!wto || wto.mod == MODFlags.wild); |
| assert(!wcto || wcto.mod == MODFlags.wildconst); |
| assert(!swto || swto.mod == (MODFlags.shared_ | MODFlags.wild)); |
| assert(!swcto || swcto.mod == 0); |
| break; |
| |
| case MODFlags.immutable_: |
| if (cto) |
| assert(cto.mod == MODFlags.const_); |
| if (ito) |
| assert(ito.mod == 0); |
| if (sto) |
| assert(sto.mod == MODFlags.shared_); |
| if (scto) |
| assert(scto.mod == (MODFlags.shared_ | MODFlags.const_)); |
| if (wto) |
| assert(wto.mod == MODFlags.wild); |
| if (wcto) |
| assert(wcto.mod == MODFlags.wildconst); |
| if (swto) |
| assert(swto.mod == (MODFlags.shared_ | MODFlags.wild)); |
| if (swcto) |
| assert(swcto.mod == (MODFlags.shared_ | MODFlags.wildconst)); |
| break; |
| |
| default: |
| assert(0); |
| } |
| |
| Type tn = nextOf(); |
| if (tn && ty != Tfunction && tn.ty != Tfunction && ty != Tenum) |
| { |
| // Verify transitivity |
| switch (mod) |
| { |
| case 0: |
| case MODFlags.const_: |
| case MODFlags.wild: |
| case MODFlags.wildconst: |
| case MODFlags.shared_: |
| case MODFlags.shared_ | MODFlags.const_: |
| case MODFlags.shared_ | MODFlags.wild: |
| case MODFlags.shared_ | MODFlags.wildconst: |
| case MODFlags.immutable_: |
| assert(tn.mod == MODFlags.immutable_ || (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 & STC.immutable_) |
| { |
| t = t.makeImmutable(); |
| } |
| else |
| { |
| if ((stc & STC.shared_) && !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 & STC.const_) && !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 & STC.wild) && !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 MODFlags.const_: |
| t = unSharedOf().constOf(); |
| break; |
| |
| case MODFlags.wild: |
| t = unSharedOf().wildOf(); |
| break; |
| |
| case MODFlags.wildconst: |
| t = unSharedOf().wildConstOf(); |
| break; |
| |
| case MODFlags.shared_: |
| t = mutableOf().sharedOf(); |
| break; |
| |
| case MODFlags.shared_ | MODFlags.const_: |
| t = sharedConstOf(); |
| break; |
| |
| case MODFlags.shared_ | MODFlags.wild: |
| t = sharedWildOf(); |
| break; |
| |
| case MODFlags.shared_ | MODFlags.wildconst: |
| t = sharedWildConstOf(); |
| break; |
| |
| case MODFlags.immutable_: |
| 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 MODFlags.const_: |
| if (isShared()) |
| { |
| if (isWild()) |
| t = sharedWildConstOf(); |
| else |
| t = sharedConstOf(); |
| } |
| else |
| { |
| if (isWild()) |
| t = wildConstOf(); |
| else |
| t = constOf(); |
| } |
| break; |
| |
| case MODFlags.wild: |
| if (isShared()) |
| { |
| if (isConst()) |
| t = sharedWildConstOf(); |
| else |
| t = sharedWildOf(); |
| } |
| else |
| { |
| if (isConst()) |
| t = wildConstOf(); |
| else |
| t = wildOf(); |
| } |
| break; |
| |
| case MODFlags.wildconst: |
| if (isShared()) |
| t = sharedWildConstOf(); |
| else |
| t = wildConstOf(); |
| break; |
| |
| case MODFlags.shared_: |
| if (isWild()) |
| { |
| if (isConst()) |
| t = sharedWildConstOf(); |
| else |
| t = sharedWildOf(); |
| } |
| else |
| { |
| if (isConst()) |
| t = sharedConstOf(); |
| else |
| t = sharedOf(); |
| } |
| break; |
| |
| case MODFlags.shared_ | MODFlags.const_: |
| if (isWild()) |
| t = sharedWildConstOf(); |
| else |
| t = sharedConstOf(); |
| break; |
| |
| case MODFlags.shared_ | MODFlags.wild: |
| if (isConst()) |
| t = sharedWildConstOf(); |
| else |
| t = sharedWildOf(); |
| break; |
| |
| case MODFlags.shared_ | MODFlags.wildconst: |
| t = sharedWildConstOf(); |
| break; |
| |
| case MODFlags.immutable_: |
| 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 & STC.immutable_) |
| mod = MODFlags.immutable_; |
| else |
| { |
| if (stc & (STC.const_ | STC.in_)) |
| mod |= MODFlags.const_; |
| if (stc & STC.wild) |
| mod |= MODFlags.wild; |
| if (stc & STC.shared_) |
| mod |= MODFlags.shared_; |
| } |
| 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.initial, dim, Type.tsize_t)); |
| // according to TypeSArray::semantic() |
| t = t.addMod(mod); |
| t = t.merge(); |
| return t; |
| } |
| |
| final bool hasDeprecatedAliasThis() |
| { |
| auto ad = isAggregate(this); |
| return ad && ad.aliasthis && (ad.aliasthis.isDeprecated || ad.aliasthis.sym.isDeprecated); |
| } |
| |
| final Type aliasthisOf() |
| { |
| auto ad = isAggregate(this); |
| if (!ad || !ad.aliasthis) |
| return null; |
| |
| auto s = ad.aliasthis.sym; |
| 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.initial, null, fd, null, this, null, FuncResolveFlag.quiet); |
| 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 ? MODFlags.mutable : 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.initial, null, td, null, this, null, FuncResolveFlag.quiet); |
| if (!fd || fd.errors || !fd.functionSemantic()) |
| return Type.terror; |
| |
| auto t = fd.type.nextOf(); |
| if (!t) |
| return Type.terror; |
| t = t.substWildTo(mod == 0 ? MODFlags.mutable : mod); |
| return t; |
| } |
| |
| //printf("%s\n", s.kind()); |
| return null; |
| } |
| |
| extern (D) 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 & AliasThisRec.typeMask); |
| if (flag == AliasThisRec.fwdref) |
| { |
| Type att = aliasthisOf(); |
| flag = att && att.implicitConvTo(this) ? AliasThisRec.yes : AliasThisRec.no; |
| } |
| *pflag = cast(AliasThisRec)(flag | (*pflag & ~AliasThisRec.typeMask)); |
| return flag == AliasThisRec.yes; |
| } |
| |
| Type makeConst() |
| { |
| //printf("Type::makeConst() %p, %s\n", this, toChars()); |
| if (mcache && mcache.cto) |
| return mcache.cto; |
| Type t = this.nullAttributes(); |
| t.mod = MODFlags.const_; |
| //printf("-Type::makeConst() %p, %s\n", t, toChars()); |
| return t; |
| } |
| |
| Type makeImmutable() |
| { |
| if (mcache && mcache.ito) |
| return mcache.ito; |
| Type t = this.nullAttributes(); |
| t.mod = MODFlags.immutable_; |
| return t; |
| } |
| |
| Type makeShared() |
| { |
| if (mcache && mcache.sto) |
| return mcache.sto; |
| Type t = this.nullAttributes(); |
| t.mod = MODFlags.shared_; |
| return t; |
| } |
| |
| Type makeSharedConst() |
| { |
| if (mcache && mcache.scto) |
| return mcache.scto; |
| Type t = this.nullAttributes(); |
| t.mod = MODFlags.shared_ | MODFlags.const_; |
| return t; |
| } |
| |
| Type makeWild() |
| { |
| if (mcache && mcache.wto) |
| return mcache.wto; |
| Type t = this.nullAttributes(); |
| t.mod = MODFlags.wild; |
| return t; |
| } |
| |
| Type makeWildConst() |
| { |
| if (mcache && mcache.wcto) |
| return mcache.wcto; |
| Type t = this.nullAttributes(); |
| t.mod = MODFlags.wildconst; |
| return t; |
| } |
| |
| Type makeSharedWild() |
| { |
| if (mcache && mcache.swto) |
| return mcache.swto; |
| Type t = this.nullAttributes(); |
| t.mod = MODFlags.shared_ | MODFlags.wild; |
| return t; |
| } |
| |
| Type makeSharedWildConst() |
| { |
| if (mcache && mcache.swcto) |
| return mcache.swcto; |
| Type t = this.nullAttributes(); |
| t.mod = MODFlags.shared_ | MODFlags.wildconst; |
| return t; |
| } |
| |
| Type makeMutable() |
| { |
| Type t = this.nullAttributes(); |
| t.mod = mod & MODFlags.shared_; |
| return t; |
| } |
| |
| Dsymbol toDsymbol(Scope* sc) |
| { |
| return null; |
| } |
| |
| /******************************* |
| * If this is a shell around another type, |
| * get that other type. |
| */ |
| final Type toBasetype() |
| { |
| /* This function is used heavily. |
| * De-virtualize it so it can be easily inlined. |
| */ |
| TypeEnum te; |
| return ((te = isTypeEnum()) !is null) ? te.toBasetype2() : this; |
| } |
| |
| bool isBaseOf(Type t, int* poffset) |
| { |
| return 0; // assume not |
| } |
| |
| /******************************** |
| * Determine if 'this' can be implicitly converted |
| * to type 'to'. |
| * Returns: |
| * MATCH.nomatch, MATCH.convert, MATCH.constant, MATCH.exact |
| */ |
| 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 MATCH.exact; |
| return MATCH.nomatch; |
| } |
| |
| /******************************* |
| * Determine if converting 'this' to 'to' is an identity operation, |
| * a conversion to const operation, or the types aren't the same. |
| * Returns: |
| * MATCH.exact 'this' == 'to' |
| * MATCH.constant 'to' is const |
| * MATCH.nomatch conversion to mutable or invariant |
| */ |
| MATCH constConv(Type to) |
| { |
| //printf("Type::constConv(this = %s, to = %s)\n", toChars(), to.toChars()); |
| if (equals(to)) |
| return MATCH.exact; |
| if (ty == to.ty && MODimplicitConv(mod, to.mod)) |
| return MATCH.constant; |
| return MATCH.nomatch; |
| } |
| |
| /*************************************** |
| * Compute MOD bits matching `this` argument type to wild parameter type. |
| * Params: |
| * t = corresponding parameter type |
| * isRef = parameter is `ref` or `out` |
| * Returns: |
| * MOD bits |
| */ |
| MOD deduceWild(Type t, bool isRef) |
| { |
| //printf("Type::deduceWild this = '%s', tprm = '%s'\n", toChars(), tprm.toChars()); |
| if (t.isWild()) |
| { |
| if (isImmutable()) |
| return MODFlags.immutable_; |
| else if (isWildConst()) |
| { |
| if (t.isWildConst()) |
| return MODFlags.wild; |
| else |
| return MODFlags.wildconst; |
| } |
| else if (isWild()) |
| return MODFlags.wild; |
| else if (isConst()) |
| return MODFlags.const_; |
| else if (isMutable()) |
| return MODFlags.mutable; |
| 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()); |
| } |
| else if (ty == Tdelegate) |
| { |
| t = new TypeDelegate(t.isTypeFunction()); |
| } |
| else |
| assert(0); |
| |
| t = t.merge(); |
| } |
| } |
| else |
| t = this; |
| |
| L1: |
| if (isWild()) |
| { |
| if (mod == MODFlags.immutable_) |
| { |
| t = t.immutableOf(); |
| } |
| else if (mod == MODFlags.wildconst) |
| { |
| t = t.wildConstOf(); |
| } |
| else if (mod == MODFlags.wild) |
| { |
| if (isWildConst()) |
| t = t.wildConstOf(); |
| else |
| t = t.wildOf(); |
| } |
| else if (mod == MODFlags.const_) |
| { |
| t = t.constOf(); |
| } |
| else |
| { |
| if (isWildConst()) |
| t = t.constOf(); |
| else |
| t = t.mutableOf(); |
| } |
| } |
| if (isConst()) |
| t = t.addMod(MODFlags.const_); |
| if (isShared()) |
| t = t.addMod(MODFlags.shared_); |
| |
| //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); |
| } |
| else |
| assert(0); |
| |
| t = t.merge(); |
| } |
| } |
| t = t.addMod(mod & ~m); |
| return t; |
| } |
| |
| /************************** |
| * Return type with the top level of it being mutable. |
| */ |
| inout(Type) toHeadMutable() inout |
| { |
| if (!mod) |
| return this; |
| Type unqualThis = cast(Type) this; |
| // `mutableOf` needs a mutable `this` only for caching |
| return cast(inout(Type)) unqualThis.mutableOf(); |
| } |
| |
| inout(ClassDeclaration) isClassHandle() inout |
| { |
| return null; |
| } |
| |
| /************************************ |
| * Return alignment to use for this type. |
| */ |
| structalign_t alignment() |
| { |
| structalign_t s; |
| s.setDefault(); |
| return s; |
| } |
| |
| /*************************************** |
| * Use when we prefer the default initializer to be a literal, |
| * rather than a global immutable variable. |
| */ |
| Expression defaultInitLiteral(const ref Loc loc) |
| { |
| static if (LOGDEFAULTINIT) |
| { |
| printf("Type::defaultInitLiteral() '%s'\n", toChars()); |
| } |
| return defaultInit(this, loc); |
| } |
| |
| // if initializer is 0 |
| bool isZeroInit(const ref Loc loc) |
| { |
| return false; // assume not |
| } |
| |
| final Identifier getTypeInfoIdent() |
| { |
| // _init_10TypeInfo_%s |
| OutBuffer buf; |
| buf.reserve(32); |
| mangleToBuffer(this, &buf); |
| |
| const slice = buf[]; |
| |
| // 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*)Mem.check(malloc(namelen)); |
| |
| 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 |
| |
| auto id = Identifier.idPool(name, length); |
| |
| if (name != namebuf.ptr) |
| free(name); |
| return id; |
| } |
| |
| /*************************************** |
| * Return !=0 if the type or any of its subtypes is wild. |
| */ |
| int hasWild() const |
| { |
| return mod & MODFlags.wild; |
| } |
| |
| /*************************************** |
| * 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; |
| } |
| |
| /************************************* |
| * Detect if this is an unsafe type because of the presence of `@system` members |
| * Returns: |
| * true if so |
| */ |
| bool hasSystemFields() |
| { |
| return false; |
| } |
| |
| /*************************************** |
| * Returns: true if type has any invariants |
| */ |
| bool hasInvariant() |
| { |
| //printf("Type::hasInvariant() %s, %d\n", toChars(), ty); |
| 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(); |
| TypeSArray tsa; |
| while ((tsa = t.isTypeSArray()) !is null) |
| t = tsa.next.toBasetype(); |
| return t; |
| } |
| |
| /******************************************* |
| * Compute number of elements for a (possibly multidimensional) static array, |
| * or 1 for other types. |
| * Params: |
| * loc = for error message |
| * Returns: |
| * number of elements, uint.max on overflow |
| */ |
| final uint numberOfElems(const ref Loc loc) |
| { |
| //printf("Type::numberOfElems()\n"); |
| uinteger_t n = 1; |
| Type tb = this; |
| while ((tb = tb.toBasetype()).ty == Tsarray) |
| { |
| bool overflow = false; |
| n = mulu(n, (cast(TypeSArray)tb).dim.toUInteger(), overflow); |
| if (overflow || n >= uint.max) |
| { |
| error(loc, "static array `%s` size overflowed to %llu", toChars(), cast(ulong)n); |
| return uint.max; |
| } |
| tb = (cast(TypeSArray)tb).next; |
| } |
| return cast(uint)n; |
| } |
| |
| /**************************************** |
| * 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; |
| } |
| |
| /******************************** |
| * true if when type is copied, it needs a copy constructor or postblit |
| * applied. Only applies to value types, not ref types. |
| */ |
| bool needsCopyOrPostblit() |
| { |
| return false; |
| } |
| |
| /********************************* |
| * |
| */ |
| bool needsNested() |
| { |
| return false; |
| } |
| |
| /************************************* |
| * https://issues.dlang.org/show_bug.cgi?id=14488 |
| * Check if the inner most base type is complex or imaginary. |
| * Should only give alerts when set to emit transitional messages. |
| * Params: |
| * loc = The source location. |
| * sc = scope of the type |
| */ |
| extern (D) final bool checkComplexTransition(const ref Loc loc, Scope* sc) |
| { |
| if (sc.isDeprecated()) |
| return false; |
| // Don't complain if we're inside a template constraint |
| // https://issues.dlang.org/show_bug.cgi?id=21831 |
| if (sc.flags & SCOPE.constraint) |
| return false; |
| |
| Type t = baseElemOf(); |
| while (t.ty == Tpointer || t.ty == Tarray) |
| t = t.nextOf().baseElemOf(); |
| |
| // Basetype is an opaque enum, nothing to check. |
| if (t.ty == Tenum && !(cast(TypeEnum)t).sym.memtype) |
| return false; |
| |
| if (t.isimaginary() || t.iscomplex()) |
| { |
| Type rt; |
| switch (t.ty) |
| { |
| case Tcomplex32: |
| case Timaginary32: |
| rt = Type.tfloat32; |
| break; |
| |
| case Tcomplex64: |
| case Timaginary64: |
| rt = Type.tfloat64; |
| break; |
| |
| case Tcomplex80: |
| case Timaginary80: |
| rt = Type.tfloat80; |
| break; |
| |
| default: |
| assert(0); |
| } |
| // @@@DEPRECATED_2.117@@@ |
| // Deprecated in 2.097 - Can be made an error from 2.117. |
| // The deprecation period is longer than usual as `cfloat`, |
| // `cdouble`, and `creal` were quite widely used. |
| if (t.iscomplex()) |
| { |
| deprecation(loc, "use of complex type `%s` is deprecated, use `std.complex.Complex!(%s)` instead", |
| toChars(), rt.toChars()); |
| return true; |
| } |
| else |
| { |
| deprecation(loc, "use of imaginary type `%s` is deprecated, use `%s` instead", |
| toChars(), rt.toChars()); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // For eliminating dynamic_cast |
| TypeBasic isTypeBasic() |
| { |
| return null; |
| } |
| |
| final pure inout nothrow @nogc |
| { |
| /**************** |
| * Is this type a pointer to a function? |
| * Returns: |
| * the function type if it is |
| */ |
| inout(TypeFunction) isPtrToFunction() |
| { |
| return (ty == Tpointer && (cast(TypePointer)this).next.ty == Tfunction) |
| ? cast(typeof(return))(cast(TypePointer)this).next |
| : null; |
| } |
| |
| /***************** |
| * Is this type a function, delegate, or pointer to a function? |
| * Returns: |
| * the function type if it is |
| */ |
| inout(TypeFunction) isFunction_Delegate_PtrToFunction() |
| { |
| return ty == Tfunction ? cast(typeof(return))this : |
| |
| ty == Tdelegate ? cast(typeof(return))(cast(TypePointer)this).next : |
| |
| ty == Tpointer && (cast(TypePointer)this).next.ty == Tfunction ? |
| cast(typeof(return))(cast(TypePointer)this).next : |
| |
| null; |
| } |
| } |
| |
| final pure inout nothrow @nogc @safe |
| { |
| inout(TypeError) isTypeError() { return ty == Terror ? cast(typeof(return))this : null; } |
| inout(TypeVector) isTypeVector() { return ty == Tvector ? cast(typeof(return))this : null; } |
| inout(TypeSArray) isTypeSArray() { return ty == Tsarray ? cast(typeof(return))this : null; } |
| inout(TypeDArray) isTypeDArray() { return ty == Tarray ? cast(typeof(return))this : null; } |
| inout(TypeAArray) isTypeAArray() { return ty == Taarray ? cast(typeof(return))this : null; } |
| inout(TypePointer) isTypePointer() { return ty == Tpointer ? cast(typeof(return))this : null; } |
| inout(TypeReference) isTypeReference() { return ty == Treference ? cast(typeof(return))this : null; } |
| inout(TypeFunction) isTypeFunction() { return ty == Tfunction ? cast(typeof(return))this : null; } |
| inout(TypeDelegate) isTypeDelegate() { return ty == Tdelegate ? cast(typeof(return))this : null; } |
| inout(TypeIdentifier) isTypeIdentifier() { return ty == Tident ? cast(typeof(return))this : null; } |
| inout(TypeInstance) isTypeInstance() { return ty == Tinstance ? cast(typeof(return))this : null; } |
| inout(TypeTypeof) isTypeTypeof() { return ty == Ttypeof ? cast(typeof(return))this : null; } |
| inout(TypeReturn) isTypeReturn() { return ty == Treturn ? cast(typeof(return))this : null; } |
| inout(TypeStruct) isTypeStruct() { return ty == Tstruct ? cast(typeof(return))this : null; } |
| inout(TypeEnum) isTypeEnum() { return ty == Tenum ? cast(typeof(return))this : null; } |
| inout(TypeClass) isTypeClass() { return ty == Tclass ? cast(typeof(return))this : null; } |
| inout(TypeTuple) isTypeTuple() { return ty == Ttuple ? cast(typeof(return))this : null; } |
| inout(TypeSlice) isTypeSlice() { return ty == Tslice ? cast(typeof(return))this : null; } |
| inout(TypeNull) isTypeNull() { return ty == Tnull ? cast(typeof(return))this : null; } |
| inout(TypeMixin) isTypeMixin() { return ty == Tmixin ? cast(typeof(return))this : null; } |
| inout(TypeTraits) isTypeTraits() { return ty == Ttraits ? cast(typeof(return))this : null; } |
| inout(TypeNoreturn) isTypeNoreturn() { return ty == Tnoreturn ? cast(typeof(return))this : null; } |
| inout(TypeTag) isTypeTag() { return ty == Ttag ? cast(typeof(return))this : null; } |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| |
| final TypeFunction toTypeFunction() |
| { |
| if (ty != Tfunction) |
| assert(0); |
| return cast(TypeFunction)this; |
| } |
| |
| extern (D) static Types* arraySyntaxCopy(Types* types) |
| { |
| Types* a = null; |
| if (types) |
| { |
| a = new Types(types.length); |
| foreach (i, t; *types) |
| { |
| (*a)[i] = t ? t.syntaxCopy() : null; |
| } |
| } |
| return a; |
| } |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class TypeError : Type |
| { |
| extern (D) this() |
| { |
| super(Terror); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "error"; |
| } |
| |
| override TypeError syntaxCopy() |
| { |
| // No semantic analysis done, no need to copy |
| return this; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return SIZE_INVALID; |
| } |
| |
| override Expression defaultInitLiteral(const ref Loc loc) |
| { |
| return ErrorExp.get(); |
| } |
| |
| 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(const ref 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 & MODFlags.wild || (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 (mcache && mcache.cto) |
| { |
| assert(mcache.cto.mod == MODFlags.const_); |
| return mcache.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 (mcache && mcache.ito) |
| { |
| assert(mcache.ito.isImmutable()); |
| return mcache.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 (mcache && mcache.sto) |
| { |
| assert(mcache.sto.mod == MODFlags.shared_); |
| return mcache.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 (mcache && mcache.scto) |
| { |
| assert(mcache.scto.mod == (MODFlags.shared_ | MODFlags.const_)); |
| return mcache.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 (mcache && mcache.wto) |
| { |
| assert(mcache.wto.mod == MODFlags.wild); |
| return mcache.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(); |
|