blob: 265f731cedda25fa478b7afcbd3e5e3629538c06 [file] [log] [blame]
/**
* 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();