| /** |
| * 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(); |
| } |
| 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 (mcache && mcache.wcto) |
| { |
| assert(mcache.wcto.mod == MODFlags.wildconst); |
| return mcache.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 (mcache && mcache.swto) |
| { |
| assert(mcache.swto.isSharedWild()); |
| return mcache.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 (mcache && mcache.swcto) |
| { |
| assert(mcache.swcto.mod == (MODFlags.shared_ | MODFlags.wildconst)); |
| return mcache.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 MATCH.exact; |
| |
| if (!(ty == to.ty && MODimplicitConv(mod, to.mod))) |
| return MATCH.nomatch; |
| |
| Type tn = to.nextOf(); |
| if (!(tn && next.ty == tn.ty)) |
| return MATCH.nomatch; |
| |
| MATCH m; |
| if (to.isConst()) // whole tail const conversion |
| { |
| // Recursive shared level check |
| m = next.constConv(tn); |
| if (m == MATCH.exact) |
| m = MATCH.constant; |
| } |
| else |
| { |
| //printf("\tnext => %s, to.next => %s\n", next.toChars(), tn.toChars()); |
| m = next.equals(tn) ? MATCH.constant : MATCH.nomatch; |
| } |
| return m; |
| } |
| |
| override final MOD 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(TOK.void_); |
| break; |
| |
| case Tint8: |
| d = Token.toChars(TOK.int8); |
| flags |= TFlags.integral; |
| break; |
| |
| case Tuns8: |
| d = Token.toChars(TOK.uns8); |
| flags |= TFlags.integral | TFlags.unsigned; |
| break; |
| |
| case Tint16: |
| d = Token.toChars(TOK.int16); |
| flags |= TFlags.integral; |
| break; |
| |
| case Tuns16: |
| d = Token.toChars(TOK.uns16); |
| flags |= TFlags.integral | TFlags.unsigned; |
| break; |
| |
| case Tint32: |
| d = Token.toChars(TOK.int32); |
| flags |= TFlags.integral; |
| break; |
| |
| case Tuns32: |
| d = Token.toChars(TOK.uns32); |
| flags |= TFlags.integral | TFlags.unsigned; |
| break; |
| |
| case Tfloat32: |
| d = Token.toChars(TOK.float32); |
| flags |= TFlags.floating | TFlags.real_; |
| break; |
| |
| case Tint64: |
| d = Token.toChars(TOK.int64); |
| flags |= TFlags.integral; |
| break; |
| |
| case Tuns64: |
| d = Token.toChars(TOK.uns64); |
| flags |= TFlags.integral | TFlags.unsigned; |
| break; |
| |
| case Tint128: |
| d = Token.toChars(TOK.int128); |
| flags |= TFlags.integral; |
| break; |
| |
| case Tuns128: |
| d = Token.toChars(TOK.uns128); |
| flags |= TFlags.integral | TFlags.unsigned; |
| break; |
| |
| case Tfloat64: |
| d = Token.toChars(TOK.float64); |
| flags |= TFlags.floating | TFlags.real_; |
| break; |
| |
| case Tfloat80: |
| d = Token.toChars(TOK.float80); |
| flags |= TFlags.floating | TFlags.real_; |
| break; |
| |
| case Timaginary32: |
| d = Token.toChars(TOK.imaginary32); |
| flags |= TFlags.floating | TFlags.imaginary; |
| break; |
| |
| case Timaginary64: |
| d = Token.toChars(TOK.imaginary64); |
| flags |= TFlags.floating | TFlags.imaginary; |
| break; |
| |
| case Timaginary80: |
| d = Token.toChars(TOK.imaginary80); |
| flags |= TFlags.floating | TFlags.imaginary; |
| break; |
| |
| case Tcomplex32: |
| d = Token.toChars(TOK.complex32); |
| flags |= TFlags.floating | TFlags.complex; |
| break; |
| |
| case Tcomplex64: |
| d = Token.toChars(TOK.complex64); |
| flags |= TFlags.floating | TFlags.complex; |
| break; |
| |
| case Tcomplex80: |
| d = Token.toChars(TOK.complex80); |
| flags |= TFlags.floating | TFlags.complex; |
| break; |
| |
| case Tbool: |
| d = "bool"; |
| flags |= TFlags.integral | TFlags.unsigned; |
| break; |
| |
| case Tchar: |
| d = Token.toChars(TOK.char_); |
| flags |= TFlags.integral | TFlags.unsigned; |
| break; |
| |
| case Twchar: |
| d = Token.toChars(TOK.wchar_); |
| flags |= TFlags.integral | TFlags.unsigned; |
| break; |
| |
| case Tdchar: |
| d = Token.toChars(TOK.dchar_); |
| flags |= TFlags.integral | TFlags.unsigned; |
| break; |
| |
| default: |
| assert(0); |
| } |
| this.dstring = d; |
| this.flags = flags; |
| merge(this); |
| } |
| |
| override const(char)* kind() const |
| { |
| return dstring; |
| } |
| |
| override TypeBasic syntaxCopy() |
| { |
| // No semantic analysis done on basic types, no need to copy |
| return this; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| 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 bool isintegral() |
| { |
| //printf("TypeBasic::isintegral('%s') x%x\n", toChars(), flags); |
| return (flags & TFlags.integral) != 0; |
| } |
| |
| override bool isfloating() |
| { |
| return (flags & TFlags.floating) != 0; |
| } |
| |
| override bool isreal() |
| { |
| return (flags & TFlags.real_) != 0; |
| } |
| |
| override bool isimaginary() |
| { |
| return (flags & TFlags.imaginary) != 0; |
| } |
| |
| override bool iscomplex() |
| { |
| return (flags & TFlags.complex) != 0; |
| } |
| |
| override bool isscalar() |
| { |
| return (flags & (TFlags.integral | TFlags.floating)) != 0; |
| } |
| |
| override bool isunsigned() |
| { |
| return (flags & TFlags.unsigned) != 0; |
| } |
| |
| override MATCH implicitConvTo(Type to) |
| { |
| //printf("TypeBasic::implicitConvTo(%s) from %s\n", to.toChars(), toChars()); |
| if (this == to) |
| return MATCH.exact; |
| |
| if (ty == to.ty) |
| { |
| if (mod == to.mod) |
| return MATCH.exact; |
| else if (MODimplicitConv(mod, to.mod)) |
| return MATCH.constant; |
| else if (!((mod ^ to.mod) & MODFlags.shared_)) // for wild matching |
| return MATCH.constant; |
| else |
| return MATCH.convert; |
| } |
| |
| if (ty == Tvoid || to.ty == Tvoid) |
| return MATCH.nomatch; |
| if (to.ty == Tbool) |
| return MATCH.nomatch; |
| |
| TypeBasic tob; |
| if (to.ty == Tvector && to.deco) |
| { |
| TypeVector tv = cast(TypeVector)to; |
| tob = tv.elementType(); |
| } |
| else if (auto te = to.isTypeEnum()) |
| { |
| EnumDeclaration ed = te.sym; |
| if (ed.isSpecial()) |
| { |
| /* Special enums that allow implicit conversions to them |
| * with a MATCH.convert |
| */ |
| tob = to.toBasetype().isTypeBasic(); |
| } |
| else |
| return MATCH.nomatch; |
| } |
| else |
| tob = to.isTypeBasic(); |
| if (!tob) |
| return MATCH.nomatch; |
| |
| if (flags & TFlags.integral) |
| { |
| // Disallow implicit conversion of integers to imaginary or complex |
| if (tob.flags & (TFlags.imaginary | TFlags.complex)) |
| return MATCH.nomatch; |
| |
| // If converting from integral to integral |
| if (tob.flags & TFlags.integral) |
| { |
| const sz = size(Loc.initial); |
| const tosz = tob.size(Loc.initial); |
| |
| /* Can't convert to smaller size |
| */ |
| if (sz > tosz) |
| return MATCH.nomatch; |
| /* Can't change sign if same size |
| */ |
| //if (sz == tosz && (flags ^ tob.flags) & TFlags.unsigned) |
| // return MATCH.nomatch; |
| } |
| } |
| else if (flags & TFlags.floating) |
| { |
| // Disallow implicit conversion of floating point to integer |
| if (tob.flags & TFlags.integral) |
| return MATCH.nomatch; |
| |
| assert(tob.flags & TFlags.floating || to.ty == Tvector); |
| |
| // Disallow implicit conversion from complex to non-complex |
| if (flags & TFlags.complex && !(tob.flags & TFlags.complex)) |
| return MATCH.nomatch; |
| |
| // Disallow implicit conversion of real or imaginary to complex |
| if (flags & (TFlags.real_ | TFlags.imaginary) && tob.flags & TFlags.complex) |
| return MATCH.nomatch; |
| |
| // Disallow implicit conversion to-from real and imaginary |
| if ((flags & (TFlags.real_ | TFlags.imaginary)) != (tob.flags & (TFlags.real_ | TFlags.imaginary))) |
| return MATCH.nomatch; |
| } |
| return MATCH.convert; |
| } |
| |
| override bool isZeroInit(const ref Loc loc) |
| { |
| switch (ty) |
| { |
| case Tchar: |
| case Twchar: |
| case Tdchar: |
| case Timaginary32: |
| case Timaginary64: |
| case Timaginary80: |
| case Tfloat32: |
| case Tfloat64: |
| case Tfloat80: |
| case Tcomplex32: |
| case Tcomplex64: |
| case Tcomplex80: |
| return false; // no |
| default: |
| return true; // yes |
| } |
| } |
| |
| // 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(Type basetype) |
| { |
| super(Tvector); |
| this.basetype = basetype; |
| } |
| |
| static TypeVector create(Type basetype) |
| { |
| return new TypeVector(basetype); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "vector"; |
| } |
| |
| override TypeVector syntaxCopy() |
| { |
| return new TypeVector(basetype.syntaxCopy()); |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return basetype.size(); |
| } |
| |
| override uint alignsize() |
| { |
| return cast(uint)basetype.size(); |
| } |
| |
| 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() |
| { |
| return false; |
| } |
| |
| override MATCH implicitConvTo(Type to) |
| { |
| //printf("TypeVector::implicitConvTo(%s) from %s\n", to.toChars(), toChars()); |
| if (this == to) |
| return MATCH.exact; |
| if (to.ty != Tvector) |
| return MATCH.nomatch; |
| |
| TypeVector tv = cast(TypeVector)to; |
| assert(basetype.ty == Tsarray && tv.basetype.ty == Tsarray); |
| |
| // Can't convert to a vector which has different size. |
| if (basetype.size() != tv.basetype.size()) |
| return MATCH.nomatch; |
| |
| // Allow conversion to void[] |
| if (tv.basetype.nextOf().ty == Tvoid) |
| return MATCH.convert; |
| |
| // Otherwise implicitly convertible only if basetypes are. |
| return basetype.implicitConvTo(tv.basetype); |
| } |
| |
| override Expression defaultInitLiteral(const ref 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(const ref Loc loc) |
| { |
| return basetype.isZeroInit(loc); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) abstract class TypeArray : TypeNext |
| { |
| final extern (D) this(TY ty, Type next) |
| { |
| super(ty, next); |
| } |
| |
| 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; |
| } |
| |
| extern (D) this(Type t) // for incomplete type |
| { |
| super(Tsarray, t); |
| //printf("TypeSArray()\n"); |
| this.dim = new IntegerExp(0); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "sarray"; |
| } |
| |
| override TypeSArray syntaxCopy() |
| { |
| Type t = next.syntaxCopy(); |
| Expression e = dim.syntaxCopy(); |
| auto result = new TypeSArray(t, e); |
| result.mod = mod; |
| return result; |
| } |
| |
| /*** |
| * C11 6.7.6.2-4 incomplete array type |
| * Returns: true if incomplete type |
| */ |
| bool isIncomplete() |
| { |
| return dim.isIntegerExp() && dim.isIntegerExp().getInteger() == 0; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| //printf("TypeSArray::size()\n"); |
| const n = numberOfElems(loc); |
| const elemsize = baseElemOf().size(loc); |
| bool overflow = false; |
| const sz = mulu(n, elemsize, overflow); |
| if (overflow || sz >= uint.max) |
| { |
| if (elemsize != SIZE_INVALID && n != uint.max) |
| error(loc, "static array `%s` size overflowed to %lld", toChars(), cast(long)sz); |
| return SIZE_INVALID; |
| } |
| return sz; |
| } |
| |
| override uint alignsize() |
| { |
| return next.alignsize(); |
| } |
| |
| override bool isString() |
| { |
| TY nty = next.toBasetype().ty; |
| return nty.isSomeChar; |
| } |
| |
| override bool isZeroInit(const ref Loc loc) |
| { |
| return next.isZeroInit(loc); |
| } |
| |
| override structalign_t alignment() |
| { |
| return next.alignment(); |
| } |
| |
| override MATCH constConv(Type to) |
| { |
| if (auto tsa = to.isTypeSArray()) |
| { |
| if (!dim.equals(tsa.dim)) |
| return MATCH.nomatch; |
| } |
| return TypeNext.constConv(to); |
| } |
| |
| override MATCH implicitConvTo(Type to) |
| { |
| //printf("TypeSArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), toChars()); |
| if (auto ta = to.isTypeDArray()) |
| { |
| if (!MODimplicitConv(next.mod, ta.next.mod)) |
| return MATCH.nomatch; |
| |
| /* Allow conversion to void[] |
| */ |
| if (ta.next.ty == Tvoid) |
| { |
| return MATCH.convert; |
| } |
| |
| MATCH m = next.constConv(ta.next); |
| if (m > MATCH.nomatch) |
| { |
| return MATCH.convert; |
| } |
| return MATCH.nomatch; |
| } |
| if (auto tsa = to.isTypeSArray()) |
| { |
| if (this == to) |
| return MATCH.exact; |
| |
| if (dim.equals(tsa.dim)) |
| { |
| MATCH m = next.implicitConvTo(tsa.next); |
| |
| /* Allow conversion to non-interface base class. |
| */ |
| if (m == MATCH.convert && |
| next.ty == Tclass) |
| { |
| if (auto toc = tsa.next.isTypeClass) |
| { |
| if (!toc.sym.isInterfaceDeclaration) |
| return MATCH.convert; |
| } |
| } |
| |
| /* 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. |
| */ |
| if (m >= MATCH.constant) |
| { |
| if (mod != to.mod) |
| m = MATCH.constant; |
| return m; |
| } |
| } |
| } |
| return MATCH.nomatch; |
| } |
| |
| override Expression defaultInitLiteral(const ref 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(d); |
| foreach (ref e; *elements) |
| e = null; |
| auto ae = new ArrayLiteralExp(Loc.initial, this, elementinit, elements); |
| return ae; |
| } |
| |
| 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 hasSystemFields() |
| { |
| return next.hasSystemFields(); |
| } |
| |
| override bool hasVoidInitPointers() |
| { |
| return next.hasVoidInitPointers(); |
| } |
| |
| override bool hasInvariant() |
| { |
| return next.hasInvariant(); |
| } |
| |
| override bool needsDestruction() |
| { |
| return next.needsDestruction(); |
| } |
| |
| override bool needsCopyOrPostblit() |
| { |
| return next.needsCopyOrPostblit(); |
| } |
| |
| /********************************* |
| * |
| */ |
| 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 TypeDArray syntaxCopy() |
| { |
| Type t = next.syntaxCopy(); |
| if (t == next) |
| return this; |
| |
| auto result = new TypeDArray(t); |
| result.mod = mod; |
| return result; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| //printf("TypeDArray::size()\n"); |
| return target.ptrsize * 2; |
| } |
| |
| override uint alignsize() |
| { |
| // A DArray consists of two ptr-sized values, so align it on pointer size |
| // boundary |
| return target.ptrsize; |
| } |
| |
| override bool isString() |
| { |
| TY nty = next.toBasetype().ty; |
| return nty.isSomeChar; |
| } |
| |
| override bool isZeroInit(const ref Loc loc) |
| { |
| return true; |
| } |
| |
| override bool isBoolean() |
| { |
| return true; |
| } |
| |
| override MATCH implicitConvTo(Type to) |
| { |
| //printf("TypeDArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), toChars()); |
| if (equals(to)) |
| return MATCH.exact; |
| |
| if (auto ta = to.isTypeDArray()) |
| { |
| if (!MODimplicitConv(next.mod, ta.next.mod)) |
| return MATCH.nomatch; // not const-compatible |
| |
| /* Allow conversion to void[] |
| */ |
| if (next.ty != Tvoid && ta.next.ty == Tvoid) |
| { |
| return MATCH.convert; |
| } |
| |
| MATCH m = next.constConv(ta.next); |
| if (m > MATCH.nomatch) |
| { |
| if (m == MATCH.exact && mod != to.mod) |
| m = MATCH.constant; |
| return m; |
| } |
| } |
| return Type.implicitConvTo(to); |
| } |
| |
| override bool hasPointers() |
| { |
| return true; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class TypeAArray : TypeArray |
| { |
| Type index; // key type |
| Loc loc; |
| |
| 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 TypeAArray syntaxCopy() |
| { |
| Type t = next.syntaxCopy(); |
| Type ti = index.syntaxCopy(); |
| if (t == next && ti == index) |
| return this; |
| |
| auto result = new TypeAArray(t, ti); |
| result.mod = mod; |
| return result; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return target.ptrsize; |
| } |
| |
| override bool isZeroInit(const ref Loc loc) |
| { |
| return true; |
| } |
| |
| override bool isBoolean() |
| { |
| return true; |
| } |
| |
| override bool hasPointers() |
| { |
| return true; |
| } |
| |
| override MATCH implicitConvTo(Type to) |
| { |
| //printf("TypeAArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), toChars()); |
| if (equals(to)) |
| return MATCH.exact; |
| |
| if (auto ta = to.isTypeAArray()) |
| { |
| if (!MODimplicitConv(next.mod, ta.next.mod)) |
| return MATCH.nomatch; // not const-compatible |
| |
| if (!MODimplicitConv(index.mod, ta.index.mod)) |
| return MATCH.nomatch; // not const-compatible |
| |
| MATCH m = next.constConv(ta.next); |
| MATCH mi = index.constConv(ta.index); |
| if (m > MATCH.nomatch && mi > MATCH.nomatch) |
| { |
| return MODimplicitConv(mod, to.mod) ? MATCH.constant : MATCH.nomatch; |
| } |
| } |
| return Type.implicitConvTo(to); |
| } |
| |
| override MATCH constConv(Type to) |
| { |
| if (auto taa = to.isTypeAArray()) |
| { |
| 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); |
| } |
| |
| static TypePointer create(Type t) |
| { |
| return new TypePointer(t); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "pointer"; |
| } |
| |
| override TypePointer syntaxCopy() |
| { |
| Type t = next.syntaxCopy(); |
| if (t == next) |
| return this; |
| |
| auto result = new TypePointer(t); |
| result.mod = mod; |
| return result; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return target.ptrsize; |
| } |
| |
| override MATCH implicitConvTo(Type to) |
| { |
| //printf("TypePointer::implicitConvTo(to = %s) %s\n", to.toChars(), toChars()); |
| if (equals(to)) |
| return MATCH.exact; |
| |
| // Only convert between pointers |
| auto tp = to.isTypePointer(); |
| if (!tp) |
| return MATCH.nomatch; |
| |
| assert(this.next); |
| assert(tp.next); |
| |
| // Conversion to void* |
| if (tp.next.ty == Tvoid) |
| { |
| // Function pointer conversion doesn't check constness? |
| if (this.next.ty == Tfunction) |
| return MATCH.convert; |
| |
| if (!MODimplicitConv(next.mod, tp.next.mod)) |
| return MATCH.nomatch; // not const-compatible |
| |
| return this.next.ty == Tvoid ? MATCH.constant : MATCH.convert; |
| } |
| |
| // Conversion between function pointers |
| if (auto thisTf = this.next.isTypeFunction()) |
| return thisTf.implicitPointerConv(tp.next); |
| |
| // Default, no implicit conversion between the pointer targets |
| MATCH m = next.constConv(tp.next); |
| |
| if (m == MATCH.exact && mod != to.mod) |
| m = MATCH.constant; |
| return m; |
| } |
| |
| override MATCH constConv(Type to) |
| { |
| if (next.ty == Tfunction) |
| { |
| if (to.nextOf() && next.equals((cast(TypeNext)to).next)) |
| return Type.constConv(to); |
| else |
| return MATCH.nomatch; |
| } |
| return TypeNext.constConv(to); |
| } |
| |
| override bool isscalar() |
| { |
| return true; |
| } |
| |
| override bool isZeroInit(const ref Loc loc) |
| { |
| return true; |
| } |
| |
| override bool hasPointers() |
| { |
| 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 TypeReference syntaxCopy() |
| { |
| Type t = next.syntaxCopy(); |
| if (t == next) |
| return this; |
| |
| auto result = new TypeReference(t); |
| result.mod = mod; |
| return result; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return target.ptrsize; |
| } |
| |
| override bool isZeroInit(const ref Loc loc) |
| { |
| return true; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| enum RET : int |
| { |
| regs = 1, // returned in registers |
| stack = 2, // returned on stack |
| } |
| |
| enum TRUSTformat : int |
| { |
| TRUSTformatDefault, // do not emit @system when trust == TRUST.default_ |
| TRUSTformatSystem, // emit @system when trust == TRUST.default_ |
| } |
| |
| alias TRUSTformatDefault = TRUSTformat.TRUSTformatDefault; |
| alias TRUSTformatSystem = TRUSTformat.TRUSTformatSystem; |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class TypeFunction : TypeNext |
| { |
| // .next is the return type |
| |
| ParameterList parameterList; // function parameters |
| |
| // These flags can be accessed like `bool` properties, |
| // getters and setters are generated for them |
| private extern (D) static struct BitFields |
| { |
| bool isnothrow; /// nothrow |
| bool isnogc; /// is @nogc |
| bool isproperty; /// can be called without parentheses |
| bool isref; /// returns a reference |
| bool isreturn; /// 'this' is returned by ref |
| bool isScopeQual; /// 'this' is scope |
| bool isreturninferred; /// 'this' is return from inference |
| bool isscopeinferred; /// 'this' is scope from inference |
| bool islive; /// is @live |
| bool incomplete; /// return type or default arguments removed |
| bool isInOutParam; /// inout on the parameters |
| bool isInOutQual; /// inout on the qualifier |
| bool isctor; /// the function is a constructor |
| bool isreturnscope; /// `this` is returned by value |
| } |
| |
| import dmd.common.bitfields : generateBitFields; |
| mixin(generateBitFields!(BitFields, ushort)); |
| |
| LINK linkage; // calling convention |
| TRUST trust; // level of trust |
| PURE purity = PURE.impure; |
| byte inuse; |
| Expressions* fargs; // function arguments |
| |
| extern (D) this(ParameterList pl, Type treturn, LINK linkage, StorageClass stc = 0) |
| { |
| super(Tfunction, treturn); |
| //if (!treturn) *(char*)0=0; |
| // assert(treturn); |
| assert(VarArg.none <= pl.varargs && pl.varargs <= VarArg.typesafe); |
| this.parameterList = pl; |
| this.linkage = linkage; |
| |
| if (stc & STC.pure_) |
| this.purity = PURE.fwdref; |
| if (stc & STC.nothrow_) |
| this.isnothrow = true; |
| if (stc & STC.nogc) |
| this.isnogc = true; |
| if (stc & STC.property) |
| this.isproperty = true; |
| if (stc & STC.live) |
| this.islive = true; |
| |
| if (stc & STC.ref_) |
| this.isref = true; |
| if (stc & STC.return_) |
| this.isreturn = true; |
| if (stc & STC.returnScope) |
| this.isreturnscope = true; |
| if (stc & STC.returninferred) |
| this.isreturninferred = true; |
| if (stc & STC.scope_) |
| this.isScopeQual = true; |
| if (stc & STC.scopeinferred) |
| this.isscopeinferred = true; |
| |
| this.trust = TRUST.default_; |
| if (stc & STC.safe) |
| this.trust = TRUST.safe; |
| else if (stc & STC.system) |
| this.trust = TRUST.system; |
| else if (stc & STC.trusted) |
| this.trust = TRUST.trusted; |
| } |
| |
| static TypeFunction create(Parameters* parameters, Type treturn, ubyte varargs, LINK linkage, StorageClass stc = 0) |
| { |
| return new TypeFunction(ParameterList(parameters, cast(VarArg)varargs), treturn, linkage, stc); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "function"; |
| } |
| |
| override TypeFunction syntaxCopy() |
| { |
| Type treturn = next ? next.syntaxCopy() : null; |
| auto t = new TypeFunction(parameterList.syntaxCopy(), treturn, linkage); |
| t.mod = mod; |
| t.isnothrow = isnothrow; |
| t.isnogc = isnogc; |
| t.islive = islive; |
| t.purity = purity; |
| t.isproperty = isproperty; |
| t.isref = isref; |
| t.isreturn = isreturn; |
| t.isreturnscope = isreturnscope; |
| t.isScopeQual = isScopeQual; |
| t.isreturninferred = isreturninferred; |
| t.isscopeinferred = isscopeinferred; |
| t.isInOutParam = isInOutParam; |
| t.isInOutQual = isInOutQual; |
| t.trust = trust; |
| t.fargs = fargs; |
| t.isctor = isctor; |
| return t; |
| } |
| |
| /******************************************** |
| * Set 'purity' field of 'this'. |
| * Do this lazily, as the parameter types might be forward referenced. |
| */ |
| void purityLevel() |
| { |
| TypeFunction tf = this; |
| if (tf.purity != PURE.fwdref) |
| return; |
| |
| purity = PURE.const_; // assume strong until something weakens it |
| |
| /* Evaluate what kind of purity based on the modifiers for the parameters |
| */ |
| foreach (i, fparam; tf.parameterList) |
| { |
| Type t = fparam.type; |
| if (!t) |
| continue; |
| |
| if (fparam.storageClass & (STC.lazy_ | STC.out_)) |
| { |
| purity = PURE.weak; |
| break; |
| } |
| const pref = (fparam.storageClass & STC.ref_) != 0; |
| if (mutabilityOfType(pref, t) == 0) |
| purity = PURE.weak; |
| } |
| |
| tf.purity = purity; |
| } |
| |
| /******************************************** |
| * Return true if there are lazy parameters. |
| */ |
| bool hasLazyParameters() |
| { |
| foreach (i, fparam; parameterList) |
| { |
| if (fparam.isLazy()) |
| return true; |
| } |
| return false; |
| } |
| |
| /******************************* |
| * Check for `extern (D) U func(T t, ...)` variadic function type, |
| * which has `_arguments[]` added as the first argument. |
| * Returns: |
| * true if D-style variadic |
| */ |
| bool isDstyleVariadic() const pure nothrow |
| { |
| return linkage == LINK.d && parameterList.varargs == VarArg.variadic; |
| } |
| |
| /************************************ |
| * Take the specified storage class for p, |
| * and use the function signature to infer whether |
| * STC.scope_ and STC.return_ should be OR'd in. |
| * (This will not affect the name mangling.) |
| * Params: |
| * tthis = type of `this` parameter, null if none |
| * p = parameter to this function |
| * Returns: |
| * storage class with STC.scope_ or STC.return_ OR'd in |
| */ |
| StorageClass parameterStorageClass(Type tthis, Parameter p) |
| { |
| //printf("parameterStorageClass(p: %s)\n", p.toChars()); |
| auto stc = p.storageClass; |
| |
| // When the preview switch is enable, `in` parameters are `scope` |
| if (stc & STC.in_ && global.params.previewIn) |
| return stc | STC.scope_; |
| |
| if (stc & (STC.scope_ | STC.return_ | STC.lazy_) || purity == PURE.impure) |
| return stc; |
| |
| /* If haven't inferred the return type yet, can't infer storage classes |
| */ |
| if (!nextOf() || !isnothrow()) |
| return stc; |
| |
| purityLevel(); |
| |
| static bool mayHavePointers(Type t) |
| { |
| if (auto ts = t.isTypeStruct()) |
| { |
| auto sym = ts.sym; |
| if (sym.members && !sym.determineFields() && sym.type != Type.terror) |
| // struct is forward referenced, so "may have" pointers |
| return true; |
| } |
| return t.hasPointers(); |
| } |
| |
| // See if p can escape via any of the other parameters |
| if (purity == PURE.weak) |
| { |
| // Check escaping through parameters |
| foreach (i, fparam; parameterList) |
| { |
| Type t = fparam.type; |
| if (!t) |
| continue; |
| t = t.baseElemOf(); // punch thru static arrays |
| if (t.isMutable() && t.hasPointers()) |
| { |
| if (fparam.isReference() && fparam != p) |
| return stc; |
| |
| if (t.ty == Tdelegate) |
| return stc; // could escape thru delegate |
| |
| if (t.ty == Tclass) |
| return stc; |
| |
| /* if t is a pointer to mutable pointer |
| */ |
| if (auto tn = t.nextOf()) |
| { |
| if (tn.isMutable() && mayHavePointers(tn)) |
| return stc; // escape through pointers |
| } |
| } |
| } |
| |
| // Check escaping through `this` |
| if (tthis && tthis.isMutable()) |
| { |
| foreach (VarDeclaration v; isAggregate(tthis).fields) |
| { |
| if (v.hasPointers()) |
| return stc; |
| } |
| } |
| } |
| |
| // Check escaping through return value |
| Type tret = nextOf().toBasetype(); |
| if (isref || tret.hasPointers()) |
| { |
| return stc | STC.scope_ | STC.return_ | STC.returnScope; |
| } |
| else |
| return stc | STC.scope_; |
| } |
| |
| override Type addStorageClass(StorageClass stc) |
| { |
| //printf("addStorageClass(%llx) %d\n", stc, (stc & STC.scope_) != 0); |
| TypeFunction t = Type.addStorageClass(stc).toTypeFunction(); |
| if ((stc & STC.pure_ && !t.purity) || |
| (stc & STC.nothrow_ && !t.isnothrow) || |
| (stc & STC.nogc && !t.isnogc) || |
| (stc & STC.scope_ && !t.isScopeQual) || |
| (stc & STC.safe && t.trust < TRUST.trusted)) |
| { |
| // Klunky to change these |
| auto tf = new TypeFunction(t.parameterList, t.next, t.linkage, 0); |
| tf.mod = t.mod; |
| tf.fargs = fargs; |
| tf.purity = t.purity; |
| tf.isnothrow = t.isnothrow; |
| tf.isnogc = t.isnogc; |
| tf.isproperty = t.isproperty; |
| tf.isref = t.isref; |
| tf.isreturn = t.isreturn; |
| tf.isreturnscope = t.isreturnscope; |
| tf.isScopeQual = t.isScopeQual; |
| tf.isreturninferred = t.isreturninferred; |
| tf.isscopeinferred = t.isscopeinferred; |
| tf.trust = t.trust; |
| tf.isInOutParam = t.isInOutParam; |
| tf.isInOutQual = t.isInOutQual; |
| tf.isctor = t.isctor; |
| |
| if (stc & STC.pure_) |
| tf.purity = PURE.fwdref; |
| if (stc & STC.nothrow_) |
| tf.isnothrow = true; |
| if (stc & STC.nogc) |
| tf.isnogc = true; |
| if (stc & STC.safe) |
| tf.trust = TRUST.safe; |
| if (stc & STC.scope_) |
| { |
| tf.isScopeQual = true; |
| if (stc & STC.scopeinferred) |
| tf.isscopeinferred = true; |
| } |
| |
| tf.deco = tf.merge().deco; |
| t = tf; |
| } |
| return t; |
| } |
| |
| override Type substWildTo(uint) |
| { |
| if (!iswild && !(mod & MODFlags.wild)) |
| 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 = MODFlags.const_; |
| |
| assert(next); |
| Type tret = next.substWildTo(m); |
| Parameters* params = parameterList.parameters; |
| if (mod & MODFlags.wild) |
| params = parameterList.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 == parameterList.parameters) |
| params = parameterList.parameters.copy(); |
| (*params)[i] = new Parameter(p.storageClass, t, null, null, null); |
| } |
| if (next == tret && params == parameterList.parameters) |
| return this; |
| |
| // Similar to TypeFunction::syntaxCopy; |
| auto t = new TypeFunction(ParameterList(params, parameterList.varargs), tret, linkage); |
| t.mod = ((mod & MODFlags.wild) ? (mod & ~MODFlags.wild) | MODFlags.const_ : mod); |
| t.isnothrow = isnothrow; |
| t.isnogc = isnogc; |
| t.purity = purity; |
| t.isproperty = isproperty; |
| t.isref = isref; |
| t.isreturn = isreturn; |
| t.isreturnscope = isreturnscope; |
| t.isScopeQual = isScopeQual; |
| t.isreturninferred = isreturninferred; |
| t.isscopeinferred = isscopeinferred; |
| t.isInOutParam = false; |
| t.isInOutQual = false; |
| t.trust = trust; |
| t.fargs = fargs; |
| t.isctor = isctor; |
| return t.merge(); |
| } |
| |
| // arguments get specially formatted |
| private const(char)* getParamError(Expression arg, Parameter par) |
| { |
| if (global.gag && !global.params.showGaggedErrors) |
| return null; |
| // show qualification when toChars() is the same but types are different |
| // https://issues.dlang.org/show_bug.cgi?id=19948 |
| // when comparing the type with strcmp, we need to drop the qualifier |
| auto at = arg.type.mutableOf().toChars(); |
| bool qual = !arg.type.equals(par.type) && strcmp(at, par.type.mutableOf().toChars()) == 0; |
| if (qual) |
| at = arg.type.toPrettyChars(true); |
| OutBuffer buf; |
| // only mention rvalue if it's relevant |
| const rv = !arg.isLvalue() && par.isReference(); |
| buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`", |
| rv ? "rvalue ".ptr : "".ptr, arg.toChars(), at, |
| parameterToChars(par, this, qual)); |
| return buf.extractChars(); |
| } |
| |
| private extern(D) const(char)* getMatchError(A...)(const(char)* format, A args) |
| { |
| if (global.gag && !global.params.showGaggedErrors) |
| return null; |
| OutBuffer buf; |
| buf.printf(format, args); |
| return buf.extractChars(); |
| } |
| |
| /******************************** |
| * 'args' are being matched to function 'this' |
| * Determine match level. |
| * Params: |
| * tthis = type of `this` pointer, null if not member function |
| * args = array of function arguments |
| * flag = 1: performing a partial ordering match |
| * pMessage = address to store error message, or null |
| * sc = context |
| * Returns: |
| * MATCHxxxx |
| */ |
| extern (D) MATCH callMatch(Type tthis, Expression[] args, int flag = 0, const(char)** pMessage = null, Scope* sc = null) |
| { |
| //printf("TypeFunction::callMatch() %s\n", toChars()); |
| MATCH match = MATCH.exact; // 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 = MATCH.constant; |
| else if ((mod & MODFlags.wild) && MODimplicitConv(t.mod, (mod & ~MODFlags.wild) | MODFlags.const_)) |
| { |
| match = MATCH.constant; |
| } |
| else |
| return MATCH.nomatch; |
| } |
| if (isWild()) |
| { |
| if (t.isWild()) |
| wildmatch |= MODFlags.wild; |
| else if (t.isConst()) |
| wildmatch |= MODFlags.const_; |
| else if (t.isImmutable()) |
| wildmatch |= MODFlags.immutable_; |
| else |
| wildmatch |= MODFlags.mutable; |
| } |
| } |
| |
| const nparams = parameterList.length; |
| const nargs = args.length; |
| if (nargs > nparams) |
| { |
| if (parameterList.varargs == VarArg.none) |
| { |
| // suppress early exit if an error message is wanted, |
| // so we can check any matching args are valid |
| if (!pMessage) |
| return MATCH.nomatch; |
| } |
| // too many args; no match |
| match = MATCH.convert; // match ... with a "conversion" match level |
| } |
| |
| // https://issues.dlang.org/show_bug.cgi?id=22997 |
| if (parameterList.varargs == VarArg.none && nparams > nargs && !parameterList[nargs].defaultArg) |
| { |
| OutBuffer buf; |
| buf.printf("too few arguments, expected `%d`, got `%d`", cast(int)nparams, cast(int)nargs); |
| if (pMessage) |
| *pMessage = buf.extractChars(); |
| return MATCH.nomatch; |
| } |
| |
| foreach (u, p; parameterList) |
| { |
| if (u == nargs) |
| break; |
| |
| Expression arg = args[u]; |
| assert(arg); |
| Type tprm = p.type; |
| Type targ = arg.type; |
| |
| if (!(p.isLazy() && tprm.ty == Tvoid && targ.ty != Tvoid)) |
| { |
| const isRef = p.isReference(); |
| wildmatch |= targ.deduceWild(tprm, isRef); |
| } |
| } |
| if (wildmatch) |
| { |
| /* Calculate wild matching modifier |
| */ |
| if (wildmatch & MODFlags.const_ || wildmatch & (wildmatch - 1)) |
| wildmatch = MODFlags.const_; |
| else if (wildmatch & MODFlags.immutable_) |
| wildmatch = MODFlags.immutable_; |
| else if (wildmatch & MODFlags.wild) |
| wildmatch = MODFlags.wild; |
| else |
| { |
| assert(wildmatch & MODFlags.mutable); |
| wildmatch = MODFlags.mutable; |
| } |
| } |
| |
| foreach (u, p; parameterList) |
| { |
| MATCH m; |
| |
| assert(p); |
| |
| // One or more arguments remain |
| if (u < nargs) |
| { |
| Expression arg = args[u]; |
| assert(arg); |
| m = argumentMatchParameter(this, p, arg, wildmatch, flag, sc, pMessage); |
| } |
| else if (p.defaultArg) |
| continue; |
| |
| /* prefer matching the element type rather than the array |
| * type when more arguments are present with T[]... |
| */ |
| if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams && nargs > nparams) |
| goto L1; |
| |
| //printf("\tm = %d\n", m); |
| if (m == MATCH.nomatch) // if no match |
| { |
| L1: |
| if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams) // if last varargs param |
| { |
| auto trailingArgs = args[u .. $]; |
| if (auto vmatch = matchTypeSafeVarArgs(this, p, trailingArgs, pMessage)) |
| return vmatch < match ? vmatch : match; |
| // Error message was already generated in `matchTypeSafeVarArgs` |
| return MATCH.nomatch; |
| } |
| if (pMessage && u >= nargs) |
| *pMessage = getMatchError("missing argument for parameter #%d: `%s`", |
| u + 1, parameterToChars(p, this, false)); |
| // If an error happened previously, `pMessage` was already filled |
| else if (pMessage && !*pMessage) |
| *pMessage = getParamError(args[u], p); |
| |
| return MATCH.nomatch; |
| } |
| if (m < match) |
| match = m; // pick worst match |
| } |
| |
| if (pMessage && !parameterList.varargs && nargs > nparams) |
| { |
| // all parameters had a match, but there are surplus args |
| *pMessage = getMatchError("expected %d argument(s), not %d", nparams, nargs); |
| return MATCH.nomatch; |
| } |
| //printf("match = %d\n", match); |
| return match; |
| } |
| |
| /+ |
| + Checks whether this function type is convertible to ` to` |
| + when used in a function pointer / delegate. |
| + |
| + Params: |
| + to = target type |
| + |
| + Returns: |
| + MATCH.nomatch: `to` is not a covaraint function |
| + MATCH.convert: `to` is a covaraint function |
| + MATCH.exact: `to` is identical to this function |
| +/ |
| private MATCH implicitPointerConv(Type to) |
| { |
| assert(to); |
| |
| if (this.equals(to)) |
| return MATCH.constant; |
| |
| if (this.covariant(to) == Covariant.yes) |
| { |
| Type tret = this.nextOf(); |
| Type toret = to.nextOf(); |
| if (tret.ty == Tclass && toret.ty == Tclass) |
| { |
| /* https://issues.dlang.org/show_bug.cgi?id=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 MATCH.nomatch; |
| } |
| return MATCH.convert; |
| } |
| |
| return MATCH.nomatch; |
| } |
| |
| /** Extends TypeNext.constConv by also checking for matching attributes **/ |
| override MATCH constConv(Type to) |
| { |
| // Attributes need to match exactly, otherwise it's an implicit conversion |
| if (this.ty != to.ty || !this.attributesEqual(cast(TypeFunction) to)) |
| return MATCH.nomatch; |
| |
| return super.constConv(to); |
| } |
| |
| extern (D) bool checkRetType(const ref 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)) |
| { |
| if (auto ts = tb.baseElemOf().isTypeStruct()) |
| { |
| if (!ts.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; |
| } |
| |
| |
| /// Returns: `true` the function is `isInOutQual` or `isInOutParam` ,`false` otherwise. |
| bool iswild() const pure nothrow @safe @nogc |
| { |
| return isInOutParam || isInOutQual; |
| } |
| |
| /// Returns: whether `this` function type has the same attributes (`@safe`,...) as `other` |
| bool attributesEqual(const scope TypeFunction other) const pure nothrow @safe @nogc |
| { |
| return this.trust == other.trust && |
| this.purity == other.purity && |
| this.isnothrow == other.isnothrow && |
| this.isnogc == other.isnogc && |
| this.islive == other.islive; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class TypeDelegate : TypeNext |
| { |
| // .next is a TypeFunction |
| |
| extern (D) this(TypeFunction t) |
| { |
| super(Tfunction, t); |
| ty = Tdelegate; |
| } |
| |
| static TypeDelegate create(TypeFunction t) |
| { |
| return new TypeDelegate(t); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "delegate"; |
| } |
| |
| override TypeDelegate syntaxCopy() |
| { |
| auto tf = next.syntaxCopy().isTypeFunction(); |
| if (tf == next) |
| return this; |
| |
| auto result = new TypeDelegate(tf); |
| result.mod = mod; |
| return result; |
| } |
| |
| override Type addStorageClass(StorageClass stc) |
| { |
| TypeDelegate t = cast(TypeDelegate)Type.addStorageClass(stc); |
| return t; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return target.ptrsize * 2; |
| } |
| |
| override uint alignsize() |
| { |
| 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.equals(to)) |
| return MATCH.exact; |
| |
| if (auto toDg = to.isTypeDelegate()) |
| { |
| MATCH m = this.next.isTypeFunction().implicitPointerConv(toDg.next); |
| |
| // Retain the old behaviour for this refactoring |
| // Should probably be changed to constant to match function pointers |
| if (m > MATCH.convert) |
| m = MATCH.convert; |
| |
| return m; |
| } |
| |
| return MATCH.nomatch; |
| } |
| |
| override bool isZeroInit(const ref Loc loc) |
| { |
| return true; |
| } |
| |
| override bool isBoolean() |
| { |
| return true; |
| } |
| |
| override bool hasPointers() |
| { |
| return true; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /** |
| * This is a shell containing a TraitsExp that can be |
| * either resolved to a type or to a symbol. |
| * |
| * The point is to allow AliasDeclarationY to use `__traits()`, see issue 7804. |
| */ |
| extern (C++) final class TypeTraits : Type |
| { |
| Loc loc; |
| /// The expression to resolve as type or symbol. |
| TraitsExp exp; |
| /// Cached type/symbol after semantic analysis. |
| RootObject obj; |
| |
| final extern (D) this(const ref Loc loc, TraitsExp exp) |
| { |
| super(Ttraits); |
| this.loc = loc; |
| this.exp = exp; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "traits"; |
| } |
| |
| override TypeTraits syntaxCopy() |
| { |
| TraitsExp te = exp.syntaxCopy(); |
| TypeTraits tt = new TypeTraits(loc, te); |
| tt.mod = mod; |
| return tt; |
| } |
| |
| override Dsymbol toDsymbol(Scope* sc) |
| { |
| Type t; |
| Expression e; |
| Dsymbol s; |
| resolve(this, loc, sc, e, t, s); |
| if (t && t.ty != Terror) |
| s = t.toDsymbol(sc); |
| else if (e) |
| s = getDsymbol(e); |
| |
| return s; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return SIZE_INVALID; |
| } |
| } |
| |
| /****** |
| * Implements mixin types. |
| * |
| * Semantic analysis will convert it to a real type. |
| */ |
| extern (C++) final class TypeMixin : Type |
| { |
| Loc loc; |
| Expressions* exps; |
| RootObject obj; // cached result of semantic analysis. |
| |
| extern (D) this(const ref Loc loc, Expressions* exps) |
| { |
| super(Tmixin); |
| this.loc = loc; |
| this.exps = exps; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "mixin"; |
| } |
| |
| override TypeMixin syntaxCopy() |
| { |
| return new TypeMixin(loc, Expression.arraySyntaxCopy(exps)); |
| } |
| |
| override Dsymbol toDsymbol(Scope* sc) |
| { |
| Type t; |
| Expression e; |
| Dsymbol s; |
| resolve(this, loc, sc, e, t, s); |
| if (t) |
| s = t.toDsymbol(sc); |
| else if (e) |
| s = getDsymbol(e); |
| |
| return s; |
| } |
| |
| 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; |
| } |
| |
| // abstract override so that using `TypeQualified.syntaxCopy` gets |
| // us a `TypeQualified` |
| abstract override TypeQualified syntaxCopy(); |
| |
| 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]; |
| with (DYNCAST) final switch (id.dyncast()) |
| { |
| case object: |
| break; |
| case expression: |
| Expression e = cast(Expression)id; |
| e = e.syntaxCopy(); |
| id = e; |
| break; |
| case dsymbol: |
| TemplateInstance ti = cast(TemplateInstance)id; |
| ti = ti.syntaxCopy(null); |
| id = ti; |
| break; |
| case type: |
| Type tx = cast(Type)id; |
| tx = tx.syntaxCopy(); |
| id = tx; |
| break; |
| case identifier: |
| case tuple: |
| case parameter: |
| case statement: |
| case condition: |
| case templateparameter: |
| case initializer: |
| } |
| 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 uinteger_t size(const ref Loc loc) |
| { |
| error(this.loc, "size of type `%s` is not known", toChars()); |
| return SIZE_INVALID; |
| } |
| |
| 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(const ref Loc loc, Identifier ident) |
| { |
| super(Tident, loc); |
| this.ident = ident; |
| } |
| |
| static TypeIdentifier create(const ref Loc loc, Identifier ident) |
| { |
| return new TypeIdentifier(loc, ident); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "identifier"; |
| } |
| |
| override TypeIdentifier syntaxCopy() |
| { |
| auto t = new TypeIdentifier(loc, ident); |
| t.syntaxCopyHelper(this); |
| t.mod = mod; |
| return t; |
| } |
| |
| /***************************************** |
| * 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(this, loc, sc, e, t, s); |
| if (t && t.ty != Tident) |
| s = t.toDsymbol(sc); |
| if (e) |
| s = getDsymbol(e); |
| |
| return s; |
| } |
| |
| 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(const ref Loc loc, TemplateInstance tempinst) |
| { |
| super(Tinstance, loc); |
| this.tempinst = tempinst; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "instance"; |
| } |
| |
| override TypeInstance syntaxCopy() |
| { |
| //printf("TypeInstance::syntaxCopy() %s, %d\n", toChars(), idents.dim); |
| auto t = new TypeInstance(loc, tempinst.syntaxCopy(null)); |
| t.syntaxCopyHelper(this); |
| t.mod = mod; |
| return t; |
| } |
| |
| override Dsymbol toDsymbol(Scope* sc) |
| { |
| Type t; |
| Expression e; |
| Dsymbol s; |
| //printf("TypeInstance::semantic(%s)\n", toChars()); |
| resolve(this, loc, sc, e, t, s); |
| if (t && t.ty != Tinstance) |
| s = t.toDsymbol(sc); |
| return s; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class TypeTypeof : TypeQualified |
| { |
| Expression exp; |
| int inuse; |
| |
| extern (D) this(const ref Loc loc, Expression exp) |
| { |
| super(Ttypeof, loc); |
| this.exp = exp; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "typeof"; |
| } |
| |
| override TypeTypeof 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(this, loc, sc, e, t, s); |
| return s; |
| } |
| |
| override uinteger_t size(const ref 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(const ref Loc loc) |
| { |
| super(Treturn, loc); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "return"; |
| } |
| |
| override TypeReturn 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(this, loc, sc, e, t, s); |
| return s; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class TypeStruct : Type |
| { |
| StructDeclaration sym; |
| AliasThisRec att = AliasThisRec.fwdref; |
| bool inuse = false; // struct currently subject of recursive method call |
| |
| extern (D) this(StructDeclaration sym) |
| { |
| super(Tstruct); |
| this.sym = sym; |
| } |
| |
| static TypeStruct create(StructDeclaration sym) |
| { |
| return new TypeStruct(sym); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "struct"; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return sym.size(loc); |
| } |
| |
| override uint alignsize() |
| { |
| sym.size(Loc.initial); // give error for forward references |
| return sym.alignsize; |
| } |
| |
| override TypeStruct syntaxCopy() |
| { |
| return this; |
| } |
| |
| override Dsymbol toDsymbol(Scope* sc) |
| { |
| return sym; |
| } |
| |
| override structalign_t alignment() |
| { |
| if (sym.alignment.isUnknown()) |
| sym.size(sym.loc); |
| return sym.alignment; |
| } |
| |
| /*************************************** |
| * Use when we prefer the default initializer to be a literal, |
| * rather than a global immutable variable. |
| */ |
| override Expression defaultInitLiteral(const ref Loc loc) |
| { |
| static if (LOGDEFAULTINIT) |
| { |
| printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars()); |
| } |
| sym.size(loc); |
| if (sym.sizeok != Sizeok.done) |
| return ErrorExp.get(); |
| |
| auto structelems = new Expressions(sym.nonHiddenFields()); |
| uint offset = 0; |
| foreach (j; 0 .. structelems.dim) |
| { |
| VarDeclaration vd = sym.fields[j]; |
| Expression e; |
| if (vd.inuse) |
| { |
| error(loc, "circular reference to `%s`", vd.toPrettyChars()); |
| return ErrorExp.get(); |
| } |
| 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 == EXP.error) |
| 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(const ref Loc loc) |
| { |
| // Determine zeroInit here, as this can be called before semantic2 |
| sym.determineSize(sym.loc); |
| return sym.zeroInit; |
| } |
| |
| override bool isAssignable() |
| { |
| bool assignable = true; |
| uint offset = ~0; // dead-store initialize to prevent spurious warning |
| |
| sym.determineSize(sym.loc); |
| |
| /* 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\n", 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() |
| { |
| return false; |
| } |
| |
| override bool needsDestruction() |
| { |
| return sym.dtor !is null; |
| } |
| |
| override bool needsCopyOrPostblit() |
| { |
| return sym.hasCopyCtor || sym.postblit; |
| } |
| |
| override bool needsNested() |
| { |
| if (inuse) return false; // circular type, error instead of crashing |
| |
| inuse = true; |
| scope(exit) inuse = false; |
| |
| 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() |
| { |
| if (sym.members && !sym.determineFields() && sym.type != Type.terror) |
| error(sym.loc, "no size because of forward references"); |
| |
| sym.determineTypeProperties(); |
| return sym.hasPointerField; |
| } |
| |
| override bool hasVoidInitPointers() |
| { |
| sym.size(Loc.initial); // give error for forward references |
| sym.determineTypeProperties(); |
| return sym.hasVoidInitPointers; |
| } |
| |
| override bool hasSystemFields() |
| { |
| sym.size(Loc.initial); // give error for forward references |
| sym.determineTypeProperties(); |
| return sym.hasSystemFields; |
| } |
| |
| override bool hasInvariant() |
| { |
| sym.size(Loc.initial); // give error for forward references |
| sym.determineTypeProperties(); |
| return sym.hasInvariant() || sym.hasFieldWithInvariant; |
| } |
| |
| extern (D) MATCH implicitConvToWithoutAliasThis(Type to) |
| { |
| MATCH m; |
| |
| if (ty == to.ty && sym == (cast(TypeStruct)to).sym) |
| { |
| m = MATCH.exact; // exact match |
| if (mod != to.mod) |
| { |
| m = MATCH.constant; |
| 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 > MATCH.nomatch) |
| continue; |
| } |
| else |
| { |
| if (m == MATCH.nomatch) |
| 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 == MATCH.nomatch) |
| return mf; |
| if (mf < m) // if field match is worse |
| m = mf; |
| offset = v.offset; |
| } |
| } |
| } |
| } |
| return m; |
| } |
| |
| extern (D) MATCH implicitConvToThroughAliasThis(Type to) |
| { |
| MATCH m; |
| if (!(ty == to.ty && sym == (cast(TypeStruct)to).sym) && sym.aliasthis && !(att & AliasThisRec.tracing)) |
| { |
| if (auto ato = aliasthisOf()) |
| { |
| att = cast(AliasThisRec)(att | AliasThisRec.tracing); |
| m = ato.implicitConvTo(to); |
| att = cast(AliasThisRec)(att & ~AliasThisRec.tracing); |
| } |
| else |
| m = MATCH.nomatch; // no match |
| } |
| return m; |
| } |
| |
| override MATCH implicitConvTo(Type to) |
| { |
| //printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to.toChars()); |
| MATCH m = implicitConvToWithoutAliasThis(to); |
| return m ? m : implicitConvToThroughAliasThis(to); |
| } |
| |
| override MATCH constConv(Type to) |
| { |
| if (equals(to)) |
| return MATCH.exact; |
| if (ty == to.ty && sym == (cast(TypeStruct)to).sym && MODimplicitConv(mod, to.mod)) |
| return MATCH.constant; |
| return MATCH.nomatch; |
| } |
| |
| override MOD 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 & AliasThisRec.tracing)) |
| { |
| if (auto ato = aliasthisOf()) |
| { |
| att = cast(AliasThisRec)(att | AliasThisRec.tracing); |
| wm = ato.deduceWild(t, isRef); |
| att = cast(AliasThisRec)(att & ~AliasThisRec.tracing); |
| } |
| } |
| |
| return wm; |
| } |
| |
| override inout(Type) toHeadMutable() inout |
| { |
| 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 TypeEnum syntaxCopy() |
| { |
| return this; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return sym.getMemtype(loc).size(loc); |
| } |
| |
| Type memType(const ref Loc loc = Loc.initial) |
| { |
| return sym.getMemtype(loc); |
| } |
| |
| override uint alignsize() |
| { |
| Type t = memType(); |
| if (t.ty == Terror) |
| return 4; |
| return t.alignsize(); |
| } |
| |
| override Dsymbol toDsymbol(Scope* sc) |
| { |
| return sym; |
| } |
| |
| override bool isintegral() |
| { |
| return memType().isintegral(); |
| } |
| |
| override bool isfloating() |
| { |
| return memType().isfloating(); |
| } |
| |
| override bool isreal() |
| { |
| return memType().isreal(); |
| } |
| |
| override bool isimaginary() |
| { |
| return memType().isimaginary(); |
| } |
| |
| override bool iscomplex() |
| { |
| return memType().iscomplex(); |
| } |
| |
| override bool isscalar() |
| { |
| return memType().isscalar(); |
| } |
| |
| override bool isunsigned() |
| { |
| return memType().isunsigned(); |
| } |
| |
| override bool isBoolean() |
| { |
| return memType().isBoolean(); |
| } |
| |
| override bool isString() |
| { |
| return memType().isString(); |
| } |
| |
| override bool isAssignable() |
| { |
| return memType().isAssignable(); |
| } |
| |
| override bool needsDestruction() |
| { |
| return memType().needsDestruction(); |
| } |
| |
| override bool needsCopyOrPostblit() |
| { |
| return memType().needsCopyOrPostblit(); |
| } |
| |
| override bool needsNested() |
| { |
| return memType().needsNested(); |
| } |
| |
| override MATCH implicitConvTo(Type to) |
| { |
| MATCH m; |
| //printf("TypeEnum::implicitConvTo() %s to %s\n", toChars(), to.toChars()); |
| if (ty == to.ty && sym == (cast(TypeEnum)to).sym) |
| m = (mod == to.mod) ? MATCH.exact : MATCH.constant; |
| else if (sym.getMemtype(Loc.initial).implicitConvTo(to)) |
| m = MATCH.convert; // match with conversions |
| else |
| m = MATCH.nomatch; // no match |
| return m; |
| } |
| |
| override MATCH constConv(Type to) |
| { |
| if (equals(to)) |
| return MATCH.exact; |
| if (ty == to.ty && sym == (cast(TypeEnum)to).sym && MODimplicitConv(mod, to.mod)) |
| return MATCH.constant; |
| return MATCH.nomatch; |
| } |
| |
| extern (D) Type toBasetype2() |
| { |
| if (!sym.members && !sym.memtype) |
| return this; |
| auto tb = sym.getMemtype(Loc.initial).toBasetype(); |
| return tb.castMod(mod); // retain modifier bits from 'this' |
| } |
| |
| override bool isZeroInit(const ref Loc loc) |
| { |
| return sym.getDefaultValue(loc).toBool().hasValue(false); |
| } |
| |
| override bool hasPointers() |
| { |
| return memType().hasPointers(); |
| } |
| |
| override bool hasVoidInitPointers() |
| { |
| return memType().hasVoidInitPointers(); |
| } |
| |
| override bool hasSystemFields() |
| { |
| return memType().hasSystemFields(); |
| } |
| |
| override bool hasInvariant() |
| { |
| return memType().hasInvariant(); |
| } |
| |
| override Type nextOf() |
| { |
| return memType().nextOf(); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class TypeClass : Type |
| { |
| ClassDeclaration sym; |
| AliasThisRec att = AliasThisRec.fwdref; |
| CPPMANGLE cppmangle = CPPMANGLE.def; |
| |
| extern (D) this(ClassDeclaration sym) |
| { |
| super(Tclass); |
| this.sym = sym; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "class"; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return target.ptrsize; |
| } |
| |
| override TypeClass syntaxCopy() |
| { |
| return this; |
| } |
| |
| override Dsymbol toDsymbol(Scope* sc) |
| { |
| return sym; |
| } |
| |
| override inout(ClassDeclaration) isClassHandle() inout |
| { |
| return sym; |
| } |
| |
| override bool isBaseOf(Type t, int* poffset) |
| { |
| if (t && t.ty == Tclass) |
| { |
| ClassDeclaration cd = (cast(TypeClass)t).sym; |
| if (cd.semanticRun < PASS.semanticdone && !cd.isBaseInfoComplete()) |
| cd.dsymbolSemantic(null); |
| if (sym.semanticRun < PASS.semanticdone && !sym.isBaseInfoComplete()) |
| sym.dsymbolSemantic(null); |
| |
| if (sym.isBaseOf(cd, poffset)) |
| return true; |
| } |
| return false; |
| } |
| |
| extern (D) MATCH implicitConvToWithoutAliasThis(Type to) |
| { |
| // Run semantic before checking whether class is convertible |
| ClassDeclaration cdto = to.isClassHandle(); |
| if (cdto) |
| { |
| //printf("TypeClass::implicitConvTo(to = '%s') %s, isbase = %d %d\n", to.toChars(), toChars(), cdto.isBaseInfoComplete(), sym.isBaseInfoComplete()); |
| if (cdto.semanticRun < PASS.semanticdone && !cdto.isBaseInfoComplete()) |
| cdto.dsymbolSemantic(null); |
| if (sym.semanticRun < PASS.semanticdone && !sym.isBaseInfoComplete()) |
| sym.dsymbolSemantic(null); |
| } |
| MATCH m = constConv(to); |
| if (m > MATCH.nomatch) |
| return m; |
| |
| if (cdto && cdto.isBaseOf(sym, null) && MODimplicitConv(mod, to.mod)) |
| { |
| //printf("'to' is base\n"); |
| return MATCH.convert; |
| } |
| return MATCH.nomatch; |
| } |
| |
| extern (D) MATCH implicitConvToThroughAliasThis(Type to) |
| { |
| MATCH m; |
| if (sym.aliasthis && !(att & AliasThisRec.tracing)) |
| { |
| if (auto ato = aliasthisOf()) |
| { |
| att = cast(AliasThisRec)(att | AliasThisRec.tracing); |
| m = ato.implicitConvTo(to); |
| att = cast(AliasThisRec)(att & ~AliasThisRec.tracing); |
| } |
| } |
| return m; |
| } |
| |
| override MATCH implicitConvTo(Type to) |
| { |
| //printf("TypeClass::implicitConvTo(to = '%s') %s\n", to.toChars(), toChars()); |
| MATCH m = implicitConvToWithoutAliasThis(to); |
| return m ? m : implicitConvToThroughAliasThis(to); |
| } |
| |
| override MATCH constConv(Type to) |
| { |
| if (equals(to)) |
| return MATCH.exact; |
| if (ty == to.ty && sym == (cast(TypeClass)to).sym && MODimplicitConv(mod, to.mod)) |
| return MATCH.constant; |
| |
| /* 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 MATCH.convert; |
| } |
| |
| return MATCH.nomatch; |
| } |
| |
| override MOD 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 & AliasThisRec.tracing)) |
| { |
| if (auto ato = aliasthisOf()) |
| { |
| att = cast(AliasThisRec)(att | AliasThisRec.tracing); |
| wm = ato.deduceWild(t, isRef); |
| att = cast(AliasThisRec)(att & ~AliasThisRec.tracing); |
| } |
| } |
| |
| return wm; |
| } |
| |
| override inout(Type) toHeadMutable() inout |
| { |
| return this; |
| } |
| |
| override bool isZeroInit(const ref Loc loc) |
| { |
| return true; |
| } |
| |
| override bool isscope() |
| { |
| return sym.stack; |
| } |
| |
| override bool isBoolean() |
| { |
| return true; |
| } |
| |
| override bool hasPointers() |
| { |
| return true; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class TypeTuple : Type |
| { |
| // 'logically immutable' cached global - don't modify! |
| __gshared TypeTuple empty = new TypeTuple(); |
| |
| 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(exps ? exps.dim : 0); |
| if (exps) |
| { |
| 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(STC.undefined_, e.type, null, 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, null)); |
| } |
| |
| extern (D) this(Type t1, Type t2) |
| { |
| super(Ttuple); |
| arguments = new Parameters(); |
| arguments.push(new Parameter(0, t1, null, null, null)); |
| arguments.push(new Parameter(0, t2, null, null, null)); |
| } |
| |
| static TypeTuple create() |
| { |
| return new TypeTuple(); |
| } |
| |
| static TypeTuple create(Type t1) |
| { |
| return new TypeTuple(t1); |
| } |
| |
| static TypeTuple create(Type t1, Type t2) |
| { |
| return new TypeTuple(t1, t2); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "tuple"; |
| } |
| |
| override TypeTuple syntaxCopy() |
| { |
| Parameters* args = Parameter.arraySyntaxCopy(arguments); |
| auto t = new TypeTuple(args); |
| t.mod = mod; |
| return t; |
| } |
| |
| override bool equals(const RootObject o) const |
| { |
| Type t = cast(Type)o; |
| //printf("TypeTuple::equals(%s, %s)\n", toChars(), t.toChars()); |
| if (this == t) |
| return true; |
| if (auto tt = t.isTypeTuple()) |
| { |
| if (arguments.dim == tt.arguments.dim) |
| { |
| for (size_t i = 0; i < tt.arguments.dim; i++) |
| { |
| const Parameter arg1 = (*arguments)[i]; |
| Parameter arg2 = (*tt.arguments)[i]; |
| if (!arg1.type.equals(arg2.type)) |
| return false; |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| override MATCH implicitConvTo(Type to) |
| { |
| if (this == to) |
| return MATCH.exact; |
| if (auto tt = to.isTypeTuple()) |
| { |
| if (arguments.dim == tt.arguments.dim) |
| { |
| MATCH m = MATCH.exact; |
| for (size_t i = 0; i < tt.arguments.dim; i++) |
| { |
| Parameter arg1 = (*arguments)[i]; |
| Parameter arg2 = (*tt.arguments)[i]; |
| MATCH mi = arg1.type.implicitConvTo(arg2.type); |
| if (mi < m) |
| m = mi; |
| } |
| return m; |
| } |
| } |
| return MATCH.nomatch; |
| } |
| |
| 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 TypeSlice syntaxCopy() |
| { |
| auto t = new TypeSlice(next.syntaxCopy(), lwr.syntaxCopy(), upr.syntaxCopy()); |
| t.mod = mod; |
| return t; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class TypeNull : Type |
| { |
| extern (D) this() |
| { |
| //printf("TypeNull %p\n", this); |
| super(Tnull); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "null"; |
| } |
| |
| override TypeNull 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 != MATCH.nomatch) |
| 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 MATCH.constant; |
| } |
| |
| return MATCH.nomatch; |
| } |
| |
| override bool hasPointers() |
| { |
| /* Although null isn't dereferencable, treat it as a pointer type for |
| * attribute inference, generic code, etc. |
| */ |
| return true; |
| } |
| |
| override bool isBoolean() |
| { |
| return true; |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return tvoidptr.size(loc); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class TypeNoreturn : Type |
| { |
| extern (D) this() |
| { |
| //printf("TypeNoreturn %p\n", this); |
| super(Tnoreturn); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "noreturn"; |
| } |
| |
| override TypeNoreturn syntaxCopy() |
| { |
| // No semantic analysis done, no need to copy |
| return this; |
| } |
| |
| override MATCH implicitConvTo(Type to) |
| { |
| //printf("TypeNoreturn::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; |
| |
| // Different qualifiers? |
| if (to.ty == Tnoreturn) |
| return MATCH.constant; |
| |
| // Implicitly convertible to any type |
| return MATCH.convert; |
| } |
| |
| override MATCH constConv(Type to) |
| { |
| // Either another noreturn or conversion to any type |
| return this.implicitConvTo(to); |
| } |
| |
| override bool isBoolean() |
| { |
| return true; // bottom type can be implicitly converted to any other type |
| } |
| |
| override uinteger_t size(const ref Loc loc) |
| { |
| return 0; |
| } |
| |
| override uint alignsize() |
| { |
| return 0; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Unlike D, C can declare/define struct/union/enum tag names |
| * inside Declarators, instead of separately as in D. |
| * The order these appear in the symbol table must be in lexical |
| * order. There isn't enough info at the parsing stage to determine if |
| * it's a declaration or a reference to an existing name, so this Type |
| * collects the necessary info and defers it to semantic(). |
| */ |
| extern (C++) final class TypeTag : Type |
| { |
| Loc loc; /// location of declaration |
| TOK tok; /// TOK.struct_, TOK.union_, TOK.enum_ |
| Identifier id; /// tag name identifier |
| Type base; /// base type for enums otherwise null |
| Dsymbols* members; /// members of struct, null if none |
| |
| Type resolved; /// type after semantic() in case there are more others |
| /// pointing to this instance, which can happen with |
| /// struct S { int a; } s1, *s2; |
| MOD mod; /// modifiers to apply after type is resolved (only MODFlags.const_ at the moment) |
| |
| extern (D) this(const ref Loc loc, TOK tok, Identifier id, Type base, Dsymbols* members) |
| { |
| //printf("TypeTag ctor %s %p\n", id ? id.toChars() : "null".ptr, this); |
| super(Ttag); |
| this.loc = loc; |
| this.tok = tok; |
| this.id = id; |
| this.base = base; |
| this.members = members; |
| this.mod = 0; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "tag"; |
| } |
| |
| override TypeTag syntaxCopy() |
| { |
| //printf("TypeTag syntaxCopy()\n"); |
| // No semantic analysis done, no need to copy |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Represents a function's formal parameters + variadics info. |
| * Length, indexing and iteration are based on a depth-first tuple expansion. |
| * https://dlang.org/spec/function.html#ParameterList |
| */ |
| extern (C++) struct ParameterList |
| { |
| /// The raw (unexpanded) formal parameters, possibly containing tuples. |
| Parameters* parameters; |
| StorageClass stc; // storage class of ... |
| VarArg varargs = VarArg.none; |
| bool hasIdentifierList; // true if C identifier-list style |
| |
| this(Parameters* parameters, VarArg varargs = VarArg.none, StorageClass stc = 0) |
| { |
| this.parameters = parameters; |
| this.varargs = varargs; |
| this.stc = stc; |
| } |
| |
| /// Returns the number of expanded parameters. Complexity: O(N). |
| size_t length() |
| { |
| return Parameter.dim(parameters); |
| } |
| |
| /// Returns the expanded parameter at the given index, or null if out of |
| /// bounds. Complexity: O(i). |
| Parameter opIndex(size_t i) |
| { |
| return Parameter.getNth(parameters, i); |
| } |
| |
| /// Iterates over the expanded parameters. Complexity: O(N). |
| /// Prefer this to avoid the O(N + N^2/2) complexity of calculating length |
| /// and calling N times opIndex. |
| extern (D) int opApply(scope Parameter.ForeachDg dg) |
| { |
| return Parameter._foreach(parameters, dg); |
| } |
| |
| /// Iterates over the expanded parameters, matching them with the unexpanded |
| /// ones, for semantic processing |
| extern (D) int opApply(scope Parameter.SemanticForeachDg dg) |
| { |
| return Parameter._foreach(this.parameters, dg); |
| } |
| |
| extern (D) ParameterList syntaxCopy() |
| { |
| return ParameterList(Parameter.arraySyntaxCopy(parameters), varargs); |
| } |
| |
| /// Compares this to another ParameterList (and expands tuples if necessary) |
| extern (D) bool opEquals(scope ref ParameterList other) const |
| { |
| if (stc != other.stc || varargs != other.varargs || (!parameters != !other.parameters)) |
| return false; |
| |
| if (this.parameters is other.parameters) |
| return true; |
| |
| size_t idx; |
| bool diff; |
| |
| // Pairwise compare each parameter |
| // Can this avoid the O(n) indexing for the second list? |
| foreach (_, p1; cast() this) |
| { |
| auto p2 = other[idx++]; |
| if (!p2 || p1 != p2) { |
| diff = true; |
| break; |
| } |
| } |
| |
| // Ensure no remaining parameters in `other` |
| return !diff && other[idx] is null; |
| } |
| } |
| |
| |
| /*********************************************************** |
| */ |
| extern (C++) final class Parameter : ASTNode |
| { |
| import dmd.attrib : UserAttributeDeclaration; |
| |
| StorageClass storageClass; |
| Type type; |
| Identifier ident; |
| Expression defaultArg; |
| UserAttributeDeclaration userAttribDecl; // user defined attributes |
| |
| extern (D) this(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) |
| { |
| this.type = type; |
| this.ident = ident; |
| this.storageClass = storageClass; |
| this.defaultArg = defaultArg; |
| this.userAttribDecl = userAttribDecl; |
| } |
| |
| static Parameter create(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) |
| { |
| return new Parameter(storageClass, type, ident, defaultArg, userAttribDecl); |
| } |
| |
| Parameter syntaxCopy() |
| { |
| return new Parameter(storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null, userAttribDecl ? userAttribDecl.syntaxCopy(null) : 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 (auto td = tel.isTypeDelegate()) |
| { |
| TypeFunction tf = td.next.toTypeFunction(); |
| if (tf.parameterList.varargs == VarArg.none && tf.parameterList.length == 0) |
| { |
| return tf.next; // return type of delegate |
| } |
| } |
| } |
| return null; |
| } |
| |
| /// Returns: Whether the function parameter is lazy |
| bool isLazy() const @safe pure nothrow @nogc |
| { |
| return (this.storageClass & (STC.lazy_)) != 0; |
| } |
| |
| /// Returns: Whether the function parameter is a reference (out / ref) |
| bool isReference() const @safe pure nothrow @nogc |
| { |
| return (this.storageClass & (STC.ref_ | STC.out_)) != 0; |
| } |
| |
| // kludge for template.isType() |
| override DYNCAST dyncast() const |
| { |
| return DYNCAST.parameter; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| |
| extern (D) static Parameters* arraySyntaxCopy(Parameters* parameters) |
| { |
| Parameters* params = null; |
| if (parameters) |
| { |
| params = new Parameters(parameters.dim); |
| for (size_t i = 0; i < params.dim; i++) |
| (*params)[i] = (*parameters)[i].syntaxCopy(); |
| } |
| return params; |
| } |
| |
| /*************************************** |
| * 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. |
| * |
| * Since `parameters` can include tuples, which would increase its |
| * length, this function allows to get the `nth` parameter as if |
| * all tuples transitively contained in `parameters` were flattened. |
| * |
| * Params: |
| * parameters = Array of `Parameter` to iterate over |
| * nth = Index of the desired parameter. |
| * |
| * Returns: |
| * The parameter at index `nth` (taking tuples into account), |
| * or `null` if out of bound. |
| */ |
| static Parameter getNth(Parameters* parameters, size_t nth) |
| { |
| 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; |
| } |
| |
| /// Type of delegate when iterating solely on the parameters |
| alias ForeachDg = extern (D) int delegate(size_t paramidx, Parameter param); |
| /// Type of delegate when iterating on both the original set of parameters, |
| /// and the type tuple. Useful for semantic analysis. |
| /// 'o' stands for 'original' and 'e' stands for 'expanded'. |
| alias SemanticForeachDg = extern (D) int delegate( |
| size_t oidx, Parameter oparam, size_t eidx, Parameter eparam); |
| |
| /*************************************** |
| * 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) |
| { |
| assert(dg !is null); |
| return _foreach(parameters, (_oidx, _oparam, idx, param) => dg(idx, param)); |
| } |
| |
| /// Ditto |
| extern (D) static int _foreach( |
| Parameters* parameters, scope SemanticForeachDg dg) |
| { |
| assert(dg !is null); |
| if (parameters is null) |
| return 0; |
| |
| size_t eidx; |
| foreach (oidx; 0 .. parameters.length) |
| { |
| Parameter oparam = (*parameters)[oidx]; |
| if (auto r = _foreachImpl(dg, oidx, oparam, eidx, /* eparam */ oparam)) |
| return r; |
| } |
| return 0; |
| } |
| |
| /// Implementation of the iteration process, which recurses in itself |
| /// and just forwards `oidx` and `oparam`. |
| extern (D) private static int _foreachImpl(scope SemanticForeachDg dg, |
| size_t oidx, Parameter oparam, ref size_t eidx, Parameter eparam) |
| { |
| if (eparam is null) |
| return 0; |
| |
| Type t = eparam.type.toBasetype(); |
| if (auto tu = t.isTypeTuple()) |
| { |
| // Check for empty tuples |
| if (tu.arguments is null) |
| return 0; |
| |
| foreach (nidx; 0 .. tu.arguments.length) |
| { |
| Parameter nextep = (*tu.arguments)[nidx]; |
| if (auto r = _foreachImpl(dg, oidx, oparam, eidx, nextep)) |
| return r; |
| } |
| } |
| else |
| { |
| if (auto r = dg(oidx, oparam, eidx, eparam)) |
| return r; |
| // The only place where we should increment eidx is here, |
| // as a TypeTuple doesn't count as a parameter (for arity) |
| // it it is empty. |
| eidx++; |
| } |
| return 0; |
| } |
| |
| 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: |
| * returnByRef = true if the function returns by ref |
| * p = Parameter to compare with |
| * previewIn = Whether `-preview=in` is being used, and thus if |
| * `in` means `scope [ref]`. |
| * |
| * Returns: |
| * true = `this` can be used in place of `p` |
| * false = nope |
| */ |
| bool isCovariant(bool returnByRef, const Parameter p, bool previewIn = global.params.previewIn) |
| const pure nothrow @nogc @safe |
| { |
| ulong thisSTC = this.storageClass; |
| ulong otherSTC = p.storageClass; |
| |
| if (previewIn) |
| { |
| if (thisSTC & STC.in_) |
| thisSTC |= STC.scope_; |
| if (otherSTC & STC.in_) |
| otherSTC |= STC.scope_; |
| } |
| |
| const mask = STC.ref_ | STC.out_ | STC.lazy_ | (previewIn ? STC.in_ : 0); |
| if ((thisSTC & mask) != (otherSTC & mask)) |
| return false; |
| return isCovariantScope(returnByRef, thisSTC, otherSTC); |
| } |
| |
| extern (D) private static bool isCovariantScope(bool returnByRef, StorageClass from, StorageClass to) pure nothrow @nogc @safe |
| { |
| // Workaround for failing covariance when finding a common type of delegates, |
| // some of which have parameters with inferred scope |
| // https://issues.dlang.org/show_bug.cgi?id=21285 |
| // The root cause is that scopeinferred is not part of the mangle, and mangle |
| // is used for type equality checks |
| if (to & STC.returninferred) |
| to &= ~STC.return_; |
| // note: f(return int* x) currently 'infers' scope without inferring `return`, in that case keep STC.scope |
| if (to & STC.scopeinferred && !(to & STC.return_)) |
| to &= ~STC.scope_; |
| |
| if (from == to) |
| return true; |
| |
| /* result is true if the 'from' can be used as a 'to' |
| */ |
| |
| if ((from ^ to) & STC.ref_) // differing in 'ref' means no covariance |
| return false; |
| |
| /* workaround until we get STC.returnScope reliably set correctly |
| */ |
| if (returnByRef) |
| { |
| from &= ~STC.returnScope; |
| to &= ~STC.returnScope; |
| } |
| else |
| { |
| from |= STC.returnScope; |
| to |= STC.returnScope; |
| } |
| return covariant[buildScopeRef(from)][buildScopeRef(to)]; |
| } |
| |
| extern (D) private static bool[ScopeRef.max + 1][ScopeRef.max + 1] covariantInit() pure nothrow @nogc @safe |
| { |
| /* Initialize covariant[][] with this: |
| |
| From\To n rs s |
| None X |
| ReturnScope X X |
| Scope X X X |
| |
| From\To r rr rs rr-s r-rs |
| Ref X X |
| ReturnRef X |
| RefScope X X X X X |
| ReturnRef-Scope X X |
| Ref-ReturnScope X X X |
| */ |
| bool[ScopeRef.max + 1][ScopeRef.max + 1] covariant; |
| |
| foreach (i; 0 .. ScopeRef.max + 1) |
| { |
| covariant[i][i] = true; |
| covariant[ScopeRef.RefScope][i] = true; |
| } |
| covariant[ScopeRef.ReturnScope][ScopeRef.None] = true; |
| covariant[ScopeRef.Scope ][ScopeRef.None] = true; |
| covariant[ScopeRef.Scope ][ScopeRef.ReturnScope] = true; |
| |
| covariant[ScopeRef.Ref ][ScopeRef.ReturnRef] = true; |
| covariant[ScopeRef.ReturnRef_Scope][ScopeRef.ReturnRef] = true; |
| covariant[ScopeRef.Ref_ReturnScope][ScopeRef.Ref ] = true; |
| covariant[ScopeRef.Ref_ReturnScope][ScopeRef.ReturnRef] = true; |
| |
| return covariant; |
| } |
| |
| extern (D) private static immutable bool[ScopeRef.max + 1][ScopeRef.max + 1] covariant = covariantInit(); |
| |
| extern (D) bool opEquals(const Parameter other) const |
| { |
| return this.storageClass == other.storageClass |
| && this.type == other.type; |
| } |
| } |
| |
| /************************************************************* |
| * For printing two types with qualification when necessary. |
| * Params: |
| * t1 = The first type to receive the type name for |
| * t2 = The second type to receive the type name for |
| * Returns: |
| * The fully-qualified names of both types if the two type names are not the same, |
| * or the unqualified names of both types if the two type names are the same. |
| */ |
| const(char*)[2] toAutoQualChars(Type t1, Type t2) |
| { |
| auto s1 = t1.toChars(); |
| auto s2 = t2.toChars(); |
| // show qualification only if it's different |
| if (!t1.equals(t2) && strcmp(s1, s2) == 0) |
| { |
| s1 = t1.toPrettyChars(true); |
| s2 = t2.toPrettyChars(true); |
| } |
| return [s1, s2]; |
| } |
| |
| |
| /** |
| * For each active modifier (MODFlags.const_, MODFlags.immutable_, etc) call `fp` with a |
| * void* for the work param and a string representation of the attribute. |
| */ |
| void modifiersApply(const TypeFunction tf, void delegate(string) dg) |
| { |
| immutable ubyte[4] modsArr = [MODFlags.const_, MODFlags.immutable_, MODFlags.wild, MODFlags.shared_]; |
| |
| foreach (modsarr; modsArr) |
| { |
| if (tf.mod & modsarr) |
| { |
| dg(MODtoString(modsarr)); |
| } |
| } |
| } |
| |
| /** |
| * For each active attribute (ref/const/nogc/etc) call `fp` with a void* for the |
| * work param and a string representation of the attribute. |
| */ |
| void attributesApply(const TypeFunction tf, void delegate(string) dg, TRUSTformat trustFormat = TRUSTformatDefault) |
| { |
| if (tf.purity) |
| dg("pure"); |
| if (tf.isnothrow) |
| dg("nothrow"); |
| if (tf.isnogc) |
| dg("@nogc"); |
| if (tf.isproperty) |
| dg("@property"); |
| if (tf.isref) |
| dg("ref"); |
| if (tf.isreturn && !tf.isreturninferred) |
| dg("return"); |
| if (tf.isScopeQual && !tf.isscopeinferred) |
| dg("scope"); |
| if (tf.islive) |
| dg("@live"); |
| |
| TRUST trustAttrib = tf.trust; |
| |
| if (trustAttrib == TRUST.default_) |
| { |
| if (trustFormat != TRUSTformatSystem) |
| return; |
| trustAttrib = TRUST.system; // avoid calling with an empty string |
| } |
| |
| dg(trustToString(trustAttrib)); |
| } |
| |
| /** |
| * If the type is a class or struct, returns the symbol for it, |
| * else null. |
| */ |
| extern (C++) AggregateDeclaration isAggregate(Type t) |
| { |
| t = t.toBasetype(); |
| if (t.ty == Tclass) |
| return (cast(TypeClass)t).sym; |
| if (t.ty == Tstruct) |
| return (cast(TypeStruct)t).sym; |
| return null; |
| } |
| |
| /*************************************************** |
| * Determine if type t can be indexed or sliced given that it is not an |
| * aggregate with operator overloads. |
| * Params: |
| * t = type to check |
| * Returns: |
| * true if an expression of type t can be e1 in an array expression |
| */ |
| bool isIndexableNonAggregate(Type t) |
| { |
| t = t.toBasetype(); |
| return (t.ty == Tpointer || t.ty == Tsarray || t.ty == Tarray || t.ty == Taarray || |
| t.ty == Ttuple || t.ty == Tvector); |
| } |
| |
| /*************************************************** |
| * Determine if type t is copyable. |
| * Params: |
| * t = type to check |
| * Returns: |
| * true if we can copy it |
| */ |
| bool isCopyable(Type t) |
| { |
| //printf("isCopyable() %s\n", t.toChars()); |
| if (auto ts = t.isTypeStruct()) |
| { |
| if (ts.sym.postblit && |
| ts.sym.postblit.storage_class & STC.disable) |
| return false; |
| if (ts.sym.hasCopyCtor) |
| { |
| // check if there is a matching overload of the copy constructor and whether it is disabled or not |
| // `assert(ctor)` fails on Win32 and Win_32_64. See: https://auto-tester.puremagic.com/pull-history.ghtml?projectid=1&repoid=1&pullid=10575 |
| Dsymbol ctor = search_function(ts.sym, Id.ctor); |
| assert(ctor); |
| scope el = new IdentifierExp(Loc.initial, Id.p); // dummy lvalue |
| el.type = cast() ts; |
| Expressions args; |
| args.push(el); |
| FuncDeclaration f = resolveFuncCall(Loc.initial, null, ctor, null, cast()ts, &args, FuncResolveFlag.quiet); |
| if (!f || f.storage_class & STC.disable) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /*************************************** |
| * Computes how a parameter may be returned. |
| * Shrinking the representation is necessary because StorageClass is so wide |
| * Params: |
| * stc = storage class of parameter |
| * Returns: |
| * value from enum ScopeRef |
| */ |
| ScopeRef buildScopeRef(StorageClass stc) pure nothrow @nogc @safe |
| { |
| if (stc & STC.out_) |
| stc |= STC.ref_; // treat `out` and `ref` the same |
| |
| ScopeRef result; |
| final switch (stc & (STC.ref_ | STC.scope_ | STC.return_)) |
| { |
| case 0: result = ScopeRef.None; break; |
| |
| /* can occur in case test/compilable/testsctreturn.d |
| * related to https://issues.dlang.org/show_bug.cgi?id=20149 |
| * where inout adds `return` without `scope` or `ref` |
| */ |
| case STC.return_: result = ScopeRef.Return; break; |
| |
| case STC.ref_: result = ScopeRef.Ref; break; |
| case STC.scope_: result = ScopeRef.Scope; break; |
| case STC.return_ | STC.ref_: result = ScopeRef.ReturnRef; break; |
| case STC.return_ | STC.scope_: result = ScopeRef.ReturnScope; break; |
| case STC.ref_ | STC.scope_: result = ScopeRef.RefScope; break; |
| |
| case STC.return_ | STC.ref_ | STC.scope_: |
| result = stc & STC.returnScope ? ScopeRef.Ref_ReturnScope |
| : ScopeRef.ReturnRef_Scope; |
| break; |
| } |
| return result; |
| } |
| |
| /** |
| * Classification of 'scope-return-ref' possibilities |
| */ |
| enum ScopeRef |
| { |
| None, |
| Scope, |
| ReturnScope, |
| Ref, |
| ReturnRef, |
| RefScope, |
| ReturnRef_Scope, |
| Ref_ReturnScope, |
| Return, |
| } |
| |
| /********************************* |
| * Give us a nice string for debugging purposes. |
| * Params: |
| * sr = value |
| * Returns: |
| * corresponding string |
| */ |
| const(char)* toChars(ScopeRef sr) pure nothrow @nogc @safe |
| { |
| with (ScopeRef) |
| { |
| static immutable char*[ScopeRef.max + 1] names = |
| [ |
| None: "None", |
| Scope: "Scope", |
| ReturnScope: "ReturnScope", |
| Ref: "Ref", |
| ReturnRef: "ReturnRef", |
| RefScope: "RefScope", |
| ReturnRef_Scope: "ReturnRef_Scope", |
| Ref_ReturnScope: "Ref_ReturnScope", |
| Return: "Return", |
| ]; |
| return names[sr]; |
| } |
| } |
| |
| /** |
| * Used by `callMatch` to check if the copy constructor may be called to |
| * copy the argument |
| * |
| * This is done by seeing if a call to the copy constructor can be made: |
| * ``` |
| * typeof(tprm) __copytmp; |
| * copytmp.__copyCtor(arg); |
| * ``` |
| */ |
| private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, |
| Expression arg, Type tprm, Scope* sc, const(char)** pMessage) |
| { |
| auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytmp"), null); |
| tmp.storage_class = STC.rvalue | STC.temp | STC.ctfe; |
| tmp.dsymbolSemantic(sc); |
| Expression ve = new VarExp(arg.loc, tmp); |
| Expression e = new DotIdExp(arg.loc, ve, Id.ctor); |
| e = new CallExp(arg.loc, e, arg); |
| //printf("e = %s\n", e.toChars()); |
| if (.trySemantic(e, sc)) |
| return true; |
| |
| if (pMessage) |
| { |
| /* https://issues.dlang.org/show_bug.cgi?id=22202 |
| * |
| * If a function was deduced by semantic on the CallExp, |
| * it means that resolveFuncCall completed succesfully. |
| * Therefore, there exists a callable copy constructor, |
| * however, it cannot be called because scope constraints |
| * such as purity, safety or nogc. |
| */ |
| OutBuffer buf; |
| auto callExp = e.isCallExp(); |
| if (auto f = callExp.f) |
| { |
| char[] s; |
| if (!f.isPure && sc.func.setImpure()) |
| s ~= "pure "; |
| if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe()) |
| s ~= "@safe "; |
| if (!f.isNogc && sc.func.setGC()) |
| s ~= "nogc "; |
| if (s) |
| { |
| s[$-1] = '\0'; |
| buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr); |
| } |
| else if (f.isGenerated() && f.isDisabled()) |
| { |
| /* https://issues.dlang.org/show_bug.cgi?id=23097 |
| * Compiler generated copy constructor failed. |
| */ |
| buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable", |
| argStruct.toChars()); |
| } |
| else |
| { |
| /* Although a copy constructor may exist, no suitable match was found. |
| * i.e: `inout` constructor creates `const` object, not mutable. |
| * Fallback to using the original generic error before bugzilla 22202. |
| */ |
| goto Lnocpctor; |
| } |
| } |
| else |
| { |
| Lnocpctor: |
| buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", |
| argStruct.toChars(), arg.type.toChars(), tprm.toChars()); |
| } |
| |
| *pMessage = buf.extractChars(); |
| } |
| return false; |
| } |
| |
| /** |
| * Match a single parameter to an argument. |
| * |
| * This function is called by `TypeFunction.callMatch` while iterating over |
| * the list of parameter. Here we check if `arg` is a match for `p`, |
| * which is mostly about checking if `arg.type` converts to `p`'s type |
| * and some check about value reference. |
| * |
| * Params: |
| * tf = The `TypeFunction`, only used for error reporting |
| * p = The parameter of `tf` being matched |
| * arg = Argument being passed (bound) to `p` |
| * wildmatch = Wild (`inout`) matching level, derived from the full argument list |
| * flag = A non-zero value means we're doing a partial ordering check |
| * (no value semantic check) |
| * sc = Scope we are in |
| * pMessage = A buffer to write the error in, or `null` |
| * |
| * Returns: Whether `trailingArgs` match `p`. |
| */ |
| private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p, |
| Expression arg, ubyte wildmatch, int flag, Scope* sc, const(char)** pMessage) |
| { |
| //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars()); |
| MATCH m; |
| Type targ = arg.type; |
| Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type; |
| |
| if (p.isLazy() && tprm.ty == Tvoid && targ.ty != Tvoid) |
| m = MATCH.convert; |
| else if (flag) |
| { |
| // for partial ordering, value is an irrelevant mockup, just look at the type |
| m = targ.implicitConvTo(tprm); |
| } |
| else |
| { |
| const isRef = p.isReference(); |
| StructDeclaration argStruct, prmStruct; |
| |
| // first look for a copy constructor |
| if (arg.isLvalue() && !isRef && targ.ty == Tstruct && tprm.ty == Tstruct) |
| { |
| // if the argument and the parameter are of the same unqualified struct type |
| argStruct = (cast(TypeStruct)targ).sym; |
| prmStruct = (cast(TypeStruct)tprm).sym; |
| } |
| |
| // check if the copy constructor may be called to copy the argument |
| if (argStruct && argStruct == prmStruct && argStruct.hasCopyCtor) |
| { |
| if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage)) |
| return MATCH.nomatch; |
| m = MATCH.exact; |
| } |
| else |
| { |
| import dmd.dcast : cimplicitConvTo; |
| m = (sc && sc.flags & SCOPE.Cfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm); |
| } |
| } |
| |
| // Non-lvalues do not match ref or out parameters |
| if (p.isReference()) |
| { |
| // https://issues.dlang.org/show_bug.cgi?id=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 & STC.out_) |
| { |
| if (pMessage) *pMessage = tf.getParamError(arg, p); |
| return MATCH.nomatch; |
| } |
| |
| if (arg.op == EXP.string_ && 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 == EXP.slice && 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 if ((p.storageClass & STC.in_) && global.params.previewIn) |
| { |
| // Allow converting a literal to an `in` which is `ref` |
| if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray) |
| { |
| Type tn = tp.nextOf(); |
| dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); |
| ta = tn.sarrayOf(dim); |
| } |
| |
| // Need to make this a rvalue through a temporary |
| m = MATCH.convert; |
| } |
| else if (global.params.rvalueRefParam != FeatureState.enabled || |
| p.storageClass & STC.out_ || |
| !arg.type.isCopyable()) // can't copy to temp for ref parameter |
| { |
| if (pMessage) *pMessage = tf.getParamError(arg, p); |
| return MATCH.nomatch; |
| } |
| else |
| { |
| /* in functionParameters() we'll convert this |
| * rvalue into a temporary |
| */ |
| m = MATCH.convert; |
| } |
| } |
| |
| /* If the match is not already perfect or if the arg |
| is not a lvalue then try the `alias this` chain |
| see https://issues.dlang.org/show_bug.cgi?id=15674 |
| and https://issues.dlang.org/show_bug.cgi?id=21905 |
| */ |
| if (ta != tp || !arg.isLvalue()) |
| { |
| Type firsttab = ta.toBasetype(); |
| while (1) |
| { |
| Type tab = ta.toBasetype(); |
| Type tat = tab.aliasthisOf(); |
| if (!tat || !tat.implicitConvTo(tprm)) |
| break; |
| if (tat == tab || tat == firsttab) |
| break; |
| ta = tat; |
| } |
| } |
| |
| /* A ref variable should work like a head-const reference. |
| * e.g. disallows: |
| * ref T <- an lvalue of const(T) argument |
| * ref T[dim] <- an lvalue of const(T[dim]) argument |
| */ |
| if (!ta.constConv(tp)) |
| { |
| if (pMessage) *pMessage = tf.getParamError(arg, p); |
| return MATCH.nomatch; |
| } |
| } |
| return m; |
| } |
| |
| /** |
| * Match the remaining arguments `trailingArgs` with parameter `p`. |
| * |
| * Assume we already checked that `p` is the last parameter of `tf`, |
| * and we want to know whether the arguments would match `p`. |
| * |
| * Params: |
| * tf = The `TypeFunction`, only used for error reporting |
| * p = The last parameter of `tf` which is variadic |
| * trailingArgs = The remaining arguments that should match `p` |
| * pMessage = A buffer to write the error in, or `null` |
| * |
| * Returns: Whether `trailingArgs` match `p`. |
| */ |
| private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p, |
| Expression[] trailingArgs, const(char)** pMessage) |
| { |
| Type tb = p.type.toBasetype(); |
| |
| switch (tb.ty) |
| { |
| case Tsarray: |
| TypeSArray tsa = cast(TypeSArray)tb; |
| dinteger_t sz = tsa.dim.toInteger(); |
| if (sz != trailingArgs.length) |
| { |
| if (pMessage) |
| *pMessage = tf.getMatchError("expected %llu variadic argument(s), not %zu", |
| sz, trailingArgs.length); |
| return MATCH.nomatch; |
| } |
| goto case Tarray; |
| case Tarray: |
| { |
| MATCH match = MATCH.exact; |
| TypeArray ta = cast(TypeArray)tb; |
| foreach (arg; trailingArgs) |
| { |
| MATCH m; |
| 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 = MATCH.exact; |
| else if (tret.toBasetype().ty == Tvoid) |
| m = MATCH.convert; |
| else |
| { |
| m = arg.implicitConvTo(tret); |
| if (m == MATCH.nomatch) |
| m = arg.implicitConvTo(ta.next); |
| } |
| } |
| else |
| m = arg.implicitConvTo(ta.next); |
| |
| if (m == MATCH.nomatch) |
| { |
| if (pMessage) *pMessage = tf.getParamError(arg, p); |
| return MATCH.nomatch; |
| } |
| if (m < match) |
| match = m; |
| } |
| return match; |
| } |
| case Tclass: |
| // We leave it up to the actual constructor call to do the matching. |
| return MATCH.exact; |
| |
| default: |
| // We can have things as `foo(int[int] wat...)` but they only match |
| // with an associative array proper. |
| if (pMessage && trailingArgs.length) *pMessage = tf.getParamError(trailingArgs[0], p); |
| return MATCH.nomatch; |
| } |
| } |