mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-07 19:36:06 +03:00

VS has different name manglings for classes and structs. With the new extern(C++, class) and extern(C++, struct) declarations, the name mangling algorithm is changed to use a mangling different from the used D entity. A common use case is to map a value type modeled as class in C++ to a struct in D. This is backport of https://github.com/dlang/dmd/pull/5875.
9791 lines
287 KiB
D
9791 lines
287 KiB
D
/**
|
|
* Compiler implementation of the D programming language
|
|
*
|
|
* Copyright: Copyright (c) 1999-2015 by Digital Mars, All Rights Reserved
|
|
* Authors: Walter Bright, http://www.digitalmars.com
|
|
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
* Source: $(DMDSRC mtype.d)
|
|
*/
|
|
|
|
module ddmd.mtype;
|
|
|
|
import core.checkedint;
|
|
import core.stdc.float_;
|
|
import core.stdc.stdarg;
|
|
import core.stdc.stdio;
|
|
import core.stdc.stdlib;
|
|
import core.stdc.string;
|
|
|
|
import ddmd.access;
|
|
import ddmd.aggregate;
|
|
import ddmd.aliasthis;
|
|
import ddmd.argtypes;
|
|
import ddmd.arrayop;
|
|
import ddmd.arraytypes;
|
|
import ddmd.attrib;
|
|
import ddmd.gluelayer;
|
|
import ddmd.complex;
|
|
import ddmd.ctfeexpr;
|
|
import ddmd.dcast;
|
|
import ddmd.dclass;
|
|
import ddmd.declaration;
|
|
import ddmd.denum;
|
|
import ddmd.dimport;
|
|
import ddmd.dmangle;
|
|
import ddmd.dmodule;
|
|
import ddmd.dscope;
|
|
import ddmd.dstruct;
|
|
import ddmd.dsymbol;
|
|
import ddmd.dtemplate;
|
|
import ddmd.errors;
|
|
import ddmd.expression;
|
|
import ddmd.func;
|
|
import ddmd.globals;
|
|
import ddmd.hdrgen;
|
|
import ddmd.id;
|
|
import ddmd.identifier;
|
|
import ddmd.imphint;
|
|
import ddmd.init;
|
|
import ddmd.opover;
|
|
import ddmd.root.longdouble;
|
|
import ddmd.root.outbuffer;
|
|
import ddmd.root.port;
|
|
import ddmd.root.rmem;
|
|
import ddmd.root.rootobject;
|
|
import ddmd.root.stringtable;
|
|
import ddmd.sideeffect;
|
|
import ddmd.target;
|
|
import ddmd.tokens;
|
|
import ddmd.visitor;
|
|
|
|
version(IN_LLVM) {
|
|
import gen.llvmhelpers;
|
|
}
|
|
|
|
enum LOGDOTEXP = 0; // log ::dotExp()
|
|
enum LOGDEFAULTINIT = 0; // log ::defaultInit()
|
|
|
|
extern (C++) __gshared int Tsize_t = Tuns32;
|
|
extern (C++) __gshared int Tptrdiff_t = Tint32;
|
|
|
|
/***************************
|
|
* Return !=0 if modfrom can be implicitly converted to modto
|
|
*/
|
|
extern (C++) bool MODimplicitConv(MOD modfrom, MOD modto)
|
|
{
|
|
if (modfrom == modto)
|
|
return true;
|
|
//printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto);
|
|
auto X(T, U)(T m, U n)
|
|
{
|
|
return ((m << 4) | n);
|
|
}
|
|
|
|
switch (X(modfrom & ~MODshared, modto & ~MODshared))
|
|
{
|
|
case X(0, MODconst):
|
|
case X(MODwild, MODconst):
|
|
case X(MODwild, MODwildconst):
|
|
case X(MODwildconst, MODconst):
|
|
return (modfrom & MODshared) == (modto & MODshared);
|
|
case X(MODimmutable, MODconst):
|
|
case X(MODimmutable, MODwildconst):
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/***************************
|
|
* Return MATCHexact or MATCHconst if a method of type '() modfrom' can call a method of type '() modto'.
|
|
*/
|
|
extern (C++) MATCH MODmethodConv(MOD modfrom, MOD modto)
|
|
{
|
|
if (modfrom == modto)
|
|
return MATCHexact;
|
|
if (MODimplicitConv(modfrom, modto))
|
|
return MATCHconst;
|
|
auto X(T, U)(T m, U n)
|
|
{
|
|
return ((m << 4) | n);
|
|
}
|
|
|
|
switch (X(modfrom, modto))
|
|
{
|
|
case X(0, MODwild):
|
|
case X(MODimmutable, MODwild):
|
|
case X(MODconst, MODwild):
|
|
case X(MODwildconst, MODwild):
|
|
case X(MODshared, MODshared | MODwild):
|
|
case X(MODshared | MODimmutable, MODshared | MODwild):
|
|
case X(MODshared | MODconst, MODshared | MODwild):
|
|
case X(MODshared | MODwildconst, MODshared | MODwild):
|
|
return MATCHconst;
|
|
default:
|
|
return MATCHnomatch;
|
|
}
|
|
}
|
|
|
|
/***************************
|
|
* Merge mod bits to form common mod.
|
|
*/
|
|
extern (C++) MOD MODmerge(MOD mod1, MOD mod2)
|
|
{
|
|
if (mod1 == mod2)
|
|
return mod1;
|
|
//printf("MODmerge(1 = %x, 2 = %x)\n", mod1, mod2);
|
|
MOD result = 0;
|
|
if ((mod1 | mod2) & MODshared)
|
|
{
|
|
// If either type is shared, the result will be shared
|
|
result |= MODshared;
|
|
mod1 &= ~MODshared;
|
|
mod2 &= ~MODshared;
|
|
}
|
|
if (mod1 == 0 || mod1 == MODmutable || mod1 == MODconst || mod2 == 0 || mod2 == MODmutable || mod2 == MODconst)
|
|
{
|
|
// If either type is mutable or const, the result will be const.
|
|
result |= MODconst;
|
|
}
|
|
else
|
|
{
|
|
// MODimmutable vs MODwild
|
|
// MODimmutable vs MODwildconst
|
|
// MODwild vs MODwildconst
|
|
assert(mod1 & MODwild || mod2 & MODwild);
|
|
result |= MODwildconst;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*********************************
|
|
* Store modifier name into buf.
|
|
*/
|
|
extern (C++) void MODtoBuffer(OutBuffer* buf, MOD mod)
|
|
{
|
|
switch (mod)
|
|
{
|
|
case 0:
|
|
break;
|
|
case MODimmutable:
|
|
buf.writestring(Token.tochars[TOKimmutable]);
|
|
break;
|
|
case MODshared:
|
|
buf.writestring(Token.tochars[TOKshared]);
|
|
break;
|
|
case MODshared | MODconst:
|
|
buf.writestring(Token.tochars[TOKshared]);
|
|
buf.writeByte(' ');
|
|
goto case; /+ fall through +/
|
|
case MODconst:
|
|
buf.writestring(Token.tochars[TOKconst]);
|
|
break;
|
|
case MODshared | MODwild:
|
|
buf.writestring(Token.tochars[TOKshared]);
|
|
buf.writeByte(' ');
|
|
goto case; /+ fall through +/
|
|
case MODwild:
|
|
buf.writestring(Token.tochars[TOKwild]);
|
|
break;
|
|
case MODshared | MODwildconst:
|
|
buf.writestring(Token.tochars[TOKshared]);
|
|
buf.writeByte(' ');
|
|
goto case; /+ fall through +/
|
|
case MODwildconst:
|
|
buf.writestring(Token.tochars[TOKwild]);
|
|
buf.writeByte(' ');
|
|
buf.writestring(Token.tochars[TOKconst]);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
/*********************************
|
|
* Return modifier name.
|
|
*/
|
|
extern (C++) char* MODtoChars(MOD mod)
|
|
{
|
|
OutBuffer buf;
|
|
buf.reserve(16);
|
|
MODtoBuffer(&buf, mod);
|
|
return buf.extractString();
|
|
}
|
|
|
|
/************************************
|
|
* Convert MODxxxx to STCxxx
|
|
*/
|
|
extern (C++) StorageClass ModToStc(uint mod)
|
|
{
|
|
StorageClass stc = 0;
|
|
if (mod & MODimmutable)
|
|
stc |= STCimmutable;
|
|
if (mod & MODconst)
|
|
stc |= STCconst;
|
|
if (mod & MODwild)
|
|
stc |= STCwild;
|
|
if (mod & MODshared)
|
|
stc |= STCshared;
|
|
return stc;
|
|
}
|
|
|
|
/************************************
|
|
* Strip all parameter's idenfiers and their default arguments for merging types.
|
|
* If some of parameter types or return type are function pointer, delegate, or
|
|
* the types which contains either, then strip also from them.
|
|
*/
|
|
extern (C++) Type stripDefaultArgs(Type t)
|
|
{
|
|
static Parameters* stripParams(Parameters* parameters)
|
|
{
|
|
Parameters* params = parameters;
|
|
if (params && params.dim > 0)
|
|
{
|
|
foreach (i; 0 .. params.dim)
|
|
{
|
|
Parameter p = (*params)[i];
|
|
Type ta = stripDefaultArgs(p.type);
|
|
if (ta != p.type || p.defaultArg || p.ident)
|
|
{
|
|
if (params == parameters)
|
|
{
|
|
params = new Parameters();
|
|
params.setDim(parameters.dim);
|
|
foreach (j; 0 .. params.dim)
|
|
(*params)[j] = (*parameters)[j];
|
|
}
|
|
(*params)[i] = new Parameter(p.storageClass, ta, null, null);
|
|
}
|
|
}
|
|
}
|
|
return params;
|
|
}
|
|
|
|
if (t is null)
|
|
return t;
|
|
if (t.ty == Tfunction)
|
|
{
|
|
TypeFunction tf = cast(TypeFunction)t;
|
|
Type tret = stripDefaultArgs(tf.next);
|
|
Parameters* params = stripParams(tf.parameters);
|
|
if (tret == tf.next && params == tf.parameters)
|
|
goto Lnot;
|
|
tf = cast(TypeFunction)tf.copy();
|
|
tf.parameters = params;
|
|
tf.next = tret;
|
|
//printf("strip %s\n <- %s\n", tf->toChars(), t->toChars());
|
|
t = tf;
|
|
}
|
|
else if (t.ty == Ttuple)
|
|
{
|
|
TypeTuple tt = cast(TypeTuple)t;
|
|
Parameters* args = stripParams(tt.arguments);
|
|
if (args == tt.arguments)
|
|
goto Lnot;
|
|
t = t.copy();
|
|
(cast(TypeTuple)t).arguments = args;
|
|
}
|
|
else if (t.ty == Tenum)
|
|
{
|
|
// TypeEnum::nextOf() may be != NULL, but it's not necessary here.
|
|
goto Lnot;
|
|
}
|
|
else
|
|
{
|
|
Type tn = t.nextOf();
|
|
Type n = stripDefaultArgs(tn);
|
|
if (n == tn)
|
|
goto Lnot;
|
|
t = t.copy();
|
|
(cast(TypeNext)t).next = n;
|
|
}
|
|
//printf("strip %s\n", t->toChars());
|
|
Lnot:
|
|
return t;
|
|
}
|
|
|
|
enum TFLAGSintegral = 1;
|
|
enum TFLAGSfloating = 2;
|
|
enum TFLAGSunsigned = 4;
|
|
enum TFLAGSreal = 8;
|
|
enum TFLAGSimaginary = 0x10;
|
|
enum TFLAGScomplex = 0x20;
|
|
|
|
extern (C++) Expression semanticLength(Scope* sc, TupleDeclaration tup, Expression exp)
|
|
{
|
|
ScopeDsymbol sym = new ArrayScopeSymbol(sc, tup);
|
|
sym.parent = sc.scopesym;
|
|
|
|
sc = sc.push(sym);
|
|
sc = sc.startCTFE();
|
|
exp = exp.semantic(sc);
|
|
sc = sc.endCTFE();
|
|
sc.pop();
|
|
|
|
return exp;
|
|
}
|
|
|
|
/**************************
|
|
* This evaluates exp while setting length to be the number
|
|
* of elements in the tuple t.
|
|
*/
|
|
extern (C++) Expression semanticLength(Scope* sc, Type t, Expression exp)
|
|
{
|
|
if (t.ty == Ttuple)
|
|
{
|
|
ScopeDsymbol sym = new ArrayScopeSymbol(sc, cast(TypeTuple)t);
|
|
sym.parent = sc.scopesym;
|
|
sc = sc.push(sym);
|
|
sc = sc.startCTFE();
|
|
exp = exp.semantic(sc);
|
|
sc = sc.endCTFE();
|
|
sc.pop();
|
|
}
|
|
else
|
|
{
|
|
sc = sc.startCTFE();
|
|
exp = exp.semantic(sc);
|
|
sc = sc.endCTFE();
|
|
}
|
|
return exp;
|
|
}
|
|
|
|
enum ENUMTY : int
|
|
{
|
|
Tarray, // slice array, aka T[]
|
|
Tsarray, // static array, aka T[dimension]
|
|
Taarray, // associative array, aka T[type]
|
|
Tpointer,
|
|
Treference,
|
|
Tfunction,
|
|
Tident,
|
|
Tclass,
|
|
Tstruct,
|
|
Tenum,
|
|
|
|
Tdelegate,
|
|
Tnone,
|
|
Tvoid,
|
|
Tint8,
|
|
Tuns8,
|
|
Tint16,
|
|
Tuns16,
|
|
Tint32,
|
|
Tuns32,
|
|
Tint64,
|
|
|
|
Tuns64,
|
|
Tfloat32,
|
|
Tfloat64,
|
|
Tfloat80,
|
|
Timaginary32,
|
|
Timaginary64,
|
|
Timaginary80,
|
|
Tcomplex32,
|
|
Tcomplex64,
|
|
Tcomplex80,
|
|
|
|
Tbool,
|
|
Tchar,
|
|
Twchar,
|
|
Tdchar,
|
|
Terror,
|
|
Tinstance,
|
|
Ttypeof,
|
|
Ttuple,
|
|
Tslice,
|
|
Treturn,
|
|
|
|
Tnull,
|
|
Tvector,
|
|
Tint128,
|
|
Tuns128,
|
|
TMAX,
|
|
}
|
|
|
|
alias Tarray = ENUMTY.Tarray;
|
|
alias Tsarray = ENUMTY.Tsarray;
|
|
alias Taarray = ENUMTY.Taarray;
|
|
alias Tpointer = ENUMTY.Tpointer;
|
|
alias Treference = ENUMTY.Treference;
|
|
alias Tfunction = ENUMTY.Tfunction;
|
|
alias Tident = ENUMTY.Tident;
|
|
alias Tclass = ENUMTY.Tclass;
|
|
alias Tstruct = ENUMTY.Tstruct;
|
|
alias Tenum = ENUMTY.Tenum;
|
|
alias Tdelegate = ENUMTY.Tdelegate;
|
|
alias Tnone = ENUMTY.Tnone;
|
|
alias Tvoid = ENUMTY.Tvoid;
|
|
alias Tint8 = ENUMTY.Tint8;
|
|
alias Tuns8 = ENUMTY.Tuns8;
|
|
alias Tint16 = ENUMTY.Tint16;
|
|
alias Tuns16 = ENUMTY.Tuns16;
|
|
alias Tint32 = ENUMTY.Tint32;
|
|
alias Tuns32 = ENUMTY.Tuns32;
|
|
alias Tint64 = ENUMTY.Tint64;
|
|
alias Tuns64 = ENUMTY.Tuns64;
|
|
alias Tfloat32 = ENUMTY.Tfloat32;
|
|
alias Tfloat64 = ENUMTY.Tfloat64;
|
|
alias Tfloat80 = ENUMTY.Tfloat80;
|
|
alias Timaginary32 = ENUMTY.Timaginary32;
|
|
alias Timaginary64 = ENUMTY.Timaginary64;
|
|
alias Timaginary80 = ENUMTY.Timaginary80;
|
|
alias Tcomplex32 = ENUMTY.Tcomplex32;
|
|
alias Tcomplex64 = ENUMTY.Tcomplex64;
|
|
alias Tcomplex80 = ENUMTY.Tcomplex80;
|
|
alias Tbool = ENUMTY.Tbool;
|
|
alias Tchar = ENUMTY.Tchar;
|
|
alias Twchar = ENUMTY.Twchar;
|
|
alias Tdchar = ENUMTY.Tdchar;
|
|
alias Terror = ENUMTY.Terror;
|
|
alias Tinstance = ENUMTY.Tinstance;
|
|
alias Ttypeof = ENUMTY.Ttypeof;
|
|
alias Ttuple = ENUMTY.Ttuple;
|
|
alias Tslice = ENUMTY.Tslice;
|
|
alias Treturn = ENUMTY.Treturn;
|
|
alias Tnull = ENUMTY.Tnull;
|
|
alias Tvector = ENUMTY.Tvector;
|
|
alias Tint128 = ENUMTY.Tint128;
|
|
alias Tuns128 = ENUMTY.Tuns128;
|
|
alias TMAX = ENUMTY.TMAX;
|
|
|
|
alias TY = ubyte;
|
|
|
|
enum MODFlags : int
|
|
{
|
|
MODconst = 1, // type is const
|
|
MODimmutable = 4, // type is immutable
|
|
MODshared = 2, // type is shared
|
|
MODwild = 8, // type is wild
|
|
MODwildconst = (MODwild | MODconst), // type is wild const
|
|
MODmutable = 0x10, // type is mutable (only used in wildcard matching)
|
|
}
|
|
|
|
alias MODconst = MODFlags.MODconst;
|
|
alias MODimmutable = MODFlags.MODimmutable;
|
|
alias MODshared = MODFlags.MODshared;
|
|
alias MODwild = MODFlags.MODwild;
|
|
alias MODwildconst = MODFlags.MODwildconst;
|
|
alias MODmutable = MODFlags.MODmutable;
|
|
|
|
alias MOD = ubyte;
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class Type : RootObject
|
|
{
|
|
public:
|
|
TY ty;
|
|
MOD mod; // modifiers MODxxxx
|
|
char* deco;
|
|
|
|
/* These are cached values that are lazily evaluated by constOf(), immutableOf(), etc.
|
|
* They should not be referenced by anybody but mtype.c.
|
|
* They can be NULL if not lazily evaluated yet.
|
|
* Note that there is no "shared immutable", because that is just immutable
|
|
* Naked == no MOD bits
|
|
*/
|
|
Type cto; // MODconst ? naked version of this type : const version
|
|
Type ito; // MODimmutable ? naked version of this type : immutable version
|
|
Type sto; // MODshared ? naked version of this type : shared mutable version
|
|
Type scto; // MODshared | MODconst ? naked version of this type : shared const version
|
|
Type wto; // MODwild ? naked version of this type : wild version
|
|
Type wcto; // MODwildconst ? naked version of this type : wild const version
|
|
Type swto; // MODshared | MODwild ? naked version of this type : shared wild version
|
|
Type swcto; // MODshared | MODwildconst ? naked version of this type : shared wild const version
|
|
|
|
Type pto; // merged pointer to this type
|
|
Type rto; // reference to this type
|
|
Type arrayof; // array of this type
|
|
|
|
TypeInfoDeclaration vtinfo; // TypeInfo object for this Type
|
|
|
|
type* ctype; // for back end
|
|
|
|
extern (C++) static __gshared Type tvoid;
|
|
extern (C++) static __gshared Type tint8;
|
|
extern (C++) static __gshared Type tuns8;
|
|
extern (C++) static __gshared Type tint16;
|
|
extern (C++) static __gshared Type tuns16;
|
|
extern (C++) static __gshared Type tint32;
|
|
extern (C++) static __gshared Type tuns32;
|
|
extern (C++) static __gshared Type tint64;
|
|
extern (C++) static __gshared Type tuns64;
|
|
extern (C++) static __gshared Type tint128;
|
|
extern (C++) static __gshared Type tuns128;
|
|
extern (C++) static __gshared Type tfloat32;
|
|
extern (C++) static __gshared Type tfloat64;
|
|
extern (C++) static __gshared Type tfloat80;
|
|
extern (C++) static __gshared Type timaginary32;
|
|
extern (C++) static __gshared Type timaginary64;
|
|
extern (C++) static __gshared Type timaginary80;
|
|
extern (C++) static __gshared Type tcomplex32;
|
|
extern (C++) static __gshared Type tcomplex64;
|
|
extern (C++) static __gshared Type tcomplex80;
|
|
extern (C++) static __gshared Type tbool;
|
|
extern (C++) static __gshared Type tchar;
|
|
extern (C++) static __gshared Type twchar;
|
|
extern (C++) static __gshared Type tdchar;
|
|
|
|
// Some special types
|
|
extern (C++) static __gshared Type tshiftcnt;
|
|
extern (C++) static __gshared Type tvoidptr; // void*
|
|
extern (C++) static __gshared Type tstring; // immutable(char)[]
|
|
extern (C++) static __gshared Type twstring; // immutable(wchar)[]
|
|
extern (C++) static __gshared Type tdstring; // immutable(dchar)[]
|
|
extern (C++) static __gshared Type tvalist; // va_list alias
|
|
extern (C++) static __gshared Type terror; // for error recovery
|
|
extern (C++) static __gshared Type tnull; // for null type
|
|
|
|
extern (C++) static __gshared Type tsize_t; // matches size_t alias
|
|
extern (C++) static __gshared Type tptrdiff_t; // matches ptrdiff_t alias
|
|
extern (C++) static __gshared Type thash_t; // matches hash_t alias
|
|
|
|
extern (C++) static __gshared ClassDeclaration dtypeinfo;
|
|
extern (C++) static __gshared ClassDeclaration typeinfoclass;
|
|
extern (C++) static __gshared ClassDeclaration typeinfointerface;
|
|
extern (C++) static __gshared ClassDeclaration typeinfostruct;
|
|
extern (C++) static __gshared ClassDeclaration typeinfopointer;
|
|
extern (C++) static __gshared ClassDeclaration typeinfoarray;
|
|
extern (C++) static __gshared ClassDeclaration typeinfostaticarray;
|
|
extern (C++) static __gshared ClassDeclaration typeinfoassociativearray;
|
|
extern (C++) static __gshared ClassDeclaration typeinfovector;
|
|
extern (C++) static __gshared ClassDeclaration typeinfoenum;
|
|
extern (C++) static __gshared ClassDeclaration typeinfofunction;
|
|
extern (C++) static __gshared ClassDeclaration typeinfodelegate;
|
|
extern (C++) static __gshared ClassDeclaration typeinfotypelist;
|
|
extern (C++) static __gshared ClassDeclaration typeinfoconst;
|
|
extern (C++) static __gshared ClassDeclaration typeinfoinvariant;
|
|
extern (C++) static __gshared ClassDeclaration typeinfoshared;
|
|
extern (C++) static __gshared ClassDeclaration typeinfowild;
|
|
|
|
extern (C++) static __gshared TemplateDeclaration rtinfo;
|
|
|
|
extern (C++) static __gshared Type[TMAX] basic;
|
|
extern (C++) static __gshared StringTable stringtable;
|
|
|
|
extern (C++) static __gshared ubyte[TMAX] sizeTy = ()
|
|
{
|
|
ubyte[TMAX] sizeTy = __traits(classInstanceSize, TypeBasic);
|
|
sizeTy[Tsarray] = __traits(classInstanceSize, TypeSArray);
|
|
sizeTy[Tarray] = __traits(classInstanceSize, TypeDArray);
|
|
sizeTy[Taarray] = __traits(classInstanceSize, TypeAArray);
|
|
sizeTy[Tpointer] = __traits(classInstanceSize, TypePointer);
|
|
sizeTy[Treference] = __traits(classInstanceSize, TypeReference);
|
|
sizeTy[Tfunction] = __traits(classInstanceSize, TypeFunction);
|
|
sizeTy[Tdelegate] = __traits(classInstanceSize, TypeDelegate);
|
|
sizeTy[Tident] = __traits(classInstanceSize, TypeIdentifier);
|
|
sizeTy[Tinstance] = __traits(classInstanceSize, TypeInstance);
|
|
sizeTy[Ttypeof] = __traits(classInstanceSize, TypeTypeof);
|
|
sizeTy[Tenum] = __traits(classInstanceSize, TypeEnum);
|
|
sizeTy[Tstruct] = __traits(classInstanceSize, TypeStruct);
|
|
sizeTy[Tclass] = __traits(classInstanceSize, TypeClass);
|
|
sizeTy[Ttuple] = __traits(classInstanceSize, TypeTuple);
|
|
sizeTy[Tslice] = __traits(classInstanceSize, TypeSlice);
|
|
sizeTy[Treturn] = __traits(classInstanceSize, TypeReturn);
|
|
sizeTy[Terror] = __traits(classInstanceSize, TypeError);
|
|
sizeTy[Tnull] = __traits(classInstanceSize, TypeNull);
|
|
sizeTy[Tvector] = __traits(classInstanceSize, TypeVector);
|
|
return sizeTy;
|
|
}();
|
|
|
|
final extern (D) this(TY ty)
|
|
{
|
|
this.ty = ty;
|
|
}
|
|
|
|
const(char)* kind() const
|
|
{
|
|
assert(false); // should be overridden
|
|
}
|
|
|
|
final Type copy()
|
|
{
|
|
Type t = cast(Type)mem.xmalloc(sizeTy[ty]);
|
|
memcpy(cast(void*)t, cast(void*)this, sizeTy[ty]);
|
|
return t;
|
|
}
|
|
|
|
Type syntaxCopy()
|
|
{
|
|
print();
|
|
fprintf(stderr, "ty = %d\n", ty);
|
|
assert(0);
|
|
}
|
|
|
|
override bool equals(RootObject o)
|
|
{
|
|
Type t = cast(Type)o;
|
|
//printf("Type::equals(%s, %s)\n", toChars(), t->toChars());
|
|
// deco strings are unique
|
|
// and semantic() has been run
|
|
if (this == o || ((t && deco == t.deco) && deco !is null))
|
|
{
|
|
//printf("deco = '%s', t->deco = '%s'\n", deco, t->deco);
|
|
return true;
|
|
}
|
|
//if (deco && t && t->deco) printf("deco = '%s', t->deco = '%s'\n", deco, t->deco);
|
|
return false;
|
|
}
|
|
|
|
final bool equivalent(Type t)
|
|
{
|
|
return immutableOf().equals(t.immutableOf());
|
|
}
|
|
|
|
// kludge for template.isType()
|
|
override final int dyncast()
|
|
{
|
|
return DYNCAST_TYPE;
|
|
}
|
|
|
|
/*******************************
|
|
* Covariant means that 'this' can substitute for 't',
|
|
* i.e. a pure function is a match for an impure type.
|
|
* Returns:
|
|
* 0 types are distinct
|
|
* 1 this is covariant with t
|
|
* 2 arguments match as far as overloading goes,
|
|
* but types are not covariant
|
|
* 3 cannot determine covariance because of forward references
|
|
* *pstc STCxxxx which would make it covariant
|
|
*/
|
|
final int covariant(Type t, StorageClass* pstc = null)
|
|
{
|
|
version (none)
|
|
{
|
|
printf("Type::covariant(t = %s) %s\n", t.toChars(), toChars());
|
|
printf("deco = %p, %p\n", deco, t.deco);
|
|
// printf("ty = %d\n", next->ty);
|
|
printf("mod = %x, %x\n", mod, t.mod);
|
|
}
|
|
if (pstc)
|
|
*pstc = 0;
|
|
StorageClass stc = 0;
|
|
int inoutmismatch = 0;
|
|
TypeFunction t1;
|
|
TypeFunction t2;
|
|
if (equals(t))
|
|
return 1; // covariant
|
|
if (ty != Tfunction || t.ty != Tfunction)
|
|
goto Ldistinct;
|
|
t1 = cast(TypeFunction)this;
|
|
t2 = cast(TypeFunction)t;
|
|
if (t1.varargs != t2.varargs)
|
|
goto Ldistinct;
|
|
if (t1.parameters && t2.parameters)
|
|
{
|
|
size_t dim = Parameter.dim(t1.parameters);
|
|
if (dim != Parameter.dim(t2.parameters))
|
|
goto Ldistinct;
|
|
for (size_t i = 0; i < dim; i++)
|
|
{
|
|
Parameter fparam1 = Parameter.getNth(t1.parameters, i);
|
|
Parameter fparam2 = Parameter.getNth(t2.parameters, i);
|
|
if (!fparam1.type.equals(fparam2.type))
|
|
{
|
|
goto Ldistinct;
|
|
}
|
|
const(StorageClass) sc = STCref | STCin | STCout | STClazy;
|
|
if ((fparam1.storageClass & sc) != (fparam2.storageClass & sc))
|
|
inoutmismatch = 1;
|
|
// We can add scope, but not subtract it
|
|
if (!(fparam1.storageClass & STCscope) && (fparam2.storageClass & STCscope))
|
|
inoutmismatch = 1;
|
|
// We can subtract return, but not add it
|
|
if ((fparam1.storageClass & STCreturn) && !(fparam2.storageClass & STCreturn))
|
|
inoutmismatch = 1;
|
|
}
|
|
}
|
|
else if (t1.parameters != t2.parameters)
|
|
{
|
|
size_t dim1 = !t1.parameters ? 0 : t1.parameters.dim;
|
|
size_t dim2 = !t2.parameters ? 0 : t2.parameters.dim;
|
|
if (dim1 || dim2)
|
|
goto Ldistinct;
|
|
}
|
|
// The argument lists match
|
|
if (inoutmismatch)
|
|
goto Lnotcovariant;
|
|
if (t1.linkage != t2.linkage)
|
|
goto Lnotcovariant;
|
|
{
|
|
// Return types
|
|
Type t1n = t1.next;
|
|
Type t2n = t2.next;
|
|
if (!t1n || !t2n) // happens with return type inference
|
|
goto Lnotcovariant;
|
|
if (t1n.equals(t2n))
|
|
goto Lcovariant;
|
|
if (t1n.ty == Tclass && t2n.ty == Tclass)
|
|
{
|
|
/* If same class type, but t2n is const, then it's
|
|
* covariant. Do this test first because it can work on
|
|
* forward references.
|
|
*/
|
|
if ((cast(TypeClass)t1n).sym == (cast(TypeClass)t2n).sym && MODimplicitConv(t1n.mod, t2n.mod))
|
|
goto Lcovariant;
|
|
// If t1n is forward referenced:
|
|
ClassDeclaration cd = (cast(TypeClass)t1n).sym;
|
|
if (cd._scope)
|
|
cd.semantic(null);
|
|
if (!cd.isBaseInfoComplete())
|
|
{
|
|
return 3; // forward references
|
|
}
|
|
}
|
|
if (t1n.ty == Tstruct && t2n.ty == Tstruct)
|
|
{
|
|
if ((cast(TypeStruct)t1n).sym == (cast(TypeStruct)t2n).sym && MODimplicitConv(t1n.mod, t2n.mod))
|
|
goto Lcovariant;
|
|
}
|
|
else if (t1n.ty == t2n.ty && t1n.implicitConvTo(t2n))
|
|
goto Lcovariant;
|
|
else if (t1n.ty == Tnull && t1n.implicitConvTo(t2n) && t1n.size() == t2n.size())
|
|
goto Lcovariant;
|
|
}
|
|
goto Lnotcovariant;
|
|
Lcovariant:
|
|
if (t1.isref != t2.isref)
|
|
goto Lnotcovariant;
|
|
// We can subtract 'return' from 'this', but cannot add it
|
|
if (t1.isreturn && !t2.isreturn)
|
|
goto Lnotcovariant;
|
|
/* Can convert mutable to const
|
|
*/
|
|
if (!MODimplicitConv(t2.mod, t1.mod))
|
|
{
|
|
version (none)
|
|
{
|
|
//stop attribute inference with const
|
|
// If adding 'const' will make it covariant
|
|
if (MODimplicitConv(t2.mod, MODmerge(t1.mod, MODconst)))
|
|
stc |= STCconst;
|
|
else
|
|
goto Lnotcovariant;
|
|
}
|
|
else
|
|
{
|
|
goto Ldistinct;
|
|
}
|
|
}
|
|
/* Can convert pure to impure, nothrow to throw, and nogc to gc
|
|
*/
|
|
if (!t1.purity && t2.purity)
|
|
stc |= STCpure;
|
|
if (!t1.isnothrow && t2.isnothrow)
|
|
stc |= STCnothrow;
|
|
if (!t1.isnogc && t2.isnogc)
|
|
stc |= STCnogc;
|
|
/* Can convert safe/trusted to system
|
|
*/
|
|
if (t1.trust <= TRUSTsystem && t2.trust >= TRUSTtrusted)
|
|
{
|
|
// Should we infer trusted or safe? Go with safe.
|
|
stc |= STCsafe;
|
|
}
|
|
if (stc)
|
|
{
|
|
if (pstc)
|
|
*pstc = stc;
|
|
goto Lnotcovariant;
|
|
}
|
|
//printf("\tcovaraint: 1\n");
|
|
return 1;
|
|
Ldistinct:
|
|
//printf("\tcovaraint: 0\n");
|
|
return 0;
|
|
Lnotcovariant:
|
|
//printf("\tcovaraint: 2\n");
|
|
return 2;
|
|
}
|
|
|
|
/********************************
|
|
* For pretty-printing a type.
|
|
*/
|
|
final override const(char)* toChars()
|
|
{
|
|
OutBuffer buf;
|
|
buf.reserve(16);
|
|
HdrGenState hgs;
|
|
hgs.fullQual = (ty == Tclass && !mod);
|
|
.toCBuffer(this, &buf, null, &hgs);
|
|
return buf.extractString();
|
|
}
|
|
|
|
final char* toPrettyChars(bool QualifyTypes = false)
|
|
{
|
|
OutBuffer buf;
|
|
buf.reserve(16);
|
|
HdrGenState hgs;
|
|
hgs.fullQual = QualifyTypes;
|
|
.toCBuffer(this, &buf, null, &hgs);
|
|
return buf.extractString();
|
|
}
|
|
|
|
final static char needThisPrefix()
|
|
{
|
|
return 'M'; // name mangling prefix for functions needing 'this'
|
|
}
|
|
|
|
final static void _init()
|
|
{
|
|
stringtable._init(14000);
|
|
// Set basic types
|
|
static __gshared TY* basetab =
|
|
[
|
|
Tvoid,
|
|
Tint8,
|
|
Tuns8,
|
|
Tint16,
|
|
Tuns16,
|
|
Tint32,
|
|
Tuns32,
|
|
Tint64,
|
|
Tuns64,
|
|
Tint128,
|
|
Tuns128,
|
|
Tfloat32,
|
|
Tfloat64,
|
|
Tfloat80,
|
|
Timaginary32,
|
|
Timaginary64,
|
|
Timaginary80,
|
|
Tcomplex32,
|
|
Tcomplex64,
|
|
Tcomplex80,
|
|
Tbool,
|
|
Tchar,
|
|
Twchar,
|
|
Tdchar,
|
|
Terror
|
|
];
|
|
for (size_t i = 0; basetab[i] != Terror; i++)
|
|
{
|
|
Type t = new TypeBasic(basetab[i]);
|
|
t = t.merge();
|
|
basic[basetab[i]] = t;
|
|
}
|
|
basic[Terror] = new TypeError();
|
|
tvoid = basic[Tvoid];
|
|
tint8 = basic[Tint8];
|
|
tuns8 = basic[Tuns8];
|
|
tint16 = basic[Tint16];
|
|
tuns16 = basic[Tuns16];
|
|
tint32 = basic[Tint32];
|
|
tuns32 = basic[Tuns32];
|
|
tint64 = basic[Tint64];
|
|
tuns64 = basic[Tuns64];
|
|
tint128 = basic[Tint128];
|
|
tuns128 = basic[Tuns128];
|
|
tfloat32 = basic[Tfloat32];
|
|
tfloat64 = basic[Tfloat64];
|
|
tfloat80 = basic[Tfloat80];
|
|
timaginary32 = basic[Timaginary32];
|
|
timaginary64 = basic[Timaginary64];
|
|
timaginary80 = basic[Timaginary80];
|
|
tcomplex32 = basic[Tcomplex32];
|
|
tcomplex64 = basic[Tcomplex64];
|
|
tcomplex80 = basic[Tcomplex80];
|
|
tbool = basic[Tbool];
|
|
tchar = basic[Tchar];
|
|
twchar = basic[Twchar];
|
|
tdchar = basic[Tdchar];
|
|
tshiftcnt = tint32;
|
|
terror = basic[Terror];
|
|
tnull = basic[Tnull];
|
|
tnull = new TypeNull();
|
|
tnull.deco = tnull.merge().deco;
|
|
tvoidptr = tvoid.pointerTo();
|
|
tstring = tchar.immutableOf().arrayOf();
|
|
twstring = twchar.immutableOf().arrayOf();
|
|
tdstring = tdchar.immutableOf().arrayOf();
|
|
tvalist = Target.va_listType();
|
|
if (global.params.isLP64)
|
|
{
|
|
Tsize_t = Tuns64;
|
|
Tptrdiff_t = Tint64;
|
|
}
|
|
else
|
|
{
|
|
Tsize_t = Tuns32;
|
|
Tptrdiff_t = Tint32;
|
|
}
|
|
tsize_t = basic[Tsize_t];
|
|
tptrdiff_t = basic[Tptrdiff_t];
|
|
thash_t = tsize_t;
|
|
}
|
|
|
|
enum SIZE_INVALID = (~cast(d_uns64)0);
|
|
|
|
final d_uns64 size()
|
|
{
|
|
return size(Loc());
|
|
}
|
|
|
|
d_uns64 size(Loc loc)
|
|
{
|
|
error(loc, "no size for type %s", toChars());
|
|
return SIZE_INVALID;
|
|
}
|
|
|
|
uint alignsize()
|
|
{
|
|
return cast(uint)size(Loc());
|
|
}
|
|
|
|
Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
if (ty == Tint128 || ty == Tuns128)
|
|
{
|
|
error(loc, "cent and ucent types not implemented");
|
|
return terror;
|
|
}
|
|
return merge();
|
|
}
|
|
|
|
final Type trySemantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("+trySemantic(%s) %d\n", toChars(), global.errors);
|
|
uint errors = global.startGagging();
|
|
Type t = semantic(loc, sc);
|
|
if (global.endGagging(errors) || t.ty == Terror) // if any errors happened
|
|
{
|
|
t = null;
|
|
}
|
|
//printf("-trySemantic(%s) %d\n", toChars(), global.errors);
|
|
return t;
|
|
}
|
|
|
|
/************************************
|
|
*/
|
|
final Type merge()
|
|
{
|
|
if (ty == Terror)
|
|
return this;
|
|
if (ty == Ttypeof)
|
|
return this;
|
|
if (ty == Tident)
|
|
return this;
|
|
if (ty == Tinstance)
|
|
return this;
|
|
if (ty == Taarray && !(cast(TypeAArray)this).index.merge().deco)
|
|
return this;
|
|
if (ty != Tenum && nextOf() && !nextOf().deco)
|
|
return this;
|
|
//printf("merge(%s)\n", toChars());
|
|
Type t = this;
|
|
assert(t);
|
|
if (!deco)
|
|
{
|
|
OutBuffer buf;
|
|
buf.reserve(32);
|
|
mangleToBuffer(this, &buf);
|
|
StringValue* sv = stringtable.update(cast(char*)buf.data, buf.offset);
|
|
if (sv.ptrvalue)
|
|
{
|
|
t = cast(Type)sv.ptrvalue;
|
|
debug
|
|
{
|
|
if (!t.deco)
|
|
printf("t = %s\n", t.toChars());
|
|
}
|
|
assert(t.deco);
|
|
//printf("old value, deco = '%s' %p\n", t->deco, t->deco);
|
|
}
|
|
else
|
|
{
|
|
sv.ptrvalue = cast(char*)(t = stripDefaultArgs(t));
|
|
deco = t.deco = cast(char*)sv.toDchars();
|
|
//printf("new value, deco = '%s' %p\n", t->deco, t->deco);
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
|
|
/*************************************
|
|
* This version does a merge even if the deco is already computed.
|
|
* Necessary for types that have a deco, but are not merged.
|
|
*/
|
|
final Type merge2()
|
|
{
|
|
//printf("merge2(%s)\n", toChars());
|
|
Type t = this;
|
|
assert(t);
|
|
if (!t.deco)
|
|
return t.merge();
|
|
StringValue* sv = stringtable.lookup(t.deco, strlen(t.deco));
|
|
if (sv && sv.ptrvalue)
|
|
{
|
|
t = cast(Type)sv.ptrvalue;
|
|
assert(t.deco);
|
|
}
|
|
else
|
|
assert(0);
|
|
return t;
|
|
}
|
|
|
|
/*********************************
|
|
* Store this type's modifier name into buf.
|
|
*/
|
|
final void modToBuffer(OutBuffer* buf)
|
|
{
|
|
if (mod)
|
|
{
|
|
buf.writeByte(' ');
|
|
MODtoBuffer(buf, mod);
|
|
}
|
|
}
|
|
|
|
/*********************************
|
|
* Return this type's modifier name.
|
|
*/
|
|
final char* modToChars()
|
|
{
|
|
OutBuffer buf;
|
|
buf.reserve(16);
|
|
modToBuffer(&buf);
|
|
return buf.extractString();
|
|
}
|
|
|
|
/** For each active modifier (MODconst, MODimmutable, etc) call fp with a
|
|
void* for the work param and a string representation of the attribute. */
|
|
final int modifiersApply(void* param, int function(void*, const(char)*) fp)
|
|
{
|
|
immutable ubyte[4] modsArr = [MODconst, MODimmutable, MODwild, MODshared];
|
|
|
|
foreach (modsarr; modsArr)
|
|
{
|
|
if (mod & modsarr)
|
|
{
|
|
if (int res = fp(param, MODtoChars(modsarr)))
|
|
return res;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool isintegral()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// real, imaginary, or complex
|
|
bool isfloating()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool isreal()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool isimaginary()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool iscomplex()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool isscalar()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool isunsigned()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool isscope()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool isString()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**************************
|
|
* When T is mutable,
|
|
* Given:
|
|
* T a, b;
|
|
* Can we bitwise assign:
|
|
* a = b;
|
|
* ?
|
|
*/
|
|
bool isAssignable()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**************************
|
|
* Returns true if T can be converted to boolean value.
|
|
*/
|
|
bool isBoolean()
|
|
{
|
|
return isscalar();
|
|
}
|
|
|
|
/*********************************
|
|
* Check type to see if it is based on a deprecated symbol.
|
|
*/
|
|
void checkDeprecated(Loc loc, Scope* sc)
|
|
{
|
|
Dsymbol s = toDsymbol(sc);
|
|
if (s)
|
|
s.checkDeprecated(loc, sc);
|
|
}
|
|
|
|
final bool isConst() const
|
|
{
|
|
return (mod & MODconst) != 0;
|
|
}
|
|
|
|
final bool isImmutable() const
|
|
{
|
|
return (mod & MODimmutable) != 0;
|
|
}
|
|
|
|
final bool isMutable() const
|
|
{
|
|
return (mod & (MODconst | MODimmutable | MODwild)) == 0;
|
|
}
|
|
|
|
final bool isShared() const
|
|
{
|
|
return (mod & MODshared) != 0;
|
|
}
|
|
|
|
final bool isSharedConst() const
|
|
{
|
|
return (mod & (MODshared | MODconst)) == (MODshared | MODconst);
|
|
}
|
|
|
|
final bool isWild() const
|
|
{
|
|
return (mod & MODwild) != 0;
|
|
}
|
|
|
|
final bool isWildConst() const
|
|
{
|
|
return (mod & MODwildconst) == MODwildconst;
|
|
}
|
|
|
|
final bool isSharedWild() const
|
|
{
|
|
return (mod & (MODshared | MODwild)) == (MODshared | MODwild);
|
|
}
|
|
|
|
final bool isNaked() const
|
|
{
|
|
return mod == 0;
|
|
}
|
|
|
|
/********************************
|
|
* Return a copy of this type with all attributes null-initialized.
|
|
* Useful for creating a type with different modifiers.
|
|
*/
|
|
final Type nullAttributes()
|
|
{
|
|
uint sz = sizeTy[ty];
|
|
Type t = cast(Type)mem.xmalloc(sz);
|
|
memcpy(cast(void*)t, cast(void*)this, sz);
|
|
// t->mod = NULL; // leave mod unchanged
|
|
t.deco = null;
|
|
t.arrayof = null;
|
|
t.pto = null;
|
|
t.rto = null;
|
|
t.cto = null;
|
|
t.ito = null;
|
|
t.sto = null;
|
|
t.scto = null;
|
|
t.wto = null;
|
|
t.wcto = null;
|
|
t.swto = null;
|
|
t.swcto = null;
|
|
t.vtinfo = null;
|
|
t.ctype = null;
|
|
if (t.ty == Tstruct)
|
|
(cast(TypeStruct)t).att = RECfwdref;
|
|
if (t.ty == Tclass)
|
|
(cast(TypeClass)t).att = RECfwdref;
|
|
return t;
|
|
}
|
|
|
|
/********************************
|
|
* Convert to 'const'.
|
|
*/
|
|
final Type constOf()
|
|
{
|
|
//printf("Type::constOf() %p %s\n", this, toChars());
|
|
if (mod == MODconst)
|
|
return this;
|
|
if (cto)
|
|
{
|
|
assert(cto.mod == MODconst);
|
|
return cto;
|
|
}
|
|
Type t = makeConst();
|
|
t = t.merge();
|
|
t.fixTo(this);
|
|
//printf("-Type::constOf() %p %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
/********************************
|
|
* Convert to 'immutable'.
|
|
*/
|
|
final Type immutableOf()
|
|
{
|
|
//printf("Type::immutableOf() %p %s\n", this, toChars());
|
|
if (isImmutable())
|
|
return this;
|
|
if (ito)
|
|
{
|
|
assert(ito.isImmutable());
|
|
return ito;
|
|
}
|
|
Type t = makeImmutable();
|
|
t = t.merge();
|
|
t.fixTo(this);
|
|
//printf("\t%p\n", t);
|
|
return t;
|
|
}
|
|
|
|
/********************************
|
|
* Make type mutable.
|
|
*/
|
|
final Type mutableOf()
|
|
{
|
|
//printf("Type::mutableOf() %p, %s\n", this, toChars());
|
|
Type t = this;
|
|
if (isImmutable())
|
|
{
|
|
t = ito; // immutable => naked
|
|
assert(!t || (t.isMutable() && !t.isShared()));
|
|
}
|
|
else if (isConst())
|
|
{
|
|
if (isShared())
|
|
{
|
|
if (isWild())
|
|
t = swcto; // shared wild const -> shared
|
|
else
|
|
t = sto; // shared const => shared
|
|
}
|
|
else
|
|
{
|
|
if (isWild())
|
|
t = wcto; // wild const -> naked
|
|
else
|
|
t = cto; // const => naked
|
|
}
|
|
assert(!t || t.isMutable());
|
|
}
|
|
else if (isWild())
|
|
{
|
|
if (isShared())
|
|
t = sto; // shared wild => shared
|
|
else
|
|
t = wto; // wild => naked
|
|
assert(!t || t.isMutable());
|
|
}
|
|
if (!t)
|
|
{
|
|
t = makeMutable();
|
|
t = t.merge();
|
|
t.fixTo(this);
|
|
}
|
|
else
|
|
t = t.merge();
|
|
assert(t.isMutable());
|
|
return t;
|
|
}
|
|
|
|
final Type sharedOf()
|
|
{
|
|
//printf("Type::sharedOf() %p, %s\n", this, toChars());
|
|
if (mod == MODshared)
|
|
return this;
|
|
if (sto)
|
|
{
|
|
assert(sto.mod == MODshared);
|
|
return sto;
|
|
}
|
|
Type t = makeShared();
|
|
t = t.merge();
|
|
t.fixTo(this);
|
|
//printf("\t%p\n", t);
|
|
return t;
|
|
}
|
|
|
|
final Type sharedConstOf()
|
|
{
|
|
//printf("Type::sharedConstOf() %p, %s\n", this, toChars());
|
|
if (mod == (MODshared | MODconst))
|
|
return this;
|
|
if (scto)
|
|
{
|
|
assert(scto.mod == (MODshared | MODconst));
|
|
return scto;
|
|
}
|
|
Type t = makeSharedConst();
|
|
t = t.merge();
|
|
t.fixTo(this);
|
|
//printf("\t%p\n", t);
|
|
return t;
|
|
}
|
|
|
|
/********************************
|
|
* Make type unshared.
|
|
* 0 => 0
|
|
* const => const
|
|
* immutable => immutable
|
|
* shared => 0
|
|
* shared const => const
|
|
* wild => wild
|
|
* wild const => wild const
|
|
* shared wild => wild
|
|
* shared wild const => wild const
|
|
*/
|
|
final Type unSharedOf()
|
|
{
|
|
//printf("Type::unSharedOf() %p, %s\n", this, toChars());
|
|
Type t = this;
|
|
if (isShared())
|
|
{
|
|
if (isWild())
|
|
{
|
|
if (isConst())
|
|
t = wcto; // shared wild const => wild const
|
|
else
|
|
t = wto; // shared wild => wild
|
|
}
|
|
else
|
|
{
|
|
if (isConst())
|
|
t = cto; // shared const => const
|
|
else
|
|
t = sto; // shared => naked
|
|
}
|
|
assert(!t || !t.isShared());
|
|
}
|
|
if (!t)
|
|
{
|
|
t = this.nullAttributes();
|
|
t.mod = mod & ~MODshared;
|
|
t.ctype = ctype;
|
|
t = t.merge();
|
|
t.fixTo(this);
|
|
}
|
|
else
|
|
t = t.merge();
|
|
assert(!t.isShared());
|
|
return t;
|
|
}
|
|
|
|
/********************************
|
|
* Convert to 'wild'.
|
|
*/
|
|
final Type wildOf()
|
|
{
|
|
//printf("Type::wildOf() %p %s\n", this, toChars());
|
|
if (mod == MODwild)
|
|
return this;
|
|
if (wto)
|
|
{
|
|
assert(wto.mod == MODwild);
|
|
return wto;
|
|
}
|
|
Type t = makeWild();
|
|
t = t.merge();
|
|
t.fixTo(this);
|
|
//printf("\t%p %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
final Type wildConstOf()
|
|
{
|
|
//printf("Type::wildConstOf() %p %s\n", this, toChars());
|
|
if (mod == MODwildconst)
|
|
return this;
|
|
if (wcto)
|
|
{
|
|
assert(wcto.mod == MODwildconst);
|
|
return wcto;
|
|
}
|
|
Type t = makeWildConst();
|
|
t = t.merge();
|
|
t.fixTo(this);
|
|
//printf("\t%p %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
final Type sharedWildOf()
|
|
{
|
|
//printf("Type::sharedWildOf() %p, %s\n", this, toChars());
|
|
if (mod == (MODshared | MODwild))
|
|
return this;
|
|
if (swto)
|
|
{
|
|
assert(swto.mod == (MODshared | MODwild));
|
|
return swto;
|
|
}
|
|
Type t = makeSharedWild();
|
|
t = t.merge();
|
|
t.fixTo(this);
|
|
//printf("\t%p %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
final Type sharedWildConstOf()
|
|
{
|
|
//printf("Type::sharedWildConstOf() %p, %s\n", this, toChars());
|
|
if (mod == (MODshared | MODwildconst))
|
|
return this;
|
|
if (swcto)
|
|
{
|
|
assert(swcto.mod == (MODshared | MODwildconst));
|
|
return swcto;
|
|
}
|
|
Type t = makeSharedWildConst();
|
|
t = t.merge();
|
|
t.fixTo(this);
|
|
//printf("\t%p %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
/**********************************
|
|
* For our new type 'this', which is type-constructed from t,
|
|
* fill in the cto, ito, sto, scto, wto shortcuts.
|
|
*/
|
|
final void fixTo(Type t)
|
|
{
|
|
// If fixing this: immutable(T*) by t: immutable(T)*,
|
|
// cache t to this->xto won't break transitivity.
|
|
Type mto = null;
|
|
Type tn = nextOf();
|
|
if (!tn || ty != Tsarray && tn.mod == t.nextOf().mod)
|
|
{
|
|
switch (t.mod)
|
|
{
|
|
case 0:
|
|
mto = t;
|
|
break;
|
|
case MODconst:
|
|
cto = t;
|
|
break;
|
|
case MODwild:
|
|
wto = t;
|
|
break;
|
|
case MODwildconst:
|
|
wcto = t;
|
|
break;
|
|
case MODshared:
|
|
sto = t;
|
|
break;
|
|
case MODshared | MODconst:
|
|
scto = t;
|
|
break;
|
|
case MODshared | MODwild:
|
|
swto = t;
|
|
break;
|
|
case MODshared | MODwildconst:
|
|
swcto = t;
|
|
break;
|
|
case MODimmutable:
|
|
ito = t;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
assert(mod != t.mod);
|
|
auto X(T, U)(T m, U n)
|
|
{
|
|
return ((m << 4) | n);
|
|
}
|
|
|
|
switch (mod)
|
|
{
|
|
case 0:
|
|
break;
|
|
case MODconst:
|
|
cto = mto;
|
|
t.cto = this;
|
|
break;
|
|
case MODwild:
|
|
wto = mto;
|
|
t.wto = this;
|
|
break;
|
|
case MODwildconst:
|
|
wcto = mto;
|
|
t.wcto = this;
|
|
break;
|
|
case MODshared:
|
|
sto = mto;
|
|
t.sto = this;
|
|
break;
|
|
case MODshared | MODconst:
|
|
scto = mto;
|
|
t.scto = this;
|
|
break;
|
|
case MODshared | MODwild:
|
|
swto = mto;
|
|
t.swto = this;
|
|
break;
|
|
case MODshared | MODwildconst:
|
|
swcto = mto;
|
|
t.swcto = this;
|
|
break;
|
|
case MODimmutable:
|
|
t.ito = this;
|
|
if (t.cto)
|
|
t.cto.ito = this;
|
|
if (t.sto)
|
|
t.sto.ito = this;
|
|
if (t.scto)
|
|
t.scto.ito = this;
|
|
if (t.wto)
|
|
t.wto.ito = this;
|
|
if (t.wcto)
|
|
t.wcto.ito = this;
|
|
if (t.swto)
|
|
t.swto.ito = this;
|
|
if (t.swcto)
|
|
t.swcto.ito = this;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
check();
|
|
t.check();
|
|
//printf("fixTo: %s, %s\n", toChars(), t->toChars());
|
|
}
|
|
|
|
/***************************
|
|
* Look for bugs in constructing types.
|
|
*/
|
|
final void check()
|
|
{
|
|
switch (mod)
|
|
{
|
|
case 0:
|
|
if (cto)
|
|
assert(cto.mod == MODconst);
|
|
if (ito)
|
|
assert(ito.mod == MODimmutable);
|
|
if (sto)
|
|
assert(sto.mod == MODshared);
|
|
if (scto)
|
|
assert(scto.mod == (MODshared | MODconst));
|
|
if (wto)
|
|
assert(wto.mod == MODwild);
|
|
if (wcto)
|
|
assert(wcto.mod == MODwildconst);
|
|
if (swto)
|
|
assert(swto.mod == (MODshared | MODwild));
|
|
if (swcto)
|
|
assert(swcto.mod == (MODshared | MODwildconst));
|
|
break;
|
|
case MODconst:
|
|
if (cto)
|
|
assert(cto.mod == 0);
|
|
if (ito)
|
|
assert(ito.mod == MODimmutable);
|
|
if (sto)
|
|
assert(sto.mod == MODshared);
|
|
if (scto)
|
|
assert(scto.mod == (MODshared | MODconst));
|
|
if (wto)
|
|
assert(wto.mod == MODwild);
|
|
if (wcto)
|
|
assert(wcto.mod == MODwildconst);
|
|
if (swto)
|
|
assert(swto.mod == (MODshared | MODwild));
|
|
if (swcto)
|
|
assert(swcto.mod == (MODshared | MODwildconst));
|
|
break;
|
|
case MODwild:
|
|
if (cto)
|
|
assert(cto.mod == MODconst);
|
|
if (ito)
|
|
assert(ito.mod == MODimmutable);
|
|
if (sto)
|
|
assert(sto.mod == MODshared);
|
|
if (scto)
|
|
assert(scto.mod == (MODshared | MODconst));
|
|
if (wto)
|
|
assert(wto.mod == 0);
|
|
if (wcto)
|
|
assert(wcto.mod == MODwildconst);
|
|
if (swto)
|
|
assert(swto.mod == (MODshared | MODwild));
|
|
if (swcto)
|
|
assert(swcto.mod == (MODshared | MODwildconst));
|
|
break;
|
|
case MODwildconst:
|
|
assert(!cto || cto.mod == MODconst);
|
|
assert(!ito || ito.mod == MODimmutable);
|
|
assert(!sto || sto.mod == MODshared);
|
|
assert(!scto || scto.mod == (MODshared | MODconst));
|
|
assert(!wto || wto.mod == MODwild);
|
|
assert(!wcto || wcto.mod == 0);
|
|
assert(!swto || swto.mod == (MODshared | MODwild));
|
|
assert(!swcto || swcto.mod == (MODshared | MODwildconst));
|
|
break;
|
|
case MODshared:
|
|
if (cto)
|
|
assert(cto.mod == MODconst);
|
|
if (ito)
|
|
assert(ito.mod == MODimmutable);
|
|
if (sto)
|
|
assert(sto.mod == 0);
|
|
if (scto)
|
|
assert(scto.mod == (MODshared | MODconst));
|
|
if (wto)
|
|
assert(wto.mod == MODwild);
|
|
if (wcto)
|
|
assert(wcto.mod == MODwildconst);
|
|
if (swto)
|
|
assert(swto.mod == (MODshared | MODwild));
|
|
if (swcto)
|
|
assert(swcto.mod == (MODshared | MODwildconst));
|
|
break;
|
|
case MODshared | MODconst:
|
|
if (cto)
|
|
assert(cto.mod == MODconst);
|
|
if (ito)
|
|
assert(ito.mod == MODimmutable);
|
|
if (sto)
|
|
assert(sto.mod == MODshared);
|
|
if (scto)
|
|
assert(scto.mod == 0);
|
|
if (wto)
|
|
assert(wto.mod == MODwild);
|
|
if (wcto)
|
|
assert(wcto.mod == MODwildconst);
|
|
if (swto)
|
|
assert(swto.mod == (MODshared | MODwild));
|
|
if (swcto)
|
|
assert(swcto.mod == (MODshared | MODwildconst));
|
|
break;
|
|
case MODshared | MODwild:
|
|
if (cto)
|
|
assert(cto.mod == MODconst);
|
|
if (ito)
|
|
assert(ito.mod == MODimmutable);
|
|
if (sto)
|
|
assert(sto.mod == MODshared);
|
|
if (scto)
|
|
assert(scto.mod == (MODshared | MODconst));
|
|
if (wto)
|
|
assert(wto.mod == MODwild);
|
|
if (wcto)
|
|
assert(wcto.mod == MODwildconst);
|
|
if (swto)
|
|
assert(swto.mod == 0);
|
|
if (swcto)
|
|
assert(swcto.mod == (MODshared | MODwildconst));
|
|
break;
|
|
case MODshared | MODwildconst:
|
|
assert(!cto || cto.mod == MODconst);
|
|
assert(!ito || ito.mod == MODimmutable);
|
|
assert(!sto || sto.mod == MODshared);
|
|
assert(!scto || scto.mod == (MODshared | MODconst));
|
|
assert(!wto || wto.mod == MODwild);
|
|
assert(!wcto || wcto.mod == MODwildconst);
|
|
assert(!swto || swto.mod == (MODshared | MODwild));
|
|
assert(!swcto || swcto.mod == 0);
|
|
break;
|
|
case MODimmutable:
|
|
if (cto)
|
|
assert(cto.mod == MODconst);
|
|
if (ito)
|
|
assert(ito.mod == 0);
|
|
if (sto)
|
|
assert(sto.mod == MODshared);
|
|
if (scto)
|
|
assert(scto.mod == (MODshared | MODconst));
|
|
if (wto)
|
|
assert(wto.mod == MODwild);
|
|
if (wcto)
|
|
assert(wcto.mod == MODwildconst);
|
|
if (swto)
|
|
assert(swto.mod == (MODshared | MODwild));
|
|
if (swcto)
|
|
assert(swcto.mod == (MODshared | MODwildconst));
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
Type tn = nextOf();
|
|
if (tn && ty != Tfunction && tn.ty != Tfunction && ty != Tenum)
|
|
{
|
|
// Verify transitivity
|
|
switch (mod)
|
|
{
|
|
case 0:
|
|
case MODconst:
|
|
case MODwild:
|
|
case MODwildconst:
|
|
case MODshared:
|
|
case MODshared | MODconst:
|
|
case MODshared | MODwild:
|
|
case MODshared | MODwildconst:
|
|
case MODimmutable:
|
|
assert(tn.mod == MODimmutable || (tn.mod & mod) == mod);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
tn.check();
|
|
}
|
|
}
|
|
|
|
/*************************************
|
|
* Apply STCxxxx bits to existing type.
|
|
* Use *before* semantic analysis is run.
|
|
*/
|
|
final Type addSTC(StorageClass stc)
|
|
{
|
|
Type t = this;
|
|
if (t.isImmutable())
|
|
{
|
|
}
|
|
else if (stc & STCimmutable)
|
|
{
|
|
t = t.makeImmutable();
|
|
}
|
|
else
|
|
{
|
|
if ((stc & STCshared) && !t.isShared())
|
|
{
|
|
if (t.isWild())
|
|
{
|
|
if (t.isConst())
|
|
t = t.makeSharedWildConst();
|
|
else
|
|
t = t.makeSharedWild();
|
|
}
|
|
else
|
|
{
|
|
if (t.isConst())
|
|
t = t.makeSharedConst();
|
|
else
|
|
t = t.makeShared();
|
|
}
|
|
}
|
|
if ((stc & STCconst) && !t.isConst())
|
|
{
|
|
if (t.isShared())
|
|
{
|
|
if (t.isWild())
|
|
t = t.makeSharedWildConst();
|
|
else
|
|
t = t.makeSharedConst();
|
|
}
|
|
else
|
|
{
|
|
if (t.isWild())
|
|
t = t.makeWildConst();
|
|
else
|
|
t = t.makeConst();
|
|
}
|
|
}
|
|
if ((stc & STCwild) && !t.isWild())
|
|
{
|
|
if (t.isShared())
|
|
{
|
|
if (t.isConst())
|
|
t = t.makeSharedWildConst();
|
|
else
|
|
t = t.makeSharedWild();
|
|
}
|
|
else
|
|
{
|
|
if (t.isConst())
|
|
t = t.makeWildConst();
|
|
else
|
|
t = t.makeWild();
|
|
}
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
|
|
/************************************
|
|
* Apply MODxxxx bits to existing type.
|
|
*/
|
|
final Type castMod(MOD mod)
|
|
{
|
|
Type t;
|
|
switch (mod)
|
|
{
|
|
case 0:
|
|
t = unSharedOf().mutableOf();
|
|
break;
|
|
case MODconst:
|
|
t = unSharedOf().constOf();
|
|
break;
|
|
case MODwild:
|
|
t = unSharedOf().wildOf();
|
|
break;
|
|
case MODwildconst:
|
|
t = unSharedOf().wildConstOf();
|
|
break;
|
|
case MODshared:
|
|
t = mutableOf().sharedOf();
|
|
break;
|
|
case MODshared | MODconst:
|
|
t = sharedConstOf();
|
|
break;
|
|
case MODshared | MODwild:
|
|
t = sharedWildOf();
|
|
break;
|
|
case MODshared | MODwildconst:
|
|
t = sharedWildConstOf();
|
|
break;
|
|
case MODimmutable:
|
|
t = immutableOf();
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
return t;
|
|
}
|
|
|
|
/************************************
|
|
* Add MODxxxx bits to existing type.
|
|
* We're adding, not replacing, so adding const to
|
|
* a shared type => "shared const"
|
|
*/
|
|
final Type addMod(MOD mod)
|
|
{
|
|
/* Add anything to immutable, and it remains immutable
|
|
*/
|
|
Type t = this;
|
|
if (!t.isImmutable())
|
|
{
|
|
//printf("addMod(%x) %s\n", mod, toChars());
|
|
switch (mod)
|
|
{
|
|
case 0:
|
|
break;
|
|
case MODconst:
|
|
if (isShared())
|
|
{
|
|
if (isWild())
|
|
t = sharedWildConstOf();
|
|
else
|
|
t = sharedConstOf();
|
|
}
|
|
else
|
|
{
|
|
if (isWild())
|
|
t = wildConstOf();
|
|
else
|
|
t = constOf();
|
|
}
|
|
break;
|
|
case MODwild:
|
|
if (isShared())
|
|
{
|
|
if (isConst())
|
|
t = sharedWildConstOf();
|
|
else
|
|
t = sharedWildOf();
|
|
}
|
|
else
|
|
{
|
|
if (isConst())
|
|
t = wildConstOf();
|
|
else
|
|
t = wildOf();
|
|
}
|
|
break;
|
|
case MODwildconst:
|
|
if (isShared())
|
|
t = sharedWildConstOf();
|
|
else
|
|
t = wildConstOf();
|
|
break;
|
|
case MODshared:
|
|
if (isWild())
|
|
{
|
|
if (isConst())
|
|
t = sharedWildConstOf();
|
|
else
|
|
t = sharedWildOf();
|
|
}
|
|
else
|
|
{
|
|
if (isConst())
|
|
t = sharedConstOf();
|
|
else
|
|
t = sharedOf();
|
|
}
|
|
break;
|
|
case MODshared | MODconst:
|
|
if (isWild())
|
|
t = sharedWildConstOf();
|
|
else
|
|
t = sharedConstOf();
|
|
break;
|
|
case MODshared | MODwild:
|
|
if (isConst())
|
|
t = sharedWildConstOf();
|
|
else
|
|
t = sharedWildOf();
|
|
break;
|
|
case MODshared | MODwildconst:
|
|
t = sharedWildConstOf();
|
|
break;
|
|
case MODimmutable:
|
|
t = immutableOf();
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
|
|
/************************************
|
|
* Add storage class modifiers to type.
|
|
*/
|
|
Type addStorageClass(StorageClass stc)
|
|
{
|
|
/* Just translate to MOD bits and let addMod() do the work
|
|
*/
|
|
MOD mod = 0;
|
|
if (stc & STCimmutable)
|
|
mod = MODimmutable;
|
|
else
|
|
{
|
|
if (stc & (STCconst | STCin))
|
|
mod |= MODconst;
|
|
if (stc & STCwild)
|
|
mod |= MODwild;
|
|
if (stc & STCshared)
|
|
mod |= MODshared;
|
|
}
|
|
return addMod(mod);
|
|
}
|
|
|
|
final Type pointerTo()
|
|
{
|
|
if (ty == Terror)
|
|
return this;
|
|
if (!pto)
|
|
{
|
|
Type t = new TypePointer(this);
|
|
if (ty == Tfunction)
|
|
{
|
|
t.deco = t.merge().deco;
|
|
pto = t;
|
|
}
|
|
else
|
|
pto = t.merge();
|
|
}
|
|
return pto;
|
|
}
|
|
|
|
final Type referenceTo()
|
|
{
|
|
if (ty == Terror)
|
|
return this;
|
|
if (!rto)
|
|
{
|
|
Type t = new TypeReference(this);
|
|
rto = t.merge();
|
|
}
|
|
return rto;
|
|
}
|
|
|
|
final Type arrayOf()
|
|
{
|
|
if (ty == Terror)
|
|
return this;
|
|
if (!arrayof)
|
|
{
|
|
Type t = new TypeDArray(this);
|
|
arrayof = t.merge();
|
|
}
|
|
return arrayof;
|
|
}
|
|
|
|
// Make corresponding static array type without semantic
|
|
final Type sarrayOf(dinteger_t dim)
|
|
{
|
|
assert(deco);
|
|
Type t = new TypeSArray(this, new IntegerExp(Loc(), dim, Type.tsize_t));
|
|
// according to TypeSArray::semantic()
|
|
t = t.addMod(mod);
|
|
t = t.merge();
|
|
return t;
|
|
}
|
|
|
|
final Type aliasthisOf()
|
|
{
|
|
auto ad = isAggregate(this);
|
|
if (!ad || !ad.aliasthis)
|
|
return null;
|
|
|
|
auto s = ad.aliasthis;
|
|
if (s.isAliasDeclaration())
|
|
s = s.toAlias();
|
|
|
|
if (s.isTupleDeclaration())
|
|
return null;
|
|
|
|
if (auto vd = s.isVarDeclaration())
|
|
{
|
|
auto t = vd.type;
|
|
if (vd.needThis())
|
|
t = t.addMod(this.mod);
|
|
return t;
|
|
}
|
|
if (auto fd = s.isFuncDeclaration())
|
|
{
|
|
fd = resolveFuncCall(Loc(), null, fd, null, this, null, 1);
|
|
if (!fd || fd.errors || !fd.functionSemantic())
|
|
return Type.terror;
|
|
|
|
auto t = fd.type.nextOf();
|
|
if (!t) // issue 14185
|
|
return Type.terror;
|
|
t = t.substWildTo(mod == 0 ? MODmutable : mod);
|
|
return t;
|
|
}
|
|
if (auto d = s.isDeclaration())
|
|
{
|
|
assert(d.type);
|
|
return d.type;
|
|
}
|
|
if (auto ed = s.isEnumDeclaration())
|
|
{
|
|
return ed.type;
|
|
}
|
|
if (auto td = s.isTemplateDeclaration())
|
|
{
|
|
assert(td._scope);
|
|
auto fd = resolveFuncCall(Loc(), null, td, null, this, null, 1);
|
|
if (!fd || fd.errors || !fd.functionSemantic())
|
|
return Type.terror;
|
|
|
|
auto t = fd.type.nextOf();
|
|
if (!t)
|
|
return Type.terror;
|
|
t = t.substWildTo(mod == 0 ? MODmutable : mod);
|
|
return t;
|
|
}
|
|
|
|
//printf("%s\n", s.kind());
|
|
return null;
|
|
}
|
|
|
|
final bool checkAliasThisRec()
|
|
{
|
|
Type tb = toBasetype();
|
|
AliasThisRec* pflag;
|
|
if (tb.ty == Tstruct)
|
|
pflag = &(cast(TypeStruct)tb).att;
|
|
else if (tb.ty == Tclass)
|
|
pflag = &(cast(TypeClass)tb).att;
|
|
else
|
|
return false;
|
|
AliasThisRec flag = cast(AliasThisRec)(*pflag & RECtypeMask);
|
|
if (flag == RECfwdref)
|
|
{
|
|
Type att = aliasthisOf();
|
|
flag = att && att.implicitConvTo(this) ? RECyes : RECno;
|
|
}
|
|
*pflag = cast(AliasThisRec)(flag | (*pflag & ~RECtypeMask));
|
|
return flag == RECyes;
|
|
}
|
|
|
|
Type makeConst()
|
|
{
|
|
//printf("Type::makeConst() %p, %s\n", this, toChars());
|
|
if (cto)
|
|
return cto;
|
|
Type t = this.nullAttributes();
|
|
t.mod = MODconst;
|
|
//printf("-Type::makeConst() %p, %s\n", t, toChars());
|
|
return t;
|
|
}
|
|
|
|
Type makeImmutable()
|
|
{
|
|
if (ito)
|
|
return ito;
|
|
Type t = this.nullAttributes();
|
|
t.mod = MODimmutable;
|
|
return t;
|
|
}
|
|
|
|
Type makeShared()
|
|
{
|
|
if (sto)
|
|
return sto;
|
|
Type t = this.nullAttributes();
|
|
t.mod = MODshared;
|
|
return t;
|
|
}
|
|
|
|
Type makeSharedConst()
|
|
{
|
|
if (scto)
|
|
return scto;
|
|
Type t = this.nullAttributes();
|
|
t.mod = MODshared | MODconst;
|
|
return t;
|
|
}
|
|
|
|
Type makeWild()
|
|
{
|
|
if (wto)
|
|
return wto;
|
|
Type t = this.nullAttributes();
|
|
t.mod = MODwild;
|
|
return t;
|
|
}
|
|
|
|
Type makeWildConst()
|
|
{
|
|
if (wcto)
|
|
return wcto;
|
|
Type t = this.nullAttributes();
|
|
t.mod = MODwildconst;
|
|
return t;
|
|
}
|
|
|
|
Type makeSharedWild()
|
|
{
|
|
if (swto)
|
|
return swto;
|
|
Type t = this.nullAttributes();
|
|
t.mod = MODshared | MODwild;
|
|
return t;
|
|
}
|
|
|
|
Type makeSharedWildConst()
|
|
{
|
|
if (swcto)
|
|
return swcto;
|
|
Type t = this.nullAttributes();
|
|
t.mod = MODshared | MODwildconst;
|
|
return t;
|
|
}
|
|
|
|
Type makeMutable()
|
|
{
|
|
Type t = this.nullAttributes();
|
|
t.mod = mod & MODshared;
|
|
return t;
|
|
}
|
|
|
|
Dsymbol toDsymbol(Scope* sc)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/*******************************
|
|
* If this is a shell around another type,
|
|
* get that other type.
|
|
*/
|
|
Type toBasetype()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
bool isBaseOf(Type t, int* poffset)
|
|
{
|
|
return 0; // assume not
|
|
}
|
|
|
|
/********************************
|
|
* Determine if 'this' can be implicitly converted
|
|
* to type 'to'.
|
|
* Returns:
|
|
* MATCHnomatch, MATCHconvert, MATCHconst, MATCHexact
|
|
*/
|
|
MATCH implicitConvTo(Type to)
|
|
{
|
|
//printf("Type::implicitConvTo(this=%p, to=%p)\n", this, to);
|
|
//printf("from: %s\n", toChars());
|
|
//printf("to : %s\n", to->toChars());
|
|
if (this.equals(to))
|
|
return MATCHexact;
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
/*******************************
|
|
* Determine if converting 'this' to 'to' is an identity operation,
|
|
* a conversion to const operation, or the types aren't the same.
|
|
* Returns:
|
|
* MATCHexact 'this' == 'to'
|
|
* MATCHconst 'to' is const
|
|
* MATCHnomatch conversion to mutable or invariant
|
|
*/
|
|
MATCH constConv(Type to)
|
|
{
|
|
//printf("Type::constConv(this = %s, to = %s)\n", toChars(), to->toChars());
|
|
if (equals(to))
|
|
return MATCHexact;
|
|
if (ty == to.ty && MODimplicitConv(mod, to.mod))
|
|
return MATCHconst;
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
/***************************************
|
|
* Return MOD bits matching this type to wild parameter type (tprm).
|
|
*/
|
|
ubyte deduceWild(Type t, bool isRef)
|
|
{
|
|
//printf("Type::deduceWild this = '%s', tprm = '%s'\n", toChars(), tprm->toChars());
|
|
if (t.isWild())
|
|
{
|
|
if (isImmutable())
|
|
return MODimmutable;
|
|
else if (isWildConst())
|
|
{
|
|
if (t.isWildConst())
|
|
return MODwild;
|
|
else
|
|
return MODwildconst;
|
|
}
|
|
else if (isWild())
|
|
return MODwild;
|
|
else if (isConst())
|
|
return MODconst;
|
|
else if (isMutable())
|
|
return MODmutable;
|
|
else
|
|
assert(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Type substWildTo(uint mod)
|
|
{
|
|
//printf("+Type::substWildTo this = %s, mod = x%x\n", toChars(), mod);
|
|
Type t;
|
|
if (Type tn = nextOf())
|
|
{
|
|
// substitution has no effect on function pointer type.
|
|
if (ty == Tpointer && tn.ty == Tfunction)
|
|
{
|
|
t = this;
|
|
goto L1;
|
|
}
|
|
t = tn.substWildTo(mod);
|
|
if (t == tn)
|
|
t = this;
|
|
else
|
|
{
|
|
if (ty == Tpointer)
|
|
t = t.pointerTo();
|
|
else if (ty == Tarray)
|
|
t = t.arrayOf();
|
|
else if (ty == Tsarray)
|
|
t = new TypeSArray(t, (cast(TypeSArray)this).dim.syntaxCopy());
|
|
else if (ty == Taarray)
|
|
{
|
|
t = new TypeAArray(t, (cast(TypeAArray)this).index.syntaxCopy());
|
|
(cast(TypeAArray)t).sc = (cast(TypeAArray)this).sc; // duplicate scope
|
|
}
|
|
else if (ty == Tdelegate)
|
|
{
|
|
t = new TypeDelegate(t);
|
|
}
|
|
else
|
|
assert(0);
|
|
t = t.merge();
|
|
}
|
|
}
|
|
else
|
|
t = this;
|
|
L1:
|
|
if (isWild())
|
|
{
|
|
if (mod == MODimmutable)
|
|
{
|
|
t = t.immutableOf();
|
|
}
|
|
else if (mod == MODwildconst)
|
|
{
|
|
t = t.wildConstOf();
|
|
}
|
|
else if (mod == MODwild)
|
|
{
|
|
if (isWildConst())
|
|
t = t.wildConstOf();
|
|
else
|
|
t = t.wildOf();
|
|
}
|
|
else if (mod == MODconst)
|
|
{
|
|
t = t.constOf();
|
|
}
|
|
else
|
|
{
|
|
if (isWildConst())
|
|
t = t.constOf();
|
|
else
|
|
t = t.mutableOf();
|
|
}
|
|
}
|
|
if (isConst())
|
|
t = t.addMod(MODconst);
|
|
if (isShared())
|
|
t = t.addMod(MODshared);
|
|
//printf("-Type::substWildTo t = %s\n", t->toChars());
|
|
return t;
|
|
}
|
|
|
|
final Type unqualify(uint m)
|
|
{
|
|
Type t = mutableOf().unSharedOf();
|
|
Type tn = ty == Tenum ? null : nextOf();
|
|
if (tn && tn.ty != Tfunction)
|
|
{
|
|
Type utn = tn.unqualify(m);
|
|
if (utn != tn)
|
|
{
|
|
if (ty == Tpointer)
|
|
t = utn.pointerTo();
|
|
else if (ty == Tarray)
|
|
t = utn.arrayOf();
|
|
else if (ty == Tsarray)
|
|
t = new TypeSArray(utn, (cast(TypeSArray)this).dim);
|
|
else if (ty == Taarray)
|
|
{
|
|
t = new TypeAArray(utn, (cast(TypeAArray)this).index);
|
|
(cast(TypeAArray)t).sc = (cast(TypeAArray)this).sc; // duplicate scope
|
|
}
|
|
else
|
|
assert(0);
|
|
t = t.merge();
|
|
}
|
|
}
|
|
t = t.addMod(mod & ~m);
|
|
return t;
|
|
}
|
|
|
|
/**************************
|
|
* Return type with the top level of it being mutable.
|
|
*/
|
|
Type toHeadMutable()
|
|
{
|
|
if (!mod)
|
|
return this;
|
|
return mutableOf();
|
|
}
|
|
|
|
ClassDeclaration isClassHandle()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/***************************************
|
|
* Calculate built-in properties which just the type is necessary.
|
|
*
|
|
* If flag == 1, don't report "not a property" error and just return NULL.
|
|
*/
|
|
Expression getProperty(Loc loc, Identifier ident, int flag)
|
|
{
|
|
Expression e;
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("Type::getProperty(type = '%s', ident = '%s')\n", toChars(), ident.toChars());
|
|
}
|
|
if (ident == Id.__sizeof)
|
|
{
|
|
d_uns64 sz = size(loc);
|
|
if (sz == SIZE_INVALID)
|
|
return new ErrorExp();
|
|
e = new IntegerExp(loc, sz, Type.tsize_t);
|
|
}
|
|
else if (ident == Id.__xalignof)
|
|
{
|
|
e = new IntegerExp(loc, alignsize(), Type.tsize_t);
|
|
}
|
|
else if (ident == Id._init)
|
|
{
|
|
Type tb = toBasetype();
|
|
e = defaultInitLiteral(loc);
|
|
if (tb.ty == Tstruct && tb.needsNested())
|
|
{
|
|
StructLiteralExp se = cast(StructLiteralExp)e;
|
|
se.useStaticInit = true;
|
|
}
|
|
}
|
|
else if (ident == Id._mangleof)
|
|
{
|
|
if (!deco)
|
|
{
|
|
error(loc, "forward reference of type %s.mangleof", toChars());
|
|
e = new ErrorExp();
|
|
}
|
|
else
|
|
{
|
|
e = new StringExp(loc, deco);
|
|
Scope sc;
|
|
e = e.semantic(&sc);
|
|
}
|
|
}
|
|
else if (ident == Id.stringof)
|
|
{
|
|
const s = toChars();
|
|
e = new StringExp(loc, cast(char*)s);
|
|
Scope sc;
|
|
e = e.semantic(&sc);
|
|
}
|
|
else if (flag && this != Type.terror)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
Dsymbol s = null;
|
|
if (ty == Tstruct || ty == Tclass || ty == Tenum)
|
|
s = toDsymbol(null);
|
|
if (s)
|
|
s = s.search_correct(ident);
|
|
if (this != Type.terror)
|
|
{
|
|
if (s)
|
|
error(loc, "no property '%s' for type '%s', did you mean '%s'?", ident.toChars(), toChars(), s.toChars());
|
|
else
|
|
error(loc, "no property '%s' for type '%s'", ident.toChars(), toChars());
|
|
}
|
|
e = new ErrorExp();
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/***************************************
|
|
* Access the members of the object e. This type is same as e->type.
|
|
*
|
|
* If flag == 1, don't report "not a property" error and just return NULL.
|
|
*/
|
|
Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
VarDeclaration v = null;
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("Type::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
|
|
}
|
|
Expression ex = e;
|
|
while (ex.op == TOKcomma)
|
|
ex = (cast(CommaExp)ex).e2;
|
|
if (ex.op == TOKdotvar)
|
|
{
|
|
DotVarExp dv = cast(DotVarExp)ex;
|
|
v = dv.var.isVarDeclaration();
|
|
}
|
|
else if (ex.op == TOKvar)
|
|
{
|
|
VarExp ve = cast(VarExp)ex;
|
|
v = ve.var.isVarDeclaration();
|
|
}
|
|
if (v)
|
|
{
|
|
if (ident == Id.offsetof)
|
|
{
|
|
if (v.isField())
|
|
{
|
|
e = new IntegerExp(e.loc, v.offset, Type.tsize_t);
|
|
return e;
|
|
}
|
|
}
|
|
else if (ident == Id._init)
|
|
{
|
|
Type tb = toBasetype();
|
|
e = defaultInitLiteral(e.loc);
|
|
if (tb.ty == Tstruct && tb.needsNested())
|
|
{
|
|
StructLiteralExp se = cast(StructLiteralExp)e;
|
|
se.useStaticInit = true;
|
|
}
|
|
goto Lreturn;
|
|
}
|
|
}
|
|
if (ident == Id.stringof)
|
|
{
|
|
/* Bugzilla 3796: this should demangle e->type->deco rather than
|
|
* pretty-printing the type.
|
|
*/
|
|
const s = e.toChars();
|
|
e = new StringExp(e.loc, cast(char*)s);
|
|
}
|
|
else
|
|
e = getProperty(e.loc, ident, flag);
|
|
Lreturn:
|
|
if (!flag || e)
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
/************************************
|
|
* Return alignment to use for this type.
|
|
*/
|
|
structalign_t alignment()
|
|
{
|
|
return STRUCTALIGN_DEFAULT;
|
|
}
|
|
|
|
/***************************************
|
|
* Figures out what to do with an undefined member reference
|
|
* for classes and structs.
|
|
*
|
|
* If flag == 1, don't report "not a property" error and just return NULL.
|
|
*/
|
|
final Expression noMember(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
assert(ty == Tstruct || ty == Tclass);
|
|
auto sym = toDsymbol(sc).isAggregateDeclaration();
|
|
assert(sym);
|
|
if (ident != Id.__sizeof &&
|
|
ident != Id.__xalignof &&
|
|
ident != Id._init &&
|
|
ident != Id._mangleof &&
|
|
ident != Id.stringof &&
|
|
ident != Id.offsetof &&
|
|
// Bugzilla 15045: Don't forward special built-in member functions.
|
|
ident != Id.ctor &&
|
|
ident != Id.dtor &&
|
|
ident != Id.__xdtor &&
|
|
ident != Id.postblit &&
|
|
ident != Id.__xpostblit)
|
|
{
|
|
/* Look for overloaded opDot() to see if we should forward request
|
|
* to it.
|
|
*/
|
|
if (auto fd = search_function(sym, Id.opDot))
|
|
{
|
|
/* Rewrite e.ident as:
|
|
* e.opDot().ident
|
|
*/
|
|
e = build_overload(e.loc, sc, e, null, fd);
|
|
e = new DotIdExp(e.loc, e, ident);
|
|
return e.semantic(sc);
|
|
}
|
|
|
|
/* Look for overloaded opDispatch to see if we should forward request
|
|
* to it.
|
|
*/
|
|
if (auto fd = search_function(sym, Id.opDispatch))
|
|
{
|
|
/* Rewrite e.ident as:
|
|
* e.opDispatch!("ident")
|
|
*/
|
|
TemplateDeclaration td = fd.isTemplateDeclaration();
|
|
if (!td)
|
|
{
|
|
fd.error("must be a template opDispatch(string s), not a %s", fd.kind());
|
|
return new ErrorExp();
|
|
}
|
|
auto se = new StringExp(e.loc, cast(char*)ident.toChars());
|
|
auto tiargs = new Objects();
|
|
tiargs.push(se);
|
|
auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs);
|
|
dti.ti.tempdecl = td;
|
|
/* opDispatch, which doesn't need IFTI, may occur instantiate error.
|
|
* It should be gagged if flag != 0.
|
|
* e.g.
|
|
* tempalte opDispatch(name) if (isValid!name) { ... }
|
|
*/
|
|
uint errors = flag ? global.startGagging() : 0;
|
|
e = dti.semanticY(sc, 0);
|
|
if (flag && global.endGagging(errors))
|
|
e = null;
|
|
return e;
|
|
}
|
|
|
|
/* See if we should forward to the alias this.
|
|
*/
|
|
if (sym.aliasthis)
|
|
{
|
|
/* Rewrite e.ident as:
|
|
* e.aliasthis.ident
|
|
*/
|
|
e = resolveAliasThis(sc, e);
|
|
auto die = new DotIdExp(e.loc, e, ident);
|
|
return die.semanticY(sc, flag);
|
|
}
|
|
}
|
|
return Type.dotExp(sc, e, ident, flag);
|
|
}
|
|
|
|
Expression defaultInit(Loc loc = Loc())
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("Type::defaultInit() '%s'\n", toChars());
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/***************************************
|
|
* Use when we prefer the default initializer to be a literal,
|
|
* rather than a global immutable variable.
|
|
*/
|
|
Expression defaultInitLiteral(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("Type::defaultInitLiteral() '%s'\n", toChars());
|
|
}
|
|
return defaultInit(loc);
|
|
}
|
|
|
|
// if initializer is 0
|
|
bool isZeroInit(Loc loc = Loc())
|
|
{
|
|
return false; // assume not
|
|
}
|
|
|
|
final Identifier getTypeInfoIdent(int internal)
|
|
{
|
|
// _init_10TypeInfo_%s
|
|
OutBuffer buf;
|
|
buf.reserve(32);
|
|
mangleToBuffer(this, &buf, internal != 0);
|
|
size_t len = buf.offset;
|
|
buf.writeByte(0);
|
|
// Allocate buffer on stack, fail over to using malloc()
|
|
char[128] namebuf;
|
|
|
|
// Hash long symbol names
|
|
char* name;
|
|
if (IN_LLVM && global.params.hashThreshold && (len > global.params.hashThreshold))
|
|
{
|
|
import std.digest.md;
|
|
auto md5hash = md5Of(buf.peekString()[0..len]);
|
|
auto hashedname = toHexString(md5hash);
|
|
static assert(hashedname.length < namebuf.length-30);
|
|
name = namebuf.ptr;
|
|
sprintf(name, "_D%lluTypeInfo_%.*s6__initZ", cast(ulong)9 + hashedname.length, hashedname.length, hashedname.ptr);
|
|
}
|
|
else
|
|
{
|
|
// else path is DDMD original:
|
|
|
|
size_t namelen = 19 + len.sizeof * 3 + len + 1;
|
|
name = namelen <= namebuf.sizeof ? namebuf.ptr : cast(char*)malloc(namelen);
|
|
assert(name);
|
|
sprintf(name, "_D%lluTypeInfo_%s6__initZ", cast(ulong)9 + len, buf.data);
|
|
//printf("%p, deco = %s, name = %s\n", this, deco, name);
|
|
assert(strlen(name) < namelen); // don't overflow the buffer
|
|
|
|
}
|
|
|
|
size_t off = 0;
|
|
static if (!IN_GCC && !IN_LLVM)
|
|
{
|
|
if (global.params.isOSX || global.params.isWindows && !global.params.is64bit)
|
|
++off; // C mangling will add '_' back in
|
|
}
|
|
auto id = Identifier.idPool(name + off, strlen(name + off));
|
|
if (name != namebuf.ptr)
|
|
free(name);
|
|
return id;
|
|
}
|
|
|
|
/***************************************
|
|
* Resolve 'this' type to either type, symbol, or expression.
|
|
* If errors happened, resolved to Type.terror.
|
|
*/
|
|
void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false)
|
|
{
|
|
//printf("Type::resolve() %s, %d\n", toChars(), ty);
|
|
Type t = semantic(loc, sc);
|
|
*pt = t;
|
|
*pe = null;
|
|
*ps = null;
|
|
}
|
|
|
|
/***************************************
|
|
* Normalize `e` as the result of Type.resolve() process.
|
|
*/
|
|
final void resolveExp(Expression e, Type *pt, Expression *pe, Dsymbol* ps)
|
|
{
|
|
*pt = null;
|
|
*pe = null;
|
|
*ps = null;
|
|
|
|
Dsymbol s;
|
|
switch (e.op)
|
|
{
|
|
case TOKerror:
|
|
*pt = Type.terror;
|
|
return;
|
|
|
|
case TOKtype:
|
|
*pt = e.type;
|
|
return;
|
|
|
|
case TOKvar:
|
|
s = (cast(VarExp)e).var;
|
|
if (s.isVarDeclaration())
|
|
goto default;
|
|
//if (s.isOverDeclaration())
|
|
// todo;
|
|
break;
|
|
|
|
case TOKtemplate:
|
|
// TemplateDeclaration
|
|
s = (cast(TemplateExp)e).td;
|
|
break;
|
|
|
|
case TOKscope:
|
|
s = (cast(ScopeExp)e).sds;
|
|
// TemplateDeclaration, TemplateInstance, Import, Package, Module
|
|
break;
|
|
|
|
case TOKfunction:
|
|
s = getDsymbol(e);
|
|
break;
|
|
|
|
//case TOKthis:
|
|
//case TOKsuper:
|
|
|
|
//case TOKtuple:
|
|
|
|
//case TOKoverloadset:
|
|
|
|
//case TOKdotvar:
|
|
//case TOKdottd:
|
|
//case TOKdotti:
|
|
//case TOKdottype:
|
|
//case TOKdotid:
|
|
|
|
default:
|
|
*pe = e;
|
|
return;
|
|
}
|
|
|
|
*ps = s;
|
|
}
|
|
|
|
/***************************************
|
|
* Return !=0 if the type or any of its subtypes is wild.
|
|
*/
|
|
int hasWild() const
|
|
{
|
|
return mod & MODwild;
|
|
}
|
|
|
|
/********************************
|
|
* We've mistakenly parsed this as a type.
|
|
* Redo it as an Expression.
|
|
* NULL if cannot.
|
|
*/
|
|
Expression toExpression()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/***************************************
|
|
* Return !=0 if type has pointers that need to
|
|
* be scanned by the GC during a collection cycle.
|
|
*/
|
|
bool hasPointers()
|
|
{
|
|
//printf("Type::hasPointers() %s, %d\n", toChars(), ty);
|
|
return false;
|
|
}
|
|
|
|
/*************************************
|
|
* If this is a type of something, return that something.
|
|
*/
|
|
Type nextOf()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/*************************************
|
|
* If this is a type of static array, return its base element type.
|
|
*/
|
|
final Type baseElemOf()
|
|
{
|
|
Type t = toBasetype();
|
|
while (t.ty == Tsarray)
|
|
t = (cast(TypeSArray)t).next.toBasetype();
|
|
return t;
|
|
}
|
|
|
|
/****************************************
|
|
* Return the mask that an integral type will
|
|
* fit into.
|
|
*/
|
|
final uinteger_t sizemask()
|
|
{
|
|
uinteger_t m;
|
|
switch (toBasetype().ty)
|
|
{
|
|
case Tbool:
|
|
m = 1;
|
|
break;
|
|
case Tchar:
|
|
case Tint8:
|
|
case Tuns8:
|
|
m = 0xFF;
|
|
break;
|
|
case Twchar:
|
|
case Tint16:
|
|
case Tuns16:
|
|
m = 0xFFFFU;
|
|
break;
|
|
case Tdchar:
|
|
case Tint32:
|
|
case Tuns32:
|
|
m = 0xFFFFFFFFU;
|
|
break;
|
|
case Tint64:
|
|
case Tuns64:
|
|
m = 0xFFFFFFFFFFFFFFFFUL;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
return m;
|
|
}
|
|
|
|
/********************************
|
|
* true if when type goes out of scope, it needs a destructor applied.
|
|
* Only applies to value types, not ref types.
|
|
*/
|
|
bool needsDestruction()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/*********************************
|
|
*
|
|
*/
|
|
bool needsNested()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/*************************************
|
|
* Bugzilla 14488: Check if the inner most base type is complex or imaginary.
|
|
* Should only give alerts when set to emit transitional messages.
|
|
*/
|
|
final void checkComplexTransition(Loc loc)
|
|
{
|
|
Type t = baseElemOf();
|
|
while (t.ty == Tpointer || t.ty == Tarray)
|
|
t = t.nextOf().baseElemOf();
|
|
if (t.isimaginary() || t.iscomplex())
|
|
{
|
|
const(char)* p = loc.toChars();
|
|
Type rt;
|
|
switch (t.ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Timaginary32:
|
|
rt = Type.tfloat32;
|
|
break;
|
|
case Tcomplex64:
|
|
case Timaginary64:
|
|
rt = Type.tfloat64;
|
|
break;
|
|
case Tcomplex80:
|
|
case Timaginary80:
|
|
rt = Type.tfloat80;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
if (t.iscomplex())
|
|
{
|
|
fprintf(global.stdmsg, "%s: use of complex type '%s' is scheduled for deprecation, use 'std.complex.Complex!(%s)' instead\n", p ? p : "", toChars(), rt.toChars());
|
|
}
|
|
else
|
|
{
|
|
fprintf(global.stdmsg, "%s: use of imaginary type '%s' is scheduled for deprecation, use '%s' instead\n", p ? p : "", toChars(), rt.toChars());
|
|
}
|
|
}
|
|
}
|
|
|
|
final static void error(Loc loc, const(char)* format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
.verror(loc, format, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
final static void warning(Loc loc, const(char)* format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
.vwarning(loc, format, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
// For eliminating dynamic_cast
|
|
TypeBasic isTypeBasic()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeError : Type
|
|
{
|
|
public:
|
|
extern (D) this()
|
|
{
|
|
super(Terror);
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
// No semantic analysis done, no need to copy
|
|
return this;
|
|
}
|
|
|
|
override d_uns64 size(Loc loc)
|
|
{
|
|
return SIZE_INVALID;
|
|
}
|
|
|
|
override Expression getProperty(Loc loc, Identifier ident, int flag)
|
|
{
|
|
return new ErrorExp();
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
return new ErrorExp();
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
return new ErrorExp();
|
|
}
|
|
|
|
override Expression defaultInitLiteral(Loc loc)
|
|
{
|
|
return new ErrorExp();
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class TypeNext : Type
|
|
{
|
|
public:
|
|
Type next;
|
|
|
|
final extern (D) this(TY ty, Type next)
|
|
{
|
|
super(ty);
|
|
this.next = next;
|
|
}
|
|
|
|
override final void checkDeprecated(Loc loc, Scope* sc)
|
|
{
|
|
Type.checkDeprecated(loc, sc);
|
|
if (next) // next can be NULL if TypeFunction and auto return type
|
|
next.checkDeprecated(loc, sc);
|
|
}
|
|
|
|
override final int hasWild() const
|
|
{
|
|
if (ty == Tfunction)
|
|
return 0;
|
|
if (ty == Tdelegate)
|
|
return Type.hasWild();
|
|
return mod & MODwild || (next && next.hasWild());
|
|
}
|
|
|
|
/*******************************
|
|
* For TypeFunction, nextOf() can return NULL if the function return
|
|
* type is meant to be inferred, and semantic() hasn't yet ben run
|
|
* on the function. After semantic(), it must no longer be NULL.
|
|
*/
|
|
override final Type nextOf()
|
|
{
|
|
return next;
|
|
}
|
|
|
|
override final Type makeConst()
|
|
{
|
|
//printf("TypeNext::makeConst() %p, %s\n", this, toChars());
|
|
if (cto)
|
|
{
|
|
assert(cto.mod == MODconst);
|
|
return cto;
|
|
}
|
|
TypeNext t = cast(TypeNext)Type.makeConst();
|
|
if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
|
|
{
|
|
if (next.isShared())
|
|
{
|
|
if (next.isWild())
|
|
t.next = next.sharedWildConstOf();
|
|
else
|
|
t.next = next.sharedConstOf();
|
|
}
|
|
else
|
|
{
|
|
if (next.isWild())
|
|
t.next = next.wildConstOf();
|
|
else
|
|
t.next = next.constOf();
|
|
}
|
|
}
|
|
//printf("TypeNext::makeConst() returns %p, %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
override final Type makeImmutable()
|
|
{
|
|
//printf("TypeNext::makeImmutable() %s\n", toChars());
|
|
if (ito)
|
|
{
|
|
assert(ito.isImmutable());
|
|
return ito;
|
|
}
|
|
TypeNext t = cast(TypeNext)Type.makeImmutable();
|
|
if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
|
|
{
|
|
t.next = next.immutableOf();
|
|
}
|
|
return t;
|
|
}
|
|
|
|
override final Type makeShared()
|
|
{
|
|
//printf("TypeNext::makeShared() %s\n", toChars());
|
|
if (sto)
|
|
{
|
|
assert(sto.mod == MODshared);
|
|
return sto;
|
|
}
|
|
TypeNext t = cast(TypeNext)Type.makeShared();
|
|
if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
|
|
{
|
|
if (next.isWild())
|
|
{
|
|
if (next.isConst())
|
|
t.next = next.sharedWildConstOf();
|
|
else
|
|
t.next = next.sharedWildOf();
|
|
}
|
|
else
|
|
{
|
|
if (next.isConst())
|
|
t.next = next.sharedConstOf();
|
|
else
|
|
t.next = next.sharedOf();
|
|
}
|
|
}
|
|
//printf("TypeNext::makeShared() returns %p, %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
override final Type makeSharedConst()
|
|
{
|
|
//printf("TypeNext::makeSharedConst() %s\n", toChars());
|
|
if (scto)
|
|
{
|
|
assert(scto.mod == (MODshared | MODconst));
|
|
return scto;
|
|
}
|
|
TypeNext t = cast(TypeNext)Type.makeSharedConst();
|
|
if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
|
|
{
|
|
if (next.isWild())
|
|
t.next = next.sharedWildConstOf();
|
|
else
|
|
t.next = next.sharedConstOf();
|
|
}
|
|
//printf("TypeNext::makeSharedConst() returns %p, %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
override final Type makeWild()
|
|
{
|
|
//printf("TypeNext::makeWild() %s\n", toChars());
|
|
if (wto)
|
|
{
|
|
assert(wto.mod == MODwild);
|
|
return wto;
|
|
}
|
|
TypeNext t = cast(TypeNext)Type.makeWild();
|
|
if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
|
|
{
|
|
if (next.isShared())
|
|
{
|
|
if (next.isConst())
|
|
t.next = next.sharedWildConstOf();
|
|
else
|
|
t.next = next.sharedWildOf();
|
|
}
|
|
else
|
|
{
|
|
if (next.isConst())
|
|
t.next = next.wildConstOf();
|
|
else
|
|
t.next = next.wildOf();
|
|
}
|
|
}
|
|
//printf("TypeNext::makeWild() returns %p, %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
override final Type makeWildConst()
|
|
{
|
|
//printf("TypeNext::makeWildConst() %s\n", toChars());
|
|
if (wcto)
|
|
{
|
|
assert(wcto.mod == MODwildconst);
|
|
return wcto;
|
|
}
|
|
TypeNext t = cast(TypeNext)Type.makeWildConst();
|
|
if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
|
|
{
|
|
if (next.isShared())
|
|
t.next = next.sharedWildConstOf();
|
|
else
|
|
t.next = next.wildConstOf();
|
|
}
|
|
//printf("TypeNext::makeWildConst() returns %p, %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
override final Type makeSharedWild()
|
|
{
|
|
//printf("TypeNext::makeSharedWild() %s\n", toChars());
|
|
if (swto)
|
|
{
|
|
assert(swto.isSharedWild());
|
|
return swto;
|
|
}
|
|
TypeNext t = cast(TypeNext)Type.makeSharedWild();
|
|
if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
|
|
{
|
|
if (next.isConst())
|
|
t.next = next.sharedWildConstOf();
|
|
else
|
|
t.next = next.sharedWildOf();
|
|
}
|
|
//printf("TypeNext::makeSharedWild() returns %p, %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
override final Type makeSharedWildConst()
|
|
{
|
|
//printf("TypeNext::makeSharedWildConst() %s\n", toChars());
|
|
if (swcto)
|
|
{
|
|
assert(swcto.mod == (MODshared | MODwildconst));
|
|
return swcto;
|
|
}
|
|
TypeNext t = cast(TypeNext)Type.makeSharedWildConst();
|
|
if (ty != Tfunction && next.ty != Tfunction && !next.isImmutable())
|
|
{
|
|
t.next = next.sharedWildConstOf();
|
|
}
|
|
//printf("TypeNext::makeSharedWildConst() returns %p, %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
override final Type makeMutable()
|
|
{
|
|
//printf("TypeNext::makeMutable() %p, %s\n", this, toChars());
|
|
TypeNext t = cast(TypeNext)Type.makeMutable();
|
|
if (ty == Tsarray)
|
|
{
|
|
t.next = next.mutableOf();
|
|
}
|
|
//printf("TypeNext::makeMutable() returns %p, %s\n", t, t->toChars());
|
|
return t;
|
|
}
|
|
|
|
override MATCH constConv(Type to)
|
|
{
|
|
//printf("TypeNext::constConv from = %s, to = %s\n", toChars(), to->toChars());
|
|
if (equals(to))
|
|
return MATCHexact;
|
|
if (!(ty == to.ty && MODimplicitConv(mod, to.mod)))
|
|
return MATCHnomatch;
|
|
Type tn = to.nextOf();
|
|
if (!(tn && next.ty == tn.ty))
|
|
return MATCHnomatch;
|
|
MATCH m;
|
|
if (to.isConst()) // whole tail const conversion
|
|
{
|
|
// Recursive shared level check
|
|
m = next.constConv(tn);
|
|
if (m == MATCHexact)
|
|
m = MATCHconst;
|
|
}
|
|
else
|
|
{
|
|
//printf("\tnext => %s, to->next => %s\n", next->toChars(), tn->toChars());
|
|
m = next.equals(tn) ? MATCHconst : MATCHnomatch;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
override final ubyte deduceWild(Type t, bool isRef)
|
|
{
|
|
if (ty == Tfunction)
|
|
return 0;
|
|
ubyte wm;
|
|
Type tn = t.nextOf();
|
|
if (!isRef && (ty == Tarray || ty == Tpointer) && tn)
|
|
{
|
|
wm = next.deduceWild(tn, true);
|
|
if (!wm)
|
|
wm = Type.deduceWild(t, true);
|
|
}
|
|
else
|
|
{
|
|
wm = Type.deduceWild(t, isRef);
|
|
if (!wm && tn)
|
|
wm = next.deduceWild(tn, true);
|
|
}
|
|
return wm;
|
|
}
|
|
|
|
final void transitive()
|
|
{
|
|
/* Invoke transitivity of type attributes
|
|
*/
|
|
next = next.addMod(mod);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeBasic : Type
|
|
{
|
|
public:
|
|
const(char)* dstring;
|
|
uint flags;
|
|
|
|
extern (D) this(TY ty)
|
|
{
|
|
super(ty);
|
|
const(char)* d;
|
|
uint flags = 0;
|
|
switch (ty)
|
|
{
|
|
case Tvoid:
|
|
d = Token.toChars(TOKvoid);
|
|
break;
|
|
case Tint8:
|
|
d = Token.toChars(TOKint8);
|
|
flags |= TFLAGSintegral;
|
|
break;
|
|
case Tuns8:
|
|
d = Token.toChars(TOKuns8);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
case Tint16:
|
|
d = Token.toChars(TOKint16);
|
|
flags |= TFLAGSintegral;
|
|
break;
|
|
case Tuns16:
|
|
d = Token.toChars(TOKuns16);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
case Tint32:
|
|
d = Token.toChars(TOKint32);
|
|
flags |= TFLAGSintegral;
|
|
break;
|
|
case Tuns32:
|
|
d = Token.toChars(TOKuns32);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
case Tfloat32:
|
|
d = Token.toChars(TOKfloat32);
|
|
flags |= TFLAGSfloating | TFLAGSreal;
|
|
break;
|
|
case Tint64:
|
|
d = Token.toChars(TOKint64);
|
|
flags |= TFLAGSintegral;
|
|
break;
|
|
case Tuns64:
|
|
d = Token.toChars(TOKuns64);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
case Tint128:
|
|
d = Token.toChars(TOKint128);
|
|
flags |= TFLAGSintegral;
|
|
break;
|
|
case Tuns128:
|
|
d = Token.toChars(TOKuns128);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
case Tfloat64:
|
|
d = Token.toChars(TOKfloat64);
|
|
flags |= TFLAGSfloating | TFLAGSreal;
|
|
break;
|
|
case Tfloat80:
|
|
d = Token.toChars(TOKfloat80);
|
|
flags |= TFLAGSfloating | TFLAGSreal;
|
|
break;
|
|
case Timaginary32:
|
|
d = Token.toChars(TOKimaginary32);
|
|
flags |= TFLAGSfloating | TFLAGSimaginary;
|
|
break;
|
|
case Timaginary64:
|
|
d = Token.toChars(TOKimaginary64);
|
|
flags |= TFLAGSfloating | TFLAGSimaginary;
|
|
break;
|
|
case Timaginary80:
|
|
d = Token.toChars(TOKimaginary80);
|
|
flags |= TFLAGSfloating | TFLAGSimaginary;
|
|
break;
|
|
case Tcomplex32:
|
|
d = Token.toChars(TOKcomplex32);
|
|
flags |= TFLAGSfloating | TFLAGScomplex;
|
|
break;
|
|
case Tcomplex64:
|
|
d = Token.toChars(TOKcomplex64);
|
|
flags |= TFLAGSfloating | TFLAGScomplex;
|
|
break;
|
|
case Tcomplex80:
|
|
d = Token.toChars(TOKcomplex80);
|
|
flags |= TFLAGSfloating | TFLAGScomplex;
|
|
break;
|
|
case Tbool:
|
|
d = "bool";
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
case Tchar:
|
|
d = Token.toChars(TOKchar);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
case Twchar:
|
|
d = Token.toChars(TOKwchar);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
case Tdchar:
|
|
d = Token.toChars(TOKdchar);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
this.dstring = d;
|
|
this.flags = flags;
|
|
merge();
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return dstring;
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
// No semantic analysis done on basic types, no need to copy
|
|
return this;
|
|
}
|
|
|
|
override d_uns64 size(Loc loc) const
|
|
{
|
|
uint size;
|
|
//printf("TypeBasic::size()\n");
|
|
switch (ty)
|
|
{
|
|
case Tint8:
|
|
case Tuns8:
|
|
size = 1;
|
|
break;
|
|
case Tint16:
|
|
case Tuns16:
|
|
size = 2;
|
|
break;
|
|
case Tint32:
|
|
case Tuns32:
|
|
case Tfloat32:
|
|
case Timaginary32:
|
|
size = 4;
|
|
break;
|
|
case Tint64:
|
|
case Tuns64:
|
|
case Tfloat64:
|
|
case Timaginary64:
|
|
size = 8;
|
|
break;
|
|
case Tfloat80:
|
|
case Timaginary80:
|
|
size = Target.realsize;
|
|
break;
|
|
case Tcomplex32:
|
|
size = 8;
|
|
break;
|
|
case Tcomplex64:
|
|
case Tint128:
|
|
case Tuns128:
|
|
size = 16;
|
|
break;
|
|
case Tcomplex80:
|
|
size = Target.realsize * 2;
|
|
break;
|
|
case Tvoid:
|
|
//size = Type::size(); // error message
|
|
size = 1;
|
|
break;
|
|
case Tbool:
|
|
size = 1;
|
|
break;
|
|
case Tchar:
|
|
size = 1;
|
|
break;
|
|
case Twchar:
|
|
size = 2;
|
|
break;
|
|
case Tdchar:
|
|
size = 4;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
//printf("TypeBasic::size() = %d\n", size);
|
|
return size;
|
|
}
|
|
|
|
override uint alignsize()
|
|
{
|
|
return Target.alignsize(this);
|
|
}
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
override uint alignment()
|
|
{
|
|
if ( (ty == Tfloat80 || ty == Timaginary80) && (size(Loc()) > 8)
|
|
&& isArchx86_64() )
|
|
{
|
|
return 16;
|
|
}
|
|
return Type.alignment();
|
|
}
|
|
}
|
|
|
|
override Expression getProperty(Loc loc, Identifier ident, int flag)
|
|
{
|
|
Expression e;
|
|
dinteger_t ivalue;
|
|
d_float80 fvalue;
|
|
//printf("TypeBasic::getProperty('%s')\n", ident->toChars());
|
|
if (ident == Id.max)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tint8:
|
|
ivalue = 0x7F;
|
|
goto Livalue;
|
|
case Tuns8:
|
|
ivalue = 0xFF;
|
|
goto Livalue;
|
|
case Tint16:
|
|
ivalue = 0x7FFFU;
|
|
goto Livalue;
|
|
case Tuns16:
|
|
ivalue = 0xFFFFU;
|
|
goto Livalue;
|
|
case Tint32:
|
|
ivalue = 0x7FFFFFFFU;
|
|
goto Livalue;
|
|
case Tuns32:
|
|
ivalue = 0xFFFFFFFFU;
|
|
goto Livalue;
|
|
case Tint64:
|
|
ivalue = 0x7FFFFFFFFFFFFFFFL;
|
|
goto Livalue;
|
|
case Tuns64:
|
|
ivalue = 0xFFFFFFFFFFFFFFFFUL;
|
|
goto Livalue;
|
|
case Tbool:
|
|
ivalue = 1;
|
|
goto Livalue;
|
|
case Tchar:
|
|
ivalue = 0xFF;
|
|
goto Livalue;
|
|
case Twchar:
|
|
ivalue = 0xFFFFU;
|
|
goto Livalue;
|
|
case Tdchar:
|
|
ivalue = 0x10FFFFU;
|
|
goto Livalue;
|
|
case Tcomplex32:
|
|
case Timaginary32:
|
|
case Tfloat32:
|
|
fvalue = FLT_MAX;
|
|
goto Lfvalue;
|
|
case Tcomplex64:
|
|
case Timaginary64:
|
|
case Tfloat64:
|
|
fvalue = DBL_MAX;
|
|
goto Lfvalue;
|
|
case Tcomplex80:
|
|
case Timaginary80:
|
|
case Tfloat80:
|
|
fvalue = Port.ldbl_max;
|
|
goto Lfvalue;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.min)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tint8:
|
|
ivalue = -128;
|
|
goto Livalue;
|
|
case Tuns8:
|
|
ivalue = 0;
|
|
goto Livalue;
|
|
case Tint16:
|
|
ivalue = -32768;
|
|
goto Livalue;
|
|
case Tuns16:
|
|
ivalue = 0;
|
|
goto Livalue;
|
|
case Tint32:
|
|
ivalue = -2147483647 - 1;
|
|
goto Livalue;
|
|
case Tuns32:
|
|
ivalue = 0;
|
|
goto Livalue;
|
|
case Tint64:
|
|
ivalue = (-9223372036854775807L - 1L);
|
|
goto Livalue;
|
|
case Tuns64:
|
|
ivalue = 0;
|
|
goto Livalue;
|
|
case Tbool:
|
|
ivalue = 0;
|
|
goto Livalue;
|
|
case Tchar:
|
|
ivalue = 0;
|
|
goto Livalue;
|
|
case Twchar:
|
|
ivalue = 0;
|
|
goto Livalue;
|
|
case Tdchar:
|
|
ivalue = 0;
|
|
goto Livalue;
|
|
case Tcomplex32:
|
|
case Timaginary32:
|
|
case Tfloat32:
|
|
case Tcomplex64:
|
|
case Timaginary64:
|
|
case Tfloat64:
|
|
case Tcomplex80:
|
|
case Timaginary80:
|
|
case Tfloat80:
|
|
error(loc, "use .min_normal property instead of .min");
|
|
return new ErrorExp();
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.min_normal)
|
|
{
|
|
Lmin_normal:
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Timaginary32:
|
|
case Tfloat32:
|
|
fvalue = FLT_MIN;
|
|
goto Lfvalue;
|
|
case Tcomplex64:
|
|
case Timaginary64:
|
|
case Tfloat64:
|
|
fvalue = DBL_MIN;
|
|
goto Lfvalue;
|
|
case Tcomplex80:
|
|
case Timaginary80:
|
|
case Tfloat80:
|
|
version(IN_LLVM)
|
|
fvalue = Port.ldbl_min_normal;
|
|
else
|
|
fvalue = LDBL_MIN;
|
|
goto Lfvalue;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.nan)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Tcomplex64:
|
|
case Tcomplex80:
|
|
case Timaginary32:
|
|
case Timaginary64:
|
|
case Timaginary80:
|
|
case Tfloat32:
|
|
case Tfloat64:
|
|
case Tfloat80:
|
|
{
|
|
fvalue = Port.ldbl_nan;
|
|
goto Lfvalue;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.infinity)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Tcomplex64:
|
|
case Tcomplex80:
|
|
case Timaginary32:
|
|
case Timaginary64:
|
|
case Timaginary80:
|
|
case Tfloat32:
|
|
case Tfloat64:
|
|
case Tfloat80:
|
|
fvalue = Port.ldbl_infinity;
|
|
goto Lfvalue;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.dig)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Timaginary32:
|
|
case Tfloat32:
|
|
ivalue = FLT_DIG;
|
|
goto Lint;
|
|
case Tcomplex64:
|
|
case Timaginary64:
|
|
case Tfloat64:
|
|
ivalue = DBL_DIG;
|
|
goto Lint;
|
|
case Tcomplex80:
|
|
case Timaginary80:
|
|
case Tfloat80:
|
|
version(IN_LLVM)
|
|
ivalue = Port.ldbl_dig;
|
|
else
|
|
ivalue = LDBL_DIG;
|
|
goto Lint;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.epsilon)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Timaginary32:
|
|
case Tfloat32:
|
|
fvalue = FLT_EPSILON;
|
|
goto Lfvalue;
|
|
case Tcomplex64:
|
|
case Timaginary64:
|
|
case Tfloat64:
|
|
fvalue = DBL_EPSILON;
|
|
goto Lfvalue;
|
|
case Tcomplex80:
|
|
case Timaginary80:
|
|
case Tfloat80:
|
|
version(IN_LLVM)
|
|
fvalue = Port.ldbl_epsilon;
|
|
else
|
|
fvalue = LDBL_EPSILON;
|
|
goto Lfvalue;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.mant_dig)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Timaginary32:
|
|
case Tfloat32:
|
|
ivalue = FLT_MANT_DIG;
|
|
goto Lint;
|
|
case Tcomplex64:
|
|
case Timaginary64:
|
|
case Tfloat64:
|
|
ivalue = DBL_MANT_DIG;
|
|
goto Lint;
|
|
case Tcomplex80:
|
|
case Timaginary80:
|
|
case Tfloat80:
|
|
version(IN_LLVM)
|
|
ivalue = Port.ldbl_mant_dig;
|
|
else
|
|
ivalue = LDBL_MANT_DIG;
|
|
goto Lint;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.max_10_exp)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Timaginary32:
|
|
case Tfloat32:
|
|
ivalue = FLT_MAX_10_EXP;
|
|
goto Lint;
|
|
case Tcomplex64:
|
|
case Timaginary64:
|
|
case Tfloat64:
|
|
ivalue = DBL_MAX_10_EXP;
|
|
goto Lint;
|
|
case Tcomplex80:
|
|
case Timaginary80:
|
|
case Tfloat80:
|
|
version(IN_LLVM)
|
|
ivalue = Port.ldbl_max_10_exp;
|
|
else
|
|
ivalue = LDBL_MAX_10_EXP;
|
|
goto Lint;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.max_exp)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Timaginary32:
|
|
case Tfloat32:
|
|
ivalue = FLT_MAX_EXP;
|
|
goto Lint;
|
|
case Tcomplex64:
|
|
case Timaginary64:
|
|
case Tfloat64:
|
|
ivalue = DBL_MAX_EXP;
|
|
goto Lint;
|
|
case Tcomplex80:
|
|
case Timaginary80:
|
|
case Tfloat80:
|
|
version(IN_LLVM)
|
|
ivalue = Port.ldbl_max_exp;
|
|
else
|
|
ivalue = LDBL_MAX_EXP;
|
|
goto Lint;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.min_10_exp)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Timaginary32:
|
|
case Tfloat32:
|
|
ivalue = FLT_MIN_10_EXP;
|
|
goto Lint;
|
|
case Tcomplex64:
|
|
case Timaginary64:
|
|
case Tfloat64:
|
|
ivalue = DBL_MIN_10_EXP;
|
|
goto Lint;
|
|
case Tcomplex80:
|
|
case Timaginary80:
|
|
case Tfloat80:
|
|
version(IN_LLVM)
|
|
ivalue = Port.ldbl_min_10_exp;
|
|
else
|
|
ivalue = LDBL_MIN_10_EXP;
|
|
goto Lint;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.min_exp)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
case Timaginary32:
|
|
case Tfloat32:
|
|
ivalue = FLT_MIN_EXP;
|
|
goto Lint;
|
|
case Tcomplex64:
|
|
case Timaginary64:
|
|
case Tfloat64:
|
|
ivalue = DBL_MIN_EXP;
|
|
goto Lint;
|
|
case Tcomplex80:
|
|
case Timaginary80:
|
|
case Tfloat80:
|
|
version(IN_LLVM)
|
|
ivalue = Port.ldbl_min_exp;
|
|
else
|
|
ivalue = LDBL_MIN_EXP;
|
|
goto Lint;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return Type.getProperty(loc, ident, flag);
|
|
Livalue:
|
|
e = new IntegerExp(loc, ivalue, this);
|
|
return e;
|
|
Lfvalue:
|
|
if (isreal() || isimaginary())
|
|
e = new RealExp(loc, fvalue, this);
|
|
else
|
|
{
|
|
complex_t cvalue;
|
|
cvalue.re = fvalue;
|
|
cvalue.im = fvalue;
|
|
//for (int i = 0; i < 20; i++)
|
|
// printf("%02x ", ((unsigned char *)&cvalue)[i]);
|
|
//printf("\n");
|
|
e = new ComplexExp(loc, cvalue, this);
|
|
}
|
|
return e;
|
|
Lint:
|
|
e = new IntegerExp(loc, ivalue, Type.tint32);
|
|
return e;
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeBasic::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
|
|
}
|
|
Type t;
|
|
if (ident == Id.re)
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
t = tfloat32;
|
|
goto L1;
|
|
case Tcomplex64:
|
|
t = tfloat64;
|
|
goto L1;
|
|
case Tcomplex80:
|
|
t = tfloat80;
|
|
goto L1;
|
|
L1:
|
|
e = e.castTo(sc, t);
|
|
break;
|
|
case Tfloat32:
|
|
case Tfloat64:
|
|
case Tfloat80:
|
|
break;
|
|
case Timaginary32:
|
|
t = tfloat32;
|
|
goto L2;
|
|
case Timaginary64:
|
|
t = tfloat64;
|
|
goto L2;
|
|
case Timaginary80:
|
|
t = tfloat80;
|
|
goto L2;
|
|
L2:
|
|
e = new RealExp(e.loc, ldouble(0.0), t);
|
|
break;
|
|
default:
|
|
e = Type.getProperty(e.loc, ident, flag);
|
|
break;
|
|
}
|
|
}
|
|
else if (ident == Id.im)
|
|
{
|
|
Type t2;
|
|
switch (ty)
|
|
{
|
|
case Tcomplex32:
|
|
t = timaginary32;
|
|
t2 = tfloat32;
|
|
goto L3;
|
|
case Tcomplex64:
|
|
t = timaginary64;
|
|
t2 = tfloat64;
|
|
goto L3;
|
|
case Tcomplex80:
|
|
t = timaginary80;
|
|
t2 = tfloat80;
|
|
goto L3;
|
|
L3:
|
|
e = e.castTo(sc, t);
|
|
e.type = t2;
|
|
break;
|
|
case Timaginary32:
|
|
t = tfloat32;
|
|
goto L4;
|
|
case Timaginary64:
|
|
t = tfloat64;
|
|
goto L4;
|
|
case Timaginary80:
|
|
t = tfloat80;
|
|
goto L4;
|
|
L4:
|
|
e = e.copy();
|
|
e.type = t;
|
|
break;
|
|
case Tfloat32:
|
|
case Tfloat64:
|
|
case Tfloat80:
|
|
e = new RealExp(e.loc, ldouble(0.0), this);
|
|
break;
|
|
default:
|
|
e = Type.getProperty(e.loc, ident, flag);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return Type.dotExp(sc, e, ident, flag);
|
|
}
|
|
if (!flag || e)
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
override bool isintegral()
|
|
{
|
|
//printf("TypeBasic::isintegral('%s') x%x\n", toChars(), flags);
|
|
return (flags & TFLAGSintegral) != 0;
|
|
}
|
|
|
|
override bool isfloating() const
|
|
{
|
|
return (flags & TFLAGSfloating) != 0;
|
|
}
|
|
|
|
override bool isreal() const
|
|
{
|
|
return (flags & TFLAGSreal) != 0;
|
|
}
|
|
|
|
override bool isimaginary() const
|
|
{
|
|
return (flags & TFLAGSimaginary) != 0;
|
|
}
|
|
|
|
override bool iscomplex() const
|
|
{
|
|
return (flags & TFLAGScomplex) != 0;
|
|
}
|
|
|
|
override bool isscalar() const
|
|
{
|
|
return (flags & (TFLAGSintegral | TFLAGSfloating)) != 0;
|
|
}
|
|
|
|
override bool isunsigned() const
|
|
{
|
|
return (flags & TFLAGSunsigned) != 0;
|
|
}
|
|
|
|
override MATCH implicitConvTo(Type to)
|
|
{
|
|
//printf("TypeBasic::implicitConvTo(%s) from %s\n", to->toChars(), toChars());
|
|
if (this == to)
|
|
return MATCHexact;
|
|
if (ty == to.ty)
|
|
{
|
|
if (mod == to.mod)
|
|
return MATCHexact;
|
|
else if (MODimplicitConv(mod, to.mod))
|
|
return MATCHconst;
|
|
else if (!((mod ^ to.mod) & MODshared)) // for wild matching
|
|
return MATCHconst;
|
|
else
|
|
return MATCHconvert;
|
|
}
|
|
if (ty == Tvoid || to.ty == Tvoid)
|
|
return MATCHnomatch;
|
|
if (to.ty == Tbool)
|
|
return MATCHnomatch;
|
|
TypeBasic tob;
|
|
if (to.ty == Tvector && to.deco)
|
|
{
|
|
TypeVector tv = cast(TypeVector)to;
|
|
tob = tv.elementType();
|
|
}
|
|
else
|
|
tob = to.isTypeBasic();
|
|
if (!tob)
|
|
return MATCHnomatch;
|
|
if (flags & TFLAGSintegral)
|
|
{
|
|
// Disallow implicit conversion of integers to imaginary or complex
|
|
if (tob.flags & (TFLAGSimaginary | TFLAGScomplex))
|
|
return MATCHnomatch;
|
|
// If converting from integral to integral
|
|
if (tob.flags & TFLAGSintegral)
|
|
{
|
|
d_uns64 sz = size(Loc());
|
|
d_uns64 tosz = tob.size(Loc());
|
|
/* Can't convert to smaller size
|
|
*/
|
|
if (sz > tosz)
|
|
return MATCHnomatch;
|
|
/* Can't change sign if same size
|
|
*/
|
|
/*if (sz == tosz && (flags ^ tob->flags) & TFLAGSunsigned)
|
|
return MATCHnomatch;*/
|
|
}
|
|
}
|
|
else if (flags & TFLAGSfloating)
|
|
{
|
|
// Disallow implicit conversion of floating point to integer
|
|
if (tob.flags & TFLAGSintegral)
|
|
return MATCHnomatch;
|
|
assert(tob.flags & TFLAGSfloating || to.ty == Tvector);
|
|
// Disallow implicit conversion from complex to non-complex
|
|
if (flags & TFLAGScomplex && !(tob.flags & TFLAGScomplex))
|
|
return MATCHnomatch;
|
|
// Disallow implicit conversion of real or imaginary to complex
|
|
if (flags & (TFLAGSreal | TFLAGSimaginary) && tob.flags & TFLAGScomplex)
|
|
return MATCHnomatch;
|
|
// Disallow implicit conversion to-from real and imaginary
|
|
if ((flags & (TFLAGSreal | TFLAGSimaginary)) != (tob.flags & (TFLAGSreal | TFLAGSimaginary)))
|
|
return MATCHnomatch;
|
|
}
|
|
return MATCHconvert;
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypeBasic::defaultInit() '%s'\n", toChars());
|
|
}
|
|
dinteger_t value = 0;
|
|
switch (ty)
|
|
{
|
|
case Tchar:
|
|
value = 0xFF;
|
|
break;
|
|
case Twchar:
|
|
case Tdchar:
|
|
value = 0xFFFF;
|
|
break;
|
|
case Timaginary32:
|
|
case Timaginary64:
|
|
case Timaginary80:
|
|
case Tfloat32:
|
|
case Tfloat64:
|
|
case Tfloat80:
|
|
return new RealExp(loc, Port.snan, this);
|
|
case Tcomplex32:
|
|
case Tcomplex64:
|
|
case Tcomplex80:
|
|
{
|
|
// Can't use fvalue + I*fvalue (the im part becomes a quiet NaN).
|
|
complex_t cvalue;
|
|
(cast(real_t*)&cvalue)[0] = Port.snan;
|
|
(cast(real_t*)&cvalue)[1] = Port.snan;
|
|
return new ComplexExp(loc, cvalue, this);
|
|
}
|
|
case Tvoid:
|
|
error(loc, "void does not have a default initializer");
|
|
return new ErrorExp();
|
|
default:
|
|
break;
|
|
}
|
|
return new IntegerExp(loc, value, this);
|
|
}
|
|
|
|
override bool isZeroInit(Loc loc) const
|
|
{
|
|
switch (ty)
|
|
{
|
|
case Tchar:
|
|
case Twchar:
|
|
case Tdchar:
|
|
case Timaginary32:
|
|
case Timaginary64:
|
|
case Timaginary80:
|
|
case Tfloat32:
|
|
case Tfloat64:
|
|
case Tfloat80:
|
|
case Tcomplex32:
|
|
case Tcomplex64:
|
|
case Tcomplex80:
|
|
return false; // no
|
|
default:
|
|
return true; // yes
|
|
}
|
|
}
|
|
|
|
// For eliminating dynamic_cast
|
|
override TypeBasic isTypeBasic()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* The basetype must be one of:
|
|
* byte[16],ubyte[16],short[8],ushort[8],int[4],uint[4],long[2],ulong[2],float[4],double[2]
|
|
* For AVX:
|
|
* byte[32],ubyte[32],short[16],ushort[16],int[8],uint[8],long[4],ulong[4],float[8],double[4]
|
|
*/
|
|
extern (C++) final class TypeVector : Type
|
|
{
|
|
public:
|
|
Type basetype;
|
|
|
|
extern (D) this(Loc loc, Type basetype)
|
|
{
|
|
super(Tvector);
|
|
this.basetype = basetype;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "vector";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
return new TypeVector(Loc(), basetype.syntaxCopy());
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
uint errors = global.errors;
|
|
basetype = basetype.semantic(loc, sc);
|
|
if (errors != global.errors)
|
|
return terror;
|
|
basetype = basetype.toBasetype().mutableOf();
|
|
if (basetype.ty != Tsarray)
|
|
{
|
|
error(loc, "T in __vector(T) must be a static array, not %s", basetype.toChars());
|
|
return terror;
|
|
}
|
|
TypeSArray t = cast(TypeSArray)basetype;
|
|
int sz = cast(int)t.size(loc);
|
|
switch (Target.checkVectorType(sz, t.nextOf()))
|
|
{
|
|
case 0:
|
|
// valid
|
|
break;
|
|
case 1:
|
|
// no support at all
|
|
error(loc, "SIMD vector types not supported on this platform");
|
|
return terror;
|
|
case 2:
|
|
// invalid size
|
|
error(loc, "%d byte vector type %s is not supported on this platform", sz, toChars());
|
|
return terror;
|
|
case 3:
|
|
// invalid base type
|
|
error(loc, "vector type %s is not supported on this platform", toChars());
|
|
return terror;
|
|
default:
|
|
assert(0);
|
|
}
|
|
return merge();
|
|
}
|
|
|
|
override d_uns64 size(Loc loc)
|
|
{
|
|
return basetype.size();
|
|
}
|
|
|
|
override uint alignsize()
|
|
{
|
|
return cast(uint)basetype.size();
|
|
}
|
|
|
|
override Expression getProperty(Loc loc, Identifier ident, int flag)
|
|
{
|
|
return Type.getProperty(loc, ident, flag);
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeVector::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
|
|
}
|
|
if (ident == Id.array)
|
|
{
|
|
version(IN_LLVM)
|
|
{
|
|
e = e.castTo(sc, basetype);
|
|
}
|
|
else
|
|
{
|
|
//e = e->castTo(sc, basetype);
|
|
// Keep lvalue-ness
|
|
e = e.copy();
|
|
e.type = basetype;
|
|
}
|
|
return e;
|
|
}
|
|
if (ident == Id._init || ident == Id.offsetof || ident == Id.stringof)
|
|
{
|
|
// init should return a new VectorExp (Bugzilla 12776)
|
|
// offsetof does not work on a cast expression, so use e directly
|
|
// stringof should not add a cast to the output
|
|
return Type.dotExp(sc, e, ident, flag);
|
|
}
|
|
return basetype.dotExp(sc, e.castTo(sc, basetype), ident, flag);
|
|
}
|
|
|
|
override bool isintegral()
|
|
{
|
|
//printf("TypeVector::isintegral('%s') x%x\n", toChars(), flags);
|
|
return basetype.nextOf().isintegral();
|
|
}
|
|
|
|
override bool isfloating()
|
|
{
|
|
return basetype.nextOf().isfloating();
|
|
}
|
|
|
|
override bool isscalar()
|
|
{
|
|
return basetype.nextOf().isscalar();
|
|
}
|
|
|
|
override bool isunsigned()
|
|
{
|
|
return basetype.nextOf().isunsigned();
|
|
}
|
|
|
|
override bool isBoolean() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override MATCH implicitConvTo(Type to)
|
|
{
|
|
//printf("TypeVector::implicitConvTo(%s) from %s\n", to->toChars(), toChars());
|
|
if (this == to)
|
|
return MATCHexact;
|
|
if (ty == to.ty)
|
|
return MATCHconvert;
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
//printf("TypeVector::defaultInit()\n");
|
|
assert(basetype.ty == Tsarray);
|
|
Expression e = basetype.defaultInit(loc);
|
|
auto ve = new VectorExp(loc, e, this);
|
|
ve.type = this;
|
|
ve.dim = cast(int)(basetype.size(loc) / elementType().size(loc));
|
|
return ve;
|
|
}
|
|
|
|
override Expression defaultInitLiteral(Loc loc)
|
|
{
|
|
//printf("TypeVector::defaultInitLiteral()\n");
|
|
assert(basetype.ty == Tsarray);
|
|
Expression e = basetype.defaultInitLiteral(loc);
|
|
auto ve = new VectorExp(loc, e, this);
|
|
ve.type = this;
|
|
ve.dim = cast(int)(basetype.size(loc) / elementType().size(loc));
|
|
return ve;
|
|
}
|
|
|
|
TypeBasic elementType()
|
|
{
|
|
assert(basetype.ty == Tsarray);
|
|
TypeSArray t = cast(TypeSArray)basetype;
|
|
TypeBasic tb = t.nextOf().isTypeBasic();
|
|
assert(tb);
|
|
return tb;
|
|
}
|
|
|
|
override bool isZeroInit(Loc loc)
|
|
{
|
|
return basetype.isZeroInit(loc);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class TypeArray : TypeNext
|
|
{
|
|
public:
|
|
final extern (D) this(TY ty, Type next)
|
|
{
|
|
super(ty, next);
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
Type n = this.next.toBasetype(); // uncover any typedef's
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
|
|
}
|
|
if (e.op == TOKtype)
|
|
{
|
|
if (ident == Id.sort || ident == Id.reverse)
|
|
{
|
|
e.error("%s is not an expression", e.toChars());
|
|
return new ErrorExp();
|
|
}
|
|
}
|
|
if (!n.isMutable())
|
|
{
|
|
if (ident == Id.sort || ident == Id.reverse)
|
|
{
|
|
error(e.loc, "can only %s a mutable array", ident.toChars());
|
|
goto Lerror;
|
|
}
|
|
}
|
|
if (ident == Id.reverse && (n.ty == Tchar || n.ty == Twchar))
|
|
{
|
|
static __gshared const(char)** reverseName = ["_adReverseChar", "_adReverseWchar"];
|
|
static __gshared FuncDeclaration* reverseFd = [null, null];
|
|
warning(e.loc, "use std.algorithm.reverse instead of .reverse property");
|
|
int i = n.ty == Twchar;
|
|
if (!reverseFd[i])
|
|
{
|
|
auto params = new Parameters();
|
|
Type next = n.ty == Twchar ? Type.twchar : Type.tchar;
|
|
Type arrty = next.arrayOf();
|
|
params.push(new Parameter(0, arrty, null, null));
|
|
reverseFd[i] = FuncDeclaration.genCfunc(params, arrty, reverseName[i]);
|
|
}
|
|
Expression ec = new VarExp(Loc(), reverseFd[i], false);
|
|
e = e.castTo(sc, n.arrayOf()); // convert to dynamic array
|
|
auto arguments = new Expressions();
|
|
arguments.push(e);
|
|
e = new CallExp(e.loc, ec, arguments);
|
|
e.type = next.arrayOf();
|
|
}
|
|
else if (ident == Id.sort && (n.ty == Tchar || n.ty == Twchar))
|
|
{
|
|
static __gshared const(char)** sortName = ["_adSortChar", "_adSortWchar"];
|
|
static __gshared FuncDeclaration* sortFd = [null, null];
|
|
warning(e.loc, "use std.algorithm.sort instead of .sort property");
|
|
int i = n.ty == Twchar;
|
|
if (!sortFd[i])
|
|
{
|
|
auto params = new Parameters();
|
|
Type next = n.ty == Twchar ? Type.twchar : Type.tchar;
|
|
Type arrty = next.arrayOf();
|
|
params.push(new Parameter(0, arrty, null, null));
|
|
sortFd[i] = FuncDeclaration.genCfunc(params, arrty, sortName[i]);
|
|
}
|
|
Expression ec = new VarExp(Loc(), sortFd[i], false);
|
|
e = e.castTo(sc, n.arrayOf()); // convert to dynamic array
|
|
auto arguments = new Expressions();
|
|
arguments.push(e);
|
|
e = new CallExp(e.loc, ec, arguments);
|
|
e.type = next.arrayOf();
|
|
}
|
|
else if (ident == Id.reverse)
|
|
{
|
|
Expression ec;
|
|
FuncDeclaration fd;
|
|
Expressions* arguments;
|
|
dinteger_t size = next.size(e.loc);
|
|
warning(e.loc, "use std.algorithm.reverse instead of .reverse property");
|
|
assert(size);
|
|
static __gshared FuncDeclaration adReverse_fd = null;
|
|
if (!adReverse_fd)
|
|
{
|
|
auto params = new Parameters();
|
|
params.push(new Parameter(0, Type.tvoid.arrayOf(), null, null));
|
|
params.push(new Parameter(0, Type.tsize_t, null, null));
|
|
adReverse_fd = FuncDeclaration.genCfunc(params, Type.tvoid.arrayOf(), Id.adReverse);
|
|
}
|
|
fd = adReverse_fd;
|
|
ec = new VarExp(Loc(), fd, false);
|
|
e = e.castTo(sc, n.arrayOf()); // convert to dynamic array
|
|
arguments = new Expressions();
|
|
arguments.push(e);
|
|
arguments.push(new IntegerExp(Loc(), size, Type.tsize_t));
|
|
e = new CallExp(e.loc, ec, arguments);
|
|
e.type = next.mutableOf().arrayOf();
|
|
}
|
|
else if (ident == Id.sort)
|
|
{
|
|
static __gshared FuncDeclaration fd = null;
|
|
Expression ec;
|
|
Expressions* arguments;
|
|
warning(e.loc, "use std.algorithm.sort instead of .sort property");
|
|
if (!fd)
|
|
{
|
|
auto params = new Parameters();
|
|
params.push(new Parameter(0, Type.tvoid.arrayOf(), null, null));
|
|
params.push(new Parameter(0, Type.dtypeinfo.type, null, null));
|
|
fd = FuncDeclaration.genCfunc(params, Type.tvoid.arrayOf(), "_adSort");
|
|
}
|
|
ec = new VarExp(Loc(), fd, false);
|
|
e = e.castTo(sc, n.arrayOf()); // convert to dynamic array
|
|
arguments = new Expressions();
|
|
arguments.push(e);
|
|
// don't convert to dynamic array
|
|
Expression tid = new TypeidExp(e.loc, n);
|
|
tid = tid.semantic(sc);
|
|
arguments.push(tid);
|
|
e = new CallExp(e.loc, ec, arguments);
|
|
e.type = next.arrayOf();
|
|
}
|
|
else
|
|
{
|
|
e = Type.dotExp(sc, e, ident, flag);
|
|
}
|
|
if (!flag || e)
|
|
e = e.semantic(sc);
|
|
return e;
|
|
Lerror:
|
|
return new ErrorExp();
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* Static array, one with a fixed dimension
|
|
*/
|
|
extern (C++) final class TypeSArray : TypeArray
|
|
{
|
|
public:
|
|
Expression dim;
|
|
|
|
extern (D) this(Type t, Expression dim)
|
|
{
|
|
super(Tsarray, t);
|
|
//printf("TypeSArray(%s)\n", dim->toChars());
|
|
this.dim = dim;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "sarray";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
Type t = next.syntaxCopy();
|
|
Expression e = dim.syntaxCopy();
|
|
t = new TypeSArray(t, e);
|
|
t.mod = mod;
|
|
return t;
|
|
}
|
|
|
|
override d_uns64 size(Loc loc)
|
|
{
|
|
dinteger_t sz;
|
|
if (!dim)
|
|
return Type.size(loc);
|
|
sz = dim.toInteger();
|
|
{
|
|
bool overflow = false;
|
|
sz = mulu(next.size(), sz, overflow);
|
|
if (overflow)
|
|
goto Loverflow;
|
|
}
|
|
return sz;
|
|
Loverflow:
|
|
error(loc, "index %lld overflow for static array", cast(long)sz);
|
|
return SIZE_INVALID;
|
|
}
|
|
|
|
override uint alignsize()
|
|
{
|
|
return next.alignsize();
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypeSArray::semantic() %s\n", toChars());
|
|
Type t;
|
|
Expression e;
|
|
Dsymbol s;
|
|
next.resolve(loc, sc, &e, &t, &s);
|
|
|
|
if (auto tup = s ? s.isTupleDeclaration() : null)
|
|
{
|
|
dim = semanticLength(sc, tup, dim);
|
|
dim = dim.ctfeInterpret();
|
|
if (dim.op == TOKerror)
|
|
return Type.terror;
|
|
uinteger_t d = dim.toUInteger();
|
|
if (d >= tup.objects.dim)
|
|
{
|
|
error(loc, "tuple index %llu exceeds %u", d, tup.objects.dim);
|
|
return Type.terror;
|
|
}
|
|
|
|
RootObject o = (*tup.objects)[cast(size_t)d];
|
|
if (o.dyncast() != DYNCAST_TYPE)
|
|
{
|
|
error(loc, "%s is not a type", toChars());
|
|
return Type.terror;
|
|
}
|
|
t = (cast(Type)o).addMod(this.mod);
|
|
return t;
|
|
}
|
|
Type tn = next.semantic(loc, sc);
|
|
if (tn.ty == Terror)
|
|
return terror;
|
|
Type tbn = tn.toBasetype();
|
|
if (dim)
|
|
{
|
|
uint errors = global.errors;
|
|
dim = semanticLength(sc, tbn, dim);
|
|
if (errors != global.errors)
|
|
goto Lerror;
|
|
dim = dim.optimize(WANTvalue);
|
|
dim = dim.ctfeInterpret();
|
|
if (dim.op == TOKerror)
|
|
goto Lerror;
|
|
errors = global.errors;
|
|
dinteger_t d1 = dim.toInteger();
|
|
if (errors != global.errors)
|
|
goto Lerror;
|
|
dim = dim.implicitCastTo(sc, tsize_t);
|
|
dim = dim.optimize(WANTvalue);
|
|
if (dim.op == TOKerror)
|
|
goto Lerror;
|
|
errors = global.errors;
|
|
dinteger_t d2 = dim.toInteger();
|
|
if (errors != global.errors)
|
|
goto Lerror;
|
|
if (dim.op == TOKerror)
|
|
goto Lerror;
|
|
if (d1 != d2)
|
|
{
|
|
Loverflow:
|
|
error(loc, "%s size %llu * %llu exceeds 16MiB size limit for static array", toChars(), cast(ulong)tbn.size(loc), cast(ulong)d1);
|
|
goto Lerror;
|
|
}
|
|
Type tbx = tbn.baseElemOf();
|
|
if (tbx.ty == Tstruct && !(cast(TypeStruct)tbx).sym.members || tbx.ty == Tenum && !(cast(TypeEnum)tbx).sym.members)
|
|
{
|
|
/* To avoid meaningess error message, skip the total size limit check
|
|
* when the bottom of element type is opaque.
|
|
*/
|
|
}
|
|
else if (tbn.isintegral() || tbn.isfloating() || tbn.ty == Tpointer || tbn.ty == Tarray || tbn.ty == Tsarray || tbn.ty == Taarray || (tbn.ty == Tstruct && ((cast(TypeStruct)tbn).sym.sizeok == SIZEOKdone)) || tbn.ty == Tclass)
|
|
{
|
|
/* Only do this for types that don't need to have semantic()
|
|
* run on them for the size, since they may be forward referenced.
|
|
*/
|
|
bool overflow = false;
|
|
if (mulu(tbn.size(loc), d2, overflow) >= 0x1000000 || overflow) // put a 'reasonable' limit on it
|
|
goto Loverflow;
|
|
}
|
|
}
|
|
switch (tbn.ty)
|
|
{
|
|
case Ttuple:
|
|
{
|
|
// Index the tuple to get the type
|
|
assert(dim);
|
|
TypeTuple tt = cast(TypeTuple)tbn;
|
|
uinteger_t d = dim.toUInteger();
|
|
if (d >= tt.arguments.dim)
|
|
{
|
|
error(loc, "tuple index %llu exceeds %u", d, tt.arguments.dim);
|
|
goto Lerror;
|
|
}
|
|
Type telem = (*tt.arguments)[cast(size_t)d].type;
|
|
return telem.addMod(this.mod);
|
|
}
|
|
case Tfunction:
|
|
case Tnone:
|
|
error(loc, "can't have array of %s", tbn.toChars());
|
|
goto Lerror;
|
|
default:
|
|
break;
|
|
}
|
|
if (tbn.isscope())
|
|
{
|
|
error(loc, "cannot have array of scope %s", tbn.toChars());
|
|
goto Lerror;
|
|
}
|
|
/* Ensure things like const(immutable(T)[3]) become immutable(T[3])
|
|
* and const(T)[3] become const(T[3])
|
|
*/
|
|
next = tn;
|
|
transitive();
|
|
t = addMod(tn.mod);
|
|
return t.merge();
|
|
Lerror:
|
|
return Type.terror;
|
|
}
|
|
|
|
override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false)
|
|
{
|
|
//printf("TypeSArray::resolve() %s\n", toChars());
|
|
next.resolve(loc, sc, pe, pt, ps, intypeid);
|
|
//printf("s = %p, e = %p, t = %p\n", *ps, *pe, *pt);
|
|
if (*pe)
|
|
{
|
|
// It's really an index expression
|
|
if (Dsymbol s = getDsymbol(*pe))
|
|
*pe = new DsymbolExp(loc, s);
|
|
*pe = new ArrayExp(loc, *pe, dim);
|
|
}
|
|
else if (*ps)
|
|
{
|
|
Dsymbol s = *ps;
|
|
if (auto tup = s.isTupleDeclaration())
|
|
{
|
|
dim = semanticLength(sc, tup, dim);
|
|
dim = dim.ctfeInterpret();
|
|
if (dim.op == TOKerror)
|
|
{
|
|
*ps = null;
|
|
*pt = Type.terror;
|
|
return;
|
|
}
|
|
uinteger_t d = dim.toUInteger();
|
|
if (d >= tup.objects.dim)
|
|
{
|
|
error(loc, "tuple index %llu exceeds length %u", d, tup.objects.dim);
|
|
*ps = null;
|
|
*pt = Type.terror;
|
|
return;
|
|
}
|
|
|
|
RootObject o = (*tup.objects)[cast(size_t)d];
|
|
if (o.dyncast() == DYNCAST_DSYMBOL)
|
|
{
|
|
*ps = cast(Dsymbol)o;
|
|
return;
|
|
}
|
|
if (o.dyncast() == DYNCAST_EXPRESSION)
|
|
{
|
|
Expression e = cast(Expression)o;
|
|
if (e.op == TOKdsymbol)
|
|
{
|
|
*ps = (cast(DsymbolExp)e).s;
|
|
*pe = null;
|
|
}
|
|
else
|
|
{
|
|
*ps = null;
|
|
*pe = e;
|
|
}
|
|
return;
|
|
}
|
|
if (o.dyncast() == DYNCAST_TYPE)
|
|
{
|
|
*ps = null;
|
|
*pt = (cast(Type)o).addMod(this.mod);
|
|
return;
|
|
}
|
|
|
|
/* Create a new TupleDeclaration which
|
|
* is a slice [d..d+1] out of the old one.
|
|
* Do it this way because TemplateInstance::semanticTiargs()
|
|
* can handle unresolved Objects this way.
|
|
*/
|
|
auto objects = new Objects();
|
|
objects.setDim(1);
|
|
(*objects)[0] = o;
|
|
*ps = new TupleDeclaration(loc, tup.ident, objects);
|
|
}
|
|
else
|
|
goto Ldefault;
|
|
}
|
|
else
|
|
{
|
|
if ((*pt).ty != Terror)
|
|
next = *pt; // prevent re-running semantic() on 'next'
|
|
Ldefault:
|
|
Type.resolve(loc, sc, pe, pt, ps, intypeid);
|
|
}
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeSArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
|
|
}
|
|
if (ident == Id.length)
|
|
{
|
|
Loc oldLoc = e.loc;
|
|
e = dim.copy();
|
|
e.loc = oldLoc;
|
|
}
|
|
else if (ident == Id.ptr)
|
|
{
|
|
if (e.op == TOKtype)
|
|
{
|
|
e.error("%s is not an expression", e.toChars());
|
|
return new ErrorExp();
|
|
}
|
|
e = e.castTo(sc, e.type.nextOf().pointerTo());
|
|
}
|
|
else
|
|
{
|
|
e = TypeArray.dotExp(sc, e, ident, flag);
|
|
}
|
|
if (!flag || e)
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
override bool isString()
|
|
{
|
|
TY nty = next.toBasetype().ty;
|
|
return nty == Tchar || nty == Twchar || nty == Tdchar;
|
|
}
|
|
|
|
override bool isZeroInit(Loc loc)
|
|
{
|
|
return next.isZeroInit(loc);
|
|
}
|
|
|
|
override structalign_t alignment()
|
|
{
|
|
return next.alignment();
|
|
}
|
|
|
|
override MATCH constConv(Type to)
|
|
{
|
|
if (to.ty == Tsarray)
|
|
{
|
|
TypeSArray tsa = cast(TypeSArray)to;
|
|
if (!dim.equals(tsa.dim))
|
|
return MATCHnomatch;
|
|
}
|
|
return TypeNext.constConv(to);
|
|
}
|
|
|
|
override MATCH implicitConvTo(Type to)
|
|
{
|
|
//printf("TypeSArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars());
|
|
if (to.ty == Tarray)
|
|
{
|
|
TypeDArray ta = cast(TypeDArray)to;
|
|
if (!MODimplicitConv(next.mod, ta.next.mod))
|
|
return MATCHnomatch;
|
|
/* Allow conversion to void[]
|
|
*/
|
|
if (ta.next.ty == Tvoid)
|
|
{
|
|
return MATCHconvert;
|
|
}
|
|
MATCH m = next.constConv(ta.next);
|
|
if (m > MATCHnomatch)
|
|
{
|
|
return MATCHconvert;
|
|
}
|
|
return MATCHnomatch;
|
|
}
|
|
if (to.ty == Tsarray)
|
|
{
|
|
if (this == to)
|
|
return MATCHexact;
|
|
TypeSArray tsa = cast(TypeSArray)to;
|
|
if (dim.equals(tsa.dim))
|
|
{
|
|
/* Since static arrays are value types, allow
|
|
* conversions from const elements to non-const
|
|
* ones, just like we allow conversion from const int
|
|
* to int.
|
|
*/
|
|
MATCH m = next.implicitConvTo(tsa.next);
|
|
if (m >= MATCHconst)
|
|
{
|
|
if (mod != to.mod)
|
|
m = MATCHconst;
|
|
return m;
|
|
}
|
|
}
|
|
}
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypeSArray::defaultInit() '%s'\n", toChars());
|
|
}
|
|
if (next.ty == Tvoid)
|
|
return tuns8.defaultInit(loc);
|
|
else
|
|
return next.defaultInit(loc);
|
|
}
|
|
|
|
override Expression defaultInitLiteral(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypeSArray::defaultInitLiteral() '%s'\n", toChars());
|
|
}
|
|
size_t d = cast(size_t)dim.toInteger();
|
|
Expression elementinit;
|
|
if (next.ty == Tvoid)
|
|
elementinit = tuns8.defaultInitLiteral(loc);
|
|
else
|
|
elementinit = next.defaultInitLiteral(loc);
|
|
auto elements = new Expressions();
|
|
elements.setDim(d);
|
|
for (size_t i = 0; i < d; i++)
|
|
(*elements)[i] = null;
|
|
auto ae = new ArrayLiteralExp(Loc(), elementinit, elements);
|
|
ae.type = this;
|
|
return ae;
|
|
}
|
|
|
|
override Expression toExpression()
|
|
{
|
|
Expression e = next.toExpression();
|
|
if (e)
|
|
e = new ArrayExp(dim.loc, e, dim);
|
|
return e;
|
|
}
|
|
|
|
override bool hasPointers()
|
|
{
|
|
/* Don't want to do this, because:
|
|
* struct S { T* array[0]; }
|
|
* may be a variable length struct.
|
|
*/
|
|
//if (dim->toInteger() == 0)
|
|
// return false;
|
|
if (next.ty == Tvoid)
|
|
{
|
|
// Arrays of void contain arbitrary data, which may include pointers
|
|
return true;
|
|
}
|
|
else
|
|
return next.hasPointers();
|
|
}
|
|
|
|
override bool needsDestruction()
|
|
{
|
|
return next.needsDestruction();
|
|
}
|
|
|
|
/*********************************
|
|
*
|
|
*/
|
|
override bool needsNested()
|
|
{
|
|
return next.needsNested();
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* Dynamic array, no dimension
|
|
*/
|
|
extern (C++) final class TypeDArray : TypeArray
|
|
{
|
|
public:
|
|
extern (D) this(Type t)
|
|
{
|
|
super(Tarray, t);
|
|
//printf("TypeDArray(t = %p)\n", t);
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "darray";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
Type t = next.syntaxCopy();
|
|
if (t == next)
|
|
t = this;
|
|
else
|
|
{
|
|
t = new TypeDArray(t);
|
|
t.mod = mod;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
override d_uns64 size(Loc loc) const
|
|
{
|
|
//printf("TypeDArray::size()\n");
|
|
return Target.ptrsize * 2;
|
|
}
|
|
|
|
override uint alignsize() const
|
|
{
|
|
// A DArray consists of two ptr-sized values, so align it on pointer size
|
|
// boundary
|
|
return Target.ptrsize;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
Type tn = next.semantic(loc, sc);
|
|
Type tbn = tn.toBasetype();
|
|
switch (tbn.ty)
|
|
{
|
|
case Ttuple:
|
|
return tbn;
|
|
case Tfunction:
|
|
case Tnone:
|
|
error(loc, "can't have array of %s", tbn.toChars());
|
|
return Type.terror;
|
|
case Terror:
|
|
return Type.terror;
|
|
default:
|
|
break;
|
|
}
|
|
if (tn.isscope())
|
|
{
|
|
error(loc, "cannot have array of scope %s", tn.toChars());
|
|
return Type.terror;
|
|
}
|
|
next = tn;
|
|
transitive();
|
|
return merge();
|
|
}
|
|
|
|
override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false)
|
|
{
|
|
//printf("TypeDArray::resolve() %s\n", toChars());
|
|
next.resolve(loc, sc, pe, pt, ps, intypeid);
|
|
//printf("s = %p, e = %p, t = %p\n", *ps, *pe, *pt);
|
|
if (*pe)
|
|
{
|
|
// It's really a slice expression
|
|
if (Dsymbol s = getDsymbol(*pe))
|
|
*pe = new DsymbolExp(loc, s);
|
|
*pe = new ArrayExp(loc, *pe);
|
|
}
|
|
else if (*ps)
|
|
{
|
|
if (auto tup = (*ps).isTupleDeclaration())
|
|
{
|
|
// keep *ps
|
|
}
|
|
else
|
|
goto Ldefault;
|
|
}
|
|
else
|
|
{
|
|
if ((*pt).ty != Terror)
|
|
next = *pt; // prevent re-running semantic() on 'next'
|
|
Ldefault:
|
|
Type.resolve(loc, sc, pe, pt, ps, intypeid);
|
|
}
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeDArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
|
|
}
|
|
if (e.op == TOKtype && (ident == Id.length || ident == Id.ptr))
|
|
{
|
|
e.error("%s is not an expression", e.toChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (ident == Id.length)
|
|
{
|
|
if (e.op == TOKstring)
|
|
{
|
|
StringExp se = cast(StringExp)e;
|
|
return new IntegerExp(se.loc, se.len, Type.tsize_t);
|
|
}
|
|
if (e.op == TOKnull)
|
|
return new IntegerExp(e.loc, 0, Type.tsize_t);
|
|
if (checkNonAssignmentArrayOp(e))
|
|
return new ErrorExp();
|
|
e = new ArrayLengthExp(e.loc, e);
|
|
e.type = Type.tsize_t;
|
|
return e;
|
|
}
|
|
else if (ident == Id.ptr)
|
|
{
|
|
e = e.castTo(sc, next.pointerTo());
|
|
return e;
|
|
}
|
|
else
|
|
{
|
|
e = TypeArray.dotExp(sc, e, ident, flag);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
override bool isString()
|
|
{
|
|
TY nty = next.toBasetype().ty;
|
|
return nty == Tchar || nty == Twchar || nty == Tdchar;
|
|
}
|
|
|
|
override bool isZeroInit(Loc loc) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool isBoolean() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override MATCH implicitConvTo(Type to)
|
|
{
|
|
//printf("TypeDArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars());
|
|
if (equals(to))
|
|
return MATCHexact;
|
|
if (to.ty == Tarray)
|
|
{
|
|
TypeDArray ta = cast(TypeDArray)to;
|
|
if (!MODimplicitConv(next.mod, ta.next.mod))
|
|
return MATCHnomatch; // not const-compatible
|
|
/* Allow conversion to void[]
|
|
*/
|
|
if (next.ty != Tvoid && ta.next.ty == Tvoid)
|
|
{
|
|
return MATCHconvert;
|
|
}
|
|
MATCH m = next.constConv(ta.next);
|
|
if (m > MATCHnomatch)
|
|
{
|
|
if (m == MATCHexact && mod != to.mod)
|
|
m = MATCHconst;
|
|
return m;
|
|
}
|
|
}
|
|
return Type.implicitConvTo(to);
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypeDArray::defaultInit() '%s'\n", toChars());
|
|
}
|
|
return new NullExp(loc, this);
|
|
}
|
|
|
|
override bool hasPointers() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeAArray : TypeArray
|
|
{
|
|
public:
|
|
Type index; // key type
|
|
Loc loc;
|
|
Scope* sc;
|
|
|
|
extern (D) this(Type t, Type index)
|
|
{
|
|
super(Taarray, t);
|
|
this.index = index;
|
|
}
|
|
|
|
static TypeAArray create(Type t, Type index)
|
|
{
|
|
return new TypeAArray(t, index);
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "aarray";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
Type t = next.syntaxCopy();
|
|
Type ti = index.syntaxCopy();
|
|
if (t == next && ti == index)
|
|
t = this;
|
|
else
|
|
{
|
|
t = new TypeAArray(t, ti);
|
|
t.mod = mod;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
override d_uns64 size(Loc loc)
|
|
{
|
|
return Target.ptrsize;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypeAArray::semantic() %s index->ty = %d\n", toChars(), index->ty);
|
|
if (deco)
|
|
return this;
|
|
this.loc = loc;
|
|
this.sc = sc;
|
|
if (sc)
|
|
sc.setNoFree();
|
|
// Deal with the case where we thought the index was a type, but
|
|
// in reality it was an expression.
|
|
if (index.ty == Tident || index.ty == Tinstance || index.ty == Tsarray || index.ty == Ttypeof || index.ty == Treturn)
|
|
{
|
|
Expression e;
|
|
Type t;
|
|
Dsymbol s;
|
|
index.resolve(loc, sc, &e, &t, &s);
|
|
if (e)
|
|
{
|
|
// It was an expression -
|
|
// Rewrite as a static array
|
|
auto tsa = new TypeSArray(next, e);
|
|
return tsa.semantic(loc, sc);
|
|
}
|
|
else if (t)
|
|
index = t.semantic(loc, sc);
|
|
else
|
|
{
|
|
index.error(loc, "index is not a type or an expression");
|
|
return Type.terror;
|
|
}
|
|
}
|
|
else
|
|
index = index.semantic(loc, sc);
|
|
index = index.merge2();
|
|
if (index.nextOf() && !index.nextOf().isImmutable())
|
|
{
|
|
index = index.constOf().mutableOf();
|
|
version (none)
|
|
{
|
|
printf("index is %p %s\n", index, index.toChars());
|
|
index.check();
|
|
printf("index->mod = x%x\n", index.mod);
|
|
printf("index->ito = x%x\n", index.ito);
|
|
if (index.ito)
|
|
{
|
|
printf("index->ito->mod = x%x\n", index.ito.mod);
|
|
printf("index->ito->ito = x%x\n", index.ito.ito);
|
|
}
|
|
}
|
|
}
|
|
switch (index.toBasetype().ty)
|
|
{
|
|
case Tfunction:
|
|
case Tvoid:
|
|
case Tnone:
|
|
case Ttuple:
|
|
error(loc, "can't have associative array key of %s", index.toBasetype().toChars());
|
|
goto case Terror;
|
|
case Terror:
|
|
return Type.terror;
|
|
default:
|
|
break;
|
|
}
|
|
Type tbase = index.baseElemOf();
|
|
while (tbase.ty == Tarray)
|
|
tbase = tbase.nextOf().baseElemOf();
|
|
if (tbase.ty == Tstruct)
|
|
{
|
|
/* AA's need typeid(index).equals() and getHash(). Issue error if not correctly set up.
|
|
*/
|
|
StructDeclaration sd = (cast(TypeStruct)tbase).sym;
|
|
if (sd._scope)
|
|
sd.semantic(null);
|
|
// duplicate a part of StructDeclaration::semanticTypeInfoMembers
|
|
if (sd.xeq && sd.xeq._scope && sd.xeq.semanticRun < PASSsemantic3done)
|
|
{
|
|
uint errors = global.startGagging();
|
|
sd.xeq.semantic3(sd.xeq._scope);
|
|
if (global.endGagging(errors))
|
|
sd.xeq = sd.xerreq;
|
|
}
|
|
//printf("AA = %s, key: xeq = %p, xhash = %p\n", toChars(), sd->xeq, sd->xhash);
|
|
const(char)* s = (index.toBasetype().ty != Tstruct) ? "bottom of " : "";
|
|
if (!sd.xeq)
|
|
{
|
|
// If sd->xhash != NULL:
|
|
// sd or its fields have user-defined toHash.
|
|
// AA assumes that its result is consistent with bitwise equality.
|
|
// else:
|
|
// bitwise equality & hashing
|
|
}
|
|
else if (sd.xeq == sd.xerreq)
|
|
{
|
|
if (search_function(sd, Id.eq))
|
|
{
|
|
error(loc, "%sAA key type %s does not have 'bool opEquals(ref const %s) const'", s, sd.toChars(), sd.toChars());
|
|
}
|
|
else
|
|
{
|
|
error(loc, "%sAA key type %s does not support const equality", s, sd.toChars());
|
|
}
|
|
return Type.terror;
|
|
}
|
|
else if (!sd.xhash)
|
|
{
|
|
if (search_function(sd, Id.eq))
|
|
{
|
|
error(loc, "%sAA key type %s should have 'size_t toHash() const nothrow @safe' if opEquals defined", s, sd.toChars());
|
|
}
|
|
else
|
|
{
|
|
error(loc, "%sAA key type %s supports const equality but doesn't support const hashing", s, sd.toChars());
|
|
}
|
|
return Type.terror;
|
|
}
|
|
else
|
|
{
|
|
// defined equality & hashing
|
|
assert(sd.xeq && sd.xhash);
|
|
/* xeq and xhash may be implicitly defined by compiler. For example:
|
|
* struct S { int[] arr; }
|
|
* With 'arr' field equality and hashing, compiler will implicitly
|
|
* generate functions for xopEquals and xtoHash in TypeInfo_Struct.
|
|
*/
|
|
}
|
|
}
|
|
else if (tbase.ty == Tclass && !(cast(TypeClass)tbase).sym.isInterfaceDeclaration())
|
|
{
|
|
ClassDeclaration cd = (cast(TypeClass)tbase).sym;
|
|
if (cd._scope)
|
|
cd.semantic(null);
|
|
if (!ClassDeclaration.object)
|
|
{
|
|
error(Loc(), "missing or corrupt object.d");
|
|
fatal();
|
|
}
|
|
static __gshared FuncDeclaration feq = null;
|
|
static __gshared FuncDeclaration fcmp = null;
|
|
static __gshared FuncDeclaration fhash = null;
|
|
if (!feq)
|
|
feq = search_function(ClassDeclaration.object, Id.eq).isFuncDeclaration();
|
|
if (!fcmp)
|
|
fcmp = search_function(ClassDeclaration.object, Id.cmp).isFuncDeclaration();
|
|
if (!fhash)
|
|
fhash = search_function(ClassDeclaration.object, Id.tohash).isFuncDeclaration();
|
|
assert(fcmp && feq && fhash);
|
|
if (feq.vtblIndex < cd.vtbl.dim && cd.vtbl[feq.vtblIndex] == feq)
|
|
{
|
|
version (all)
|
|
{
|
|
if (fcmp.vtblIndex < cd.vtbl.dim && cd.vtbl[fcmp.vtblIndex] != fcmp)
|
|
{
|
|
const(char)* s = (index.toBasetype().ty != Tclass) ? "bottom of " : "";
|
|
error(loc, "%sAA key type %s now requires equality rather than comparison", s, cd.toChars());
|
|
errorSupplemental(loc, "Please override Object.opEquals and toHash.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
next = next.semantic(loc, sc).merge2();
|
|
transitive();
|
|
switch (next.toBasetype().ty)
|
|
{
|
|
case Tfunction:
|
|
case Tvoid:
|
|
case Tnone:
|
|
case Ttuple:
|
|
error(loc, "can't have associative array of %s", next.toChars());
|
|
goto case Terror;
|
|
case Terror:
|
|
return Type.terror;
|
|
default:
|
|
break;
|
|
}
|
|
if (next.isscope())
|
|
{
|
|
error(loc, "cannot have array of scope %s", next.toChars());
|
|
return Type.terror;
|
|
}
|
|
return merge();
|
|
}
|
|
|
|
override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false)
|
|
{
|
|
//printf("TypeAArray::resolve() %s\n", toChars());
|
|
// Deal with the case where we thought the index was a type, but
|
|
// in reality it was an expression.
|
|
if (index.ty == Tident || index.ty == Tinstance || index.ty == Tsarray)
|
|
{
|
|
Expression e;
|
|
Type t;
|
|
Dsymbol s;
|
|
index.resolve(loc, sc, &e, &t, &s, intypeid);
|
|
if (e)
|
|
{
|
|
// It was an expression -
|
|
// Rewrite as a static array
|
|
auto tsa = new TypeSArray(next, e);
|
|
tsa.mod = this.mod; // just copy mod field so tsa's semantic is not yet done
|
|
return tsa.resolve(loc, sc, pe, pt, ps, intypeid);
|
|
}
|
|
else if (t)
|
|
index = t;
|
|
else
|
|
index.error(loc, "index is not a type or an expression");
|
|
}
|
|
Type.resolve(loc, sc, pe, pt, ps, intypeid);
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeAArray::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
|
|
}
|
|
if (ident == Id.length)
|
|
{
|
|
static __gshared FuncDeclaration fd_aaLen = null;
|
|
if (fd_aaLen is null)
|
|
{
|
|
auto fparams = new Parameters();
|
|
fparams.push(new Parameter(STCin, this, null, null));
|
|
fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen);
|
|
TypeFunction tf = cast(TypeFunction)fd_aaLen.type;
|
|
tf.purity = PUREconst;
|
|
tf.isnothrow = true;
|
|
tf.isnogc = false;
|
|
}
|
|
Expression ev = new VarExp(e.loc, fd_aaLen, false);
|
|
e = new CallExp(e.loc, ev, e);
|
|
e.type = (cast(TypeFunction)fd_aaLen.type).next;
|
|
}
|
|
else
|
|
e = Type.dotExp(sc, e, ident, flag);
|
|
return e;
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypeAArray::defaultInit() '%s'\n", toChars());
|
|
}
|
|
return new NullExp(loc, this);
|
|
}
|
|
|
|
override bool isZeroInit(Loc loc) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool isBoolean() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override Expression toExpression()
|
|
{
|
|
Expression e = next.toExpression();
|
|
if (e)
|
|
{
|
|
Expression ei = index.toExpression();
|
|
if (ei)
|
|
return new ArrayExp(loc, e, ei);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
override bool hasPointers() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override MATCH implicitConvTo(Type to)
|
|
{
|
|
//printf("TypeAArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars());
|
|
if (equals(to))
|
|
return MATCHexact;
|
|
if (to.ty == Taarray)
|
|
{
|
|
TypeAArray ta = cast(TypeAArray)to;
|
|
if (!MODimplicitConv(next.mod, ta.next.mod))
|
|
return MATCHnomatch; // not const-compatible
|
|
if (!MODimplicitConv(index.mod, ta.index.mod))
|
|
return MATCHnomatch; // not const-compatible
|
|
MATCH m = next.constConv(ta.next);
|
|
MATCH mi = index.constConv(ta.index);
|
|
if (m > MATCHnomatch && mi > MATCHnomatch)
|
|
{
|
|
return MODimplicitConv(mod, to.mod) ? MATCHconst : MATCHnomatch;
|
|
}
|
|
}
|
|
return Type.implicitConvTo(to);
|
|
}
|
|
|
|
override MATCH constConv(Type to)
|
|
{
|
|
if (to.ty == Taarray)
|
|
{
|
|
TypeAArray taa = cast(TypeAArray)to;
|
|
MATCH mindex = index.constConv(taa.index);
|
|
MATCH mkey = next.constConv(taa.next);
|
|
// Pick the worst match
|
|
return mkey < mindex ? mkey : mindex;
|
|
}
|
|
return Type.constConv(to);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypePointer : TypeNext
|
|
{
|
|
public:
|
|
extern (D) this(Type t)
|
|
{
|
|
super(Tpointer, t);
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "pointer";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
Type t = next.syntaxCopy();
|
|
if (t == next)
|
|
t = this;
|
|
else
|
|
{
|
|
t = new TypePointer(t);
|
|
t.mod = mod;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypePointer::semantic() %s\n", toChars());
|
|
if (deco)
|
|
return this;
|
|
Type n = next.semantic(loc, sc);
|
|
switch (n.toBasetype().ty)
|
|
{
|
|
case Ttuple:
|
|
error(loc, "can't have pointer to %s", n.toChars());
|
|
goto case Terror;
|
|
case Terror:
|
|
return Type.terror;
|
|
default:
|
|
break;
|
|
}
|
|
if (n != next)
|
|
{
|
|
deco = null;
|
|
}
|
|
next = n;
|
|
if (next.ty != Tfunction)
|
|
{
|
|
transitive();
|
|
return merge();
|
|
}
|
|
version (none)
|
|
{
|
|
return merge();
|
|
}
|
|
else
|
|
{
|
|
deco = merge().deco;
|
|
/* Don't return merge(), because arg identifiers and default args
|
|
* can be different
|
|
* even though the types match
|
|
*/
|
|
return this;
|
|
}
|
|
}
|
|
|
|
override d_uns64 size(Loc loc) const
|
|
{
|
|
return Target.ptrsize;
|
|
}
|
|
|
|
override MATCH implicitConvTo(Type to)
|
|
{
|
|
//printf("TypePointer::implicitConvTo(to = %s) %s\n", to->toChars(), toChars());
|
|
if (equals(to))
|
|
return MATCHexact;
|
|
if (next.ty == Tfunction)
|
|
{
|
|
if (to.ty == Tpointer)
|
|
{
|
|
TypePointer tp = cast(TypePointer)to;
|
|
if (tp.next.ty == Tfunction)
|
|
{
|
|
if (next.equals(tp.next))
|
|
return MATCHconst;
|
|
if (next.covariant(tp.next) == 1)
|
|
{
|
|
Type tret = this.next.nextOf();
|
|
Type toret = tp.next.nextOf();
|
|
if (tret.ty == Tclass && toret.ty == Tclass)
|
|
{
|
|
/* Bugzilla 10219: Check covariant interface return with offset tweaking.
|
|
* interface I {}
|
|
* class C : Object, I {}
|
|
* I function() dg = function C() {} // should be error
|
|
*/
|
|
int offset = 0;
|
|
if (toret.isBaseOf(tret, &offset) && offset != 0)
|
|
return MATCHnomatch;
|
|
}
|
|
return MATCHconvert;
|
|
}
|
|
}
|
|
else if (tp.next.ty == Tvoid)
|
|
{
|
|
// Allow conversions to void*
|
|
return MATCHconvert;
|
|
}
|
|
}
|
|
return MATCHnomatch;
|
|
}
|
|
else if (to.ty == Tpointer)
|
|
{
|
|
TypePointer tp = cast(TypePointer)to;
|
|
assert(tp.next);
|
|
if (!MODimplicitConv(next.mod, tp.next.mod))
|
|
return MATCHnomatch; // not const-compatible
|
|
/* Alloc conversion to void*
|
|
*/
|
|
if (next.ty != Tvoid && tp.next.ty == Tvoid)
|
|
{
|
|
return MATCHconvert;
|
|
}
|
|
MATCH m = next.constConv(tp.next);
|
|
if (m > MATCHnomatch)
|
|
{
|
|
if (m == MATCHexact && mod != to.mod)
|
|
m = MATCHconst;
|
|
return m;
|
|
}
|
|
}
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
override MATCH constConv(Type to)
|
|
{
|
|
if (next.ty == Tfunction)
|
|
{
|
|
if (to.nextOf() && next.equals((cast(TypeNext)to).next))
|
|
return Type.constConv(to);
|
|
else
|
|
return MATCHnomatch;
|
|
}
|
|
return TypeNext.constConv(to);
|
|
}
|
|
|
|
override bool isscalar() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypePointer::defaultInit() '%s'\n", toChars());
|
|
}
|
|
return new NullExp(loc, this);
|
|
}
|
|
|
|
override bool isZeroInit(Loc loc) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool hasPointers() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeReference : TypeNext
|
|
{
|
|
public:
|
|
extern (D) this(Type t)
|
|
{
|
|
super(Treference, t);
|
|
// BUG: what about references to static arrays?
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "reference";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
Type t = next.syntaxCopy();
|
|
if (t == next)
|
|
t = this;
|
|
else
|
|
{
|
|
t = new TypeReference(t);
|
|
t.mod = mod;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypeReference::semantic()\n");
|
|
Type n = next.semantic(loc, sc);
|
|
if (n != next)
|
|
deco = null;
|
|
next = n;
|
|
transitive();
|
|
return merge();
|
|
}
|
|
|
|
override d_uns64 size(Loc loc) const
|
|
{
|
|
return Target.ptrsize;
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeReference::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
|
|
}
|
|
// References just forward things along
|
|
return next.dotExp(sc, e, ident, flag);
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypeReference::defaultInit() '%s'\n", toChars());
|
|
}
|
|
return new NullExp(loc, this);
|
|
}
|
|
|
|
override bool isZeroInit(Loc loc) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
enum RET : int
|
|
{
|
|
RETregs = 1, // returned in registers
|
|
RETstack = 2, // returned on stack
|
|
}
|
|
|
|
alias RETregs = RET.RETregs;
|
|
alias RETstack = RET.RETstack;
|
|
|
|
enum TRUST : int
|
|
{
|
|
TRUSTdefault = 0,
|
|
TRUSTsystem = 1, // @system (same as TRUSTdefault)
|
|
TRUSTtrusted = 2, // @trusted
|
|
TRUSTsafe = 3, // @safe
|
|
}
|
|
|
|
alias TRUSTdefault = TRUST.TRUSTdefault;
|
|
alias TRUSTsystem = TRUST.TRUSTsystem;
|
|
alias TRUSTtrusted = TRUST.TRUSTtrusted;
|
|
alias TRUSTsafe = TRUST.TRUSTsafe;
|
|
|
|
enum TRUSTformat : int
|
|
{
|
|
TRUSTformatDefault, // do not emit @system when trust == TRUSTdefault
|
|
TRUSTformatSystem, // emit @system when trust == TRUSTdefault
|
|
}
|
|
|
|
alias TRUSTformatDefault = TRUSTformat.TRUSTformatDefault;
|
|
alias TRUSTformatSystem = TRUSTformat.TRUSTformatSystem;
|
|
|
|
enum PURE : int
|
|
{
|
|
PUREimpure = 0, // not pure at all
|
|
PUREfwdref = 1, // it's pure, but not known which level yet
|
|
PUREweak = 2, // no mutable globals are read or written
|
|
PUREconst = 3, // parameters are values or const
|
|
PUREstrong = 4, // parameters are values or immutable
|
|
}
|
|
|
|
alias PUREimpure = PURE.PUREimpure;
|
|
alias PUREfwdref = PURE.PUREfwdref;
|
|
alias PUREweak = PURE.PUREweak;
|
|
alias PUREconst = PURE.PUREconst;
|
|
alias PUREstrong = PURE.PUREstrong;
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeFunction : TypeNext
|
|
{
|
|
public:
|
|
// .next is the return type
|
|
|
|
Parameters* parameters; // function parameters
|
|
int varargs; // 1: T t, ...) style for variable number of arguments
|
|
// 2: T t ...) style for variable number of arguments
|
|
bool isnothrow; // true: nothrow
|
|
bool isnogc; // true: is @nogc
|
|
bool isproperty; // can be called without parentheses
|
|
bool isref; // true: returns a reference
|
|
bool isreturn; // true: 'this' is returned by ref
|
|
LINK linkage; // calling convention
|
|
TRUST trust; // level of trust
|
|
PURE purity = PUREimpure;
|
|
ubyte iswild; // bit0: inout on params, bit1: inout on qualifier
|
|
Expressions* fargs; // function arguments
|
|
int inuse;
|
|
|
|
extern (D) this(Parameters* parameters, Type treturn, int varargs, LINK linkage, StorageClass stc = 0)
|
|
{
|
|
super(Tfunction, treturn);
|
|
//if (!treturn) *(char*)0=0;
|
|
// assert(treturn);
|
|
assert(0 <= varargs && varargs <= 2);
|
|
this.parameters = parameters;
|
|
this.varargs = varargs;
|
|
this.linkage = linkage;
|
|
if (stc & STCpure)
|
|
this.purity = PUREfwdref;
|
|
if (stc & STCnothrow)
|
|
this.isnothrow = true;
|
|
if (stc & STCnogc)
|
|
this.isnogc = true;
|
|
if (stc & STCproperty)
|
|
this.isproperty = true;
|
|
if (stc & STCref)
|
|
this.isref = true;
|
|
if (stc & STCreturn)
|
|
this.isreturn = true;
|
|
this.trust = TRUSTdefault;
|
|
if (stc & STCsafe)
|
|
this.trust = TRUSTsafe;
|
|
if (stc & STCsystem)
|
|
this.trust = TRUSTsystem;
|
|
if (stc & STCtrusted)
|
|
this.trust = TRUSTtrusted;
|
|
}
|
|
|
|
static TypeFunction create(Parameters* parameters, Type treturn, int varargs, LINK linkage, StorageClass stc = 0)
|
|
{
|
|
return new TypeFunction(parameters, treturn, varargs, linkage, stc);
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "function";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
Type treturn = next ? next.syntaxCopy() : null;
|
|
Parameters* params = Parameter.arraySyntaxCopy(parameters);
|
|
auto t = new TypeFunction(params, treturn, varargs, linkage);
|
|
t.mod = mod;
|
|
t.isnothrow = isnothrow;
|
|
t.isnogc = isnogc;
|
|
t.purity = purity;
|
|
t.isproperty = isproperty;
|
|
t.isref = isref;
|
|
t.isreturn = isreturn;
|
|
t.iswild = iswild;
|
|
t.trust = trust;
|
|
t.fargs = fargs;
|
|
return t;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
if (deco) // if semantic() already run
|
|
{
|
|
//printf("already done\n");
|
|
return this;
|
|
}
|
|
//printf("TypeFunction::semantic() this = %p\n", this);
|
|
//printf("TypeFunction::semantic() %s, sc->stc = %llx, fargs = %p\n", toChars(), sc->stc, fargs);
|
|
bool errors = false;
|
|
/* Copy in order to not mess up original.
|
|
* This can produce redundant copies if inferring return type,
|
|
* as semantic() will get called again on this.
|
|
*/
|
|
TypeFunction tf = cast(TypeFunction)copy();
|
|
if (parameters)
|
|
{
|
|
tf.parameters = parameters.copy();
|
|
for (size_t i = 0; i < parameters.dim; i++)
|
|
{
|
|
Parameter p = cast(Parameter)mem.xmalloc(__traits(classInstanceSize, Parameter));
|
|
memcpy(cast(void*)p, cast(void*)(*parameters)[i], __traits(classInstanceSize, Parameter));
|
|
(*tf.parameters)[i] = p;
|
|
}
|
|
}
|
|
if (sc.stc & STCpure)
|
|
tf.purity = PUREfwdref;
|
|
if (sc.stc & STCnothrow)
|
|
tf.isnothrow = true;
|
|
if (sc.stc & STCnogc)
|
|
tf.isnogc = true;
|
|
if (sc.stc & STCref)
|
|
tf.isref = true;
|
|
if (sc.stc & STCreturn)
|
|
tf.isreturn = true;
|
|
if (sc.stc & STCsafe)
|
|
tf.trust = TRUSTsafe;
|
|
if (sc.stc & STCsystem)
|
|
tf.trust = TRUSTsystem;
|
|
if (sc.stc & STCtrusted)
|
|
tf.trust = TRUSTtrusted;
|
|
if (sc.stc & STCproperty)
|
|
tf.isproperty = true;
|
|
tf.linkage = sc.linkage;
|
|
version (none)
|
|
{
|
|
/* If the parent is @safe, then this function defaults to safe
|
|
* too.
|
|
* If the parent's @safe-ty is inferred, then this function's @safe-ty needs
|
|
* to be inferred first.
|
|
*/
|
|
if (tf.trust == TRUSTdefault)
|
|
for (Dsymbol p = sc.func; p; p = p.toParent2())
|
|
{
|
|
FuncDeclaration fd = p.isFuncDeclaration();
|
|
if (fd)
|
|
{
|
|
if (fd.isSafeBypassingInference())
|
|
tf.trust = TRUSTsafe; // default to @safe
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
bool wildreturn = false;
|
|
if (tf.next)
|
|
{
|
|
sc = sc.push();
|
|
sc.stc &= ~(STC_TYPECTOR | STC_FUNCATTR);
|
|
tf.next = tf.next.semantic(loc, sc);
|
|
sc = sc.pop();
|
|
errors |= tf.checkRetType(loc);
|
|
if (tf.next.isscope() && !(sc.flags & SCOPEctor))
|
|
{
|
|
error(loc, "functions cannot return scope %s", tf.next.toChars());
|
|
errors = true;
|
|
}
|
|
if (tf.next.hasWild())
|
|
wildreturn = true;
|
|
}
|
|
ubyte wildparams = 0;
|
|
if (tf.parameters)
|
|
{
|
|
/* Create a scope for evaluating the default arguments for the parameters
|
|
*/
|
|
Scope* argsc = sc.push();
|
|
argsc.stc = 0; // don't inherit storage class
|
|
argsc.protection = Prot(PROTpublic);
|
|
argsc.func = null;
|
|
size_t dim = Parameter.dim(tf.parameters);
|
|
for (size_t i = 0; i < dim; i++)
|
|
{
|
|
Parameter fparam = Parameter.getNth(tf.parameters, i);
|
|
tf.inuse++;
|
|
fparam.type = fparam.type.semantic(loc, argsc);
|
|
if (tf.inuse == 1)
|
|
tf.inuse--;
|
|
if (fparam.type.ty == Terror)
|
|
{
|
|
errors = true;
|
|
continue;
|
|
}
|
|
fparam.type = fparam.type.addStorageClass(fparam.storageClass);
|
|
if (fparam.storageClass & (STCauto | STCalias | STCstatic))
|
|
{
|
|
if (!fparam.type)
|
|
continue;
|
|
}
|
|
Type t = fparam.type.toBasetype();
|
|
if (t.ty == Tfunction)
|
|
{
|
|
error(loc, "cannot have parameter of function type %s", fparam.type.toChars());
|
|
errors = true;
|
|
}
|
|
else if (!(fparam.storageClass & (STCref | STCout)) &&
|
|
(t.ty == Tstruct || t.ty == Tsarray || t.ty == Tenum))
|
|
{
|
|
Type tb2 = t.baseElemOf();
|
|
if (tb2.ty == Tstruct && !(cast(TypeStruct)tb2).sym.members ||
|
|
tb2.ty == Tenum && !(cast(TypeEnum)tb2).sym.memtype)
|
|
{
|
|
error(loc, "cannot have parameter of opaque type %s by value", fparam.type.toChars());
|
|
errors = true;
|
|
}
|
|
}
|
|
else if (!(fparam.storageClass & STClazy) && t.ty == Tvoid)
|
|
{
|
|
error(loc, "cannot have parameter of type %s", fparam.type.toChars());
|
|
errors = true;
|
|
}
|
|
if ((fparam.storageClass & (STCref | STCwild)) == (STCref | STCwild))
|
|
{
|
|
// 'ref inout' implies 'return'
|
|
fparam.storageClass |= STCreturn;
|
|
}
|
|
if (fparam.storageClass & STCreturn && !(fparam.storageClass & (STCref | STCout)))
|
|
{
|
|
error(loc, "'return' can only be used with 'ref' or 'out'");
|
|
errors = true;
|
|
}
|
|
if (fparam.storageClass & (STCref | STClazy))
|
|
{
|
|
}
|
|
else if (fparam.storageClass & STCout)
|
|
{
|
|
if (ubyte m = fparam.type.mod & (MODimmutable | MODconst | MODwild))
|
|
{
|
|
error(loc, "cannot have %s out parameter of type %s", MODtoChars(m), t.toChars());
|
|
errors = true;
|
|
}
|
|
else
|
|
{
|
|
Type tv = t;
|
|
while (tv.ty == Tsarray)
|
|
tv = tv.nextOf().toBasetype();
|
|
if (tv.ty == Tstruct && (cast(TypeStruct)tv).sym.noDefaultCtor)
|
|
{
|
|
error(loc, "cannot have out parameter of type %s because the default construction is disabled", fparam.type.toChars());
|
|
errors = true;
|
|
}
|
|
}
|
|
}
|
|
if (t.hasWild())
|
|
{
|
|
wildparams |= 1;
|
|
//if (tf->next && !wildreturn)
|
|
// error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with 'ref')");
|
|
}
|
|
if (fparam.defaultArg)
|
|
{
|
|
Expression e = fparam.defaultArg;
|
|
if (fparam.storageClass & (STCref | STCout))
|
|
{
|
|
e = e.semantic(argsc);
|
|
e = resolveProperties(argsc, e);
|
|
}
|
|
else
|
|
{
|
|
e = inferType(e, fparam.type);
|
|
Initializer iz = new ExpInitializer(e.loc, e);
|
|
iz = iz.semantic(argsc, fparam.type, INITnointerpret);
|
|
e = iz.toExpression();
|
|
}
|
|
if (e.op == TOKfunction) // see Bugzilla 4820
|
|
{
|
|
FuncExp fe = cast(FuncExp)e;
|
|
// Replace function literal with a function symbol,
|
|
// since default arg expression must be copied when used
|
|
// and copying the literal itself is wrong.
|
|
e = new VarExp(e.loc, fe.fd, false);
|
|
e = new AddrExp(e.loc, e);
|
|
e = e.semantic(argsc);
|
|
}
|
|
e = e.implicitCastTo(argsc, fparam.type);
|
|
// default arg must be an lvalue
|
|
if (fparam.storageClass & (STCout | STCref))
|
|
e = e.toLvalue(argsc, e);
|
|
fparam.defaultArg = e;
|
|
if (e.op == TOKerror)
|
|
errors = true;
|
|
}
|
|
/* If fparam after semantic() turns out to be a tuple, the number of parameters may
|
|
* change.
|
|
*/
|
|
if (t.ty == Ttuple)
|
|
{
|
|
/* TypeFunction::parameter also is used as the storage of
|
|
* Parameter objects for FuncDeclaration. So we should copy
|
|
* the elements of TypeTuple::arguments to avoid unintended
|
|
* sharing of Parameter object among other functions.
|
|
*/
|
|
TypeTuple tt = cast(TypeTuple)t;
|
|
if (tt.arguments && tt.arguments.dim)
|
|
{
|
|
/* Propagate additional storage class from tuple parameters to their
|
|
* element-parameters.
|
|
* Make a copy, as original may be referenced elsewhere.
|
|
*/
|
|
size_t tdim = tt.arguments.dim;
|
|
auto newparams = new Parameters();
|
|
newparams.setDim(tdim);
|
|
for (size_t j = 0; j < tdim; j++)
|
|
{
|
|
Parameter narg = (*tt.arguments)[j];
|
|
|
|
// Bugzilla 12744: If the storage classes of narg
|
|
// conflict with the ones in fparam, it's ignored.
|
|
StorageClass stc = fparam.storageClass | narg.storageClass;
|
|
StorageClass stc1 = fparam.storageClass & (STCref | STCout | STClazy);
|
|
StorageClass stc2 = narg.storageClass & (STCref | STCout | STClazy);
|
|
if (stc1 && stc2 && stc1 != stc2)
|
|
{
|
|
OutBuffer buf1; stcToBuffer(&buf1, stc1 | ((stc1 & STCref) ? (fparam.storageClass & STCauto) : 0));
|
|
OutBuffer buf2; stcToBuffer(&buf2, stc2);
|
|
|
|
error(loc, "incompatible parameter storage classes '%s' and '%s'",
|
|
buf1.peekString(), buf2.peekString());
|
|
errors = true;
|
|
stc = stc1 | (stc & ~(STCref | STCout | STClazy));
|
|
}
|
|
|
|
(*newparams)[j] = new Parameter(
|
|
stc, narg.type, narg.ident, narg.defaultArg);
|
|
}
|
|
fparam.type = new TypeTuple(newparams);
|
|
}
|
|
fparam.storageClass = 0;
|
|
/* Reset number of parameters, and back up one to do this fparam again,
|
|
* now that it is a tuple
|
|
*/
|
|
dim = Parameter.dim(tf.parameters);
|
|
i--;
|
|
continue;
|
|
}
|
|
/* Resolve "auto ref" storage class to be either ref or value,
|
|
* based on the argument matching the parameter
|
|
*/
|
|
if (fparam.storageClass & STCauto)
|
|
{
|
|
if (fargs && i < fargs.dim && (fparam.storageClass & STCref))
|
|
{
|
|
Expression farg = (*fargs)[i];
|
|
if (farg.isLvalue())
|
|
{
|
|
// ref parameter
|
|
}
|
|
else
|
|
fparam.storageClass &= ~STCref; // value parameter
|
|
fparam.storageClass &= ~STCauto; // Bugzilla 14656
|
|
fparam.storageClass |= STCautoref;
|
|
}
|
|
else
|
|
{
|
|
error(loc, "'auto' can only be used as part of 'auto ref' for template function parameters");
|
|
errors = true;
|
|
}
|
|
}
|
|
// Remove redundant storage classes for type, they are already applied
|
|
fparam.storageClass &= ~(STC_TYPECTOR | STCin);
|
|
}
|
|
argsc.pop();
|
|
}
|
|
if (tf.isWild())
|
|
wildparams |= 2;
|
|
if (wildreturn && !wildparams)
|
|
{
|
|
error(loc, "inout on return means inout must be on a parameter as well for %s", toChars());
|
|
errors = true;
|
|
}
|
|
tf.iswild = wildparams;
|
|
if (tf.inuse)
|
|
{
|
|
error(loc, "recursive type");
|
|
tf.inuse = 0;
|
|
errors = true;
|
|
}
|
|
if (tf.isproperty && (tf.varargs || Parameter.dim(tf.parameters) > 2))
|
|
{
|
|
error(loc, "properties can only have zero, one, or two parameter");
|
|
errors = true;
|
|
}
|
|
if (tf.varargs == 1 && tf.linkage != LINKd && Parameter.dim(tf.parameters) == 0)
|
|
{
|
|
error(loc, "variadic functions with non-D linkage must have at least one parameter");
|
|
errors = true;
|
|
}
|
|
if (errors)
|
|
return terror;
|
|
if (tf.next)
|
|
tf.deco = tf.merge().deco;
|
|
/* Don't return merge(), because arg identifiers and default args
|
|
* can be different
|
|
* even though the types match
|
|
*/
|
|
return tf;
|
|
}
|
|
|
|
/********************************************
|
|
* Do this lazily, as the parameter types might be forward referenced.
|
|
*/
|
|
void purityLevel()
|
|
{
|
|
TypeFunction tf = this;
|
|
if (tf.purity != PUREfwdref)
|
|
return;
|
|
/* Evaluate what kind of purity based on the modifiers for the parameters
|
|
*/
|
|
tf.purity = PUREstrong; // assume strong until something weakens it
|
|
size_t dim = Parameter.dim(tf.parameters);
|
|
if (!dim)
|
|
return;
|
|
for (size_t i = 0; i < dim; i++)
|
|
{
|
|
Parameter fparam = Parameter.getNth(tf.parameters, i);
|
|
Type t = fparam.type;
|
|
if (!t)
|
|
continue;
|
|
if (fparam.storageClass & (STClazy | STCout))
|
|
{
|
|
tf.purity = PUREweak;
|
|
break;
|
|
}
|
|
if (fparam.storageClass & STCref)
|
|
{
|
|
if (t.mod & MODimmutable)
|
|
continue;
|
|
if (t.mod & (MODconst | MODwild))
|
|
{
|
|
tf.purity = PUREconst;
|
|
continue;
|
|
}
|
|
tf.purity = PUREweak;
|
|
break;
|
|
}
|
|
t = t.baseElemOf();
|
|
if (!t.hasPointers())
|
|
continue;
|
|
if (t.mod & MODimmutable)
|
|
continue;
|
|
/* Accept immutable(T)[] and immutable(T)* as being strongly pure
|
|
*/
|
|
if (t.ty == Tarray || t.ty == Tpointer)
|
|
{
|
|
Type tn = t.nextOf().toBasetype();
|
|
if (tn.mod & MODimmutable)
|
|
continue;
|
|
if (tn.mod & (MODconst | MODwild))
|
|
{
|
|
tf.purity = PUREconst;
|
|
continue;
|
|
}
|
|
}
|
|
/* 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.
|
|
*/
|
|
if (t.mod & (MODconst | MODwild))
|
|
{
|
|
tf.purity = PUREconst;
|
|
continue;
|
|
}
|
|
/* Should catch delegates and function pointers, and fold in their purity
|
|
*/
|
|
tf.purity = PUREweak; // err on the side of too strict
|
|
break;
|
|
}
|
|
}
|
|
|
|
/********************************************
|
|
* Return true if there are lazy parameters.
|
|
*/
|
|
bool hasLazyParameters()
|
|
{
|
|
size_t dim = Parameter.dim(parameters);
|
|
for (size_t i = 0; i < dim; i++)
|
|
{
|
|
Parameter fparam = Parameter.getNth(parameters, i);
|
|
if (fparam.storageClass & STClazy)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/***************************
|
|
* Examine function signature for parameter p and see if
|
|
* p can 'escape' the scope of the function.
|
|
*/
|
|
bool parameterEscapes(Parameter p)
|
|
{
|
|
purityLevel();
|
|
/* Scope parameters do not escape.
|
|
* Allow 'lazy' to imply 'scope' -
|
|
* lazy parameters can be passed along
|
|
* as lazy parameters to the next function, but that isn't
|
|
* escaping.
|
|
*/
|
|
if (p.storageClass & (STCscope | STClazy))
|
|
return false;
|
|
if (p.storageClass & STCreturn)
|
|
return true;
|
|
/* If haven't inferred the return type yet, assume it escapes
|
|
*/
|
|
if (!nextOf())
|
|
return true;
|
|
if (purity > PUREweak)
|
|
{
|
|
/* With pure functions, we need only be concerned if p escapes
|
|
* via any return statement.
|
|
*/
|
|
Type tret = nextOf().toBasetype();
|
|
if (!isref && !tret.hasPointers())
|
|
{
|
|
/* The result has no references, so p could not be escaping
|
|
* that way.
|
|
*/
|
|
return false;
|
|
}
|
|
}
|
|
/* Assume it escapes in the absence of better information.
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
override Type addStorageClass(StorageClass stc)
|
|
{
|
|
TypeFunction t = cast(TypeFunction)Type.addStorageClass(stc);
|
|
if ((stc & STCpure && !t.purity) || (stc & STCnothrow && !t.isnothrow) || (stc & STCnogc && !t.isnogc) || (stc & STCsafe && t.trust < TRUSTtrusted))
|
|
{
|
|
// Klunky to change these
|
|
auto tf = new TypeFunction(t.parameters, t.next, t.varargs, t.linkage, 0);
|
|
tf.mod = t.mod;
|
|
tf.fargs = fargs;
|
|
tf.purity = t.purity;
|
|
tf.isnothrow = t.isnothrow;
|
|
tf.isnogc = t.isnogc;
|
|
tf.isproperty = t.isproperty;
|
|
tf.isref = t.isref;
|
|
tf.isreturn = t.isreturn;
|
|
tf.trust = t.trust;
|
|
tf.iswild = t.iswild;
|
|
if (stc & STCpure)
|
|
tf.purity = PUREfwdref;
|
|
if (stc & STCnothrow)
|
|
tf.isnothrow = true;
|
|
if (stc & STCnogc)
|
|
tf.isnogc = true;
|
|
if (stc & STCsafe)
|
|
tf.trust = TRUSTsafe;
|
|
tf.deco = tf.merge().deco;
|
|
t = tf;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
/** For each active attribute (ref/const/nogc/etc) call fp with a void* for the
|
|
work param and a string representation of the attribute. */
|
|
int attributesApply(void* param, int function(void*, const(char)*) fp, TRUSTformat trustFormat = TRUSTformatDefault)
|
|
{
|
|
int res = 0;
|
|
if (purity)
|
|
res = fp(param, "pure");
|
|
if (res)
|
|
return res;
|
|
if (isnothrow)
|
|
res = fp(param, "nothrow");
|
|
if (res)
|
|
return res;
|
|
if (isnogc)
|
|
res = fp(param, "@nogc");
|
|
if (res)
|
|
return res;
|
|
if (isproperty)
|
|
res = fp(param, "@property");
|
|
if (res)
|
|
return res;
|
|
if (isref)
|
|
res = fp(param, "ref");
|
|
if (res)
|
|
return res;
|
|
if (isreturn)
|
|
res = fp(param, "return");
|
|
if (res)
|
|
return res;
|
|
TRUST trustAttrib = trust;
|
|
if (trustAttrib == TRUSTdefault)
|
|
{
|
|
// Print out "@system" when trust equals TRUSTdefault (if desired).
|
|
if (trustFormat == TRUSTformatSystem)
|
|
trustAttrib = TRUSTsystem;
|
|
else
|
|
return res; // avoid calling with an empty string
|
|
}
|
|
return fp(param, trustToChars(trustAttrib));
|
|
}
|
|
|
|
override Type substWildTo(uint)
|
|
{
|
|
if (!iswild && !(mod & MODwild))
|
|
return this;
|
|
// Substitude inout qualifier of function type to mutable or immutable
|
|
// would break type system. Instead substitude inout to the most weak
|
|
// qualifer - const.
|
|
uint m = MODconst;
|
|
assert(next);
|
|
Type tret = next.substWildTo(m);
|
|
Parameters* params = parameters;
|
|
if (mod & MODwild)
|
|
params = parameters.copy();
|
|
for (size_t i = 0; i < params.dim; i++)
|
|
{
|
|
Parameter p = (*params)[i];
|
|
Type t = p.type.substWildTo(m);
|
|
if (t == p.type)
|
|
continue;
|
|
if (params == parameters)
|
|
params = parameters.copy();
|
|
(*params)[i] = new Parameter(p.storageClass, t, null, null);
|
|
}
|
|
if (next == tret && params == parameters)
|
|
return this;
|
|
// Similar to TypeFunction::syntaxCopy;
|
|
auto t = new TypeFunction(params, tret, varargs, linkage);
|
|
t.mod = ((mod & MODwild) ? (mod & ~MODwild) | MODconst : mod);
|
|
t.isnothrow = isnothrow;
|
|
t.isnogc = isnogc;
|
|
t.purity = purity;
|
|
t.isproperty = isproperty;
|
|
t.isref = isref;
|
|
t.isreturn = isreturn;
|
|
t.iswild = 0;
|
|
t.trust = trust;
|
|
t.fargs = fargs;
|
|
return t.merge();
|
|
}
|
|
|
|
/********************************
|
|
* 'args' are being matched to function 'this'
|
|
* Determine match level.
|
|
* Input:
|
|
* flag 1 performing a partial ordering match
|
|
* Returns:
|
|
* MATCHxxxx
|
|
*/
|
|
MATCH callMatch(Type tthis, Expressions* args, int flag = 0)
|
|
{
|
|
//printf("TypeFunction::callMatch() %s\n", toChars());
|
|
MATCH match = MATCHexact; // assume exact match
|
|
ubyte wildmatch = 0;
|
|
if (tthis)
|
|
{
|
|
Type t = tthis;
|
|
if (t.toBasetype().ty == Tpointer)
|
|
t = t.toBasetype().nextOf(); // change struct* to struct
|
|
if (t.mod != mod)
|
|
{
|
|
if (MODimplicitConv(t.mod, mod))
|
|
match = MATCHconst;
|
|
else if ((mod & MODwild) && MODimplicitConv(t.mod, (mod & ~MODwild) | MODconst))
|
|
{
|
|
match = MATCHconst;
|
|
}
|
|
else
|
|
return MATCHnomatch;
|
|
}
|
|
if (isWild())
|
|
{
|
|
if (t.isWild())
|
|
wildmatch |= MODwild;
|
|
else if (t.isConst())
|
|
wildmatch |= MODconst;
|
|
else if (t.isImmutable())
|
|
wildmatch |= MODimmutable;
|
|
else
|
|
wildmatch |= MODmutable;
|
|
}
|
|
}
|
|
size_t nparams = Parameter.dim(parameters);
|
|
size_t nargs = args ? args.dim : 0;
|
|
if (nparams == nargs)
|
|
{
|
|
}
|
|
else if (nargs > nparams)
|
|
{
|
|
if (varargs == 0)
|
|
goto Nomatch;
|
|
// too many args; no match
|
|
match = MATCHconvert; // match ... with a "conversion" match level
|
|
}
|
|
for (size_t u = 0; u < nargs; u++)
|
|
{
|
|
if (u >= nparams)
|
|
break;
|
|
Parameter p = Parameter.getNth(parameters, u);
|
|
Expression arg = (*args)[u];
|
|
assert(arg);
|
|
Type tprm = p.type;
|
|
Type targ = arg.type;
|
|
if (!(p.storageClass & STClazy && tprm.ty == Tvoid && targ.ty != Tvoid))
|
|
{
|
|
bool isRef = (p.storageClass & (STCref | STCout)) != 0;
|
|
wildmatch |= targ.deduceWild(tprm, isRef);
|
|
}
|
|
}
|
|
if (wildmatch)
|
|
{
|
|
/* Calculate wild matching modifier
|
|
*/
|
|
if (wildmatch & MODconst || wildmatch & (wildmatch - 1))
|
|
wildmatch = MODconst;
|
|
else if (wildmatch & MODimmutable)
|
|
wildmatch = MODimmutable;
|
|
else if (wildmatch & MODwild)
|
|
wildmatch = MODwild;
|
|
else
|
|
{
|
|
assert(wildmatch & MODmutable);
|
|
wildmatch = MODmutable;
|
|
}
|
|
}
|
|
for (size_t u = 0; u < nparams; u++)
|
|
{
|
|
MATCH m;
|
|
Parameter p = Parameter.getNth(parameters, u);
|
|
assert(p);
|
|
if (u >= nargs)
|
|
{
|
|
if (p.defaultArg)
|
|
continue;
|
|
goto L1;
|
|
// try typesafe variadics
|
|
}
|
|
{
|
|
Expression arg = (*args)[u];
|
|
assert(arg);
|
|
//printf("arg: %s, type: %s\n", arg->toChars(), arg->type->toChars());
|
|
|
|
Type targ = arg.type;
|
|
Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type;
|
|
|
|
if (p.storageClass & STClazy && tprm.ty == Tvoid && targ.ty != Tvoid)
|
|
m = MATCHconvert;
|
|
else
|
|
{
|
|
//printf("%s of type %s implicitConvTo %s\n", arg->toChars(), targ->toChars(), tprm->toChars());
|
|
if (flag)
|
|
{
|
|
// for partial ordering, value is an irrelevant mockup, just look at the type
|
|
m = targ.implicitConvTo(tprm);
|
|
}
|
|
else
|
|
m = arg.implicitConvTo(tprm);
|
|
//printf("match %d\n", m);
|
|
}
|
|
|
|
// Non-lvalues do not match ref or out parameters
|
|
if (p.storageClass & (STCref | STCout))
|
|
{
|
|
// Bugzilla 13783: Don't use toBasetype() to handle enum types.
|
|
Type ta = targ;
|
|
Type tp = tprm;
|
|
//printf("fparam[%d] ta = %s, tp = %s\n", u, ta->toChars(), tp->toChars());
|
|
|
|
if (m && !arg.isLvalue())
|
|
{
|
|
if (p.storageClass & STCout)
|
|
goto Nomatch;
|
|
|
|
if (arg.op == TOKstring && tp.ty == Tsarray)
|
|
{
|
|
if (ta.ty != Tsarray)
|
|
{
|
|
Type tn = tp.nextOf().castMod(ta.nextOf().mod);
|
|
dinteger_t dim = (cast(StringExp)arg).len;
|
|
ta = tn.sarrayOf(dim);
|
|
}
|
|
}
|
|
else if (arg.op == TOKslice && tp.ty == Tsarray)
|
|
{
|
|
// Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
|
|
if (ta.ty != Tsarray)
|
|
{
|
|
Type tn = ta.nextOf();
|
|
dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger();
|
|
ta = tn.sarrayOf(dim);
|
|
}
|
|
}
|
|
else
|
|
goto Nomatch;
|
|
}
|
|
|
|
/* Find most derived alias this type being matched.
|
|
* Bugzilla 15674: Allow on both ref and out parameters.
|
|
*/
|
|
while (1)
|
|
{
|
|
Type tat = ta.toBasetype().aliasthisOf();
|
|
if (!tat || !tat.implicitConvTo(tprm))
|
|
break;
|
|
ta = tat;
|
|
}
|
|
|
|
/* A ref variable should work like a head-const reference.
|
|
* e.g. disallows:
|
|
* ref T <- an lvalue of const(T) argument
|
|
* ref T[dim] <- an lvalue of const(T[dim]) argument
|
|
*/
|
|
if (!ta.constConv(tp))
|
|
goto Nomatch;
|
|
}
|
|
}
|
|
|
|
/* prefer matching the element type rather than the array
|
|
* type when more arguments are present with T[]...
|
|
*/
|
|
if (varargs == 2 && u + 1 == nparams && nargs > nparams)
|
|
goto L1;
|
|
//printf("\tm = %d\n", m);
|
|
if (m == MATCHnomatch) // if no match
|
|
{
|
|
L1:
|
|
if (varargs == 2 && u + 1 == nparams) // if last varargs param
|
|
{
|
|
Type tb = p.type.toBasetype();
|
|
TypeSArray tsa;
|
|
dinteger_t sz;
|
|
switch (tb.ty)
|
|
{
|
|
case Tsarray:
|
|
tsa = cast(TypeSArray)tb;
|
|
sz = tsa.dim.toInteger();
|
|
if (sz != nargs - u)
|
|
goto Nomatch;
|
|
goto case Tarray;
|
|
case Tarray:
|
|
{
|
|
TypeArray ta = cast(TypeArray)tb;
|
|
for (; u < nargs; u++)
|
|
{
|
|
Expression arg = (*args)[u];
|
|
assert(arg);
|
|
/* If lazy array of delegates,
|
|
* convert arg(s) to delegate(s)
|
|
*/
|
|
Type tret = p.isLazyArray();
|
|
if (tret)
|
|
{
|
|
if (ta.next.equals(arg.type))
|
|
m = MATCHexact;
|
|
else if (tret.toBasetype().ty == Tvoid)
|
|
m = MATCHconvert;
|
|
else
|
|
{
|
|
m = arg.implicitConvTo(tret);
|
|
if (m == MATCHnomatch)
|
|
m = arg.implicitConvTo(ta.next);
|
|
}
|
|
}
|
|
else
|
|
m = arg.implicitConvTo(ta.next);
|
|
if (m == MATCHnomatch)
|
|
goto Nomatch;
|
|
if (m < match)
|
|
match = m;
|
|
}
|
|
goto Ldone;
|
|
}
|
|
case Tclass:
|
|
// Should see if there's a constructor match?
|
|
// Or just leave it ambiguous?
|
|
goto Ldone;
|
|
default:
|
|
goto Nomatch;
|
|
}
|
|
}
|
|
goto Nomatch;
|
|
}
|
|
if (m < match)
|
|
match = m; // pick worst match
|
|
}
|
|
Ldone:
|
|
//printf("match = %d\n", match);
|
|
return match;
|
|
Nomatch:
|
|
//printf("no match\n");
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
bool checkRetType(Loc loc)
|
|
{
|
|
Type tb = next.toBasetype();
|
|
if (tb.ty == Tfunction)
|
|
{
|
|
error(loc, "functions cannot return a function");
|
|
next = Type.terror;
|
|
}
|
|
if (tb.ty == Ttuple)
|
|
{
|
|
error(loc, "functions cannot return a tuple");
|
|
next = Type.terror;
|
|
}
|
|
if (!isref && (tb.ty == Tstruct || tb.ty == Tsarray))
|
|
{
|
|
Type tb2 = tb.baseElemOf();
|
|
if (tb2.ty == Tstruct && !(cast(TypeStruct)tb2).sym.members)
|
|
{
|
|
error(loc, "functions cannot return opaque type %s by value", tb.toChars());
|
|
next = Type.terror;
|
|
}
|
|
}
|
|
if (tb.ty == Terror)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc) const
|
|
{
|
|
error(loc, "function does not have a default initializer");
|
|
return new ErrorExp();
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeDelegate : TypeNext
|
|
{
|
|
public:
|
|
// .next is a TypeFunction
|
|
|
|
extern (D) this(Type t)
|
|
{
|
|
super(Tfunction, t);
|
|
ty = Tdelegate;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "delegate";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
Type t = next.syntaxCopy();
|
|
if (t == next)
|
|
t = this;
|
|
else
|
|
{
|
|
t = new TypeDelegate(t);
|
|
t.mod = mod;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypeDelegate::semantic() %s\n", toChars());
|
|
if (deco) // if semantic() already run
|
|
{
|
|
//printf("already done\n");
|
|
return this;
|
|
}
|
|
next = next.semantic(loc, sc);
|
|
if (next.ty != Tfunction)
|
|
return terror;
|
|
/* In order to deal with Bugzilla 4028, perhaps default arguments should
|
|
* be removed from next before the merge.
|
|
*/
|
|
version (none)
|
|
{
|
|
return merge();
|
|
}
|
|
else
|
|
{
|
|
/* Don't return merge(), because arg identifiers and default args
|
|
* can be different
|
|
* even though the types match
|
|
*/
|
|
deco = merge().deco;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
override d_uns64 size(Loc loc) const
|
|
{
|
|
return Target.ptrsize * 2;
|
|
}
|
|
|
|
override uint alignsize() const
|
|
{
|
|
return Target.ptrsize;
|
|
}
|
|
|
|
override MATCH implicitConvTo(Type to)
|
|
{
|
|
//printf("TypeDelegate::implicitConvTo(this=%p, to=%p)\n", this, to);
|
|
//printf("from: %s\n", toChars());
|
|
//printf("to : %s\n", to->toChars());
|
|
if (this == to)
|
|
return MATCHexact;
|
|
version (all)
|
|
{
|
|
// not allowing covariant conversions because it interferes with overriding
|
|
if (to.ty == Tdelegate && this.nextOf().covariant(to.nextOf()) == 1)
|
|
{
|
|
Type tret = this.next.nextOf();
|
|
Type toret = (cast(TypeDelegate)to).next.nextOf();
|
|
if (tret.ty == Tclass && toret.ty == Tclass)
|
|
{
|
|
/* Bugzilla 10219: Check covariant interface return with offset tweaking.
|
|
* interface I {}
|
|
* class C : Object, I {}
|
|
* I delegate() dg = delegate C() {} // should be error
|
|
*/
|
|
int offset = 0;
|
|
if (toret.isBaseOf(tret, &offset) && offset != 0)
|
|
return MATCHnomatch;
|
|
}
|
|
return MATCHconvert;
|
|
}
|
|
}
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypeDelegate::defaultInit() '%s'\n", toChars());
|
|
}
|
|
return new NullExp(loc, this);
|
|
}
|
|
|
|
override bool isZeroInit(Loc loc) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool isBoolean() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeDelegate::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
|
|
}
|
|
if (ident == Id.ptr)
|
|
{
|
|
e = new DelegatePtrExp(e.loc, e);
|
|
e = e.semantic(sc);
|
|
}
|
|
else if (ident == Id.funcptr)
|
|
{
|
|
e = new DelegateFuncptrExp(e.loc, e);
|
|
e = e.semantic(sc);
|
|
}
|
|
else
|
|
{
|
|
e = Type.dotExp(sc, e, ident, flag);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
override bool hasPointers() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class TypeQualified : Type
|
|
{
|
|
public:
|
|
Loc loc;
|
|
|
|
// array of Identifier and TypeInstance,
|
|
// representing ident.ident!tiargs.ident. ... etc.
|
|
Objects idents;
|
|
|
|
final extern (D) this(TY ty, Loc loc)
|
|
{
|
|
super(ty);
|
|
this.loc = loc;
|
|
}
|
|
|
|
final void syntaxCopyHelper(TypeQualified t)
|
|
{
|
|
//printf("TypeQualified::syntaxCopyHelper(%s) %s\n", t->toChars(), toChars());
|
|
idents.setDim(t.idents.dim);
|
|
for (size_t i = 0; i < idents.dim; i++)
|
|
{
|
|
RootObject id = t.idents[i];
|
|
if (id.dyncast() == DYNCAST_DSYMBOL)
|
|
{
|
|
TemplateInstance ti = cast(TemplateInstance)id;
|
|
ti = cast(TemplateInstance)ti.syntaxCopy(null);
|
|
id = ti;
|
|
}
|
|
else if (id.dyncast() == DYNCAST_EXPRESSION)
|
|
{
|
|
Expression e = cast(Expression)id;
|
|
e = e.syntaxCopy();
|
|
id = e;
|
|
}
|
|
else if (id.dyncast() == DYNCAST_TYPE)
|
|
{
|
|
Type tx = cast(Type)id;
|
|
tx = tx.syntaxCopy();
|
|
id = tx;
|
|
}
|
|
idents[i] = id;
|
|
}
|
|
}
|
|
|
|
final void addIdent(Identifier ident)
|
|
{
|
|
idents.push(ident);
|
|
}
|
|
|
|
final void addInst(TemplateInstance inst)
|
|
{
|
|
idents.push(inst);
|
|
}
|
|
|
|
final void addIndex(RootObject e)
|
|
{
|
|
idents.push(e);
|
|
}
|
|
|
|
override d_uns64 size(Loc loc)
|
|
{
|
|
error(this.loc, "size of type %s is not known", toChars());
|
|
return SIZE_INVALID;
|
|
}
|
|
|
|
/*************************************
|
|
* Resolve a tuple index.
|
|
*/
|
|
final void resolveTupleIndex(Loc loc, Scope* sc, Dsymbol s, Expression* pe, Type* pt, Dsymbol* ps, RootObject oindex)
|
|
{
|
|
*pt = null;
|
|
*ps = null;
|
|
*pe = null;
|
|
auto tup = s.isTupleDeclaration();
|
|
auto eindex = isExpression(oindex);
|
|
auto tindex = isType(oindex);
|
|
auto sindex = isDsymbol(oindex);
|
|
if (!tup)
|
|
{
|
|
// It's really an index expression
|
|
if (tindex)
|
|
eindex = new TypeExp(loc, tindex);
|
|
else if (sindex)
|
|
eindex = DsymbolExp.resolve(loc, sc, sindex, false);
|
|
Expression e = new IndexExp(loc, DsymbolExp.resolve(loc, sc, s, false), eindex);
|
|
e = e.semantic(sc);
|
|
resolveExp(e, pt, pe, ps);
|
|
return;
|
|
}
|
|
|
|
// Convert oindex to Expression, then try to resolve to constant.
|
|
if (tindex)
|
|
tindex.resolve(loc, sc, &eindex, &tindex, &sindex);
|
|
if (sindex)
|
|
eindex = DsymbolExp.resolve(loc, sc, sindex, false);
|
|
if (!eindex)
|
|
{
|
|
.error(loc, "index is %s not an expression", oindex.toChars());
|
|
*pt = Type.terror;
|
|
return;
|
|
}
|
|
|
|
eindex = semanticLength(sc, tup, eindex);
|
|
eindex = eindex.ctfeInterpret();
|
|
if (eindex.op == TOKerror)
|
|
{
|
|
*pt = Type.terror;
|
|
return;
|
|
}
|
|
const(uinteger_t) d = eindex.toUInteger();
|
|
if (d >= tup.objects.dim)
|
|
{
|
|
.error(loc, "tuple index %llu exceeds length %u", d, tup.objects.dim);
|
|
*pt = Type.terror;
|
|
return;
|
|
}
|
|
|
|
RootObject o = (*tup.objects)[cast(size_t)d];
|
|
*pt = isType(o);
|
|
*ps = isDsymbol(o);
|
|
*pe = isExpression(o);
|
|
if (*pt)
|
|
*pt = (*pt).semantic(loc, sc);
|
|
if (*pe)
|
|
resolveExp(*pe, pt, pe, ps);
|
|
}
|
|
|
|
final Expression toExpressionHelper(Expression e, size_t i = 0)
|
|
{
|
|
//printf("toExpressionHelper(e = %s %s)\n", Token.toChars(e.op), e.toChars());
|
|
for (; i < idents.dim; i++)
|
|
{
|
|
RootObject id = idents[i];
|
|
//printf("\t[%d] e: '%s', id: '%s'\n", i, e.toChars(), id.toChars());
|
|
|
|
switch (id.dyncast())
|
|
{
|
|
// ... '. ident'
|
|
case DYNCAST_IDENTIFIER:
|
|
e = new DotIdExp(e.loc, e, cast(Identifier)id);
|
|
break;
|
|
|
|
// ... '. name!(tiargs)'
|
|
case DYNCAST_DSYMBOL:
|
|
auto ti = (cast(Dsymbol)id).isTemplateInstance();
|
|
assert(ti);
|
|
e = new DotTemplateInstanceExp(e.loc, e, ti.name, ti.tiargs);
|
|
break;
|
|
|
|
// ... '[type]'
|
|
case DYNCAST_TYPE: // Bugzilla 1215
|
|
e = new ArrayExp(loc, e, new TypeExp(loc, cast(Type)id));
|
|
break;
|
|
|
|
// ... '[expr]'
|
|
case DYNCAST_EXPRESSION: // Bugzilla 1215
|
|
e = new ArrayExp(loc, e, cast(Expression)id);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/*************************************
|
|
* Takes an array of Identifiers and figures out if
|
|
* it represents a Type or an Expression.
|
|
* Output:
|
|
* if expression, *pe is set
|
|
* if type, *pt is set
|
|
*/
|
|
final void resolveHelper(Loc loc, Scope* sc, Dsymbol s, Dsymbol scopesym,
|
|
Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false)
|
|
{
|
|
version (none)
|
|
{
|
|
printf("TypeQualified::resolveHelper(sc = %p, idents = '%s')\n", sc, toChars());
|
|
if (scopesym)
|
|
printf("\tscopesym = '%s'\n", scopesym.toChars());
|
|
}
|
|
*pe = null;
|
|
*pt = null;
|
|
*ps = null;
|
|
if (s)
|
|
{
|
|
//printf("\t1: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind());
|
|
Declaration d = s.isDeclaration();
|
|
if (d && (d.storage_class & STCtemplateparameter))
|
|
s = s.toAlias();
|
|
else
|
|
s.checkDeprecated(loc, sc); // check for deprecated aliases
|
|
s = s.toAlias();
|
|
//printf("\t2: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind());
|
|
for (size_t i = 0; i < idents.dim; i++)
|
|
{
|
|
RootObject id = idents[i];
|
|
if (id.dyncast() == DYNCAST_EXPRESSION ||
|
|
id.dyncast() == DYNCAST_TYPE)
|
|
{
|
|
Type tx;
|
|
Expression ex;
|
|
Dsymbol sx;
|
|
resolveTupleIndex(loc, sc, s, &ex, &tx, &sx, id);
|
|
if (sx)
|
|
{
|
|
s = sx.toAlias();
|
|
continue;
|
|
}
|
|
if (tx)
|
|
ex = new TypeExp(loc, tx);
|
|
assert(ex);
|
|
|
|
ex = toExpressionHelper(ex, i + 1);
|
|
ex = ex.semantic(sc);
|
|
resolveExp(ex, pt, pe, ps);
|
|
return;
|
|
}
|
|
|
|
Type t = s.getType(); // type symbol, type alias, or type tuple?
|
|
uint errorsave = global.errors;
|
|
Dsymbol sm = s.searchX(loc, sc, id);
|
|
if (sm && !symbolIsVisible(sc, sm))
|
|
{
|
|
.deprecation(loc, "%s is not visible from module %s", sm.toPrettyChars(), sc._module.toChars());
|
|
// sm = null;
|
|
}
|
|
if (global.errors != errorsave)
|
|
{
|
|
*pt = Type.terror;
|
|
return;
|
|
}
|
|
//printf("\t3: s = %p %s %s, sm = %p\n", s, s->kind(), s->toChars(), sm);
|
|
if (intypeid && !t && sm && sm.needThis())
|
|
goto L3;
|
|
if (VarDeclaration v = s.isVarDeclaration())
|
|
{
|
|
if (v.storage_class & (STCconst | STCimmutable | STCmanifest) ||
|
|
v.type.isConst() || v.type.isImmutable())
|
|
{
|
|
// Bugzilla 13087: this.field is not constant always
|
|
if (!v.isThisDeclaration())
|
|
goto L3;
|
|
}
|
|
}
|
|
if (!sm)
|
|
{
|
|
if (!t)
|
|
{
|
|
if (s.isDeclaration()) // var, func, or tuple declaration?
|
|
{
|
|
t = s.isDeclaration().type;
|
|
if (!t && s.isTupleDeclaration()) // expression tuple?
|
|
goto L3;
|
|
}
|
|
else if (s.isTemplateInstance() ||
|
|
s.isImport() || s.isPackage() || s.isModule())
|
|
{
|
|
goto L3;
|
|
}
|
|
}
|
|
if (t)
|
|
{
|
|
sm = t.toDsymbol(sc);
|
|
if (sm && id.dyncast() == DYNCAST_IDENTIFIER)
|
|
{
|
|
sm = sm.search(loc, cast(Identifier)id);
|
|
if (sm)
|
|
goto L2;
|
|
}
|
|
L3:
|
|
Expression e;
|
|
VarDeclaration v = s.isVarDeclaration();
|
|
FuncDeclaration f = s.isFuncDeclaration();
|
|
if (intypeid || !v && !f)
|
|
e = DsymbolExp.resolve(loc, sc, s, true);
|
|
else
|
|
e = new VarExp(loc, s.isDeclaration(), true);
|
|
|
|
e = toExpressionHelper(e, i);
|
|
e = e.semantic(sc);
|
|
resolveExp(e, pt, pe, ps);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (id.dyncast() == DYNCAST_DSYMBOL)
|
|
{
|
|
// searchX already handles errors for template instances
|
|
assert(global.errors);
|
|
}
|
|
else
|
|
{
|
|
assert(id.dyncast() == DYNCAST_IDENTIFIER);
|
|
sm = s.search_correct(cast(Identifier)id);
|
|
if (sm)
|
|
error(loc, "identifier '%s' of '%s' is not defined, did you mean %s '%s'?", id.toChars(), toChars(), sm.kind(), sm.toChars());
|
|
else
|
|
error(loc, "identifier '%s' of '%s' is not defined", id.toChars(), toChars());
|
|
}
|
|
*pe = new ErrorExp();
|
|
}
|
|
return;
|
|
}
|
|
L2:
|
|
s = sm.toAlias();
|
|
}
|
|
|
|
if (auto em = s.isEnumMember())
|
|
{
|
|
// It's not a type, it's an expression
|
|
*pe = em.getVarExp(loc, sc);
|
|
return;
|
|
}
|
|
if (VarDeclaration v = s.isVarDeclaration())
|
|
{
|
|
/* This is mostly same with DsymbolExp::semantic(), but we cannot use it
|
|
* because some variables used in type context need to prevent lowering
|
|
* to a literal or contextful expression. For example:
|
|
*
|
|
* enum a = 1; alias b = a;
|
|
* template X(alias e){ alias v = e; } alias x = X!(1);
|
|
* struct S { int v; alias w = v; }
|
|
* // TypeIdentifier 'a', 'e', and 'v' should be TOKvar,
|
|
* // because getDsymbol() need to work in AliasDeclaration::semantic().
|
|
*/
|
|
if (!v.type || !v.type.deco)
|
|
{
|
|
if (v.inuse) // Bugzilla 9494
|
|
{
|
|
error(loc, "circular reference to '%s'", v.toPrettyChars());
|
|
*pe = new ErrorExp();
|
|
return;
|
|
}
|
|
if (v.sem < SemanticDone && v._scope)
|
|
v.semantic(null);
|
|
}
|
|
assert(v.type); // Bugzilla 14642
|
|
if (v.type.ty == Terror)
|
|
*pt = Type.terror;
|
|
else
|
|
*pe = new VarExp(loc, v);
|
|
return;
|
|
}
|
|
if (auto fld = s.isFuncLiteralDeclaration())
|
|
{
|
|
//printf("'%s' is a function literal\n", fld.toChars());
|
|
*pe = new FuncExp(loc, fld);
|
|
*pe = (*pe).semantic(sc);
|
|
return;
|
|
}
|
|
version (none)
|
|
{
|
|
if (FuncDeclaration fd = s.isFuncDeclaration())
|
|
{
|
|
*pe = new DsymbolExp(loc, fd);
|
|
return;
|
|
}
|
|
}
|
|
|
|
L1:
|
|
Type t = s.getType();
|
|
if (!t)
|
|
{
|
|
// If the symbol is an import, try looking inside the import
|
|
if (Import si = s.isImport())
|
|
{
|
|
s = si.search(loc, s.ident);
|
|
if (s && s != si)
|
|
goto L1;
|
|
s = si;
|
|
}
|
|
*ps = s;
|
|
return;
|
|
}
|
|
if (t.ty == Tinstance && t != this && !t.deco)
|
|
{
|
|
if (!(cast(TypeInstance)t).tempinst.errors)
|
|
error(loc, "forward reference to '%s'", t.toChars());
|
|
*pt = Type.terror;
|
|
return;
|
|
}
|
|
|
|
if (t.ty == Ttuple)
|
|
*pt = t;
|
|
else
|
|
*pt = t.merge();
|
|
}
|
|
if (!s)
|
|
{
|
|
const(char)* p = mutableOf().unSharedOf().toChars();
|
|
const(char)* n = importHint(p);
|
|
if (n)
|
|
error(loc, "'%s' is not defined, perhaps you need to import %s; ?", p, n);
|
|
else
|
|
{
|
|
auto id = new Identifier(p, TOKidentifier);
|
|
s = sc.search_correct(id);
|
|
if (s)
|
|
error(loc, "undefined identifier '%s', did you mean %s '%s'?", p, s.kind(), s.toChars());
|
|
else
|
|
error(loc, "undefined identifier '%s'", p);
|
|
}
|
|
*pt = Type.terror;
|
|
}
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeIdentifier : TypeQualified
|
|
{
|
|
public:
|
|
Identifier ident;
|
|
|
|
// The symbol representing this identifier, before alias resolution
|
|
Dsymbol originalSymbol;
|
|
|
|
extern (D) this(Loc loc, Identifier ident)
|
|
{
|
|
super(Tident, loc);
|
|
this.ident = ident;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "identifier";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
auto t = new TypeIdentifier(loc, ident);
|
|
t.syntaxCopyHelper(this);
|
|
t.mod = mod;
|
|
return t;
|
|
}
|
|
|
|
/*************************************
|
|
* Takes an array of Identifiers and figures out if
|
|
* it represents a Type or an Expression.
|
|
* Output:
|
|
* if expression, *pe is set
|
|
* if type, *pt is set
|
|
*/
|
|
override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false)
|
|
{
|
|
//printf("TypeIdentifier::resolve(sc = %p, idents = '%s')\n", sc, toChars());
|
|
if ((ident.equals(Id._super) || ident.equals(Id.This)) && !hasThis(sc))
|
|
{
|
|
AggregateDeclaration ad = sc.getStructClassScope();
|
|
if (ad)
|
|
{
|
|
ClassDeclaration cd = ad.isClassDeclaration();
|
|
if (cd)
|
|
{
|
|
if (ident.equals(Id.This))
|
|
ident = cd.ident;
|
|
else if (cd.baseClass && ident.equals(Id._super))
|
|
ident = cd.baseClass.ident;
|
|
}
|
|
else
|
|
{
|
|
StructDeclaration sd = ad.isStructDeclaration();
|
|
if (sd && ident.equals(Id.This))
|
|
ident = sd.ident;
|
|
}
|
|
}
|
|
}
|
|
if (ident == Id.ctfe)
|
|
{
|
|
error(loc, "variable __ctfe cannot be read at compile time");
|
|
*pe = null;
|
|
*ps = null;
|
|
*pt = Type.terror;
|
|
return;
|
|
}
|
|
Dsymbol scopesym;
|
|
Dsymbol s = sc.search(loc, ident, &scopesym);
|
|
resolveHelper(loc, sc, s, scopesym, pe, pt, ps, intypeid);
|
|
if (*pt)
|
|
(*pt) = (*pt).addMod(mod);
|
|
}
|
|
|
|
/*****************************************
|
|
* See if type resolves to a symbol, if so,
|
|
* return that symbol.
|
|
*/
|
|
override Dsymbol toDsymbol(Scope* sc)
|
|
{
|
|
//printf("TypeIdentifier::toDsymbol('%s')\n", toChars());
|
|
if (!sc)
|
|
return null;
|
|
Type t;
|
|
Expression e;
|
|
Dsymbol s;
|
|
resolve(loc, sc, &e, &t, &s);
|
|
if (t && t.ty != Tident)
|
|
s = t.toDsymbol(sc);
|
|
if (e)
|
|
s = getDsymbol(e);
|
|
return s;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
Type t;
|
|
Expression e;
|
|
Dsymbol s;
|
|
//printf("TypeIdentifier::semantic(%s)\n", toChars());
|
|
resolve(loc, sc, &e, &t, &s);
|
|
if (t)
|
|
{
|
|
//printf("\tit's a type %d, %s, %s\n", t->ty, t->toChars(), t->deco);
|
|
t = t.addMod(mod);
|
|
}
|
|
else
|
|
{
|
|
if (s)
|
|
{
|
|
s.error(loc, "is used as a type");
|
|
//assert(0);
|
|
}
|
|
else
|
|
error(loc, "%s is used as a type", toChars());
|
|
t = terror;
|
|
}
|
|
//t->print();
|
|
return t;
|
|
}
|
|
|
|
override Expression toExpression()
|
|
{
|
|
return toExpressionHelper(new IdentifierExp(loc, ident));
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* Similar to TypeIdentifier, but with a TemplateInstance as the root
|
|
*/
|
|
extern (C++) final class TypeInstance : TypeQualified
|
|
{
|
|
public:
|
|
TemplateInstance tempinst;
|
|
|
|
extern (D) this(Loc loc, TemplateInstance tempinst)
|
|
{
|
|
super(Tinstance, loc);
|
|
this.tempinst = tempinst;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "instance";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
//printf("TypeInstance::syntaxCopy() %s, %d\n", toChars(), idents.dim);
|
|
auto t = new TypeInstance(loc, cast(TemplateInstance)tempinst.syntaxCopy(null));
|
|
t.syntaxCopyHelper(this);
|
|
t.mod = mod;
|
|
return t;
|
|
}
|
|
|
|
override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false)
|
|
{
|
|
// Note close similarity to TypeIdentifier::resolve()
|
|
Dsymbol s;
|
|
*pe = null;
|
|
*pt = null;
|
|
*ps = null;
|
|
version (none)
|
|
{
|
|
if (!idents.dim)
|
|
{
|
|
error(loc, "template instance '%s' has no identifier", toChars());
|
|
return;
|
|
}
|
|
}
|
|
//id = (Identifier *)idents.data[0];
|
|
//printf("TypeInstance::resolve(sc = %p, idents = '%s')\n", sc, id->toChars());
|
|
s = tempinst;
|
|
if (s)
|
|
{
|
|
//printf("s = %s\n", s->toChars());
|
|
s.semantic(sc);
|
|
if (!global.gag && tempinst.errors)
|
|
{
|
|
*pt = terror;
|
|
return;
|
|
}
|
|
}
|
|
resolveHelper(loc, sc, s, null, pe, pt, ps, intypeid);
|
|
if (*pt)
|
|
*pt = (*pt).addMod(mod);
|
|
//printf("pt = '%s'\n", (*pt)->toChars());
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
Type t;
|
|
Expression e;
|
|
Dsymbol s;
|
|
//printf("TypeInstance::semantic(%p, %s)\n", this, toChars());
|
|
{
|
|
uint errors = global.errors;
|
|
resolve(loc, sc, &e, &t, &s);
|
|
// if we had an error evaluating the symbol, suppress further errors
|
|
if (!t && errors != global.errors)
|
|
return terror;
|
|
}
|
|
if (!t)
|
|
{
|
|
if (!e && s && s.errors)
|
|
{
|
|
// if there was an error evaluating the symbol, it might actually
|
|
// be a type. Avoid misleading error messages.
|
|
error(loc, "%s had previous errors", toChars());
|
|
}
|
|
else
|
|
error(loc, "%s is used as a type", toChars());
|
|
t = terror;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
override Dsymbol toDsymbol(Scope* sc)
|
|
{
|
|
Type t;
|
|
Expression e;
|
|
Dsymbol s;
|
|
//printf("TypeInstance::semantic(%s)\n", toChars());
|
|
resolve(loc, sc, &e, &t, &s);
|
|
if (t && t.ty != Tinstance)
|
|
s = t.toDsymbol(sc);
|
|
return s;
|
|
}
|
|
|
|
override Expression toExpression()
|
|
{
|
|
return toExpressionHelper(new ScopeExp(loc, tempinst));
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeTypeof : TypeQualified
|
|
{
|
|
public:
|
|
Expression exp;
|
|
int inuse;
|
|
|
|
extern (D) this(Loc loc, Expression exp)
|
|
{
|
|
super(Ttypeof, loc);
|
|
this.exp = exp;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "typeof";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
//printf("TypeTypeof::syntaxCopy() %s\n", toChars());
|
|
auto t = new TypeTypeof(loc, exp.syntaxCopy());
|
|
t.syntaxCopyHelper(this);
|
|
t.mod = mod;
|
|
return t;
|
|
}
|
|
|
|
override Dsymbol toDsymbol(Scope* sc)
|
|
{
|
|
//printf("TypeTypeof::toDsymbol('%s')\n", toChars());
|
|
Expression e;
|
|
Type t;
|
|
Dsymbol s;
|
|
resolve(loc, sc, &e, &t, &s);
|
|
return s;
|
|
}
|
|
|
|
override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false)
|
|
{
|
|
*pe = null;
|
|
*pt = null;
|
|
*ps = null;
|
|
//printf("TypeTypeof::resolve(sc = %p, idents = '%s')\n", sc, toChars());
|
|
//static int nest; if (++nest == 50) *(char*)0=0;
|
|
if (inuse)
|
|
{
|
|
inuse = 2;
|
|
error(loc, "circular typeof definition");
|
|
Lerr:
|
|
*pt = Type.terror;
|
|
inuse--;
|
|
return;
|
|
}
|
|
inuse++;
|
|
|
|
/* Currently we cannot evalute 'exp' in speculative context, because
|
|
* the type implementation may leak to the final execution. Consider:
|
|
*
|
|
* struct S(T) {
|
|
* string toString() const { return "x"; }
|
|
* }
|
|
* void main() {
|
|
* alias X = typeof(S!int());
|
|
* assert(typeid(X).xtoString(null) == "x");
|
|
* }
|
|
*/
|
|
Scope* sc2 = sc.push();
|
|
sc2.intypeof = 1;
|
|
exp = exp.semantic(sc2);
|
|
exp = resolvePropertiesOnly(sc2, exp);
|
|
sc2.pop();
|
|
|
|
if (exp.op == TOKtype ||
|
|
exp.op == TOKscope)
|
|
{
|
|
if (exp.checkType())
|
|
goto Lerr;
|
|
|
|
/* Today, 'typeof(func)' returns void if func is a
|
|
* function template (TemplateExp), or
|
|
* template lambda (FuncExp).
|
|
* It's actually used in Phobos as an idiom, to branch code for
|
|
* template functions.
|
|
*/
|
|
}
|
|
if (auto f = exp.op == TOKvar ? (cast( VarExp)exp).var.isFuncDeclaration()
|
|
: exp.op == TOKdotvar ? (cast(DotVarExp)exp).var.isFuncDeclaration() : null)
|
|
{
|
|
if (f.checkForwardRef(loc))
|
|
goto Lerr;
|
|
}
|
|
if (auto f = isFuncAddress(exp))
|
|
{
|
|
if (f.checkForwardRef(loc))
|
|
goto Lerr;
|
|
}
|
|
|
|
Type t = exp.type;
|
|
if (!t)
|
|
{
|
|
error(loc, "expression (%s) has no type", exp.toChars());
|
|
goto Lerr;
|
|
}
|
|
if (t.ty == Ttypeof)
|
|
{
|
|
error(loc, "forward reference to %s", toChars());
|
|
goto Lerr;
|
|
}
|
|
if (idents.dim == 0)
|
|
*pt = t;
|
|
else
|
|
{
|
|
if (Dsymbol s = t.toDsymbol(sc))
|
|
resolveHelper(loc, sc, s, null, pe, pt, ps, intypeid);
|
|
else
|
|
{
|
|
auto e = toExpressionHelper(new TypeExp(loc, t));
|
|
e = e.semantic(sc);
|
|
resolveExp(e, pt, pe, ps);
|
|
}
|
|
}
|
|
if (*pt)
|
|
(*pt) = (*pt).addMod(mod);
|
|
inuse--;
|
|
return;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypeTypeof::semantic() %s\n", toChars());
|
|
Expression e;
|
|
Type t;
|
|
Dsymbol s;
|
|
resolve(loc, sc, &e, &t, &s);
|
|
if (s && (t = s.getType()) !is null)
|
|
t = t.addMod(mod);
|
|
if (!t)
|
|
{
|
|
error(loc, "%s is used as a type", toChars());
|
|
t = Type.terror;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
override d_uns64 size(Loc loc)
|
|
{
|
|
if (exp.type)
|
|
return exp.type.size(loc);
|
|
else
|
|
return TypeQualified.size(loc);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeReturn : TypeQualified
|
|
{
|
|
public:
|
|
extern (D) this(Loc loc)
|
|
{
|
|
super(Treturn, loc);
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "return";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
auto t = new TypeReturn(loc);
|
|
t.syntaxCopyHelper(this);
|
|
t.mod = mod;
|
|
return t;
|
|
}
|
|
|
|
override Dsymbol toDsymbol(Scope* sc)
|
|
{
|
|
Expression e;
|
|
Type t;
|
|
Dsymbol s;
|
|
resolve(loc, sc, &e, &t, &s);
|
|
return s;
|
|
}
|
|
|
|
override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false)
|
|
{
|
|
*pe = null;
|
|
*pt = null;
|
|
*ps = null;
|
|
//printf("TypeReturn::resolve(sc = %p, idents = '%s')\n", sc, toChars());
|
|
Type t;
|
|
{
|
|
FuncDeclaration func = sc.func;
|
|
if (!func)
|
|
{
|
|
error(loc, "typeof(return) must be inside function");
|
|
goto Lerr;
|
|
}
|
|
if (func.fes)
|
|
func = func.fes.func;
|
|
t = func.type.nextOf();
|
|
if (!t)
|
|
{
|
|
error(loc, "cannot use typeof(return) inside function %s with inferred return type", sc.func.toChars());
|
|
goto Lerr;
|
|
}
|
|
}
|
|
if (idents.dim == 0)
|
|
*pt = t;
|
|
else
|
|
{
|
|
if (Dsymbol s = t.toDsymbol(sc))
|
|
resolveHelper(loc, sc, s, null, pe, pt, ps, intypeid);
|
|
else
|
|
{
|
|
auto e = toExpressionHelper(new TypeExp(loc, t));
|
|
e = e.semantic(sc);
|
|
resolveExp(e, pt, pe, ps);
|
|
}
|
|
}
|
|
if (*pt)
|
|
(*pt) = (*pt).addMod(mod);
|
|
return;
|
|
|
|
Lerr:
|
|
*pt = Type.terror;
|
|
return;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypeReturn::semantic() %s\n", toChars());
|
|
Expression e;
|
|
Type t;
|
|
Dsymbol s;
|
|
resolve(loc, sc, &e, &t, &s);
|
|
if (s && (t = s.getType()) !is null)
|
|
t = t.addMod(mod);
|
|
if (!t)
|
|
{
|
|
error(loc, "%s is used as a type", toChars());
|
|
t = Type.terror;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
// Whether alias this dependency is recursive or not.
|
|
enum AliasThisRec : int
|
|
{
|
|
RECno = 0, // no alias this recursion
|
|
RECyes = 1, // alias this has recursive dependency
|
|
RECfwdref = 2, // not yet known
|
|
RECtypeMask = 3, // mask to read no/yes/fwdref
|
|
RECtracing = 0x4, // mark in progress of implicitConvTo/deduceWild
|
|
RECtracingDT = 0x8, // mark in progress of deduceType
|
|
}
|
|
|
|
alias RECno = AliasThisRec.RECno;
|
|
alias RECyes = AliasThisRec.RECyes;
|
|
alias RECfwdref = AliasThisRec.RECfwdref;
|
|
alias RECtypeMask = AliasThisRec.RECtypeMask;
|
|
alias RECtracing = AliasThisRec.RECtracing;
|
|
alias RECtracingDT = AliasThisRec.RECtracingDT;
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeStruct : Type
|
|
{
|
|
public:
|
|
StructDeclaration sym;
|
|
AliasThisRec att = RECfwdref;
|
|
CPPMANGLE cppmangle = CPPMANGLE.def;
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
// cache the hasUnalignedFields check
|
|
// 0 = not checked, 1 = aligned, 2 = unaligned
|
|
int unaligned;
|
|
}
|
|
|
|
extern (D) this(StructDeclaration sym)
|
|
{
|
|
super(Tstruct);
|
|
this.sym = sym;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "struct";
|
|
}
|
|
|
|
override d_uns64 size(Loc loc)
|
|
{
|
|
return sym.size(loc);
|
|
}
|
|
|
|
override uint alignsize()
|
|
{
|
|
sym.size(Loc()); // give error for forward references
|
|
return sym.alignsize;
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypeStruct::semantic('%s')\n", sym.toChars());
|
|
if (deco)
|
|
return this;
|
|
|
|
/* Don't semantic for sym because it should be deferred until
|
|
* sizeof needed or its members accessed.
|
|
*/
|
|
// instead, parent should be set correctly
|
|
assert(sym.parent);
|
|
if (sym.type.ty == Terror)
|
|
return Type.terror;
|
|
this.cppmangle = sc.cppmangle;
|
|
return merge();
|
|
}
|
|
|
|
override Dsymbol toDsymbol(Scope* sc)
|
|
{
|
|
return sym;
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
Dsymbol s;
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars());
|
|
}
|
|
// Bugzilla 14010
|
|
if (ident == Id._mangleof)
|
|
return getProperty(e.loc, ident, flag);
|
|
if (!sym.members)
|
|
{
|
|
error(e.loc, "struct %s is forward referenced", sym.toChars());
|
|
return new ErrorExp();
|
|
}
|
|
/* If e.tupleof
|
|
*/
|
|
if (ident == Id._tupleof)
|
|
{
|
|
/* Create a TupleExp out of the fields of the struct e:
|
|
* (e.field0, e.field1, e.field2, ...)
|
|
*/
|
|
e = e.semantic(sc); // do this before turning on noaccesscheck
|
|
e.type.size(); // do semantic of type
|
|
auto exps = new Expressions();
|
|
exps.reserve(sym.fields.dim);
|
|
Expression e0 = null;
|
|
Expression ev = e.op == TOKtype ? null : e;
|
|
if (sc.func && ev && !isTrivialExp(ev))
|
|
{
|
|
Identifier id = Identifier.generateId("__tup");
|
|
auto ei = new ExpInitializer(e.loc, ev);
|
|
auto vd = new VarDeclaration(e.loc, null, id, ei);
|
|
vd.storage_class |= STCtemp | STCctfe | (ev.isLvalue() ? STCref | STCforeach : STCrvalue);
|
|
e0 = new DeclarationExp(e.loc, vd);
|
|
ev = new VarExp(e.loc, vd);
|
|
}
|
|
for (size_t i = 0; i < sym.fields.dim; i++)
|
|
{
|
|
VarDeclaration v = sym.fields[i];
|
|
Expression ex;
|
|
if (ev)
|
|
ex = new DotVarExp(e.loc, ev, v);
|
|
else
|
|
{
|
|
ex = new VarExp(e.loc, v);
|
|
ex.type = ex.type.addMod(e.type.mod);
|
|
}
|
|
exps.push(ex);
|
|
}
|
|
e = new TupleExp(e.loc, e0, exps);
|
|
Scope* sc2 = sc.push();
|
|
sc2.flags = sc.flags | SCOPEnoaccesscheck;
|
|
e = e.semantic(sc2);
|
|
sc2.pop();
|
|
return e;
|
|
}
|
|
if (e.op == TOKdot)
|
|
{
|
|
DotExp de = cast(DotExp)e;
|
|
if (de.e1.op == TOKscope)
|
|
{
|
|
assert(0); // cannot find a case where this happens; leave
|
|
// assert in until we do
|
|
// ScopeExp se = cast(ScopeExp)de.e1;
|
|
// s = se.sds.search(e.loc, ident);
|
|
// e = de.e1;
|
|
// goto L1;
|
|
}
|
|
}
|
|
|
|
Dsymbol searchSym()
|
|
{
|
|
int flags = 0;
|
|
Dsymbol sold = void;
|
|
if (global.params.bug10378 || global.params.check10378)
|
|
{
|
|
sold = sym.search(e.loc, ident, flags);
|
|
if (!global.params.check10378)
|
|
return sold;
|
|
}
|
|
|
|
auto s = sym.search(e.loc, ident, flags | SearchLocalsOnly);
|
|
if (global.params.check10378)
|
|
{
|
|
alias snew = s;
|
|
if (sold !is snew)
|
|
Scope.deprecation10378(e.loc, sold, snew);
|
|
if (global.params.bug10378)
|
|
s = sold;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
s = searchSym();
|
|
L1:
|
|
if (!s)
|
|
{
|
|
if (sym._scope) // it's a fwd ref, maybe we can resolve it
|
|
{
|
|
sym.semantic(null);
|
|
s = searchSym();
|
|
}
|
|
if (!s)
|
|
return noMember(sc, e, ident, flag);
|
|
}
|
|
if (!symbolIsVisible(sc, s))
|
|
{
|
|
.deprecation(e.loc, "%s is not visible from module %s", s.toPrettyChars(), sc._module.toPrettyChars());
|
|
// return noMember(sc, e, ident, flag);
|
|
}
|
|
if (!s.isFuncDeclaration()) // because of overloading
|
|
s.checkDeprecated(e.loc, sc);
|
|
s = s.toAlias();
|
|
|
|
if (auto em = s.isEnumMember())
|
|
{
|
|
return em.getVarExp(e.loc, sc);
|
|
}
|
|
|
|
VarDeclaration v = s.isVarDeclaration();
|
|
if (v && (!v.type || !v.type.deco))
|
|
{
|
|
if (v.inuse) // Bugzilla 9494
|
|
{
|
|
e.error("circular reference to '%s'", v.toPrettyChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (v._scope)
|
|
{
|
|
v.semantic(v._scope);
|
|
s = v.toAlias(); // Need this if 'v' is a tuple variable
|
|
v = s.isVarDeclaration();
|
|
}
|
|
}
|
|
if (v && !v.isDataseg() && (v.storage_class & STCmanifest))
|
|
{
|
|
checkAccess(e.loc, sc, null, v);
|
|
Expression ve = new VarExp(e.loc, v);
|
|
ve = ve.semantic(sc);
|
|
return ve;
|
|
}
|
|
if (auto t = s.getType())
|
|
{
|
|
return (new TypeExp(e.loc, t)).semantic(sc);
|
|
}
|
|
|
|
TemplateMixin tm = s.isTemplateMixin();
|
|
if (tm)
|
|
{
|
|
Expression de = new DotExp(e.loc, e, new ScopeExp(e.loc, tm));
|
|
de.type = e.type;
|
|
return de;
|
|
}
|
|
TemplateDeclaration td = s.isTemplateDeclaration();
|
|
if (td)
|
|
{
|
|
if (e.op == TOKtype)
|
|
e = new TemplateExp(e.loc, td);
|
|
else
|
|
e = new DotTemplateExp(e.loc, e, td);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
TemplateInstance ti = s.isTemplateInstance();
|
|
if (ti)
|
|
{
|
|
if (!ti.semanticRun)
|
|
{
|
|
ti.semantic(sc);
|
|
if (!ti.inst || ti.errors) // if template failed to expand
|
|
return new ErrorExp();
|
|
}
|
|
s = ti.inst.toAlias();
|
|
if (!s.isTemplateInstance())
|
|
goto L1;
|
|
if (e.op == TOKtype)
|
|
e = new ScopeExp(e.loc, ti);
|
|
else
|
|
e = new DotExp(e.loc, e, new ScopeExp(e.loc, ti));
|
|
return e.semantic(sc);
|
|
}
|
|
if (s.isImport() || s.isModule() || s.isPackage())
|
|
{
|
|
e = DsymbolExp.resolve(e.loc, sc, s, false);
|
|
return e;
|
|
}
|
|
OverloadSet o = s.isOverloadSet();
|
|
if (o)
|
|
{
|
|
auto oe = new OverExp(e.loc, o);
|
|
if (e.op == TOKtype)
|
|
return oe;
|
|
return new DotExp(e.loc, e, oe);
|
|
}
|
|
Declaration d = s.isDeclaration();
|
|
debug
|
|
{
|
|
if (!d)
|
|
printf("d = %s '%s'\n", s.kind(), s.toChars());
|
|
}
|
|
assert(d);
|
|
if (e.op == TOKtype)
|
|
{
|
|
/* It's:
|
|
* Struct.d
|
|
*/
|
|
if (TupleDeclaration tup = d.isTupleDeclaration())
|
|
{
|
|
e = new TupleExp(e.loc, tup);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
if (d.needThis() && sc.intypeof != 1)
|
|
{
|
|
/* Rewrite as:
|
|
* this.d
|
|
*/
|
|
if (hasThis(sc))
|
|
{
|
|
e = new DotVarExp(e.loc, new ThisExp(e.loc), d);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
}
|
|
if (d.semanticRun == PASSinit && d._scope)
|
|
d.semantic(d._scope);
|
|
checkAccess(e.loc, sc, e, d);
|
|
auto ve = new VarExp(e.loc, d);
|
|
if (d.isVarDeclaration() && d.needThis())
|
|
ve.type = d.type.addMod(e.type.mod);
|
|
return ve;
|
|
}
|
|
bool unreal = e.op == TOKvar && (cast(VarExp)e).var.isField();
|
|
if (d.isDataseg() || unreal && d.isField())
|
|
{
|
|
// (e, d)
|
|
checkAccess(e.loc, sc, e, d);
|
|
Expression ve = new VarExp(e.loc, d);
|
|
e = unreal ? ve : new CommaExp(e.loc, e, ve);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
e = new DotVarExp(e.loc, e, d);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
override structalign_t alignment()
|
|
{
|
|
if (sym.alignment == 0)
|
|
sym.size(Loc());
|
|
return sym.alignment;
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypeStruct::defaultInit() '%s'\n", toChars());
|
|
}
|
|
Declaration d = new SymbolDeclaration(sym.loc, sym);
|
|
assert(d);
|
|
d.type = this;
|
|
d.storage_class |= STCrvalue; // Bugzilla 14398
|
|
return new VarExp(sym.loc, d);
|
|
}
|
|
|
|
/***************************************
|
|
* Use when we prefer the default initializer to be a literal,
|
|
* rather than a global immutable variable.
|
|
*/
|
|
override Expression defaultInitLiteral(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars());
|
|
}
|
|
sym.size(loc);
|
|
if (sym.sizeok != SIZEOKdone)
|
|
return new ErrorExp();
|
|
auto structelems = new Expressions();
|
|
structelems.setDim(sym.fields.dim - sym.isNested());
|
|
uint offset = 0;
|
|
for (size_t j = 0; j < structelems.dim; j++)
|
|
{
|
|
VarDeclaration vd = sym.fields[j];
|
|
Expression e;
|
|
if (vd.inuse)
|
|
{
|
|
error(loc, "circular reference to '%s'", vd.toPrettyChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (vd.offset < offset || vd.type.size() == 0)
|
|
e = null;
|
|
else if (vd._init)
|
|
{
|
|
if (vd._init.isVoidInitializer())
|
|
e = null;
|
|
else
|
|
e = vd.getConstInitializer(false);
|
|
}
|
|
else
|
|
e = vd.type.defaultInitLiteral(loc);
|
|
if (e && e.op == TOKerror)
|
|
return e;
|
|
if (e)
|
|
offset = vd.offset + cast(uint)vd.type.size();
|
|
(*structelems)[j] = e;
|
|
}
|
|
auto structinit = new StructLiteralExp(loc, sym, structelems);
|
|
/* Copy from the initializer symbol for larger symbols,
|
|
* otherwise the literals expressed as code get excessively large.
|
|
*/
|
|
if (size(loc) > Target.ptrsize * 4 && !needsNested())
|
|
structinit.useStaticInit = true;
|
|
structinit.type = this;
|
|
return structinit;
|
|
}
|
|
|
|
override bool isZeroInit(Loc loc) const
|
|
{
|
|
return sym.zeroInit != 0;
|
|
}
|
|
|
|
override bool isAssignable()
|
|
{
|
|
bool assignable = true;
|
|
uint offset = ~0; // dead-store initialize to prevent spurious warning
|
|
/* If any of the fields are const or immutable,
|
|
* then one cannot assign this struct.
|
|
*/
|
|
for (size_t i = 0; i < sym.fields.dim; i++)
|
|
{
|
|
VarDeclaration v = sym.fields[i];
|
|
//printf("%s [%d] v = (%s) %s, v->offset = %d, v->parent = %s", sym->toChars(), i, v->kind(), v->toChars(), v->offset, v->parent->kind());
|
|
if (i == 0)
|
|
{
|
|
}
|
|
else if (v.offset == offset)
|
|
{
|
|
/* If any fields of anonymous union are assignable,
|
|
* then regard union as assignable.
|
|
* This is to support unsafe things like Rebindable templates.
|
|
*/
|
|
if (assignable)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (!assignable)
|
|
return false;
|
|
}
|
|
assignable = v.type.isMutable() && v.type.isAssignable();
|
|
offset = v.offset;
|
|
//printf(" -> assignable = %d\n", assignable);
|
|
}
|
|
return assignable;
|
|
}
|
|
|
|
override bool isBoolean() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override bool needsDestruction() const
|
|
{
|
|
return sym.dtor !is null;
|
|
}
|
|
|
|
override bool needsNested()
|
|
{
|
|
if (sym.isNested())
|
|
return true;
|
|
for (size_t i = 0; i < sym.fields.dim; i++)
|
|
{
|
|
VarDeclaration v = sym.fields[i];
|
|
if (!v.isDataseg() && v.type.needsNested())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
override bool hasPointers()
|
|
{
|
|
// Probably should cache this information in sym rather than recompute
|
|
StructDeclaration s = sym;
|
|
sym.size(Loc()); // give error for forward references
|
|
for (size_t i = 0; i < s.fields.dim; i++)
|
|
{
|
|
Declaration d = s.fields[i];
|
|
if (d.storage_class & STCref || d.hasPointers())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
override MATCH implicitConvTo(Type to)
|
|
{
|
|
MATCH m;
|
|
//printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to.toChars());
|
|
if (ty == to.ty && sym == (cast(TypeStruct)to).sym)
|
|
{
|
|
m = MATCHexact; // exact match
|
|
if (mod != to.mod)
|
|
{
|
|
m = MATCHconst;
|
|
if (MODimplicitConv(mod, to.mod))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
/* Check all the fields. If they can all be converted,
|
|
* allow the conversion.
|
|
*/
|
|
uint offset = ~0; // dead-store to prevent spurious warning
|
|
for (size_t i = 0; i < sym.fields.dim; i++)
|
|
{
|
|
VarDeclaration v = sym.fields[i];
|
|
if (i == 0)
|
|
{
|
|
}
|
|
else if (v.offset == offset)
|
|
{
|
|
if (m > MATCHnomatch)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (m <= MATCHnomatch)
|
|
return m;
|
|
}
|
|
// 'from' type
|
|
Type tvf = v.type.addMod(mod);
|
|
// 'to' type
|
|
Type tv = v.type.addMod(to.mod);
|
|
// field match
|
|
MATCH mf = tvf.implicitConvTo(tv);
|
|
//printf("\t%s => %s, match = %d\n", v->type->toChars(), tv->toChars(), mf);
|
|
if (mf <= MATCHnomatch)
|
|
return mf;
|
|
if (mf < m) // if field match is worse
|
|
m = mf;
|
|
offset = v.offset;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (sym.aliasthis && !(att & RECtracing))
|
|
{
|
|
att = cast(AliasThisRec)(att | RECtracing);
|
|
m = aliasthisOf().implicitConvTo(to);
|
|
att = cast(AliasThisRec)(att & ~RECtracing);
|
|
}
|
|
else
|
|
m = MATCHnomatch; // no match
|
|
return m;
|
|
}
|
|
|
|
override MATCH constConv(Type to)
|
|
{
|
|
if (equals(to))
|
|
return MATCHexact;
|
|
if (ty == to.ty && sym == (cast(TypeStruct)to).sym && MODimplicitConv(mod, to.mod))
|
|
return MATCHconst;
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
override ubyte deduceWild(Type t, bool isRef)
|
|
{
|
|
if (ty == t.ty && sym == (cast(TypeStruct)t).sym)
|
|
return Type.deduceWild(t, isRef);
|
|
ubyte wm = 0;
|
|
if (t.hasWild() && sym.aliasthis && !(att & RECtracing))
|
|
{
|
|
att = cast(AliasThisRec)(att | RECtracing);
|
|
wm = aliasthisOf().deduceWild(t, isRef);
|
|
att = cast(AliasThisRec)(att & ~RECtracing);
|
|
}
|
|
return wm;
|
|
}
|
|
|
|
override Type toHeadMutable()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeEnum : Type
|
|
{
|
|
public:
|
|
EnumDeclaration sym;
|
|
|
|
extern (D) this(EnumDeclaration sym)
|
|
{
|
|
super(Tenum);
|
|
this.sym = sym;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "enum";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override d_uns64 size(Loc loc)
|
|
{
|
|
return sym.getMemtype(loc).size(loc);
|
|
}
|
|
|
|
override uint alignsize()
|
|
{
|
|
Type t = sym.getMemtype(Loc());
|
|
if (t.ty == Terror)
|
|
return 4;
|
|
return t.alignsize();
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypeEnum::semantic() %s\n", toChars());
|
|
if (deco)
|
|
return this;
|
|
return merge();
|
|
}
|
|
|
|
override Dsymbol toDsymbol(Scope* sc)
|
|
{
|
|
return sym;
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeEnum::dotExp(e = '%s', ident = '%s') '%s'\n", e.toChars(), ident.toChars(), toChars());
|
|
}
|
|
// Bugzilla 14010
|
|
if (ident == Id._mangleof)
|
|
return getProperty(e.loc, ident, flag);
|
|
if (sym._scope)
|
|
sym.semantic(sym._scope);
|
|
if (!sym.members)
|
|
{
|
|
if (!flag)
|
|
{
|
|
sym.error("is forward referenced when looking for '%s'", ident.toChars());
|
|
e = new ErrorExp();
|
|
}
|
|
else
|
|
e = null;
|
|
return e;
|
|
}
|
|
Dsymbol s = sym.search(e.loc, ident);
|
|
if (!s)
|
|
{
|
|
if (ident == Id.max || ident == Id.min || ident == Id._init)
|
|
{
|
|
return getProperty(e.loc, ident, flag);
|
|
}
|
|
|
|
Expression res = sym.getMemtype(Loc()).dotExp(sc, e, ident, 1);
|
|
if (flag != 1 && !res)
|
|
{
|
|
if (auto ns = sym.search_correct(ident))
|
|
e.error("no property '%s' for type '%s'. Did you mean '%s.%s' ?", ident.toChars(), toChars(), toChars(),
|
|
ns.toChars());
|
|
else
|
|
e.error("no property '%s' for type '%s'", ident.toChars(),
|
|
toChars());
|
|
|
|
return new ErrorExp();
|
|
}
|
|
return res;
|
|
}
|
|
EnumMember m = s.isEnumMember();
|
|
return m.getVarExp(e.loc, sc);
|
|
}
|
|
|
|
override Expression getProperty(Loc loc, Identifier ident, int flag)
|
|
{
|
|
Expression e;
|
|
if (ident == Id.max || ident == Id.min)
|
|
{
|
|
return sym.getMaxMinValue(loc, ident);
|
|
}
|
|
else if (ident == Id._init)
|
|
{
|
|
e = defaultInitLiteral(loc);
|
|
}
|
|
else if (ident == Id.stringof)
|
|
{
|
|
const s = toChars();
|
|
e = new StringExp(loc, cast(char*)s);
|
|
Scope sc;
|
|
e = e.semantic(&sc);
|
|
}
|
|
else if (ident == Id._mangleof)
|
|
{
|
|
e = Type.getProperty(loc, ident, flag);
|
|
}
|
|
else
|
|
{
|
|
e = toBasetype().getProperty(loc, ident, flag);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
override bool isintegral()
|
|
{
|
|
return sym.getMemtype(Loc()).isintegral();
|
|
}
|
|
|
|
override bool isfloating()
|
|
{
|
|
return sym.getMemtype(Loc()).isfloating();
|
|
}
|
|
|
|
override bool isreal()
|
|
{
|
|
return sym.getMemtype(Loc()).isreal();
|
|
}
|
|
|
|
override bool isimaginary()
|
|
{
|
|
return sym.getMemtype(Loc()).isimaginary();
|
|
}
|
|
|
|
override bool iscomplex()
|
|
{
|
|
return sym.getMemtype(Loc()).iscomplex();
|
|
}
|
|
|
|
override bool isscalar()
|
|
{
|
|
return sym.getMemtype(Loc()).isscalar();
|
|
}
|
|
|
|
override bool isunsigned()
|
|
{
|
|
return sym.getMemtype(Loc()).isunsigned();
|
|
}
|
|
|
|
override bool isBoolean()
|
|
{
|
|
return sym.getMemtype(Loc()).isBoolean();
|
|
}
|
|
|
|
override bool isString()
|
|
{
|
|
return sym.getMemtype(Loc()).isString();
|
|
}
|
|
|
|
override bool isAssignable()
|
|
{
|
|
return sym.getMemtype(Loc()).isAssignable();
|
|
}
|
|
|
|
override bool needsDestruction()
|
|
{
|
|
return sym.getMemtype(Loc()).needsDestruction();
|
|
}
|
|
|
|
override bool needsNested()
|
|
{
|
|
return sym.getMemtype(Loc()).needsNested();
|
|
}
|
|
|
|
override MATCH implicitConvTo(Type to)
|
|
{
|
|
MATCH m;
|
|
//printf("TypeEnum::implicitConvTo()\n");
|
|
if (ty == to.ty && sym == (cast(TypeEnum)to).sym)
|
|
m = (mod == to.mod) ? MATCHexact : MATCHconst;
|
|
else if (sym.getMemtype(Loc()).implicitConvTo(to))
|
|
m = MATCHconvert; // match with conversions
|
|
else
|
|
m = MATCHnomatch; // no match
|
|
return m;
|
|
}
|
|
|
|
override MATCH constConv(Type to)
|
|
{
|
|
if (equals(to))
|
|
return MATCHexact;
|
|
if (ty == to.ty && sym == (cast(TypeEnum)to).sym && MODimplicitConv(mod, to.mod))
|
|
return MATCHconst;
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
override Type toBasetype()
|
|
{
|
|
if (!sym.members && !sym.memtype)
|
|
return this;
|
|
return sym.getMemtype(Loc()).toBasetype();
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypeEnum::defaultInit() '%s'\n", toChars());
|
|
}
|
|
// Initialize to first member of enum
|
|
Expression e = sym.getDefaultValue(loc);
|
|
e = e.copy();
|
|
e.loc = loc;
|
|
e.type = this; // to deal with const, immutable, etc., variants
|
|
return e;
|
|
}
|
|
|
|
override bool isZeroInit(Loc loc)
|
|
{
|
|
return sym.getDefaultValue(loc).isBool(false);
|
|
}
|
|
|
|
override bool hasPointers()
|
|
{
|
|
return sym.getMemtype(Loc()).hasPointers();
|
|
}
|
|
|
|
override Type nextOf()
|
|
{
|
|
return sym.getMemtype(Loc()).nextOf();
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeClass : Type
|
|
{
|
|
public:
|
|
ClassDeclaration sym;
|
|
AliasThisRec att = RECfwdref;
|
|
CPPMANGLE cppmangle = CPPMANGLE.def;
|
|
|
|
extern (D) this(ClassDeclaration sym)
|
|
{
|
|
super(Tclass);
|
|
this.sym = sym;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "class";
|
|
}
|
|
|
|
override d_uns64 size(Loc loc) const
|
|
{
|
|
return Target.ptrsize;
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypeClass::semantic(%s)\n", sym.toChars());
|
|
if (deco)
|
|
return this;
|
|
|
|
/* Don't semantic for sym because it should be deferred until
|
|
* sizeof needed or its members accessed.
|
|
*/
|
|
// instead, parent should be set correctly
|
|
assert(sym.parent);
|
|
if (sym.type.ty == Terror)
|
|
return Type.terror;
|
|
this.cppmangle = sc.cppmangle;
|
|
return merge();
|
|
}
|
|
|
|
override Dsymbol toDsymbol(Scope* sc)
|
|
{
|
|
return sym;
|
|
}
|
|
|
|
override Expression dotExp(Scope* sc, Expression e, Identifier ident, int flag)
|
|
{
|
|
Dsymbol s;
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeClass::dotExp(e='%s', ident='%s')\n", e.toChars(), ident.toChars());
|
|
}
|
|
if (e.op == TOKdot)
|
|
{
|
|
DotExp de = cast(DotExp)e;
|
|
if (de.e1.op == TOKscope)
|
|
{
|
|
ScopeExp se = cast(ScopeExp)de.e1;
|
|
s = se.sds.search(e.loc, ident);
|
|
e = de.e1;
|
|
goto L1;
|
|
}
|
|
}
|
|
// Bugzilla 12543
|
|
if (ident == Id.__sizeof || ident == Id.__xalignof || ident == Id._mangleof)
|
|
{
|
|
return Type.getProperty(e.loc, ident, 0);
|
|
}
|
|
if (ident == Id._tupleof)
|
|
{
|
|
/* Create a TupleExp
|
|
*/
|
|
e = e.semantic(sc); // do this before turning on noaccesscheck
|
|
/* If this is called in the middle of a class declaration,
|
|
* class Inner {
|
|
* int x;
|
|
* alias typeof(Inner.tupleof) T;
|
|
* int y;
|
|
* }
|
|
* then Inner.y will be omitted from the tuple.
|
|
*/
|
|
// Detect that error, and at least try to run semantic() on it if we can
|
|
sym.size(e.loc);
|
|
auto exps = new Expressions();
|
|
exps.reserve(sym.fields.dim);
|
|
Expression e0 = null;
|
|
Expression ev = e.op == TOKtype ? null : e;
|
|
if (sc.func && ev && !isTrivialExp(ev))
|
|
{
|
|
Identifier id = Identifier.generateId("__tup");
|
|
auto ei = new ExpInitializer(e.loc, ev);
|
|
auto vd = new VarDeclaration(e.loc, null, id, ei);
|
|
vd.storage_class |= STCtemp | STCctfe | (ev.isLvalue() ? STCref | STCforeach : STCrvalue);
|
|
e0 = new DeclarationExp(e.loc, vd);
|
|
ev = new VarExp(e.loc, vd);
|
|
}
|
|
for (size_t i = 0; i < sym.fields.dim; i++)
|
|
{
|
|
VarDeclaration v = sym.fields[i];
|
|
// Don't include hidden 'this' pointer
|
|
if (v.isThisDeclaration())
|
|
continue;
|
|
Expression ex;
|
|
if (ev)
|
|
ex = new DotVarExp(e.loc, ev, v);
|
|
else
|
|
{
|
|
ex = new VarExp(e.loc, v);
|
|
ex.type = ex.type.addMod(e.type.mod);
|
|
}
|
|
exps.push(ex);
|
|
}
|
|
e = new TupleExp(e.loc, e0, exps);
|
|
Scope* sc2 = sc.push();
|
|
sc2.flags = sc.flags | SCOPEnoaccesscheck;
|
|
e = e.semantic(sc2);
|
|
sc2.pop();
|
|
return e;
|
|
}
|
|
|
|
Dsymbol searchSym()
|
|
{
|
|
int flags = 0;
|
|
Dsymbol sold = void;
|
|
if (global.params.bug10378 || global.params.check10378)
|
|
{
|
|
sold = sym.search(e.loc, ident, flags | IgnoreSymbolVisibility);
|
|
if (!global.params.check10378)
|
|
return sold;
|
|
}
|
|
|
|
auto s = sym.search(e.loc, ident, flags | SearchLocalsOnly);
|
|
if (!s)
|
|
{
|
|
s = sym.search(e.loc, ident, flags | SearchLocalsOnly | IgnoreSymbolVisibility);
|
|
if (s && !(flags & IgnoreErrors))
|
|
.deprecation(e.loc, "%s is not visible from class %s", s.toPrettyChars(), sym.toChars());
|
|
}
|
|
if (global.params.check10378)
|
|
{
|
|
alias snew = s;
|
|
if (sold !is snew)
|
|
Scope.deprecation10378(e.loc, sold, snew);
|
|
if (global.params.bug10378)
|
|
s = sold;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
s = searchSym();
|
|
L1:
|
|
if (!s)
|
|
{
|
|
// See if it's 'this' class or a base class
|
|
if (sym.ident == ident)
|
|
{
|
|
if (e.op == TOKtype)
|
|
return Type.getProperty(e.loc, ident, 0);
|
|
return new DotTypeExp(e.loc, e, sym);
|
|
}
|
|
if (ClassDeclaration cbase = sym.searchBase(ident))
|
|
{
|
|
if (e.op == TOKtype)
|
|
return Type.getProperty(e.loc, ident, 0);
|
|
if (InterfaceDeclaration ifbase = cbase.isInterfaceDeclaration())
|
|
e = new CastExp(e.loc, e, ifbase.type);
|
|
else
|
|
e = new DotTypeExp(e.loc, e, cbase);
|
|
return e;
|
|
}
|
|
if (ident == Id.classinfo)
|
|
{
|
|
assert(Type.typeinfoclass);
|
|
Type t = Type.typeinfoclass.type;
|
|
if (e.op == TOKtype || e.op == TOKdottype)
|
|
{
|
|
/* For type.classinfo, we know the classinfo
|
|
* at compile time.
|
|
*/
|
|
if (!sym.vclassinfo)
|
|
sym.vclassinfo = new TypeInfoClassDeclaration(sym.type);
|
|
e = new VarExp(e.loc, sym.vclassinfo);
|
|
e = e.addressOf();
|
|
e.type = t; // do this so we don't get redundant dereference
|
|
}
|
|
else
|
|
{
|
|
/* For class objects, the classinfo reference is the first
|
|
* entry in the vtbl[]
|
|
*/
|
|
e = new PtrExp(e.loc, e);
|
|
e.type = t.pointerTo();
|
|
if (sym.isInterfaceDeclaration())
|
|
{
|
|
if (sym.isCPPinterface())
|
|
{
|
|
/* C++ interface vtbl[]s are different in that the
|
|
* first entry is always pointer to the first virtual
|
|
* function, not classinfo.
|
|
* We can't get a .classinfo for it.
|
|
*/
|
|
error(e.loc, "no .classinfo for C++ interface objects");
|
|
}
|
|
/* For an interface, the first entry in the vtbl[]
|
|
* is actually a pointer to an instance of struct Interface.
|
|
* The first member of Interface is the .classinfo,
|
|
* so add an extra pointer indirection.
|
|
*/
|
|
e.type = e.type.pointerTo();
|
|
e = new PtrExp(e.loc, e);
|
|
e.type = t.pointerTo();
|
|
}
|
|
e = new PtrExp(e.loc, e, t);
|
|
}
|
|
return e;
|
|
}
|
|
if (ident == Id.__vptr)
|
|
{
|
|
/* The pointer to the vtbl[]
|
|
* *cast(immutable(void*)**)e
|
|
*/
|
|
e = e.castTo(sc, tvoidptr.immutableOf().pointerTo().pointerTo());
|
|
e = new PtrExp(e.loc, e);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
if (ident == Id.__monitor)
|
|
{
|
|
/* The handle to the monitor (call it a void*)
|
|
* *(cast(void**)e + 1)
|
|
*/
|
|
e = e.castTo(sc, tvoidptr.pointerTo());
|
|
e = new AddExp(e.loc, e, new IntegerExp(1));
|
|
e = new PtrExp(e.loc, e);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
if (ident == Id.outer && sym.vthis)
|
|
{
|
|
if (sym.vthis._scope)
|
|
sym.vthis.semantic(null);
|
|
|
|
if (auto cdp = sym.toParent2().isClassDeclaration())
|
|
{
|
|
auto dve = new DotVarExp(e.loc, e, sym.vthis);
|
|
dve.type = cdp.type.addMod(e.type.mod);
|
|
return dve;
|
|
}
|
|
|
|
/* Bugzilla 15839: Find closest parent class through nested functions.
|
|
*/
|
|
for (auto p = sym.toParent2(); p; p = p.toParent2())
|
|
{
|
|
auto fd = p.isFuncDeclaration();
|
|
if (!fd)
|
|
break;
|
|
if (fd.isNested())
|
|
continue;
|
|
auto ad = fd.isThis();
|
|
if (!ad)
|
|
break;
|
|
if (auto cdp = ad.isClassDeclaration())
|
|
{
|
|
auto ve = new ThisExp(e.loc);
|
|
|
|
ve.var = fd.vthis;
|
|
const nestedError = fd.vthis.checkNestedReference(sc, e.loc);
|
|
assert(!nestedError);
|
|
|
|
ve.type = fd.vthis.type.addMod(e.type.mod);
|
|
return ve;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Continue to show enclosing function's frame (stack or closure).
|
|
auto dve = new DotVarExp(e.loc, e, sym.vthis);
|
|
dve.type = sym.vthis.type.addMod(e.type.mod);
|
|
return dve;
|
|
}
|
|
else
|
|
{
|
|
return noMember(sc, e, ident, flag);
|
|
}
|
|
}
|
|
if (!symbolIsVisible(sc, s))
|
|
{
|
|
.deprecation(e.loc, "%s is not visible from module %s", s.toPrettyChars(), sc._module.toChars());
|
|
// return noMember(sc, e, ident, flag);
|
|
}
|
|
if (!s.isFuncDeclaration()) // because of overloading
|
|
s.checkDeprecated(e.loc, sc);
|
|
s = s.toAlias();
|
|
|
|
if (auto em = s.isEnumMember())
|
|
{
|
|
return em.getVarExp(e.loc, sc);
|
|
}
|
|
|
|
VarDeclaration v = s.isVarDeclaration();
|
|
if (v && (!v.type || !v.type.deco))
|
|
{
|
|
if (v.inuse) // Bugzilla 9494
|
|
{
|
|
e.error("circular reference to '%s'", v.toPrettyChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (v._scope)
|
|
{
|
|
v.semantic(v._scope);
|
|
s = v.toAlias(); // Need this if 'v' is a tuple variable
|
|
v = s.isVarDeclaration();
|
|
}
|
|
}
|
|
if (v && !v.isDataseg() && (v.storage_class & STCmanifest))
|
|
{
|
|
checkAccess(e.loc, sc, null, v);
|
|
Expression ve = new VarExp(e.loc, v);
|
|
ve = ve.semantic(sc);
|
|
return ve;
|
|
}
|
|
if (auto t = s.getType())
|
|
{
|
|
return (new TypeExp(e.loc, t)).semantic(sc);
|
|
}
|
|
|
|
TemplateMixin tm = s.isTemplateMixin();
|
|
if (tm)
|
|
{
|
|
Expression de = new DotExp(e.loc, e, new ScopeExp(e.loc, tm));
|
|
de.type = e.type;
|
|
return de;
|
|
}
|
|
TemplateDeclaration td = s.isTemplateDeclaration();
|
|
if (td)
|
|
{
|
|
if (e.op == TOKtype)
|
|
e = new TemplateExp(e.loc, td);
|
|
else
|
|
e = new DotTemplateExp(e.loc, e, td);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
TemplateInstance ti = s.isTemplateInstance();
|
|
if (ti)
|
|
{
|
|
if (!ti.semanticRun)
|
|
{
|
|
ti.semantic(sc);
|
|
if (!ti.inst || ti.errors) // if template failed to expand
|
|
return new ErrorExp();
|
|
}
|
|
s = ti.inst.toAlias();
|
|
if (!s.isTemplateInstance())
|
|
goto L1;
|
|
if (e.op == TOKtype)
|
|
e = new ScopeExp(e.loc, ti);
|
|
else
|
|
e = new DotExp(e.loc, e, new ScopeExp(e.loc, ti));
|
|
return e.semantic(sc);
|
|
}
|
|
if (s.isImport() || s.isModule() || s.isPackage())
|
|
{
|
|
e = DsymbolExp.resolve(e.loc, sc, s, false);
|
|
return e;
|
|
}
|
|
OverloadSet o = s.isOverloadSet();
|
|
if (o)
|
|
{
|
|
auto oe = new OverExp(e.loc, o);
|
|
if (e.op == TOKtype)
|
|
return oe;
|
|
return new DotExp(e.loc, e, oe);
|
|
}
|
|
Declaration d = s.isDeclaration();
|
|
if (!d)
|
|
{
|
|
e.error("%s.%s is not a declaration", e.toChars(), ident.toChars());
|
|
return new ErrorExp();
|
|
}
|
|
if (e.op == TOKtype)
|
|
{
|
|
/* It's:
|
|
* Class.d
|
|
*/
|
|
// If Class is in a failed template, return an error
|
|
TemplateInstance tiparent = d.isInstantiated();
|
|
if (tiparent && tiparent.errors)
|
|
return new ErrorExp();
|
|
if (TupleDeclaration tup = d.isTupleDeclaration())
|
|
{
|
|
e = new TupleExp(e.loc, tup);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
if (d.needThis() && sc.intypeof != 1)
|
|
{
|
|
/* Rewrite as:
|
|
* this.d
|
|
*/
|
|
if (hasThis(sc))
|
|
{
|
|
// This is almost same as getRightThis() in expression.c
|
|
Expression e1 = new ThisExp(e.loc);
|
|
e1 = e1.semantic(sc);
|
|
L2:
|
|
Type t = e1.type.toBasetype();
|
|
ClassDeclaration cd = e.type.isClassHandle();
|
|
ClassDeclaration tcd = t.isClassHandle();
|
|
if (cd && tcd && (tcd == cd || cd.isBaseOf(tcd, null)))
|
|
{
|
|
e = new DotTypeExp(e1.loc, e1, cd);
|
|
e = new DotVarExp(e.loc, e, d);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
if (tcd && tcd.isNested())
|
|
{
|
|
/* e1 is the 'this' pointer for an inner class: tcd.
|
|
* Rewrite it as the 'this' pointer for the outer class.
|
|
*/
|
|
e1 = new DotVarExp(e.loc, e1, tcd.vthis);
|
|
e1.type = tcd.vthis.type;
|
|
e1.type = e1.type.addMod(t.mod);
|
|
// Do not call checkNestedRef()
|
|
//e1 = e1->semantic(sc);
|
|
// Skip up over nested functions, and get the enclosing
|
|
// class type.
|
|
int n = 0;
|
|
for (s = tcd.toParent(); s && s.isFuncDeclaration(); s = s.toParent())
|
|
{
|
|
FuncDeclaration f = s.isFuncDeclaration();
|
|
if (f.vthis)
|
|
{
|
|
//printf("rewriting e1 to %s's this\n", f->toChars());
|
|
n++;
|
|
e1 = new VarExp(e.loc, f.vthis);
|
|
}
|
|
else
|
|
{
|
|
e = new VarExp(e.loc, d);
|
|
return e;
|
|
}
|
|
}
|
|
if (s && s.isClassDeclaration())
|
|
{
|
|
e1.type = s.isClassDeclaration().type;
|
|
e1.type = e1.type.addMod(t.mod);
|
|
if (n > 1)
|
|
e1 = e1.semantic(sc);
|
|
}
|
|
else
|
|
e1 = e1.semantic(sc);
|
|
goto L2;
|
|
}
|
|
}
|
|
}
|
|
//printf("e = %s, d = %s\n", e->toChars(), d->toChars());
|
|
if (d.semanticRun == PASSinit && d._scope)
|
|
d.semantic(d._scope);
|
|
checkAccess(e.loc, sc, e, d);
|
|
auto ve = new VarExp(e.loc, d);
|
|
if (d.isVarDeclaration() && d.needThis())
|
|
ve.type = d.type.addMod(e.type.mod);
|
|
return ve;
|
|
}
|
|
bool unreal = e.op == TOKvar && (cast(VarExp)e).var.isField();
|
|
if (d.isDataseg() || unreal && d.isField())
|
|
{
|
|
// (e, d)
|
|
checkAccess(e.loc, sc, e, d);
|
|
Expression ve = new VarExp(e.loc, d);
|
|
e = unreal ? ve : new CommaExp(e.loc, e, ve);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
if (d.parent && d.toParent().isModule())
|
|
{
|
|
// (e, d)
|
|
auto ve = new VarExp(e.loc, d);
|
|
e = new CommaExp(e.loc, e, ve);
|
|
e.type = d.type;
|
|
return e;
|
|
}
|
|
|
|
e = new DotVarExp(e.loc, e, d);
|
|
e = e.semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
override ClassDeclaration isClassHandle()
|
|
{
|
|
return sym;
|
|
}
|
|
|
|
override bool isBaseOf(Type t, int* poffset)
|
|
{
|
|
if (t && t.ty == Tclass)
|
|
{
|
|
ClassDeclaration cd = (cast(TypeClass)t).sym;
|
|
if (sym.isBaseOf(cd, poffset))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
override MATCH implicitConvTo(Type to)
|
|
{
|
|
//printf("TypeClass::implicitConvTo(to = '%s') %s\n", to->toChars(), toChars());
|
|
MATCH m = constConv(to);
|
|
if (m > MATCHnomatch)
|
|
return m;
|
|
ClassDeclaration cdto = to.isClassHandle();
|
|
if (cdto)
|
|
{
|
|
//printf("TypeClass::implicitConvTo(to = '%s') %s, isbase = %d %d\n", to->toChars(), toChars(), cdto->isBaseInfoComplete(), sym->isBaseInfoComplete());
|
|
if (cdto._scope && !cdto.isBaseInfoComplete())
|
|
cdto.semantic(null);
|
|
if (sym._scope && !sym.isBaseInfoComplete())
|
|
sym.semantic(null);
|
|
if (cdto.isBaseOf(sym, null) && MODimplicitConv(mod, to.mod))
|
|
{
|
|
//printf("'to' is base\n");
|
|
return MATCHconvert;
|
|
}
|
|
}
|
|
m = MATCHnomatch;
|
|
if (sym.aliasthis && !(att & RECtracing))
|
|
{
|
|
att = cast(AliasThisRec)(att | RECtracing);
|
|
m = aliasthisOf().implicitConvTo(to);
|
|
att = cast(AliasThisRec)(att & ~RECtracing);
|
|
}
|
|
return m;
|
|
}
|
|
|
|
override MATCH constConv(Type to)
|
|
{
|
|
if (equals(to))
|
|
return MATCHexact;
|
|
if (ty == to.ty && sym == (cast(TypeClass)to).sym && MODimplicitConv(mod, to.mod))
|
|
return MATCHconst;
|
|
/* Conversion derived to const(base)
|
|
*/
|
|
int offset = 0;
|
|
if (to.isBaseOf(this, &offset) && offset == 0 && MODimplicitConv(mod, to.mod))
|
|
{
|
|
// Disallow:
|
|
// derived to base
|
|
// inout(derived) to inout(base)
|
|
if (!to.isMutable() && !to.isWild())
|
|
return MATCHconvert;
|
|
}
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
override ubyte deduceWild(Type t, bool isRef)
|
|
{
|
|
ClassDeclaration cd = t.isClassHandle();
|
|
if (cd && (sym == cd || cd.isBaseOf(sym, null)))
|
|
return Type.deduceWild(t, isRef);
|
|
ubyte wm = 0;
|
|
if (t.hasWild() && sym.aliasthis && !(att & RECtracing))
|
|
{
|
|
att = cast(AliasThisRec)(att | RECtracing);
|
|
wm = aliasthisOf().deduceWild(t, isRef);
|
|
att = cast(AliasThisRec)(att & ~RECtracing);
|
|
}
|
|
return wm;
|
|
}
|
|
|
|
override Type toHeadMutable()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
static if (LOGDEFAULTINIT)
|
|
{
|
|
printf("TypeClass::defaultInit() '%s'\n", toChars());
|
|
}
|
|
return new NullExp(loc, this);
|
|
}
|
|
|
|
override bool isZeroInit(Loc loc) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool isscope() const
|
|
{
|
|
return sym.isscope;
|
|
}
|
|
|
|
override bool isBoolean() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool hasPointers() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeTuple : Type
|
|
{
|
|
public:
|
|
Parameters* arguments; // types making up the tuple
|
|
|
|
extern (D) this(Parameters* arguments)
|
|
{
|
|
super(Ttuple);
|
|
//printf("TypeTuple(this = %p)\n", this);
|
|
this.arguments = arguments;
|
|
//printf("TypeTuple() %p, %s\n", this, toChars());
|
|
debug
|
|
{
|
|
if (arguments)
|
|
{
|
|
for (size_t i = 0; i < arguments.dim; i++)
|
|
{
|
|
Parameter arg = (*arguments)[i];
|
|
assert(arg && arg.type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************
|
|
* Form TypeTuple from the types of the expressions.
|
|
* Assume exps[] is already tuple expanded.
|
|
*/
|
|
extern (D) this(Expressions* exps)
|
|
{
|
|
super(Ttuple);
|
|
auto arguments = new Parameters();
|
|
if (exps)
|
|
{
|
|
arguments.setDim(exps.dim);
|
|
for (size_t i = 0; i < exps.dim; i++)
|
|
{
|
|
Expression e = (*exps)[i];
|
|
if (e.type.ty == Ttuple)
|
|
e.error("cannot form tuple of tuples");
|
|
auto arg = new Parameter(STCundefined, e.type, null, null);
|
|
(*arguments)[i] = arg;
|
|
}
|
|
}
|
|
this.arguments = arguments;
|
|
//printf("TypeTuple() %p, %s\n", this, toChars());
|
|
}
|
|
|
|
static TypeTuple create(Parameters* arguments)
|
|
{
|
|
return new TypeTuple(arguments);
|
|
}
|
|
|
|
/*******************************************
|
|
* Type tuple with 0, 1 or 2 types in it.
|
|
*/
|
|
extern (D) this()
|
|
{
|
|
super(Ttuple);
|
|
arguments = new Parameters();
|
|
}
|
|
|
|
extern (D) this(Type t1)
|
|
{
|
|
super(Ttuple);
|
|
arguments = new Parameters();
|
|
arguments.push(new Parameter(0, t1, null, null));
|
|
}
|
|
|
|
extern (D) this(Type t1, Type t2)
|
|
{
|
|
super(Ttuple);
|
|
arguments = new Parameters();
|
|
arguments.push(new Parameter(0, t1, null, null));
|
|
arguments.push(new Parameter(0, t2, null, null));
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "tuple";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
Parameters* args = Parameter.arraySyntaxCopy(arguments);
|
|
Type t = new TypeTuple(args);
|
|
t.mod = mod;
|
|
return t;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypeTuple::semantic(this = %p)\n", this);
|
|
//printf("TypeTuple::semantic() %p, %s\n", this, toChars());
|
|
if (!deco)
|
|
deco = merge().deco;
|
|
/* Don't return merge(), because a tuple with one type has the
|
|
* same deco as that type.
|
|
*/
|
|
return this;
|
|
}
|
|
|
|
override bool equals(RootObject o)
|
|
{
|
|
Type t = cast(Type)o;
|
|
//printf("TypeTuple::equals(%s, %s)\n", toChars(), t->toChars());
|
|
if (this == t)
|
|
return true;
|
|
if (t.ty == Ttuple)
|
|
{
|
|
TypeTuple tt = cast(TypeTuple)t;
|
|
if (arguments.dim == tt.arguments.dim)
|
|
{
|
|
for (size_t i = 0; i < tt.arguments.dim; i++)
|
|
{
|
|
Parameter arg1 = (*arguments)[i];
|
|
Parameter arg2 = (*tt.arguments)[i];
|
|
if (!arg1.type.equals(arg2.type))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
override Expression getProperty(Loc loc, Identifier ident, int flag)
|
|
{
|
|
Expression e;
|
|
static if (LOGDOTEXP)
|
|
{
|
|
printf("TypeTuple::getProperty(type = '%s', ident = '%s')\n", toChars(), ident.toChars());
|
|
}
|
|
if (ident == Id.length)
|
|
{
|
|
e = new IntegerExp(loc, arguments.dim, Type.tsize_t);
|
|
}
|
|
else if (ident == Id._init)
|
|
{
|
|
e = defaultInitLiteral(loc);
|
|
}
|
|
else if (flag)
|
|
{
|
|
e = null;
|
|
}
|
|
else
|
|
{
|
|
error(loc, "no property '%s' for tuple '%s'", ident.toChars(), toChars());
|
|
e = new ErrorExp();
|
|
}
|
|
return e;
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc)
|
|
{
|
|
auto exps = new Expressions();
|
|
exps.setDim(arguments.dim);
|
|
for (size_t i = 0; i < arguments.dim; i++)
|
|
{
|
|
Parameter p = (*arguments)[i];
|
|
assert(p.type);
|
|
Expression e = p.type.defaultInitLiteral(loc);
|
|
if (e.op == TOKerror)
|
|
return e;
|
|
(*exps)[i] = e;
|
|
}
|
|
return new TupleExp(loc, exps);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* This is so we can slice a TypeTuple
|
|
*/
|
|
extern (C++) final class TypeSlice : TypeNext
|
|
{
|
|
public:
|
|
Expression lwr;
|
|
Expression upr;
|
|
|
|
extern (D) this(Type next, Expression lwr, Expression upr)
|
|
{
|
|
super(Tslice, next);
|
|
//printf("TypeSlice[%s .. %s]\n", lwr->toChars(), upr->toChars());
|
|
this.lwr = lwr;
|
|
this.upr = upr;
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "slice";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
Type t = new TypeSlice(next.syntaxCopy(), lwr.syntaxCopy(), upr.syntaxCopy());
|
|
t.mod = mod;
|
|
return t;
|
|
}
|
|
|
|
override Type semantic(Loc loc, Scope* sc)
|
|
{
|
|
//printf("TypeSlice::semantic() %s\n", toChars());
|
|
Type tn = next.semantic(loc, sc);
|
|
//printf("next: %s\n", tn->toChars());
|
|
Type tbn = tn.toBasetype();
|
|
if (tbn.ty != Ttuple)
|
|
{
|
|
error(loc, "can only slice tuple types, not %s", tbn.toChars());
|
|
return Type.terror;
|
|
}
|
|
TypeTuple tt = cast(TypeTuple)tbn;
|
|
lwr = semanticLength(sc, tbn, lwr);
|
|
upr = semanticLength(sc, tbn, upr);
|
|
lwr = lwr.ctfeInterpret();
|
|
upr = upr.ctfeInterpret();
|
|
if (lwr.op == TOKerror || upr.op == TOKerror)
|
|
return Type.terror;
|
|
uinteger_t i1 = lwr.toUInteger();
|
|
uinteger_t i2 = upr.toUInteger();
|
|
if (!(i1 <= i2 && i2 <= tt.arguments.dim))
|
|
{
|
|
error(loc, "slice [%llu..%llu] is out of range of [0..%u]", i1, i2, tt.arguments.dim);
|
|
return Type.terror;
|
|
}
|
|
|
|
next = tn;
|
|
transitive();
|
|
auto args = new Parameters();
|
|
args.reserve(cast(size_t)(i2 - i1));
|
|
for (size_t i = cast(size_t)i1; i < cast(size_t)i2; i++)
|
|
{
|
|
Parameter arg = (*tt.arguments)[i];
|
|
args.push(arg);
|
|
}
|
|
Type t = new TypeTuple(args);
|
|
return t.semantic(loc, sc);
|
|
}
|
|
|
|
override void resolve(Loc loc, Scope* sc, Expression* pe, Type* pt, Dsymbol* ps, bool intypeid = false)
|
|
{
|
|
next.resolve(loc, sc, pe, pt, ps, intypeid);
|
|
if (*pe)
|
|
{
|
|
// It's really a slice expression
|
|
if (Dsymbol s = getDsymbol(*pe))
|
|
*pe = new DsymbolExp(loc, s);
|
|
*pe = new ArrayExp(loc, *pe, new IntervalExp(loc, lwr, upr));
|
|
}
|
|
else if (*ps)
|
|
{
|
|
Dsymbol s = *ps;
|
|
TupleDeclaration td = s.isTupleDeclaration();
|
|
if (td)
|
|
{
|
|
/* It's a slice of a TupleDeclaration
|
|
*/
|
|
ScopeDsymbol sym = new ArrayScopeSymbol(sc, td);
|
|
sym.parent = sc.scopesym;
|
|
sc = sc.push(sym);
|
|
sc = sc.startCTFE();
|
|
lwr = lwr.semantic(sc);
|
|
upr = upr.semantic(sc);
|
|
sc = sc.endCTFE();
|
|
sc = sc.pop();
|
|
lwr = lwr.ctfeInterpret();
|
|
upr = upr.ctfeInterpret();
|
|
uinteger_t i1 = lwr.toUInteger();
|
|
uinteger_t i2 = upr.toUInteger();
|
|
if (!(i1 <= i2 && i2 <= td.objects.dim))
|
|
{
|
|
error(loc, "slice [%llu..%llu] is out of range of [0..%u]", i1, i2, td.objects.dim);
|
|
goto Ldefault;
|
|
}
|
|
if (i1 == 0 && i2 == td.objects.dim)
|
|
{
|
|
*ps = td;
|
|
return;
|
|
}
|
|
/* Create a new TupleDeclaration which
|
|
* is a slice [i1..i2] out of the old one.
|
|
*/
|
|
auto objects = new Objects();
|
|
objects.setDim(cast(size_t)(i2 - i1));
|
|
for (size_t i = 0; i < objects.dim; i++)
|
|
{
|
|
(*objects)[i] = (*td.objects)[cast(size_t)i1 + i];
|
|
}
|
|
auto tds = new TupleDeclaration(loc, td.ident, objects);
|
|
*ps = tds;
|
|
}
|
|
else
|
|
goto Ldefault;
|
|
}
|
|
else
|
|
{
|
|
if ((*pt).ty != Terror)
|
|
next = *pt; // prevent re-running semantic() on 'next'
|
|
Ldefault:
|
|
Type.resolve(loc, sc, pe, pt, ps, intypeid);
|
|
}
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TypeNull : Type
|
|
{
|
|
public:
|
|
extern (D) this()
|
|
{
|
|
super(Tnull);
|
|
}
|
|
|
|
override const(char)* kind() const
|
|
{
|
|
return "null";
|
|
}
|
|
|
|
override Type syntaxCopy()
|
|
{
|
|
// No semantic analysis done, no need to copy
|
|
return this;
|
|
}
|
|
|
|
override MATCH implicitConvTo(Type to)
|
|
{
|
|
//printf("TypeNull::implicitConvTo(this=%p, to=%p)\n", this, to);
|
|
//printf("from: %s\n", toChars());
|
|
//printf("to : %s\n", to->toChars());
|
|
MATCH m = Type.implicitConvTo(to);
|
|
if (m != MATCHnomatch)
|
|
return m;
|
|
// NULL implicitly converts to any pointer type or dynamic array
|
|
//if (type->ty == Tpointer && type->nextOf()->ty == Tvoid)
|
|
{
|
|
Type tb = to.toBasetype();
|
|
if (tb.ty == Tnull || tb.ty == Tpointer || tb.ty == Tarray || tb.ty == Taarray || tb.ty == Tclass || tb.ty == Tdelegate)
|
|
return MATCHconst;
|
|
}
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
override bool isBoolean() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override d_uns64 size(Loc loc) const
|
|
{
|
|
return tvoidptr.size(loc);
|
|
}
|
|
|
|
override Expression defaultInit(Loc loc) const
|
|
{
|
|
return new NullExp(Loc(), Type.tnull);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class Parameter : RootObject
|
|
{
|
|
public:
|
|
StorageClass storageClass;
|
|
Type type;
|
|
Identifier ident;
|
|
Expression defaultArg;
|
|
|
|
extern (D) this(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg)
|
|
{
|
|
this.type = type;
|
|
this.ident = ident;
|
|
this.storageClass = storageClass;
|
|
this.defaultArg = defaultArg;
|
|
}
|
|
|
|
static Parameter create(StorageClass storageClass, Type type, Identifier ident, Expression defaultArg)
|
|
{
|
|
return new Parameter(storageClass, type, ident, defaultArg);
|
|
}
|
|
|
|
Parameter syntaxCopy()
|
|
{
|
|
return new Parameter(storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null);
|
|
}
|
|
|
|
/****************************************************
|
|
* Determine if parameter is a lazy array of delegates.
|
|
* If so, return the return type of those delegates.
|
|
* If not, return NULL.
|
|
*
|
|
* Returns T if the type is one of the following forms:
|
|
* T delegate()[]
|
|
* T delegate()[dim]
|
|
*/
|
|
Type isLazyArray()
|
|
{
|
|
Type tb = type.toBasetype();
|
|
if (tb.ty == Tsarray || tb.ty == Tarray)
|
|
{
|
|
Type tel = (cast(TypeArray)tb).next.toBasetype();
|
|
if (tel.ty == Tdelegate)
|
|
{
|
|
TypeDelegate td = cast(TypeDelegate)tel;
|
|
TypeFunction tf = cast(TypeFunction)td.next;
|
|
if (!tf.varargs && Parameter.dim(tf.parameters) == 0)
|
|
{
|
|
return tf.next; // return type of delegate
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// kludge for template.isType()
|
|
override int dyncast()
|
|
{
|
|
return DYNCAST_PARAMETER;
|
|
}
|
|
|
|
void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
|
|
static Parameters* arraySyntaxCopy(Parameters* parameters)
|
|
{
|
|
Parameters* params = null;
|
|
if (parameters)
|
|
{
|
|
params = new Parameters();
|
|
params.setDim(parameters.dim);
|
|
for (size_t i = 0; i < params.dim; i++)
|
|
(*params)[i] = (*parameters)[i].syntaxCopy();
|
|
}
|
|
return params;
|
|
}
|
|
|
|
/****************************************
|
|
* Determine if parameter list is really a template parameter list
|
|
* (i.e. it has auto or alias parameters)
|
|
*/
|
|
extern (D) static int isTPL(Parameters* parameters)
|
|
{
|
|
//printf("Parameter::isTPL()\n");
|
|
|
|
int isTPLDg(size_t n, Parameter p)
|
|
{
|
|
if (p.storageClass & (STCalias | STCauto | STCstatic))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
return _foreach(parameters, &isTPLDg);
|
|
}
|
|
|
|
/***************************************
|
|
* Determine number of arguments, folding in tuples.
|
|
*/
|
|
static size_t dim(Parameters* parameters)
|
|
{
|
|
size_t nargs = 0;
|
|
|
|
int dimDg(size_t n, Parameter p)
|
|
{
|
|
++nargs;
|
|
return 0;
|
|
}
|
|
|
|
_foreach(parameters, &dimDg);
|
|
return nargs;
|
|
}
|
|
|
|
/***************************************
|
|
* Get nth Parameter, folding in tuples.
|
|
* Returns:
|
|
* Parameter* nth Parameter
|
|
* NULL not found, *pn gets incremented by the number
|
|
* of Parameters
|
|
*/
|
|
static Parameter getNth(Parameters* parameters, size_t nth, size_t* pn = null)
|
|
{
|
|
Parameter param;
|
|
|
|
int getNthParamDg(size_t n, Parameter p)
|
|
{
|
|
if (n == nth)
|
|
{
|
|
param = p;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int res = _foreach(parameters, &getNthParamDg);
|
|
return res ? param : null;
|
|
}
|
|
|
|
alias ForeachDg = extern (D) int delegate(size_t paramidx, Parameter param);
|
|
|
|
/***************************************
|
|
* Expands tuples in args in depth first order. Calls
|
|
* dg(void *ctx, size_t argidx, Parameter *arg) for each Parameter.
|
|
* If dg returns !=0, stops and returns that value else returns 0.
|
|
* Use this function to avoid the O(N + N^2/2) complexity of
|
|
* calculating dim and calling N times getNth.
|
|
*/
|
|
extern (D) static int _foreach(Parameters* parameters, scope ForeachDg dg, size_t* pn = null)
|
|
{
|
|
assert(dg);
|
|
if (!parameters)
|
|
return 0;
|
|
size_t n = pn ? *pn : 0; // take over index
|
|
int result = 0;
|
|
foreach (i; 0 .. parameters.dim)
|
|
{
|
|
Parameter p = (*parameters)[i];
|
|
Type t = p.type.toBasetype();
|
|
if (t.ty == Ttuple)
|
|
{
|
|
TypeTuple tu = cast(TypeTuple)t;
|
|
result = _foreach(tu.arguments, dg, &n);
|
|
}
|
|
else
|
|
result = dg(n++, p);
|
|
if (result)
|
|
break;
|
|
}
|
|
if (pn)
|
|
*pn = n; // update index
|
|
return result;
|
|
}
|
|
}
|