Files
ldc/dmd2/mtype.c
David Nadlinger 5f3ba41574 Removed redundant global.params.cpu field.
Now that we have global.params.targetTriple, the information
is only duplicated.
2013-02-07 17:36:54 +01:00

9916 lines
257 KiB
C

// Compiler implementation of the D programming language
// Copyright (c) 1999-2012 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// http://www.dsource.org/projects/dmd/browser/trunk/src/mtype.c
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.
#define __C99FEATURES__ 1 // Needed on Solaris for NaN and more
#define __USE_ISOC99 1 // so signbit() gets defined
#if defined (__sun)
#include <alloca.h>
#endif
#ifdef __DMC__
#include <math.h>
#else
#include <cmath>
#endif
#include <stdio.h>
#include <assert.h>
#include <float.h>
#if _MSC_VER
#include <malloc.h>
#include <complex>
#include <limits>
#elif __DMC__
#include <complex.h>
#elif __MINGW32__
#include <malloc.h>
#endif
#include "rmem.h"
#include "port.h"
#include "dsymbol.h"
#include "mtype.h"
#include "scope.h"
#include "init.h"
#include "expression.h"
#include "statement.h"
#include "attrib.h"
#include "declaration.h"
#include "template.h"
#include "id.h"
#include "enum.h"
#include "import.h"
#include "aggregate.h"
#include "hdrgen.h"
#if IN_LLVM
//#include "gen/tollvm.h"
Ir* Type::sir = NULL;
unsigned GetTypeAlignment(Ir* ir, Type* t);
unsigned GetPointerSize(Ir* ir);
unsigned GetTypeStoreSize(Ir* ir, Type* t);
unsigned GetTypeAllocSize(Ir* ir, Type* t);
#endif
FuncDeclaration *hasThis(Scope *sc);
void ObjectNotFound(Identifier *id);
#define LOGDOTEXP 0 // log ::dotExp()
#define LOGDEFAULTINIT 0 // log ::defaultInit()
// Allow implicit conversion of T[] to T*
#define IMPLICIT_ARRAY_TO_PTR global.params.useDeprecated
/* These have default values for 32 bit code, they get
* adjusted for 64 bit code.
*/
int PTRSIZE = 4;
/* REALSIZE = size a real consumes in memory
* REALPAD = 'padding' added to the CPU real size to bring it up to REALSIZE
* REALALIGNSIZE = alignment for reals
*/
#if TARGET_OSX
int REALSIZE = 16;
int REALPAD = 6;
int REALALIGNSIZE = 16;
#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS
int REALSIZE = 12; // LDC_FIXME: We differ from DMD here, yet target defines are never set?!
int REALPAD = 2;
int REALALIGNSIZE = 4;
#elif defined(IN_GCC)
int REALSIZE = 0;
int REALPAD = 0;
int REALALIGNSIZE = 0;
#else
int REALSIZE = 10;
int REALPAD = 0;
int REALALIGNSIZE = 2;
#endif
int Tsize_t = Tuns32;
int Tptrdiff_t = Tint32;
#if _WIN32 && !(defined __MINGW32__ || defined _MSC_VER)
static double zero = 0;
double Port::nan = NAN;
double Port::infinity = 1/zero;
#endif
/***************************** Type *****************************/
ClassDeclaration *Type::typeinfo;
ClassDeclaration *Type::typeinfoclass;
ClassDeclaration *Type::typeinfointerface;
ClassDeclaration *Type::typeinfostruct;
ClassDeclaration *Type::typeinfotypedef;
ClassDeclaration *Type::typeinfopointer;
ClassDeclaration *Type::typeinfoarray;
ClassDeclaration *Type::typeinfostaticarray;
ClassDeclaration *Type::typeinfoassociativearray;
ClassDeclaration *Type::typeinfovector;
ClassDeclaration *Type::typeinfoenum;
ClassDeclaration *Type::typeinfofunction;
ClassDeclaration *Type::typeinfodelegate;
ClassDeclaration *Type::typeinfotypelist;
ClassDeclaration *Type::typeinfoconst;
ClassDeclaration *Type::typeinfoinvariant;
ClassDeclaration *Type::typeinfoshared;
ClassDeclaration *Type::typeinfowild;
TemplateDeclaration *Type::associativearray;
TemplateDeclaration *Type::rtinfo;
Type *Type::tvoidptr;
Type *Type::tstring;
Type *Type::basic[TMAX];
unsigned char Type::mangleChar[TMAX];
unsigned short Type::sizeTy[TMAX];
StringTable Type::stringtable;
#if IN_LLVM
StringTable Type::deco_stringtable;
#endif
Type::Type(TY ty)
{
this->ty = ty;
this->mod = 0;
this->deco = NULL;
#if DMDV2
this->cto = NULL;
this->ito = NULL;
this->sto = NULL;
this->scto = NULL;
this->wto = NULL;
this->swto = NULL;
#endif
this->pto = NULL;
this->rto = NULL;
this->arrayof = NULL;
this->vtinfo = NULL;
#if IN_DMD
this->ctype = NULL;
#endif
#if IN_LLVM
this->irtype = NULL;
#endif
}
Type *Type::syntaxCopy()
{
print();
fprintf(stdmsg, "ty = %d\n", ty);
assert(0);
return this;
}
int Type::equals(Object *o)
{ Type *t;
t = (Type *)o;
//printf("Type::equals(%s, %s)\n", toChars(), t->toChars());
if (this == o ||
((t && deco == t->deco) && // deco strings are unique
deco != NULL)) // and semantic() has been run
{
//printf("deco = '%s', t->deco = '%s'\n", deco, t->deco);
return 1;
}
//if (deco && t && t->deco) printf("deco = '%s', t->deco = '%s'\n", deco, t->deco);
return 0;
}
char Type::needThisPrefix()
{
return 'M'; // name mangling prefix for functions needing 'this'
}
#if IN_LLVM
void Type::init(Ir* _sir)
#else
void Type::init()
#endif
{
stringtable.init(1543);
#if IN_LLVM
deco_stringtable.init();
#endif
Lexer::initKeywords();
for (size_t i = 0; i < TMAX; i++)
sizeTy[i] = sizeof(TypeBasic);
sizeTy[Tsarray] = sizeof(TypeSArray);
sizeTy[Tarray] = sizeof(TypeDArray);
sizeTy[Taarray] = sizeof(TypeAArray);
sizeTy[Tpointer] = sizeof(TypePointer);
sizeTy[Treference] = sizeof(TypeReference);
sizeTy[Tfunction] = sizeof(TypeFunction);
sizeTy[Tdelegate] = sizeof(TypeDelegate);
sizeTy[Tident] = sizeof(TypeIdentifier);
sizeTy[Tinstance] = sizeof(TypeInstance);
sizeTy[Ttypeof] = sizeof(TypeTypeof);
sizeTy[Tenum] = sizeof(TypeEnum);
sizeTy[Ttypedef] = sizeof(TypeTypedef);
sizeTy[Tstruct] = sizeof(TypeStruct);
sizeTy[Tclass] = sizeof(TypeClass);
sizeTy[Ttuple] = sizeof(TypeTuple);
sizeTy[Tslice] = sizeof(TypeSlice);
sizeTy[Treturn] = sizeof(TypeReturn);
sizeTy[Terror] = sizeof(TypeError);
sizeTy[Tnull] = sizeof(TypeNull);
mangleChar[Tarray] = 'A';
mangleChar[Tsarray] = 'G';
mangleChar[Taarray] = 'H';
mangleChar[Tpointer] = 'P';
mangleChar[Treference] = 'R';
mangleChar[Tfunction] = 'F';
mangleChar[Tident] = 'I';
mangleChar[Tclass] = 'C';
mangleChar[Tstruct] = 'S';
mangleChar[Tenum] = 'E';
mangleChar[Ttypedef] = 'T';
mangleChar[Tdelegate] = 'D';
mangleChar[Tnone] = 'n';
mangleChar[Tvoid] = 'v';
mangleChar[Tint8] = 'g';
mangleChar[Tuns8] = 'h';
mangleChar[Tint16] = 's';
mangleChar[Tuns16] = 't';
mangleChar[Tint32] = 'i';
mangleChar[Tuns32] = 'k';
mangleChar[Tint64] = 'l';
mangleChar[Tuns64] = 'm';
mangleChar[Tfloat32] = 'f';
mangleChar[Tfloat64] = 'd';
mangleChar[Tfloat80] = 'e';
mangleChar[Timaginary32] = 'o';
mangleChar[Timaginary64] = 'p';
mangleChar[Timaginary80] = 'j';
mangleChar[Tcomplex32] = 'q';
mangleChar[Tcomplex64] = 'r';
mangleChar[Tcomplex80] = 'c';
mangleChar[Tbool] = 'b';
mangleChar[Tascii] = 'a';
mangleChar[Twchar] = 'u';
mangleChar[Tdchar] = 'w';
// '@' shouldn't appear anywhere in the deco'd names
mangleChar[Tinstance] = '@';
mangleChar[Terror] = '@';
mangleChar[Ttypeof] = '@';
mangleChar[Ttuple] = 'B';
mangleChar[Tslice] = '@';
mangleChar[Treturn] = '@';
mangleChar[Tvector] = '@';
mangleChar[Tint128] = '@';
mangleChar[Tuns128] = '@';
mangleChar[Tnull] = 'n'; // same as TypeNone
for (size_t i = 0; i < TMAX; i++)
{ if (!mangleChar[i])
fprintf(stdmsg, "ty = %llu\n", (ulonglong)i);
assert(mangleChar[i]);
}
// Set basic types
static TY basetab[] =
{ Tvoid, Tint8, Tuns8, Tint16, Tuns16, Tint32, Tuns32, Tint64, Tuns64,
Tint128, Tuns128,
Tfloat32, Tfloat64, Tfloat80,
Timaginary32, Timaginary64, Timaginary80,
Tcomplex32, Tcomplex64, Tcomplex80,
Tbool,
Tascii, Twchar, Tdchar };
for (size_t i = 0; i < sizeof(basetab) / sizeof(basetab[0]); i++)
{ Type *t = new TypeBasic(basetab[i]);
t = t->merge();
basic[basetab[i]] = t;
}
basic[Terror] = new TypeError();
tnull = new TypeNull();
tnull->deco = tnull->merge()->deco;
tvoidptr = tvoid->pointerTo();
tstring = tchar->invariantOf()->arrayOf();
#if IN_DMD
if (global.params.is64bit)
{
PTRSIZE = 8;
if (global.params.isLinux || global.params.isFreeBSD || global.params.isSolaris)
{
REALSIZE = 16;
REALPAD = 6;
REALALIGNSIZE = 16;
}
Tsize_t = Tuns64;
Tptrdiff_t = Tint64;
}
else
{
PTRSIZE = 4;
#if TARGET_OSX
REALSIZE = 16;
REALPAD = 6;
#elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS
REALSIZE = 12;
REALPAD = 2;
#elif TARGET_WINDOS
REALSIZE = 10;
REALPAD = 0;
#elif defined(IN_GCC)
#else
assert(0);
#endif
Tsize_t = Tuns32;
Tptrdiff_t = Tint32;
}
#endif
#if IN_LLVM
sir = _sir;
if (global.params.is64bit)
{
Tsize_t = Tuns64;
Tptrdiff_t = Tint64;
}
else
{
Tsize_t = Tuns32;
Tptrdiff_t = Tint32;
}
PTRSIZE = GetPointerSize(sir);
REALSIZE = GetTypeAllocSize(sir, Type::basic[Tfloat80]);
REALPAD = REALSIZE - GetTypeStoreSize(sir, Type::basic[Tfloat80]);
REALALIGNSIZE = GetTypeAlignment(sir, Type::basic[Tfloat80]);
#endif
}
d_uns64 Type::size()
{
return size(0);
}
d_uns64 Type::size(Loc loc)
{
error(loc, "no size for type %s", toChars());
return 1;
}
unsigned Type::alignsize()
{
return size(0);
}
Type *Type::semantic(Loc loc, Scope *sc)
{
if (ty == Tint128 || ty == Tuns128)
{
error(loc, "cent and ucent types not implemented");
return terror;
}
return merge();
}
Type *Type::trySemantic(Loc loc, Scope *sc)
{
//printf("+trySemantic(%s) %d\n", toChars(), global.errors);
unsigned errors = global.startGagging();
Type *t = semantic(loc, sc);
if (global.endGagging(errors)) // if any errors happened
{
t = NULL;
}
//printf("-trySemantic(%s) %d\n", toChars(), global.errors);
return t;
}
/********************************
* Convert to 'const'.
*/
Type *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'.
*/
Type *Type::invariantOf()
{
//printf("Type::invariantOf() %p %s\n", this, toChars());
if (isImmutable())
{
return this;
}
if (ito)
{
assert(ito->isImmutable());
return ito;
}
Type *t = makeInvariant();
t = t->merge();
t->fixTo(this);
//printf("\t%p\n", t);
return t;
}
/********************************
* Make type mutable.
*/
Type *Type::mutableOf()
{
//printf("Type::mutableOf() %p, %s\n", this, toChars());
Type *t = this;
if (isConst())
{ if (isShared())
t = sto; // shared const => shared
else
t = cto; // const => naked
assert(!t || t->isMutable());
}
else if (isImmutable())
{ t = ito; // immutable => naked
assert(!t || (t->isMutable() && !t->isShared()));
}
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;
}
Type *Type::sharedOf()
{
//printf("Type::sharedOf() %p, %s\n", this, toChars());
if (mod == MODshared)
{
return this;
}
if (sto)
{
assert(sto->isShared());
return sto;
}
Type *t = makeShared();
t = t->merge();
t->fixTo(this);
//printf("\t%p\n", t);
return t;
}
Type *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
* shared wild => wild
*/
Type *Type::unSharedOf()
{
//printf("Type::unSharedOf() %p, %s\n", this, toChars());
Type *t = this;
if (isShared())
{
if (isConst())
t = cto; // shared const => const
else if (isWild())
t = wto; // shared wild => wild
else
t = sto;
assert(!t || !t->isShared());
}
if (!t)
{
unsigned sz = sizeTy[ty];
t = (Type *)mem.malloc(sz);
memcpy(t, this, sz);
t->mod = mod & ~MODshared;
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->swto = NULL;
t->vtinfo = NULL;
t = t->merge();
t->fixTo(this);
}
else
t = t->merge();
assert(!t->isShared());
return t;
}
/********************************
* Convert to 'wild'.
*/
Type *Type::wildOf()
{
//printf("Type::wildOf() %p %s\n", this, toChars());
if (mod == MODwild)
{
return this;
}
if (wto)
{
assert(wto->isWild());
return wto;
}
Type *t = makeWild();
t = t->merge();
t->fixTo(this);
//printf("\t%p %s\n", t, t->toChars());
return t;
}
Type *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\n", t);
return t;
}
/**********************************
* For our new type 'this', which is type-constructed from t,
* fill in the cto, ito, sto, scto, wto shortcuts.
*/
void Type::fixTo(Type *t)
{
ito = t->ito;
#if 0
/* Cannot do these because these are not fully transitive:
* there can be a shared ptr to immutable, for example.
* Immutable subtypes are always immutable, though.
*/
cto = t->cto;
sto = t->sto;
scto = t->scto;
#endif
assert(mod != t->mod);
#define X(m, n) (((m) << 4) | (n))
switch (X(mod, t->mod))
{
case X(0, MODconst):
cto = t;
break;
case X(0, MODimmutable):
ito = t;
break;
case X(0, MODshared):
sto = t;
break;
case X(0, MODshared | MODconst):
scto = t;
break;
case X(0, MODwild):
wto = t;
break;
case X(0, MODshared | MODwild):
swto = t;
break;
case X(MODconst, 0):
cto = NULL;
goto L2;
case X(MODconst, MODimmutable):
ito = t;
goto L2;
case X(MODconst, MODshared):
sto = t;
goto L2;
case X(MODconst, MODshared | MODconst):
scto = t;
goto L2;
case X(MODconst, MODwild):
wto = t;
goto L2;
case X(MODconst, MODshared | MODwild):
swto = t;
L2:
t->cto = this;
break;
case X(MODimmutable, 0):
ito = NULL;
goto L3;
case X(MODimmutable, MODconst):
cto = t;
goto L3;
case X(MODimmutable, MODshared):
sto = t;
goto L3;
case X(MODimmutable, MODshared | MODconst):
scto = t;
goto L3;
case X(MODimmutable, MODwild):
wto = t;
goto L3;
case X(MODimmutable, MODshared | MODwild):
swto = t;
L3:
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->swto) t->swto->ito = this;
break;
case X(MODshared, 0):
sto = NULL;
goto L4;
case X(MODshared, MODconst):
cto = t;
goto L4;
case X(MODshared, MODimmutable):
ito = t;
goto L4;
case X(MODshared, MODshared | MODconst):
scto = t;
goto L4;
case X(MODshared, MODwild):
wto = t;
goto L4;
case X(MODshared, MODshared | MODwild):
swto = t;
L4:
t->sto = this;
break;
case X(MODshared | MODconst, 0):
scto = NULL;
goto L5;
case X(MODshared | MODconst, MODconst):
cto = t;
goto L5;
case X(MODshared | MODconst, MODimmutable):
ito = t;
goto L5;
case X(MODshared | MODconst, MODwild):
wto = t;
goto L5;
case X(MODshared | MODconst, MODshared):
sto = t;
goto L5;
case X(MODshared | MODconst, MODshared | MODwild):
swto = t;
L5:
t->scto = this;
break;
case X(MODwild, 0):
wto = NULL;
goto L6;
case X(MODwild, MODconst):
cto = t;
goto L6;
case X(MODwild, MODimmutable):
ito = t;
goto L6;
case X(MODwild, MODshared):
sto = t;
goto L6;
case X(MODwild, MODshared | MODconst):
scto = t;
goto L6;
case X(MODwild, MODshared | MODwild):
swto = t;
L6:
t->wto = this;
break;
case X(MODshared | MODwild, 0):
swto = NULL;
goto L7;
case X(MODshared | MODwild, MODconst):
cto = t;
goto L7;
case X(MODshared | MODwild, MODimmutable):
ito = t;
goto L7;
case X(MODshared | MODwild, MODshared):
sto = t;
goto L7;
case X(MODshared | MODwild, MODshared | MODconst):
scto = t;
goto L7;
case X(MODshared | MODwild, MODwild):
wto = t;
L7:
t->swto = this;
break;
default:
assert(0);
}
#undef X
check();
t->check();
//printf("fixTo: %s, %s\n", toChars(), t->toChars());
}
/***************************
* Look for bugs in constructing types.
*/
void Type::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 (swto) assert(swto->mod == (MODshared | MODwild));
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 (swto) assert(swto->mod == (MODshared | MODwild));
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 (swto) assert(swto->mod == (MODshared | MODwild));
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 (swto) assert(swto->mod == (MODshared | MODwild));
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 (swto) assert(swto->mod == (MODshared | MODwild));
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 (swto) assert(swto->mod == (MODshared | MODwild));
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 (swto) assert(swto->mod == 0);
break;
default:
assert(0);
}
Type *tn = nextOf();
if (tn && ty != Tfunction && tn->ty != Tfunction)
{ // Verify transitivity
switch (mod)
{
case 0:
break;
case MODconst:
assert(tn->mod & MODimmutable || tn->mod & MODconst);
break;
case MODimmutable:
assert(tn->mod == MODimmutable);
break;
case MODshared:
assert(tn->mod & MODimmutable || tn->mod & MODshared);
break;
case MODshared | MODconst:
assert(tn->mod & MODimmutable || tn->mod & (MODshared | MODconst));
break;
case MODwild:
assert(tn->mod);
break;
case MODshared | MODwild:
assert(tn->mod == MODimmutable || tn->mod == (MODshared | MODconst) || tn->mod == (MODshared | MODwild));
break;
default:
assert(0);
}
tn->check();
}
}
Type *Type::makeConst()
{
//printf("Type::makeConst() %p, %s\n", this, toChars());
if (cto)
return cto;
unsigned sz = sizeTy[ty];
Type *t = (Type *)mem.malloc(sz);
memcpy(t, this, sz);
t->mod = MODconst;
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->swto = NULL;
t->vtinfo = NULL;
#if IN_DMD
t->ctype = NULL;
#endif
//printf("-Type::makeConst() %p, %s\n", t, toChars());
return t;
}
Type *Type::makeInvariant()
{
if (ito)
return ito;
unsigned sz = sizeTy[ty];
Type *t = (Type *)mem.malloc(sz);
memcpy(t, this, sz);
t->mod = MODimmutable;
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->swto = NULL;
t->vtinfo = NULL;
#if IN_DMD
t->ctype = NULL;
#endif
return t;
}
Type *Type::makeShared()
{
if (sto)
return sto;
unsigned sz = sizeTy[ty];
Type *t = (Type *)mem.malloc(sz);
memcpy(t, this, sz);
t->mod = MODshared;
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->swto = NULL;
t->vtinfo = NULL;
#if IN_DMD
t->ctype = NULL;
#endif
return t;
}
Type *Type::makeSharedConst()
{
if (scto)
return scto;
unsigned sz = sizeTy[ty];
Type *t = (Type *)mem.malloc(sz);
memcpy(t, this, sz);
t->mod = MODshared | MODconst;
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->swto = NULL;
t->vtinfo = NULL;
#if IN_DMD
t->ctype = NULL;
#endif
return t;
}
Type *Type::makeWild()
{
if (wto)
return wto;
unsigned sz = sizeTy[ty];
Type *t = (Type *)mem.malloc(sz);
memcpy(t, this, sz);
t->mod = MODwild;
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->swto = NULL;
t->vtinfo = NULL;
#if IN_DMD
t->ctype = NULL;
#endif
return t;
}
Type *Type::makeSharedWild()
{
if (swto)
return swto;
unsigned sz = sizeTy[ty];
Type *t = (Type *)mem.malloc(sz);
memcpy(t, this, sz);
t->mod = MODshared | MODwild;
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->swto = NULL;
t->vtinfo = NULL;
#if IN_DMD
t->ctype = NULL;
#endif
return t;
}
Type *Type::makeMutable()
{
unsigned sz = sizeTy[ty];
Type *t = (Type *)mem.malloc(sz);
memcpy(t, this, sz);
t->mod = mod & MODshared;
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->swto = NULL;
t->vtinfo = NULL;
#if IN_DMD
t->ctype = NULL;
#endif
return t;
}
/*************************************
* Apply STCxxxx bits to existing type.
* Use *before* semantic analysis is run.
*/
Type *Type::addSTC(StorageClass stc)
{ Type *t = this;
if (stc & STCconst)
{ if (t->isShared())
t = t->makeSharedConst();
else
t = t->makeConst();
}
if (stc & STCimmutable)
t = t->makeInvariant();
if (stc & STCshared)
{ if (t->isConst())
t = t->makeSharedConst();
else
t = t->makeShared();
}
if (stc & STCwild)
{ if (t->isShared())
t = t->makeSharedWild();
else
t = t->makeWild();
}
return t;
}
/************************************
* Apply MODxxxx bits to existing type.
*/
Type *Type::castMod(unsigned mod)
{ Type *t;
switch (mod)
{
case 0:
t = unSharedOf()->mutableOf();
break;
case MODconst:
t = unSharedOf()->constOf();
break;
case MODimmutable:
t = invariantOf();
break;
case MODshared:
t = mutableOf()->sharedOf();
break;
case MODshared | MODconst:
t = sharedConstOf();
break;
case MODwild:
t = unSharedOf()->wildOf();
break;
case MODshared | MODwild:
t = sharedWildOf();
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"
*/
Type *Type::addMod(unsigned mod)
{ Type *t = this;
/* Add anything to immutable, and it remains immutable
*/
if (!t->isImmutable())
{
//printf("addMod(%x) %s\n", mod, toChars());
switch (mod)
{
case 0:
break;
case MODconst:
if (isShared())
t = sharedConstOf();
else
t = constOf();
break;
case MODimmutable:
t = invariantOf();
break;
case MODshared:
if (isConst())
t = sharedConstOf();
else if (isWild())
t = sharedWildOf();
else
t = sharedOf();
break;
case MODshared | MODconst:
t = sharedConstOf();
break;
case MODwild:
if (isConst())
;
else if (isShared())
t = sharedWildOf();
else
t = wildOf();
break;
case MODshared | MODwild:
if (isConst())
t = sharedConstOf();
else
t = sharedWildOf();
break;
default:
assert(0);
}
}
return t;
}
/************************************
* Add storage class modifiers to type.
*/
Type *Type::addStorageClass(StorageClass stc)
{
/* Just translate to MOD bits and let addMod() do the work
*/
unsigned mod = 0;
if (stc & STCimmutable)
mod = MODimmutable;
else
{ if (stc & (STCconst | STCin))
mod = MODconst;
else if (stc & STCwild) // const takes precedence over wild
mod |= MODwild;
if (stc & STCshared)
mod |= MODshared;
}
return addMod(mod);
}
Type *Type::pointerTo()
{
if (ty == Terror)
return this;
if (!pto)
{ Type *t;
t = new TypePointer(this);
pto = t->merge();
}
return pto;
}
Type *Type::referenceTo()
{
if (ty == Terror)
return this;
if (!rto)
{ Type *t;
t = new TypeReference(this);
rto = t->merge();
}
return rto;
}
Type *Type::arrayOf()
{
if (ty == Terror)
return this;
if (!arrayof)
{ Type *t;
t = new TypeDArray(this);
arrayof = t->merge();
}
return arrayof;
}
Type *Type::aliasthisOf()
{
AggregateDeclaration *ad = NULL;
if (ty == Tclass)
{
ad = ((TypeClass *)this)->sym;
goto L1;
}
else if (ty == Tstruct)
{
ad = ((TypeStruct *)this)->sym;
L1:
if (!ad->aliasthis)
return NULL;
Declaration *d = ad->aliasthis->isDeclaration();
if (d)
{ assert(d->type);
Type *t = d->type;
if (d->isVarDeclaration() && d->needThis())
{
t = t->addMod(this->mod);
}
else if (d->isFuncDeclaration())
{
FuncDeclaration *fd = (FuncDeclaration *)d;
Expression *ethis = this->defaultInit(0);
fd = fd->overloadResolve(0, ethis, NULL, 1);
if (fd)
{ TypeFunction *tf = (TypeFunction *)fd->type;
if (!tf->next && fd->inferRetType)
{
TemplateInstance *spec = fd->isSpeculative();
int olderrs = global.errors;
// If it isn't speculative, we need to show errors
unsigned oldgag = global.gag;
if (global.gag && !spec)
global.gag = 0;
fd->semantic3(fd->scope);
global.gag = oldgag;
// Update the template instantiation with the number
// of errors which occured.
if (spec && global.errors != olderrs)
spec->errors = global.errors - olderrs;
tf = (TypeFunction *)fd->type;
}
t = tf->next;
if (tf->isWild())
t = t->substWildTo(mod == 0 ? MODmutable : mod);
}
}
return t;
}
EnumDeclaration *ed = ad->aliasthis->isEnumDeclaration();
if (ed)
{
Type *t = ed->type;
return t;
}
TemplateDeclaration *td = ad->aliasthis->isTemplateDeclaration();
if (td)
{ assert(td->scope);
Expression *ethis = defaultInit(0);
FuncDeclaration *fd = td->deduceFunctionTemplate(td->scope, 0, NULL, ethis, NULL, 1);
if (fd)
{
//if (!fd->type->nextOf() && fd->inferRetType)
{
TemplateInstance *spec = fd->isSpeculative();
int olderrs = global.errors;
fd->semantic3(fd->scope);
// Update the template instantiation with the number
// of errors which occured.
if (spec && global.errors != olderrs)
spec->errors = global.errors - olderrs;
}
if (!fd->errors)
{
Type *t = fd->type->nextOf();
t = t->substWildTo(mod == 0 ? MODmutable : mod);
return t;
}
}
return Type::terror;
}
//printf("%s\n", ad->aliasthis->kind());
}
return NULL;
}
Dsymbol *Type::toDsymbol(Scope *sc)
{
return NULL;
}
/*******************************
* If this is a shell around another type,
* get that other type.
*/
Type *Type::toBasetype()
{
return this;
}
/***************************
* Return !=0 if modfrom can be implicitly converted to modto
*/
int MODimplicitConv(unsigned char modfrom, unsigned char modto)
{
if (modfrom == modto)
return 1;
//printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto);
#define X(m, n) (((m) << 4) | (n))
switch (X(modfrom, modto))
{
case X(0, MODconst):
case X(MODimmutable, MODconst):
case X(MODwild, MODconst):
case X(MODimmutable, MODconst | MODshared):
case X(MODshared, MODconst | MODshared):
case X(MODwild | MODshared, MODconst | MODshared):
return 1;
default:
return 0;
}
#undef X
}
/***************************
* Return !=0 if a method of type '() modfrom' can call a method of type '() modto'.
*/
int MODmethodConv(unsigned char modfrom, unsigned char modto)
{
if (MODimplicitConv(modfrom, modto))
return 1;
#define X(m, n) (((m) << 4) | (n))
switch (X(modfrom, modto))
{
case X(0, MODwild):
case X(MODimmutable, MODwild):
case X(MODconst, MODwild):
case X(MODshared, MODshared|MODwild):
case X(MODshared|MODimmutable, MODshared|MODwild):
case X(MODshared|MODconst, MODshared|MODwild):
return 1;
default:
return 0;
}
#undef X
}
/***************************
* Merge mod bits to form common mod.
*/
int MODmerge(unsigned char mod1, unsigned char mod2)
{
if (mod1 == mod2)
return mod1;
//printf("MODmerge(1 = %x, 2 = %x)\n", modfrom, modto);
#define X(m, n) (((m) << 4) | (n))
// cases are commutative
#define Y(m, n) X(m, n): case X(n, m)
switch (X(mod1, mod2))
{
#if 0
case X(0, 0):
case X(MODconst, MODconst):
case X(MODimmutable, MODimmutable):
case X(MODshared, MODshared):
case X(MODshared | MODconst, MODshared | MODconst):
case X(MODwild, MODwild):
case X(MODshared | MODwild, MODshared | MODwild):
#endif
case Y(0, MODconst):
case Y(0, MODimmutable):
case Y(MODconst, MODimmutable):
case Y(MODconst, MODwild):
case Y(0, MODwild):
case Y(MODimmutable, MODwild):
return MODconst;
case Y(0, MODshared):
return MODshared;
case Y(0, MODshared | MODconst):
case Y(MODconst, MODshared):
case Y(MODconst, MODshared | MODconst):
case Y(MODimmutable, MODshared):
case Y(MODimmutable, MODshared | MODconst):
case Y(MODshared, MODshared | MODconst):
case Y(0, MODshared | MODwild):
case Y(MODconst, MODshared | MODwild):
case Y(MODimmutable, MODshared | MODwild):
case Y(MODshared, MODwild):
case Y(MODshared, MODshared | MODwild):
case Y(MODshared | MODconst, MODwild):
case Y(MODshared | MODconst, MODshared | MODwild):
return MODshared | MODconst;
case Y(MODwild, MODshared | MODwild):
return MODshared | MODwild;
default:
assert(0);
}
#undef Y
#undef X
assert(0);
return 0;
}
/*********************************
* Mangling for mod.
*/
void MODtoDecoBuffer(OutBuffer *buf, unsigned char mod)
{
switch (mod)
{ case 0:
break;
case MODconst:
buf->writeByte('x');
break;
case MODimmutable:
buf->writeByte('y');
break;
case MODshared:
buf->writeByte('O');
break;
case MODshared | MODconst:
buf->writestring("Ox");
break;
case MODwild:
buf->writestring("Ng");
break;
case MODshared | MODwild:
buf->writestring("ONg");
break;
default:
assert(0);
}
}
/*********************************
* Name for mod.
*/
void MODtoBuffer(OutBuffer *buf, unsigned char 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(' ');
case MODconst:
buf->writestring(Token::tochars[TOKconst]);
break;
case MODshared | MODwild:
buf->writestring(Token::tochars[TOKshared]);
buf->writeByte(' ');
case MODwild:
buf->writestring(Token::tochars[TOKwild]);
break;
default:
assert(0);
}
}
/********************************
* Name mangling.
* Input:
* flag 0x100 do not do const/invariant
*/
void Type::toDecoBuffer(OutBuffer *buf, int flag, bool mangle) // Possible conflict from merge
{
if (flag != mod && flag != 0x100)
{
MODtoDecoBuffer(buf, mod);
}
buf->writeByte(mangleChar[ty]);
}
/********************************
* For pretty-printing a type.
*/
char *Type::toChars()
{ OutBuffer *buf;
HdrGenState hgs;
buf = new OutBuffer();
toCBuffer(buf, NULL, &hgs);
return buf->toChars();
}
void Type::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs)
{
toCBuffer2(buf, hgs, 0);
if (ident)
{ buf->writeByte(' ');
buf->writestring(ident->toChars());
}
}
void Type::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
buf->writestring(toChars());
}
void Type::toCBuffer3(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{
if (!(mod & MODshared) && (this->mod & MODshared))
{
MODtoBuffer(buf, this->mod & MODshared);
buf->writeByte('(');
}
int m1 = this->mod & ~MODshared;
int m2 = (mod ^ m1) & m1;
if (m2)
{
MODtoBuffer(buf, m2);
buf->writeByte('(');
toCBuffer2(buf, hgs, this->mod);
buf->writeByte(')');
}
else
toCBuffer2(buf, hgs, this->mod);
if (!(mod & MODshared) && (this->mod & MODshared))
{
buf->writeByte(')');
}
}
}
void Type::modToBuffer(OutBuffer *buf)
{
if (mod)
{
buf->writeByte(' ');
MODtoBuffer(buf, mod);
}
}
/************************************
*/
Type *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 && !((TypeAArray *)this)->index->merge()->deco)
return this;
if (nextOf() && !nextOf()->merge()->deco)
return this;
//printf("merge(%s)\n", toChars());
Type *t = this;
assert(t);
if (!deco)
{
OutBuffer buf;
StringValue *sv;
//if (next)
//next = next->merge();
toDecoBuffer(&buf, 0, false);
sv = stringtable.update((char *)buf.data, buf.offset);
if (sv->ptrvalue)
{ t = (Type *) sv->ptrvalue;
#ifdef DEBUG
if (!t->deco)
printf("t = %s\n", t->toChars());
#endif
assert(t->deco);
//printf("old value, deco = '%s' %p\n", t->deco, t->deco);
}
else
{
sv->ptrvalue = this;
#if IN_LLVM
// we still need deco strings to be unique
// or Type::equals fails, which breaks a bunch of stuff,
// like covariant member function overloads.
// TODO: Check if and why this is still needed.
OutBuffer mangle;
toDecoBuffer(&mangle, 0, true);
StringValue* sv2 = deco_stringtable.update((char *)mangle.data, mangle.offset);
if (sv2->ptrvalue)
{ Type* t2 = (Type *) sv2->ptrvalue;
assert(t2->deco);
deco = t2->deco;
}
else
{
sv2->ptrvalue = this;
deco = (char *)sv2->toDchars();
}
#else
deco = (char *)sv->toDchars();
#endif
//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.
*/
Type *Type::merge2()
{
//printf("merge2(%s)\n", toChars());
Type *t = this;
assert(t);
if (!t->deco)
return t->merge();
StringValue *sv = deco_stringtable.lookup((char *)t->deco, strlen(t->deco));
if (sv && sv->ptrvalue)
{ t = (Type *) sv->ptrvalue;
assert(t->deco);
}
else
assert(0);
return t;
}
int Type::isintegral()
{
return FALSE;
}
int Type::isfloating()
{
return FALSE;
}
int Type::isreal()
{
return FALSE;
}
int Type::isimaginary()
{
return FALSE;
}
int Type::iscomplex()
{
return FALSE;
}
int Type::isscalar()
{
return FALSE;
}
int Type::isunsigned()
{
return FALSE;
}
ClassDeclaration *Type::isClassHandle()
{
return NULL;
}
int Type::isscope()
{
return FALSE;
}
int Type::isString()
{
return FALSE;
}
/**************************
* Given:
* T a, b;
* Can we assign:
* a = b;
* ?
*/
int Type::isAssignable(int blit)
{
return TRUE;
}
int Type::checkBoolean()
{
return isscalar();
}
/********************************
* TRUE if when type goes out of scope, it needs a destructor applied.
* Only applies to value types, not ref types.
*/
int Type::needsDestruction()
{
return FALSE;
}
/*********************************
*
*/
bool Type::needsNested()
{
return false;
}
/*********************************
* Check type to see if it is based on a deprecated symbol.
*/
void Type::checkDeprecated(Loc loc, Scope *sc)
{
Dsymbol *s = toDsymbol(sc);
if (s)
s->checkDeprecated(loc, sc);
}
Expression *Type::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
printf("Type::defaultInit() '%s'\n", toChars());
#endif
return NULL;
}
/***************************************
* Use when we prefer the default initializer to be a literal,
* rather than a global immutable variable.
*/
Expression *Type::defaultInitLiteral(Loc loc)
{
#if LOGDEFAULTINIT
printf("Type::defaultInitLiteral() '%s'\n", toChars());
#endif
return defaultInit(loc);
}
int Type::isZeroInit(Loc loc)
{
return 0; // assume not
}
int Type::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 Type::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 == to)
return MATCHexact;
#if IN_LLVM
if (deco == to->deco)
return MATCHexact;
#endif
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 Type::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).
*/
unsigned Type::wildConvTo(Type *tprm)
{
//printf("Type::wildConvTo this = '%s', tprm = '%s'\n", toChars(), tprm->toChars());
if (tprm->isWild() && implicitConvTo(tprm->substWildTo(MODconst)))
{
if (isWild())
return MODwild;
else if (isConst())
return MODconst;
else if (isImmutable())
return MODimmutable;
else if (isMutable())
return MODmutable;
else
assert(0);
}
return 0;
}
Type *Type::substWildTo(unsigned mod)
{
//printf("+Type::substWildTo this = %s, mod = x%x\n", toChars(), mod);
Type *t;
if (nextOf())
{
t = nextOf()->substWildTo(mod);
if (t == nextOf())
t = this;
else
{
if (ty == Tpointer)
t = t->pointerTo();
else if (ty == Tarray)
t = t->arrayOf();
else if (ty == Tsarray)
t = new TypeSArray(t, ((TypeSArray *)this)->dim->syntaxCopy());
else if (ty == Taarray)
{
t = new TypeAArray(t, ((TypeAArray *)this)->index->syntaxCopy());
((TypeAArray *)t)->sc = ((TypeAArray *)this)->sc; // duplicate scope
}
else
assert(0);
t = t->merge();
}
}
else
t = this;
if (isWild())
{
if (mod & MODconst)
t = isShared() ? t->sharedConstOf() : t->constOf();
else if (mod & MODimmutable)
t = t->invariantOf();
else if (mod & MODwild)
t = isShared() ? t->sharedWildOf() : t->wildOf();
else
t = isShared() ? t->sharedOf() : t->mutableOf();
}
//printf("-Type::substWildTo t = %s\n", t->toChars());
return t;
}
/**************************
* Return type with the top level of it being mutable.
*/
Type *Type::toHeadMutable()
{
if (!mod)
return this;
return mutableOf();
}
Expression *Type::getProperty(Loc loc, Identifier *ident)
{ Expression *e;
#if LOGDOTEXP
printf("Type::getProperty(type = '%s', ident = '%s')\n", toChars(), ident->toChars());
#endif
if (ident == Id::__sizeof)
{
e = new IntegerExp(loc, size(loc), Type::tsize_t);
}
else if (ident == Id::__xalignof)
{
e = new IntegerExp(loc, alignsize(), Type::tsize_t);
}
else if (ident == Id::typeinfo)
{
error(loc, ".typeinfo deprecated, use typeid(type)");
e = getTypeInfo(NULL);
}
else if (ident == Id::init)
{
Type *tb = toBasetype();
e = defaultInitLiteral(loc);
if (tb->ty == Tstruct && tb->needsNested())
{
StructLiteralExp *se = (StructLiteralExp *)e;
#if IN_LLVM
se->sinit = (StaticStructInitDeclaration*)
(((VarExp*)defaultInit(loc))->var);
#else
se->sinit = se->sd->toInitializer();
#endif
}
}
else if (ident == Id::mangleof)
{ const char *s;
if (!deco)
{ s = toChars();
error(loc, "forward reference of type %s.mangleof", s);
}
else
s = deco;
e = new StringExp(loc, (char *)s, strlen(s), 'c');
Scope sc;
e = e->semantic(&sc);
}
else if (ident == Id::stringof)
{ char *s = toChars();
e = new StringExp(loc, s, strlen(s), 'c');
Scope sc;
e = e->semantic(&sc);
}
else
{
Dsymbol *s = NULL;
if (ty == Tstruct || ty == Tclass || ty == Tenum || ty == Ttypedef)
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;
}
Expression *Type::dotExp(Scope *sc, Expression *e, Identifier *ident)
{ VarDeclaration *v = NULL;
#if LOGDOTEXP
printf("Type::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
if (e->op == TOKdotvar)
{
DotVarExp *dv = (DotVarExp *)e;
v = dv->var->isVarDeclaration();
}
else if (e->op == TOKvar)
{
VarExp *ve = (VarExp *)e;
v = ve->var->isVarDeclaration();
}
if (v)
{
if (ident == Id::offset)
{
error(e->loc, ".offset deprecated, use .offsetof");
goto Loffset;
}
else if (ident == Id::offsetof)
{
Loffset:
if (v->storage_class & STCfield)
{
e = new IntegerExp(e->loc, v->offset, Type::tsize_t);
return e;
}
}
else if (ident == Id::init)
{
#if IN_LLVM
// LDC_FIXME: Port the below (from 2.061).
if (toBasetype()->ty == Tstruct &&
((TypeStruct *)toBasetype())->sym->isNested())
{
e = defaultInit(e->loc);
}
else
e = defaultInitLiteral(e->loc);
#else
Type *tb = toBasetype();
e = defaultInitLiteral(e->loc);
if (tb->ty == Tstruct && tb->needsNested())
{
StructLiteralExp *se = (StructLiteralExp *)e;
se->sinit = se->sd->toInitializer();
}
#endif
goto Lreturn;
}
}
if (ident == Id::typeinfo)
{
error(e->loc, ".typeinfo deprecated, use typeid(type)");
e = getTypeInfo(sc);
}
else if (ident == Id::stringof)
{ /* Bugzilla 3796: this should demangle e->type->deco rather than
* pretty-printing the type.
*/
char *s = e->toChars();
e = new StringExp(e->loc, s, strlen(s), 'c');
}
else
e = getProperty(e->loc, ident);
Lreturn:
e = e->semantic(sc);
return e;
}
/************************************
* Return alignment to use for this type.
*/
structalign_t Type::alignment()
{
return STRUCTALIGN_DEFAULT;
}
/***************************************
* Figures out what to do with an undefined member reference
* for classes and structs.
*/
Expression *Type::noMember(Scope *sc, Expression *e, Identifier *ident)
{
assert(ty == Tstruct || ty == Tclass);
AggregateDeclaration *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)
{
/* Look for overloaded opDot() to see if we should forward request
* to it.
*/
Dsymbol *fd = search_function(sym, Id::opDot);
if (fd)
{ /* 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.
*/
fd = search_function(sym, Id::opDispatch);
if (fd)
{
/* 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();
}
StringExp *se = new StringExp(e->loc, ident->toChars());
Objects *tiargs = new Objects();
tiargs->push(se);
DotTemplateInstanceExp *dti = new DotTemplateInstanceExp(e->loc, e, Id::opDispatch, tiargs);
dti->ti->tempdecl = td;
return dti->semantic(sc, 1);
}
/* See if we should forward to the alias this.
*/
if (sym->aliasthis)
{ /* Rewrite e.ident as:
* e.aliasthis.ident
*/
e = resolveAliasThis(sc, e);
DotIdExp *die = new DotIdExp(e->loc, e, ident);
return die->semantic(sc, 1);
}
}
return Type::dotExp(sc, e, ident);
}
void Type::error(Loc loc, const char *format, ...)
{
va_list ap;
va_start(ap, format);
::verror(loc, format, ap);
va_end( ap );
}
void Type::warning(Loc loc, const char *format, ...)
{
va_list ap;
va_start(ap, format);
::vwarning(loc, format, ap);
va_end( ap );
}
Identifier *Type::getTypeInfoIdent(int internal)
{
// _init_10TypeInfo_%s
OutBuffer buf;
Identifier *id;
char *name;
size_t len;
if (internal)
{ buf.writeByte(mangleChar[ty]);
if (ty == Tarray)
buf.writeByte(mangleChar[((TypeArray *)this)->next->ty]);
}
else
toDecoBuffer(&buf, 0, true);
len = buf.offset;
name = (char *)alloca(19 + sizeof(len) * 3 + len + 1);
buf.writeByte(0);
#if TARGET_OSX
// The LINKc will prepend the _
sprintf(name, "D%dTypeInfo_%s6__initZ", 9 + len, buf.data);
#else
sprintf(name, "_D%dTypeInfo_%s6__initZ", 9 + len, buf.data);
#endif
#if !IN_LLVM
if (global.params.isWindows && !global.params.is64bit)
name++; // C mangling will add it back in
#endif
//printf("name = %s\n", name);
id = Lexer::idPool(name);
return id;
}
TypeBasic *Type::isTypeBasic()
{
return NULL;
}
void Type::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
//printf("Type::resolve() %s, %d\n", toChars(), ty);
Type *t = semantic(loc, sc);
*pt = t;
*pe = NULL;
*ps = NULL;
}
/*******************************
* tparams == NULL:
* If one of the subtypes of this type is a TypeIdentifier,
* i.e. it's an unresolved type, return that type.
* tparams != NULL:
* Only when the TypeIdentifier is one of template parameters,
* return that type.
*/
Type *Type::reliesOnTident(TemplateParameters *tparams)
{
return NULL;
}
/***************************************
* Return !=0 if the type or any of its subtypes is wild.
*/
int Type::hasWild()
{
return mod & MODwild;
}
/********************************
* We've mistakenly parsed this as a type.
* Redo it as an Expression.
* NULL if cannot.
*/
Expression *Type::toExpression()
{
return NULL;
}
/***************************************
* Return !=0 if type has pointers that need to
* be scanned by the GC during a collection cycle.
*/
int Type::hasPointers()
{
//printf("Type::hasPointers() %s, %d\n", toChars(), ty);
return FALSE;
}
/*************************************
* If this is a type of something, return that something.
*/
Type *Type::nextOf()
{
return NULL;
}
/****************************************
* Return the mask that an integral type will
* fit into.
*/
uinteger_t Type::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 = 0xFFFFUL; break;
case Tdchar:
case Tint32:
case Tuns32: m = 0xFFFFFFFFUL; break;
case Tint64:
case Tuns64: m = 0xFFFFFFFFFFFFFFFFULL; break;
default:
assert(0);
}
return m;
}
/* ============================= TypeError =========================== */
TypeError::TypeError()
: Type(Terror)
{
}
Type *TypeError::syntaxCopy()
{
// No semantic analysis done, no need to copy
return this;
}
void TypeError::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs)
{
buf->writestring("_error_");
}
d_uns64 TypeError::size(Loc loc) { return 1; }
Expression *TypeError::getProperty(Loc loc, Identifier *ident) { return new ErrorExp(); }
Expression *TypeError::dotExp(Scope *sc, Expression *e, Identifier *ident) { return new ErrorExp(); }
Expression *TypeError::defaultInit(Loc loc) { return new ErrorExp(); }
Expression *TypeError::defaultInitLiteral(Loc loc) { return new ErrorExp(); }
/* ============================= TypeNext =========================== */
TypeNext::TypeNext(TY ty, Type *next)
: Type(ty)
{
this->next = next;
}
void TypeNext::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
Type::toDecoBuffer(buf, flag, mangle);
assert(next != this);
//printf("this = %p, ty = %d, next = %p, ty = %d\n", this, this->ty, next, next->ty);
next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod, mangle);
}
void TypeNext::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);
}
Type *TypeNext::reliesOnTident(TemplateParameters *tparams)
{
return next->reliesOnTident(tparams);
}
int TypeNext::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.
*/
Type *TypeNext::nextOf()
{
return next;
}
Type *TypeNext::makeConst()
{
//printf("TypeNext::makeConst() %p, %s\n", this, toChars());
if (cto)
{ assert(cto->mod == MODconst);
return cto;
}
TypeNext *t = (TypeNext *)Type::makeConst();
if (ty != Tfunction && next->ty != Tfunction &&
//(next->deco || next->ty == Tfunction) &&
!next->isImmutable() && !next->isConst())
{ if (next->isShared())
t->next = next->sharedConstOf();
else
t->next = next->constOf();
}
if (ty == Taarray)
{
((TypeAArray *)t)->impl = NULL; // lazily recompute it
}
//printf("TypeNext::makeConst() returns %p, %s\n", t, t->toChars());
return t;
}
Type *TypeNext::makeInvariant()
{
//printf("TypeNext::makeInvariant() %s\n", toChars());
if (ito)
{ assert(ito->isImmutable());
return ito;
}
TypeNext *t = (TypeNext *)Type::makeInvariant();
if (ty != Tfunction && next->ty != Tfunction &&
//(next->deco || next->ty == Tfunction) &&
!next->isImmutable())
{ t->next = next->invariantOf();
}
if (ty == Taarray)
{
((TypeAArray *)t)->impl = NULL; // lazily recompute it
}
return t;
}
Type *TypeNext::makeShared()
{
//printf("TypeNext::makeShared() %s\n", toChars());
if (sto)
{ assert(sto->mod == MODshared);
return sto;
}
TypeNext *t = (TypeNext *)Type::makeShared();
if (ty != Tfunction && next->ty != Tfunction &&
//(next->deco || next->ty == Tfunction) &&
!next->isImmutable() && !next->isShared())
{
if (next->isConst())
t->next = next->sharedConstOf();
else if (next->isWild())
t->next = next->sharedWildOf();
else
t->next = next->sharedOf();
}
if (ty == Taarray)
{
((TypeAArray *)t)->impl = NULL; // lazily recompute it
}
//printf("TypeNext::makeShared() returns %p, %s\n", t, t->toChars());
return t;
}
Type *TypeNext::makeSharedConst()
{
//printf("TypeNext::makeSharedConst() %s\n", toChars());
if (scto)
{ assert(scto->mod == (MODshared | MODconst));
return scto;
}
TypeNext *t = (TypeNext *)Type::makeSharedConst();
if (ty != Tfunction && next->ty != Tfunction &&
//(next->deco || next->ty == Tfunction) &&
!next->isImmutable() && !next->isSharedConst())
{
t->next = next->sharedConstOf();
}
if (ty == Taarray)
{
((TypeAArray *)t)->impl = NULL; // lazily recompute it
}
//printf("TypeNext::makeSharedConst() returns %p, %s\n", t, t->toChars());
return t;
}
Type *TypeNext::makeWild()
{
//printf("TypeNext::makeWild() %s\n", toChars());
if (wto)
{ assert(wto->mod == MODwild);
return wto;
}
TypeNext *t = (TypeNext *)Type::makeWild();
if (ty != Tfunction && next->ty != Tfunction &&
//(next->deco || next->ty == Tfunction) &&
!next->isImmutable() && !next->isConst() && !next->isWild())
{
if (next->isShared())
t->next = next->sharedWildOf();
else
t->next = next->wildOf();
}
if (ty == Taarray)
{
((TypeAArray *)t)->impl = NULL; // lazily recompute it
}
//printf("TypeNext::makeWild() returns %p, %s\n", t, t->toChars());
return t;
}
Type *TypeNext::makeSharedWild()
{
//printf("TypeNext::makeSharedWild() %s\n", toChars());
if (swto)
{ assert(swto->isSharedWild());
return swto;
}
TypeNext *t = (TypeNext *)Type::makeSharedWild();
if (ty != Tfunction && next->ty != Tfunction &&
//(next->deco || next->ty == Tfunction) &&
!next->isImmutable() && !next->isSharedConst())
{
if (next->isConst())
t->next = next->sharedConstOf();
else
t->next = next->sharedWildOf();
}
if (ty == Taarray)
{
((TypeAArray *)t)->impl = NULL; // lazily recompute it
}
//printf("TypeNext::makeSharedWild() returns %p, %s\n", t, t->toChars());
return t;
}
Type *TypeNext::makeMutable()
{
//printf("TypeNext::makeMutable() %p, %s\n", this, toChars());
TypeNext *t = (TypeNext *)Type::makeMutable();
if (ty == Tsarray)
{
t->next = next->mutableOf();
}
if (ty == Taarray)
{
((TypeAArray *)t)->impl = NULL; // lazily recompute it
}
//printf("TypeNext::makeMutable() returns %p, %s\n", t, t->toChars());
return t;
}
MATCH TypeNext::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;
}
unsigned TypeNext::wildConvTo(Type *tprm)
{
if (ty == Tfunction)
return 0;
unsigned mod = 0;
Type *tn = tprm->nextOf();
if (!tn)
return 0;
mod = next->wildConvTo(tn);
if (!mod)
mod = Type::wildConvTo(tprm);
return mod;
}
void TypeNext::transitive()
{
/* Invoke transitivity of type attributes
*/
next = next->addMod(mod);
}
/* ============================= TypeBasic =========================== */
TypeBasic::TypeBasic(TY ty)
: Type(ty)
{ const char *d;
unsigned flags;
#define TFLAGSintegral 1
#define TFLAGSfloating 2
#define TFLAGSunsigned 4
#define TFLAGSreal 8
#define TFLAGSimaginary 0x10
#define TFLAGScomplex 0x20
#define TFLAGSvector 0x40 // valid for a SIMD vector type
flags = 0;
switch (ty)
{
case Tvoid: d = Token::toChars(TOKvoid);
flags |= TFLAGSvector;
break;
case Tint8: d = Token::toChars(TOKint8);
flags |= TFLAGSintegral | TFLAGSvector;
break;
case Tuns8: d = Token::toChars(TOKuns8);
flags |= TFLAGSintegral | TFLAGSunsigned | TFLAGSvector;
break;
case Tint16: d = Token::toChars(TOKint16);
flags |= TFLAGSintegral | TFLAGSvector;
break;
case Tuns16: d = Token::toChars(TOKuns16);
flags |= TFLAGSintegral | TFLAGSunsigned | TFLAGSvector;
break;
case Tint32: d = Token::toChars(TOKint32);
flags |= TFLAGSintegral | TFLAGSvector;
break;
case Tuns32: d = Token::toChars(TOKuns32);
flags |= TFLAGSintegral | TFLAGSunsigned | TFLAGSvector;
break;
case Tfloat32: d = Token::toChars(TOKfloat32);
flags |= TFLAGSfloating | TFLAGSreal | TFLAGSvector;
break;
case Tint64: d = Token::toChars(TOKint64);
flags |= TFLAGSintegral | TFLAGSvector;
break;
case Tuns64: d = Token::toChars(TOKuns64);
flags |= TFLAGSintegral | TFLAGSunsigned | TFLAGSvector;
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 | TFLAGSvector;
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 Tascii: 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();
}
Type *TypeBasic::syntaxCopy()
{
// No semantic analysis done on basic types, no need to copy
return this;
}
char *TypeBasic::toChars()
{
return Type::toChars();
}
void TypeBasic::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
//printf("TypeBasic::toCBuffer2(mod = %d, this->mod = %d)\n", mod, this->mod);
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
buf->writestring(dstring);
}
d_uns64 TypeBasic::size(Loc loc)
{ unsigned 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 = REALSIZE; break;
case Tcomplex32:
size = 8; break;
case Tcomplex64:
case Tint128:
case Tuns128:
size = 16; break;
case Tcomplex80:
size = REALSIZE * 2; break;
case Tvoid:
//size = Type::size(); // error message
size = 1;
break;
case Tbool: size = 1; break;
case Tascii: size = 1; break;
case Twchar: size = 2; break;
case Tdchar: size = 4; break;
default:
assert(0);
break;
}
//printf("TypeBasic::size() = %d\n", size);
return size;
}
unsigned TypeBasic::alignsize()
{
if (ty == Tvoid)
return 1;
return GetTypeAlignment(sir, this);
#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS
case Tint64:
case Tuns64:
sz = global.params.is64bit ? 8 : 4;
break;
case Tfloat64:
case Timaginary64:
sz = global.params.is64bit ? 8 : 4;
break;
case Tcomplex32:
sz = 4;
break;
case Tcomplex64:
sz = global.params.is64bit ? 8 : 4;
break;
#endif
#if IN_DMD
default:
sz = size(0);
break;
}
return sz;
#endif
}
#if IN_LLVM
unsigned TypeBasic::alignment()
{
if (global.params.targetTriple.getArch() == llvm::Triple::x86_64 &&
(ty == Tfloat80 || ty == Timaginary80))
{
return 16;
}
return Type::alignment();
}
#endif
Expression *TypeBasic::getProperty(Loc loc, Identifier *ident)
{
Expression *e;
d_int64 ivalue;
#ifdef IN_GCC
real_t fvalue;
#else
d_float80 fvalue;
#endif
//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 = 0x7FFFUL; goto Livalue;
case Tuns16: ivalue = 0xFFFFUL; goto Livalue;
case Tint32: ivalue = 0x7FFFFFFFUL; goto Livalue;
case Tuns32: ivalue = 0xFFFFFFFFUL; goto Livalue;
case Tint64: ivalue = 0x7FFFFFFFFFFFFFFFLL; goto Livalue;
case Tuns64: ivalue = 0xFFFFFFFFFFFFFFFFULL; goto Livalue;
case Tbool: ivalue = 1; goto Livalue;
case Tchar: ivalue = 0xFF; goto Livalue;
case Twchar: ivalue = 0xFFFFUL; goto Livalue;
case Tdchar: ivalue = 0x10FFFFUL; 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;
}
}
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 = -2147483647L - 1; goto Livalue;
case Tuns32: ivalue = 0; goto Livalue;
case Tint64: ivalue = (-9223372036854775807LL-1LL); 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:
warning(loc, "min property is deprecated, use min_normal instead");
goto Lmin_normal;
}
}
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: fvalue = LDBL_MIN; goto Lfvalue;
}
}
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::nan;
goto Lfvalue;
}
}
}
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::infinity;
goto Lfvalue;
}
}
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: ivalue = LDBL_DIG; goto Lint;
}
}
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: fvalue = LDBL_EPSILON; goto Lfvalue;
}
}
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: ivalue = LDBL_MANT_DIG; goto Lint;
}
}
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: ivalue = LDBL_MAX_10_EXP; goto Lint;
}
}
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: ivalue = LDBL_MAX_EXP; goto Lint;
}
}
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: ivalue = LDBL_MIN_10_EXP; goto Lint;
}
}
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: ivalue = LDBL_MIN_EXP; goto Lint;
}
}
return Type::getProperty(loc, ident);
Livalue:
e = new IntegerExp(loc, ivalue, this);
return e;
Lfvalue:
if (isreal() || isimaginary())
e = new RealExp(loc, fvalue, this);
else
{
complex_t cvalue;
#if __DMC__
//((real_t *)&cvalue)[0] = fvalue;
//((real_t *)&cvalue)[1] = fvalue;
cvalue = fvalue + fvalue * I;
#else
cvalue.re = fvalue;
cvalue.im = fvalue;
#endif
//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;
}
Expression *TypeBasic::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
printf("TypeBasic::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
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);
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);
break;
}
}
else
{
return Type::dotExp(sc, e, ident);
}
e = e->semantic(sc);
return e;
}
Expression *TypeBasic::defaultInit(Loc loc)
{ dinteger_t value = 0;
#if SNAN_DEFAULT_INIT
/*
* Use a payload which is different from the machine NaN,
* so that uninitialised variables can be
* detected even if exceptions are disabled.
*/
union
{ unsigned short us[8];
longdouble ld;
} snan = {{ 0, 0, 0, 0xA000, 0x7FFF }};
/*
* Although long doubles are 10 bytes long, some
* C ABIs pad them out to 12 or even 16 bytes, so
* leave enough space in the snan array.
*/
assert(REALSIZE <= sizeof(snan));
d_float80 fvalue = snan.ld;
#endif
#if LOGDEFAULTINIT
printf("TypeBasic::defaultInit() '%s'\n", toChars());
#endif
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:
#if SNAN_DEFAULT_INIT
return new RealExp(loc, fvalue, this);
#else
return getProperty(loc, Id::nan);
#endif
case Tcomplex32:
case Tcomplex64:
case Tcomplex80:
#if SNAN_DEFAULT_INIT
{ // Can't use fvalue + I*fvalue (the im part becomes a quiet NaN).
complex_t cvalue;
((real_t *)&cvalue)[0] = fvalue;
((real_t *)&cvalue)[1] = fvalue;
return new ComplexExp(loc, cvalue, this);
}
#else
return getProperty(loc, Id::nan);
#endif
case Tvoid:
error(loc, "void does not have a default initializer");
return new ErrorExp();
}
return new IntegerExp(loc, value, this);
}
int TypeBasic::isZeroInit(Loc loc)
{
switch (ty)
{
case Tchar:
case Twchar:
case Tdchar:
case Timaginary32:
case Timaginary64:
case Timaginary80:
case Tfloat32:
case Tfloat64:
case Tfloat80:
case Tcomplex32:
case Tcomplex64:
case Tcomplex80:
return 0; // no
}
return 1; // yes
}
int TypeBasic::isintegral()
{
//printf("TypeBasic::isintegral('%s') x%x\n", toChars(), flags);
return flags & TFLAGSintegral;
}
int TypeBasic::isfloating()
{
return flags & TFLAGSfloating;
}
int TypeBasic::isreal()
{
return flags & TFLAGSreal;
}
int TypeBasic::isimaginary()
{
return flags & TFLAGSimaginary;
}
int TypeBasic::iscomplex()
{
return flags & TFLAGScomplex;
}
int TypeBasic::isunsigned()
{
return flags & TFLAGSunsigned;
}
int TypeBasic::isscalar()
{
return flags & (TFLAGSintegral | TFLAGSfloating);
}
MATCH TypeBasic::implicitConvTo(Type *to)
{
//printf("TypeBasic::implicitConvTo(%s) from %s\n", to->toChars(), toChars());
if (this == to)
return MATCHexact;
#if DMDV2
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;
}
#endif
if (ty == Tvoid || to->ty == Tvoid)
return MATCHnomatch;
if (to->ty == Tbool)
return MATCHnomatch;
TypeBasic *tob;
if (to->ty == Tvector && to->deco)
{
TypeVector *tv = (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 DMDV2
// If converting from integral to integral
if (tob->flags & TFLAGSintegral)
{ d_uns64 sz = size(0);
d_uns64 tosz = tob->size(0);
/* 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;*/
}
#endif
}
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;
}
TypeBasic *TypeBasic::isTypeBasic()
{
return (TypeBasic *)this;
}
/* ============================= TypeVector =========================== */
/* 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]
*/
TypeVector::TypeVector(Loc loc, Type *basetype)
: Type(Tvector)
{
this->basetype = basetype;
}
Type *TypeVector::syntaxCopy()
{
return new TypeVector(0, basetype->syntaxCopy());
}
Type *TypeVector::semantic(Loc loc, Scope *sc)
{
int 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 = (TypeSArray *)basetype;
if (sc && sc->parameterSpecialization && t->dim->op == TOKvar &&
((VarExp *)t->dim)->var->storage_class & STCtemplateparameter)
{
/* It could be a template parameter N which has no value yet:
* template Foo(T : __vector(T[N]), size_t N);
*/
return this;
}
d_uns64 sz = t->size(loc);
if (sz != 16 && sz != 32)
{ error(loc, "base type of __vector must be a 16 or 32 byte static array, not %s", t->toChars());
return terror;
}
TypeBasic *tb = t->nextOf()->isTypeBasic();
if (!tb || !(tb->flags & TFLAGSvector))
{ error(loc, "base type of __vector must be a static array of an arithmetic type, not %s", t->toChars());
return terror;
}
return merge();
}
TypeBasic *TypeVector::elementType()
{
assert(basetype->ty == Tsarray);
TypeSArray *t = (TypeSArray *)basetype;
TypeBasic *tb = t->nextOf()->isTypeBasic();
assert(tb);
return tb;
}
int TypeVector::checkBoolean()
{
return FALSE;
}
char *TypeVector::toChars()
{
return Type::toChars();
}
void TypeVector::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
//printf("TypeVector::toCBuffer2(mod = %d, this->mod = %d)\n", mod, this->mod);
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
buf->writestring("__vector(");
basetype->toCBuffer2(buf, hgs, this->mod);
buf->writestring(")");
}
void TypeVector::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
if (flag != mod && flag != 0x100)
{
MODtoDecoBuffer(buf, mod);
}
buf->writestring("Nh");
basetype->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod, mangle);
}
d_uns64 TypeVector::size(Loc loc)
{
return basetype->size();
}
unsigned TypeVector::alignsize()
{
return (unsigned)basetype->size();
}
Expression *TypeVector::getProperty(Loc loc, Identifier *ident)
{
return basetype->getProperty(loc, ident);
}
Expression *TypeVector::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
printf("TypeVector::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
if (ident == Id::array)
{
e = e->castTo(sc, basetype);
return e;
}
return basetype->dotExp(sc, e->castTo(sc, basetype), ident);
}
Expression *TypeVector::defaultInit(Loc loc)
{
return basetype->defaultInit(loc);
}
int TypeVector::isZeroInit(Loc loc)
{
return basetype->isZeroInit(loc);
}
int TypeVector::isintegral()
{
//printf("TypeVector::isintegral('%s') x%x\n", toChars(), flags);
return basetype->nextOf()->isintegral();
}
int TypeVector::isfloating()
{
return basetype->nextOf()->isfloating();
}
int TypeVector::isunsigned()
{
return basetype->nextOf()->isunsigned();
}
int TypeVector::isscalar()
{
return basetype->nextOf()->isscalar();
}
MATCH TypeVector::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;
}
/***************************** TypeArray *****************************/
TypeArray::TypeArray(TY ty, Type *next)
: TypeNext(ty, next)
{
}
Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
Type *n = this->next->toBasetype(); // uncover any typedef's
#if LOGDOTEXP
printf("TypeArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
if (!n->isMutable())
if (ident == Id::sort || ident == Id::reverse)
error(e->loc, "can only %s a mutable array", ident->toChars());
if (ident == Id::reverse && (n->ty == Tchar || n->ty == Twchar))
{
Expression *ec;
Expressions *arguments;
//LDC: Build arguments.
static FuncDeclaration *adReverseChar_fd = NULL;
if(!adReverseChar_fd) {
Parameters* args = new Parameters;
Type* arrty = Type::tchar->arrayOf();
args->push(new Parameter(STCin, arrty, NULL, NULL));
adReverseChar_fd = FuncDeclaration::genCfunc(args, arrty, "_adReverseChar");
}
static FuncDeclaration *adReverseWchar_fd = NULL;
if(!adReverseWchar_fd) {
Parameters* args = new Parameters;
Type* arrty = Type::twchar->arrayOf();
args->push(new Parameter(STCin, arrty, NULL, NULL));
adReverseWchar_fd = FuncDeclaration::genCfunc(args, arrty, "_adReverseWchar");
}
if(n->ty == Twchar)
ec = new VarExp(0, adReverseWchar_fd);
else
ec = new VarExp(0, adReverseChar_fd);
e = e->castTo(sc, n->arrayOf()); // convert to dynamic array
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))
{
Expression *ec;
Expressions *arguments;
//LDC: Build arguments.
static FuncDeclaration *adSortChar_fd = NULL;
if(!adSortChar_fd) {
Parameters* args = new Parameters;
Type* arrty = Type::tchar->arrayOf();
args->push(new Parameter(STCin, arrty, NULL, NULL));
adSortChar_fd = FuncDeclaration::genCfunc(args, arrty, "_adSortChar");
}
static FuncDeclaration *adSortWchar_fd = NULL;
if(!adSortWchar_fd) {
Parameters* args = new Parameters;
Type* arrty = Type::twchar->arrayOf();
args->push(new Parameter(STCin, arrty, NULL, NULL));
adSortWchar_fd = FuncDeclaration::genCfunc(args, arrty, "_adSortWchar");
}
if(n->ty == Twchar)
ec = new VarExp(0, adSortWchar_fd);
else
ec = new VarExp(0, adSortChar_fd);
e = e->castTo(sc, n->arrayOf()); // convert to dynamic array
arguments = new Expressions();
arguments->push(e);
e = new CallExp(e->loc, ec, arguments);
e->type = next->arrayOf();
}
else if (ident == Id::reverse || ident == Id::dup || ident == Id::idup)
{
Expression *ec;
Expressions *arguments;
int size = next->size(e->loc);
int dup;
Expression *olde = e;
assert(size);
dup = (ident == Id::dup || ident == Id::idup);
//LDC: Build arguments.
static FuncDeclaration *adDup_fd = NULL;
if(!adDup_fd) {
Parameters* args = new Parameters;
args->push(new Parameter(STCin, Type::typeinfo->type, NULL, NULL));
args->push(new Parameter(STCin, Type::tvoid->arrayOf(), NULL, NULL));
adDup_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::adDup);
}
static FuncDeclaration *adReverse_fd = NULL;
if(!adReverse_fd) {
Parameters* args = new Parameters;
args->push(new Parameter(STCin, Type::tvoid->arrayOf(), NULL, NULL));
args->push(new Parameter(STCin, Type::tsize_t, NULL, NULL));
adReverse_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::adReverse);
}
if(dup)
ec = new VarExp(0, adDup_fd);
else
ec = new VarExp(0, adReverse_fd);
e = e->castTo(sc, n->arrayOf()); // convert to dynamic array
arguments = new Expressions();
if (dup)
arguments->push(getTypeInfo(sc));
// LDC repaint array type to void[]
if (n->ty != Tvoid) {
CastExp *exp = new CastExp(e->loc, e, e->type);
exp->type = Type::tvoid->arrayOf();
exp->disableOptimization = true;
e = exp;
}
arguments->push(e);
if (!dup)
arguments->push(new IntegerExp(0, size, Type::tsize_t));
e = new CallExp(e->loc, ec, arguments);
if (ident == Id::idup)
{ Type *einv = next->invariantOf();
if (next->implicitConvTo(einv) < MATCHconst)
error(e->loc, "cannot implicitly convert element type %s to immutable in %s.idup",
next->toChars(), olde->toChars());
e->type = einv->arrayOf();
}
else if (ident == Id::dup)
{
Type *emut = next->mutableOf();
if (next->implicitConvTo(emut) < MATCHconst)
error(e->loc, "cannot implicitly convert element type %s to mutable in %s.dup",
next->toChars(), olde->toChars());
e->type = emut->arrayOf();
}
else
e->type = next->mutableOf()->arrayOf();
}
else if (ident == Id::sort)
{
Expression *ec;
Expressions *arguments;
//LDC: Build arguments.
static FuncDeclaration *adSort_fd = NULL;
if(!adSort_fd) {
Parameters* args = new Parameters;
args->push(new Parameter(STCin, Type::tvoid->arrayOf(), NULL, NULL));
args->push(new Parameter(STCin, Type::typeinfo->type, NULL, NULL));
adSort_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), "_adSort");
}
ec = new VarExp(0, adSort_fd);
e = e->castTo(sc, n->arrayOf()); // convert to dynamic array
arguments = new Expressions();
// LDC repaint array type to void[]
if (n->ty != Tvoid) {
CastExp *exp = new CastExp(e->loc, e, e->type);
exp->type = Type::tvoid->arrayOf();
exp->disableOptimization = true;
e = exp;
}
arguments->push(e);
// LDC, we don't support the getInternalTypeInfo
// optimization arbitrarily, not yet at least...
arguments->push(n->getTypeInfo(sc));
e = new CallExp(e->loc, ec, arguments);
e->type = next->arrayOf();
}
else
{
e = Type::dotExp(sc, e, ident);
}
e = e->semantic(sc);
return e;
}
/***************************** TypeSArray *****************************/
TypeSArray::TypeSArray(Type *t, Expression *dim)
: TypeArray(Tsarray, t)
{
//printf("TypeSArray(%s)\n", dim->toChars());
this->dim = dim;
}
Type *TypeSArray::syntaxCopy()
{
Type *t = next->syntaxCopy();
Expression *e = dim->syntaxCopy();
t = new TypeSArray(t, e);
t->mod = mod;
return t;
}
d_uns64 TypeSArray::size(Loc loc)
{ dinteger_t sz;
if (!dim)
return Type::size(loc);
sz = dim->toInteger();
{ dinteger_t n, n2;
n = next->size();
n2 = n * sz;
if (n && (n2 / n) != sz)
goto Loverflow;
sz = n2;
}
return sz;
Loverflow:
error(loc, "index %lld overflow for static array", sz);
return 1;
}
unsigned TypeSArray::alignsize()
{
return next->alignsize();
}
/**************************
* This evaluates exp while setting length to be the number
* of elements in the tuple t.
*/
Expression *semanticLength(Scope *sc, Type *t, Expression *exp)
{
if (t->ty == Ttuple)
{ ScopeDsymbol *sym = new ArrayScopeSymbol(sc, (TypeTuple *)t);
sym->parent = sc->scopesym;
sc = sc->push(sym);
exp = exp->semantic(sc);
sc->pop();
}
else
exp = exp->semantic(sc);
return exp;
}
Expression *semanticLength(Scope *sc, TupleDeclaration *s, Expression *exp)
{
ScopeDsymbol *sym = new ArrayScopeSymbol(sc, s);
sym->parent = sc->scopesym;
sc = sc->push(sym);
exp = exp->semantic(sc);
sc->pop();
return exp;
}
void TypeSArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
//printf("TypeSArray::resolve() %s\n", toChars());
next->resolve(loc, sc, pe, pt, ps);
//printf("s = %p, e = %p, t = %p\n", *ps, *pe, *pt);
if (*pe)
{ // It's really an index expression
Expressions *exps = new Expressions();
exps->setDim(1);
(*exps)[0] = dim;
Expression *e = new ArrayExp(loc, *pe, exps);
*pe = e;
}
else if (*ps)
{ Dsymbol *s = *ps;
TupleDeclaration *td = s->isTupleDeclaration();
if (td)
{
ScopeDsymbol *sym = new ArrayScopeSymbol(sc, td);
sym->parent = sc->scopesym;
sc = sc->push(sym);
dim = dim->semantic(sc);
dim = dim->ctfeInterpret();
uinteger_t d = dim->toUInteger();
sc = sc->pop();
if (d >= td->objects->dim)
{ error(loc, "tuple index %llu exceeds length %u", d, td->objects->dim);
goto Ldefault;
}
Object *o = (*td->objects)[(size_t)d];
if (o->dyncast() == DYNCAST_DSYMBOL)
{
*ps = (Dsymbol *)o;
return;
}
if (o->dyncast() == DYNCAST_EXPRESSION)
{
Expression *e = (Expression *)o;
if (e->op == TOKdsymbol)
{
*ps = ((DsymbolExp *)e)->s;
*pe = NULL;
}
else
{
*ps = NULL;
*pe = e;
}
return;
}
if (o->dyncast() == DYNCAST_TYPE)
{
*ps = NULL;
*pt = ((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.
*/
Objects *objects = new Objects;
objects->setDim(1);
(*objects)[0] = o;
TupleDeclaration *tds = new TupleDeclaration(loc, td->ident, objects);
*ps = tds;
}
else
goto Ldefault;
}
else
{
Ldefault:
Type::resolve(loc, sc, pe, pt, ps);
}
}
Type *TypeSArray::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 (dim && s && s->isTupleDeclaration())
{ TupleDeclaration *sd = s->isTupleDeclaration();
dim = semanticLength(sc, sd, dim);
dim = dim->ctfeInterpret();
uinteger_t d = dim->toUInteger();
if (d >= sd->objects->dim)
{ error(loc, "tuple index %llu exceeds %u", d, sd->objects->dim);
return Type::terror;
}
Object *o = (*sd->objects)[(size_t)d];
if (o->dyncast() != DYNCAST_TYPE)
{ error(loc, "%s is not a type", toChars());
return Type::terror;
}
t = ((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)
{ dinteger_t n, n2;
int errors = global.errors;
dim = semanticLength(sc, tbn, dim);
if (errors != global.errors)
goto Lerror;
dim = dim->optimize(WANTvalue);
if (sc && sc->parameterSpecialization && dim->op == TOKvar &&
((VarExp *)dim)->var->storage_class & STCtemplateparameter)
{
/* It could be a template parameter N which has no value yet:
* template Foo(T : T[N], size_t N);
*/
return this;
}
dim = dim->ctfeInterpret();
errors = global.errors;
dinteger_t d1 = dim->toInteger();
if (errors != global.errors)
goto Lerror;
dim = dim->implicitCastTo(sc, tsize_t);
dim = dim->optimize(WANTvalue);
errors = global.errors;
dinteger_t d2 = dim->toInteger();
if (errors != global.errors)
goto Lerror;
if (dim->op == TOKerror)
goto Lerror;
if (d1 != d2)
goto Loverflow;
if (tbn->isintegral() ||
tbn->isfloating() ||
tbn->ty == Tpointer ||
tbn->ty == Tarray ||
tbn->ty == Tsarray ||
tbn->ty == Taarray ||
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.
*/
n = tbn->size(loc);
n2 = n * d2;
if ((int)n2 < 0)
goto Loverflow;
if (n2 >= 0x1000000) // put a 'reasonable' limit on it
goto Loverflow;
if (n && n2 / n != d2)
{
Loverflow:
error(loc, "index %lld overflow for static array", d1);
goto Lerror;
}
}
}
switch (tbn->ty)
{
case Ttuple:
{ // Index the tuple to get the type
assert(dim);
TypeTuple *tt = (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;
}
Parameter *arg = (*tt->arguments)[(size_t)d];
return arg->type->addMod(this->mod);
}
case Tstruct:
{ TypeStruct *ts = (TypeStruct *)tbn;
if (0 && ts->sym->isnested)
{ error(loc, "cannot have static array of inner struct %s", ts->toChars());
goto Lerror;
}
break;
}
case Tfunction:
case Tnone:
error(loc, "can't have array of %s", tbn->toChars());
goto Lerror;
}
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;
}
void TypeSArray::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
Type::toDecoBuffer(buf, flag, mangle);
if (dim)
buf->printf("%llu", dim->toInteger());
if (next)
/* Note that static arrays are value types, so
* for a parameter, propagate the 0x100 to the next
* level, since for T[4][3], any const should apply to the T,
* not the [4].
*/
next->toDecoBuffer(buf, (flag & 0x100) ? flag : mod, mangle);
}
void TypeSArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
next->toCBuffer2(buf, hgs, this->mod);
buf->printf("[%s]", dim->toChars());
}
Expression *TypeSArray::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
printf("TypeSArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
if (ident == Id::length)
{
e = dim;
}
else if (ident == Id::ptr)
{
e = e->castTo(sc, next->pointerTo());
}
else
{
e = TypeArray::dotExp(sc, e, ident);
}
e = e->semantic(sc);
return e;
}
structalign_t TypeSArray::alignment()
{
return next->alignment();
}
int TypeSArray::isString()
{
TY nty = next->toBasetype()->ty;
return nty == Tchar || nty == Twchar || nty == Tdchar;
}
MATCH TypeSArray::constConv(Type *to)
{
if (to->ty == Tsarray)
{
TypeSArray *tsa = (TypeSArray *)to;
if (!dim->equals(tsa->dim))
return MATCHnomatch;
}
return TypeNext::constConv(to);
}
MATCH TypeSArray::implicitConvTo(Type *to)
{
//printf("TypeSArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars());
// Allow implicit conversion of static array to pointer or dynamic array
if (IMPLICIT_ARRAY_TO_PTR && to->ty == Tpointer)
{
TypePointer *tp = (TypePointer *)to;
if (!MODimplicitConv(next->mod, tp->next->mod))
return MATCHnomatch;
if (tp->next->ty == Tvoid || next->constConv(tp->next) != MATCHnomatch)
{
return MATCHconvert;
}
return MATCHnomatch;
}
if (to->ty == Tarray)
{
TypeDArray *ta = (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 = (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;
}
Expression *TypeSArray::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeSArray::defaultInit() '%s'\n", toChars());
#endif
return next->defaultInit(loc);
}
int TypeSArray::isZeroInit(Loc loc)
{
return next->isZeroInit(loc);
}
int TypeSArray::needsDestruction()
{
return next->needsDestruction();
}
/*********************************
*
*/
bool TypeSArray::needsNested()
{
return next->needsNested();
}
Expression *TypeSArray::defaultInitLiteral(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeSArray::defaultInitLiteral() '%s'\n", toChars());
#endif
size_t d = dim->toInteger();
Expression *elementinit = next->defaultInitLiteral(loc);
Expressions *elements = new Expressions();
elements->setDim(d);
for (size_t i = 0; i < d; i++)
(*elements)[i] = elementinit;
ArrayLiteralExp *ae = new ArrayLiteralExp(0, elements);
ae->type = this;
return ae;
}
Expression *TypeSArray::toExpression()
{
Expression *e = next->toExpression();
if (e)
{ Expressions *arguments = new Expressions();
arguments->push(dim);
e = new ArrayExp(dim->loc, e, arguments);
}
return e;
}
int TypeSArray::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();
}
/***************************** TypeDArray *****************************/
TypeDArray::TypeDArray(Type *t)
: TypeArray(Tarray, t)
{
//printf("TypeDArray(t = %p)\n", t);
}
Type *TypeDArray::syntaxCopy()
{
Type *t = next->syntaxCopy();
if (t == next)
t = this;
else
{ t = new TypeDArray(t);
t->mod = mod;
}
return t;
}
d_uns64 TypeDArray::size(Loc loc)
{
//printf("TypeDArray::size()\n");
return PTRSIZE * 2;
}
unsigned TypeDArray::alignsize()
{
// A DArray consists of two ptr-sized values, so align it on pointer size
// boundary
return PTRSIZE;
}
Type *TypeDArray::semantic(Loc loc, Scope *sc)
{
Type *tn = next->semantic(loc,sc);
Type *tbn = tn->toBasetype();
switch (tbn->ty)
{
case Tfunction:
case Tnone:
case Ttuple:
error(loc, "can't have array of %s", tbn->toChars());
case Terror:
return Type::terror;
case Tstruct:
{ TypeStruct *ts = (TypeStruct *)tbn;
if (0 && ts->sym->isnested)
error(loc, "cannot have dynamic array of inner struct %s", ts->toChars());
break;
}
}
if (tn->isscope())
error(loc, "cannot have array of scope %s", tn->toChars());
next = tn;
transitive();
return merge();
}
void TypeDArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
//printf("TypeDArray::resolve() %s\n", toChars());
next->resolve(loc, sc, pe, pt, ps);
//printf("s = %p, e = %p, t = %p\n", *ps, *pe, *pt);
if (*pe)
{ // It's really a slice expression
Expression *e = new SliceExp(loc, *pe, NULL, NULL);
*pe = e;
}
else if (*ps)
{
TupleDeclaration *td = (*ps)->isTupleDeclaration();
if (td)
; // keep *ps
else
goto Ldefault;
}
else
{
Ldefault:
Type::resolve(loc, sc, pe, pt, ps);
}
}
void TypeDArray::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
Type::toDecoBuffer(buf, flag, mangle);
if (next)
next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod, mangle);
}
void TypeDArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
if (equals(tstring))
buf->writestring("string");
else
{ next->toCBuffer2(buf, hgs, this->mod);
buf->writestring("[]");
}
}
Expression *TypeDArray::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
printf("TypeDArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
if (ident == Id::length)
{
if (e->op == TOKstring)
{ StringExp *se = (StringExp *)e;
return new IntegerExp(se->loc, se->len, Type::tindex);
}
if (e->op == TOKnull)
return new IntegerExp(e->loc, 0, Type::tindex);
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);
}
return e;
}
int TypeDArray::isString()
{
TY nty = next->toBasetype()->ty;
return nty == Tchar || nty == Twchar || nty == Tdchar;
}
MATCH TypeDArray::implicitConvTo(Type *to)
{
//printf("TypeDArray::implicitConvTo(to = %s) this = %s\n", to->toChars(), toChars());
if (equals(to))
return MATCHexact;
// Allow implicit conversion of array to pointer
if (IMPLICIT_ARRAY_TO_PTR && to->ty == Tpointer)
{
TypePointer *tp = (TypePointer *)to;
/* Allow conversion to void*
*/
if (tp->next->ty == Tvoid &&
MODimplicitConv(next->mod, tp->next->mod))
{
return MATCHconvert;
}
return next->constConv(to);
}
if (to->ty == Tarray)
{
TypeDArray *ta = (TypeDArray *)to;
if (!MODimplicitConv(next->mod, ta->next->mod))
return MATCHnomatch; // not const-compatible
// Check head inout conversion:
// T [] -> inout(const(T)[])
// const(T)[] -> inout(const(T)[])
if (isMutable() && ta->isWild())
if ((next->isMutable() || next->isConst()) && ta->next->isConst())
return MATCHnomatch;
/* 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);
}
Expression *TypeDArray::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeDArray::defaultInit() '%s'\n", toChars());
#endif
return new NullExp(loc, this);
}
int TypeDArray::isZeroInit(Loc loc)
{
return 1;
}
int TypeDArray::checkBoolean()
{
return TRUE;
}
int TypeDArray::hasPointers()
{
return TRUE;
}
/***************************** TypeAArray *****************************/
TypeAArray::TypeAArray(Type *t, Type *index)
: TypeArray(Taarray, t)
{
this->index = index;
this->impl = NULL;
this->loc = 0;
this->sc = NULL;
}
Type *TypeAArray::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;
}
d_uns64 TypeAArray::size(Loc loc)
{
return PTRSIZE /* * 2*/;
}
Type *TypeAArray::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)
{
Expression *e;
Type *t;
Dsymbol *s;
index->resolve(loc, sc, &e, &t, &s);
if (e)
{ // It was an expression -
// Rewrite as a static array
TypeSArray *tsa;
tsa = new TypeSArray(next, e);
return tsa->semantic(loc,sc);
}
else if (t)
index = t;
else
{ index->error(loc, "index is not a type or an expression");
return Type::terror;
}
}
else
index = index->semantic(loc,sc);
if (index->nextOf() && !index->nextOf()->isImmutable())
{
index = index->constOf()->mutableOf();
#if 0
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);
}
#endif
}
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());
case Terror:
return Type::terror;
}
next = next->semantic(loc,sc);
transitive();
switch (next->toBasetype()->ty)
{
case Tfunction:
case Tvoid:
case Tnone:
error(loc, "can't have associative array of %s", next->toChars());
case Terror:
return Type::terror;
}
if (next->isscope())
{ error(loc, "cannot have array of scope %s", next->toChars());
return Type::terror;
}
return merge();
}
StructDeclaration *TypeAArray::getImpl()
{
// Do it lazily
if (!impl)
{
Type *index = this->index;
Type *next = this->next;
if (index->reliesOnTident() || next->reliesOnTident())
{
error(loc, "cannot create associative array %s", toChars());
index = terror;
next = terror;
// Head off future failures
StructDeclaration *s = new StructDeclaration(0, NULL);
s->type = terror;
impl = s;
return impl;
}
/* This is really a proxy for the template instance AssocArray!(index, next)
* But the instantiation can fail if it is a template specialization field
* which has Tident's instead of real types.
*/
Objects *tiargs = new Objects();
tiargs->push(index->substWildTo(MODconst)); // hack for bug7757
tiargs->push(next ->substWildTo(MODconst)); // hack for bug7757
// Create AssociativeArray!(index, next)
#if 1
if (! Type::associativearray)
{
ObjectNotFound(Id::AssociativeArray);
}
TemplateInstance *ti = new TemplateInstance(loc, Type::associativearray, tiargs);
#else
//Expression *e = new IdentifierExp(loc, Id::object);
Expression *e = new IdentifierExp(loc, Id::empty);
//e = new DotIdExp(loc, e, Id::object);
DotTemplateInstanceExp *dti = new DotTemplateInstanceExp(loc,
e,
Id::AssociativeArray,
tiargs);
dti->semantic(sc);
TemplateInstance *ti = dti->ti;
#endif
ti->semantic(sc);
ti->semantic2(sc);
ti->semantic3(sc);
impl = ti->toAlias()->isStructDeclaration();
#ifdef DEBUG
if (!impl)
{ Dsymbol *s = ti->toAlias();
printf("%s %s\n", s->kind(), s->toChars());
}
#endif
assert(impl);
}
return impl;
}
void TypeAArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
//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);
if (e)
{ // It was an expression -
// Rewrite as a static array
TypeSArray *tsa = new TypeSArray(next, e);
return tsa->addMod(this->mod)->resolve(loc, sc, pe, pt, ps);
}
else if (t)
index = t;
else
index->error(loc, "index is not a type or an expression");
}
Type::resolve(loc, sc, pe, pt, ps);
}
Expression *TypeAArray::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
printf("TypeAArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
#if 0
if (ident == Id::length)
{
Expression *ec;
Expressions *arguments;
//LDC: Build arguments.
static FuncDeclaration *aaLen_fd = NULL;
if(!aaLen_fd) {
Arguments* args = new Arguments;
args->push(new Argument(STCin, Type::tvoid->pointerTo(), NULL, NULL));
aaLen_fd = FuncDeclaration::genCfunc(args, Type::tsize_t, Id::aaLen);
}
ec = new VarExp(0, aaLen_fd);
arguments = new Expressions();
arguments->push(e);
e = new CallExp(e->loc, ec, arguments);
e->type = aaLen_fd->type->nextOf();
}
else
if (ident == Id::keys)
{
Expression *ec;
Expressions *arguments;
int size = index->size(e->loc);
assert(size);
//LDC: Build arguments.
static FuncDeclaration *aaKeys_fd = NULL;
if(!aaKeys_fd) {
Arguments* args = new Arguments;
args->push(new Argument(STCin, Type::tvoid->pointerTo(), NULL, NULL));
args->push(new Argument(STCin, Type::tsize_t, NULL, NULL));
aaKeys_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::aaKeys);
}
ec = new VarExp(0, aaKeys_fd);
arguments = new Expressions();
arguments->push(e);
arguments->push(new IntegerExp(0, size, Type::tsize_t));
e = new CallExp(e->loc, ec, arguments);
e->type = index->arrayOf();
}
else if (ident == Id::values)
{
Expression *ec;
Expressions *arguments;
//LDC: Build arguments.
static FuncDeclaration *aaValues_fd = NULL;
if(!aaValues_fd) {
Arguments* args = new Arguments;
args->push(new Argument(STCin, Type::tvoid->pointerTo(), NULL, NULL));
args->push(new Argument(STCin, Type::tsize_t, NULL, NULL));
args->push(new Argument(STCin, Type::tsize_t, NULL, NULL));
aaValues_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::aaValues);
}
ec = new VarExp(0, aaValues_fd);
arguments = new Expressions();
arguments->push(e);
size_t keysize = index->size(e->loc);
keysize = (keysize + PTRSIZE - 1) & ~(PTRSIZE - 1);
arguments->push(new IntegerExp(0, keysize, Type::tsize_t));
arguments->push(new IntegerExp(0, next->size(e->loc), Type::tsize_t));
e = new CallExp(e->loc, ec, arguments);
e->type = next->arrayOf();
}
else if (ident == Id::rehash)
{
Expression *ec;
Expressions *arguments;
//LDC: Build arguments.
static FuncDeclaration *aaRehash_fd = NULL;
if(!aaRehash_fd) {
Arguments* args = new Arguments;
args->push(new Argument(STCin, Type::tvoid->pointerTo(), NULL, NULL));
args->push(new Argument(STCin, Type::typeinfo->type, NULL, NULL));
aaRehash_fd = FuncDeclaration::genCfunc(args, Type::tvoidptr, Id::aaRehash);
}
ec = new VarExp(0, aaRehash_fd);
arguments = new Expressions();
arguments->push(e->addressOf(sc));
arguments->push(index->getInternalTypeInfo(sc)); // LDC doesn't support getInternalTypeInfo, see above
e = new CallExp(e->loc, ec, arguments);
e->type = this;
}
else
#endif
if (ident != Id::__sizeof &&
ident != Id::__xalignof &&
ident != Id::init &&
ident != Id::mangleof &&
ident != Id::stringof &&
ident != Id::offsetof)
{
//printf("test1: %s, %s\n", e->toChars(), e->type->toChars());
Type *t = getImpl()->type;
//printf("test2: %s, %s\n", e->toChars(), e->type->toChars());
e->type = t;
e = t->dotExp(sc, e, ident);
//printf("test3: %s, %s\n", e->toChars(), e->type->toChars());
}
else
e = Type::dotExp(sc, e, ident);
return e;
}
void TypeAArray::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
Type::toDecoBuffer(buf, flag, mangle);
index->toDecoBuffer(buf, 0, mangle);
next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod, mangle);
}
void TypeAArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
next->toCBuffer2(buf, hgs, this->mod);
buf->writeByte('[');
index->toCBuffer2(buf, hgs, 0);
buf->writeByte(']');
}
Expression *TypeAArray::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeAArray::defaultInit() '%s'\n", toChars());
#endif
return new NullExp(loc, this);
}
int TypeAArray::isZeroInit(Loc loc)
{
return TRUE;
}
int TypeAArray::checkBoolean()
{
return TRUE;
}
Expression *TypeAArray::toExpression()
{
Expression *e = next->toExpression();
if (e)
{
Expression *ei = index->toExpression();
if (ei)
{
Expressions *arguments = new Expressions();
arguments->push(ei);
return new ArrayExp(loc, e, arguments);
}
}
return NULL;
}
int TypeAArray::hasPointers()
{
return TRUE;
}
MATCH TypeAArray::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 = (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
// Check head inout conversion:
// V [K] -> inout(const(V)[K])
// const(V)[K] -> inout(const(V)[K])
if (isMutable() && ta->isWild())
if ((next->isMutable() || next->isConst()) && ta->next->isConst())
return MATCHnomatch;
MATCH m = next->constConv(ta->next);
MATCH mi = index->constConv(ta->index);
if (m != MATCHnomatch && mi != MATCHnomatch)
{
return MODimplicitConv(mod, to->mod) ? MATCHconst : MATCHnomatch;
}
}
else if (to->ty == Tstruct && ((TypeStruct *)to)->sym->ident == Id::AssociativeArray)
{
int errs = global.startGagging();
Type *from = getImpl()->type;
if (global.endGagging(errs))
{
return MATCHnomatch;
}
return from->implicitConvTo(to);
}
return Type::implicitConvTo(to);
}
MATCH TypeAArray::constConv(Type *to)
{
if (to->ty == Taarray)
{
TypeAArray *taa = (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);
}
Type *TypeAArray::reliesOnTident(TemplateParameters *tparams)
{
Type *t = TypeNext::reliesOnTident(tparams);
if (!t)
t = index->reliesOnTident(tparams);
return t;
}
/***************************** TypePointer *****************************/
TypePointer::TypePointer(Type *t)
: TypeNext(Tpointer, t)
{
}
Type *TypePointer::syntaxCopy()
{
Type *t = next->syntaxCopy();
if (t == next)
t = this;
else
{ t = new TypePointer(t);
t->mod = mod;
}
return t;
}
Type *TypePointer::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());
case Terror:
return Type::terror;
}
if (n != next)
{
deco = NULL;
}
next = n;
if (next->ty != Tfunction)
{ transitive();
return merge();
}
#if 1
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;
#endif
}
d_uns64 TypePointer::size(Loc loc)
{
return PTRSIZE;
}
void TypePointer::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
//printf("TypePointer::toCBuffer2() next = %d\n", next->ty);
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
next->toCBuffer2(buf, hgs, this->mod);
if (next->ty != Tfunction)
buf->writeByte('*');
}
MATCH TypePointer::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 = (TypePointer*)to;
if (tp->next->ty == Tfunction)
{
if (next->equals(tp->next))
return MATCHconst;
if (next->covariant(tp->next) == 1)
return MATCHconvert;
}
else if (tp->next->ty == Tvoid)
{
// Allow conversions to void*
return MATCHconvert;
}
}
return MATCHnomatch;
}
else if (to->ty == Tpointer)
{
TypePointer *tp = (TypePointer *)to;
assert(tp->next);
if (!MODimplicitConv(next->mod, tp->next->mod))
return MATCHnomatch; // not const-compatible
// Check head inout conversion:
// T * -> inout(const(T)*)
// const(T)* -> inout(const(T)*)
if (isMutable() && tp->isWild())
if ((next->isMutable() || next->isConst()) && tp->next->isConst())
return MATCHnomatch;
/* 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;
}
MATCH TypePointer::constConv(Type *to)
{
if (next->ty == Tfunction)
{
if (to->nextOf() && next->equals(((TypeNext*)to)->next))
return Type::constConv(to);
else
return MATCHnomatch;
}
return TypeNext::constConv(to);
}
int TypePointer::isscalar()
{
return TRUE;
}
Expression *TypePointer::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypePointer::defaultInit() '%s'\n", toChars());
#endif
return new NullExp(loc, this);
}
int TypePointer::isZeroInit(Loc loc)
{
return 1;
}
int TypePointer::hasPointers()
{
return TRUE;
}
/***************************** TypeReference *****************************/
TypeReference::TypeReference(Type *t)
: TypeNext(Treference, t)
{
// BUG: what about references to static arrays?
}
Type *TypeReference::syntaxCopy()
{
Type *t = next->syntaxCopy();
if (t == next)
t = this;
else
{ t = new TypeReference(t);
t->mod = mod;
}
return t;
}
Type *TypeReference::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();
}
d_uns64 TypeReference::size(Loc loc)
{
return PTRSIZE;
}
void TypeReference::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
next->toCBuffer2(buf, hgs, this->mod);
buf->writeByte('&');
}
Expression *TypeReference::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
printf("TypeReference::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
// References just forward things along
return next->dotExp(sc, e, ident);
}
Expression *TypeReference::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeReference::defaultInit() '%s'\n", toChars());
#endif
return new NullExp(loc, this);
}
int TypeReference::isZeroInit(Loc loc)
{
return 1;
}
/***************************** TypeFunction *****************************/
TypeFunction::TypeFunction(Parameters *parameters, Type *treturn, int varargs, enum LINK linkage, StorageClass stc)
: TypeNext(Tfunction, treturn)
{
//if (!treturn) *(char*)0=0;
// assert(treturn);
assert(0 <= varargs && varargs <= 2);
this->parameters = parameters;
this->varargs = varargs;
this->linkage = linkage;
this->inuse = 0;
this->isnothrow = false;
this->purity = PUREimpure;
this->isproperty = false;
this->isref = false;
this->iswild = false;
this->fargs = NULL;
#if IN_LLVM
this->funcdecl = NULL;
#endif
if (stc & STCpure)
this->purity = PUREfwdref;
if (stc & STCnothrow)
this->isnothrow = true;
if (stc & STCproperty)
this->isproperty = true;
if (stc & STCref)
this->isref = true;
this->trust = TRUSTdefault;
if (stc & STCsafe)
this->trust = TRUSTsafe;
if (stc & STCsystem)
this->trust = TRUSTsystem;
if (stc & STCtrusted)
this->trust = TRUSTtrusted;
}
Type *TypeFunction::syntaxCopy()
{
Type *treturn = next ? next->syntaxCopy() : NULL;
Parameters *params = Parameter::arraySyntaxCopy(parameters);
TypeFunction *t = new TypeFunction(params, treturn, varargs, linkage);
t->mod = mod;
t->isnothrow = isnothrow;
t->purity = purity;
t->isproperty = isproperty;
t->isref = isref;
t->trust = trust;
t->fargs = fargs;
return t;
}
/*******************************
* 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
*/
int Type::covariant(Type *t, StorageClass *pstc)
{
#if 0
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);
#endif
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 = (TypeFunction *)this;
t2 = (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 *arg1 = Parameter::getNth(t1->parameters, i);
Parameter *arg2 = Parameter::getNth(t2->parameters, i);
if (!arg1->type->equals(arg2->type))
{
#if 0 // turn on this for contravariant argument types, see bugzilla 3075
// BUG: cannot convert ref to const to ref to immutable
// We can add const, but not subtract it
if (arg2->type->implicitConvTo(arg1->type) < MATCHconst)
#endif
goto Ldistinct;
}
const StorageClass sc = STCref | STCin | STCout | STClazy;
if ((arg1->storageClass & sc) != (arg2->storageClass & sc))
inoutmismatch = 1;
// We can add scope, but not subtract it
if (!(arg1->storageClass & STCscope) && (arg2->storageClass & STCscope))
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 (((TypeClass *)t1n)->sym == ((TypeClass *)t2n)->sym &&
MODimplicitConv(t1n->mod, t2n->mod))
goto Lcovariant;
// If t1n is forward referenced:
ClassDeclaration *cd = ((TypeClass *)t1n)->sym;
// if (cd->scope)
// cd->semantic(NULL);
#if 0
if (!cd->baseClass && cd->baseclasses->dim && !cd->isInterfaceDeclaration())
#else
if (!cd->isBaseInfoComplete())
#endif
{
return 3; // forward references
}
}
if (t1n->ty == Tstruct && t2n->ty == Tstruct)
{
if (((TypeStruct *)t1n)->sym == ((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))
goto Lcovariant;
}
goto Lnotcovariant;
Lcovariant:
if (t1->isref != t2->isref)
goto Lnotcovariant;
/* Can convert mutable to const
*/
if (!MODimplicitConv(t2->mod, t1->mod))
{
// If adding 'const' will make it covariant
if (MODimplicitConv(t2->mod, MODmerge(t1->mod, MODconst)))
stc |= STCconst;
else
goto Lnotcovariant;
}
/* Can convert pure to impure, and nothrow to throw
*/
if (!t1->purity && t2->purity)
stc |= STCpure;
if (!t1->isnothrow && t2->isnothrow)
stc |= STCnothrow;
/* 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;
}
void TypeFunction::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{ unsigned char mc;
//printf("TypeFunction::toDecoBuffer() this = %p %s\n", this, toChars());
//static int nest; if (++nest == 50) *(char*)0=0;
if (inuse)
{ inuse = 2; // flag error to caller
return;
}
inuse++;
MODtoDecoBuffer(buf, mod);
switch (linkage)
{
case LINKd: mc = 'F'; break;
case LINKc: mc = 'U'; break;
case LINKwindows: mc = 'W'; break;
case LINKpascal: mc = 'V'; break;
case LINKcpp: mc = 'R'; break;
// LDC
case LINKintrinsic: mc = 'Q'; break;
default:
assert(0);
}
buf->writeByte(mc);
if (purity || isnothrow || isproperty || isref || trust)
{
if (purity)
buf->writestring("Na");
if (isnothrow)
buf->writestring("Nb");
if (isref)
buf->writestring("Nc");
if (isproperty)
buf->writestring("Nd");
switch (trust)
{
case TRUSTtrusted:
buf->writestring("Ne");
break;
case TRUSTsafe:
buf->writestring("Nf");
break;
default: break;
}
}
#if IN_LLVM
// if we're not producing a mangle string, add the this
// type to prevent merging different member function
if (!mangle && funcdecl)
{
if (funcdecl->needThis())
{
AggregateDeclaration* ad = funcdecl->isMember2();
buf->writeByte('M');
ad->type->toDecoBuffer(buf, 0, false);
}
if (FuncLiteralDeclaration *literal = funcdecl->isFuncLiteralDeclaration()) {
// Never merge types of function literals of different kind
if (literal->tok == TOKdelegate) {
buf->writeByte('D');
} else if (literal->tok == TOKfunction) {
buf->writeByte('F');
} else if (literal->tok == TOKreserved) {
static int counter = 0;
buf->writeByte('L');
// And never merge types of lambdas, because we don't know whether
// they need a nested context argument or not.
buf->printf("%i", counter++);
}
}
/* BUG This causes problems with delegate types
On the other hand, the llvm type for nested functions *is* different
so not doing anything here may be lead to bugs!
A sane solution would be DtoType(Dsymbol)...
if (funcdecl->isNested())
{
buf->writeByte('M');
if (funcdecl->toParent2() && funcdecl->toParent2()->isFuncDeclaration())
{
FuncDeclaration* fd = funcdecl->toParent2()->isFuncDeclaration();
fd->type->toDecoBuffer(buf, 0, false);
}
}*/
}
#endif
// Write argument types
Parameter::argsToDecoBuffer(buf, parameters, mangle);
//if (buf->data[buf->offset - 1] == '@') halt();
buf->writeByte('Z' - varargs); // mark end of arg list
assert(next);
next->toDecoBuffer(buf, 0, mangle);
inuse--;
}
void TypeFunction::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs)
{
toCBufferWithAttributes(buf, ident, hgs, this, NULL);
}
void TypeFunction::toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TypeFunction *attrs, TemplateDeclaration *td)
{
//printf("TypeFunction::toCBuffer() this = %p\n", this);
if (inuse)
{ inuse = 2; // flag error to caller
return;
}
inuse++;
/* Use 'storage class' style for attributes
*/
if (attrs->mod)
{
MODtoBuffer(buf, attrs->mod);
buf->writeByte(' ');
}
if (attrs->purity)
buf->writestring("pure ");
if (attrs->isnothrow)
buf->writestring("nothrow ");
if (attrs->isproperty)
buf->writestring("@property ");
if (attrs->isref)
buf->writestring("ref ");
switch (attrs->trust)
{
case TRUSTsystem:
buf->writestring("@system ");
break;
case TRUSTtrusted:
buf->writestring("@trusted ");
break;
case TRUSTsafe:
buf->writestring("@safe ");
break;
default: break;
}
if (hgs->ddoc != 1)
{
const char *p = NULL;
switch (attrs->linkage)
{
case LINKd: p = NULL; break;
case LINKc: p = "C"; break;
case LINKwindows: p = "Windows"; break;
case LINKpascal: p = "Pascal"; break;
case LINKcpp: p = "C++"; break;
// LDC
case LINKintrinsic: p = "Intrinsic"; break;
default:
assert(0);
}
if (!hgs->hdrgen && p)
{
buf->writestring("extern (");
buf->writestring(p);
buf->writestring(") ");
}
}
if (!ident || ident->toHChars2() == ident->toChars())
{ if (next)
next->toCBuffer2(buf, hgs, 0);
else if (hgs->ddoc)
buf->writestring("auto");
}
if (ident)
{
if (next || hgs->ddoc)
buf->writeByte(' ');
buf->writestring(ident->toHChars2());
}
if (td)
{ buf->writeByte('(');
for (size_t i = 0; i < td->origParameters->dim; i++)
{
TemplateParameter *tp = (*td->origParameters)[i];
if (i)
buf->writestring(", ");
tp->toCBuffer(buf, hgs);
}
buf->writeByte(')');
}
Parameter::argsToCBuffer(buf, hgs, parameters, varargs);
inuse--;
}
// kind is inserted before the argument list and will usually be "function" or "delegate".
void functionToCBuffer2(TypeFunction *t, OutBuffer *buf, HdrGenState *hgs, int mod, const char *kind)
{
if (hgs->ddoc != 1)
{
const char *p = NULL;
switch (t->linkage)
{
case LINKd: p = NULL; break;
case LINKc: p = "C"; break;
case LINKwindows: p = "Windows"; break;
case LINKpascal: p = "Pascal"; break;
case LINKcpp: p = "C++"; break;
// LDC
case LINKintrinsic: p = "Intrinsic"; break;
default:
assert(0);
}
if (!hgs->hdrgen && p)
{
buf->writestring("extern (");
buf->writestring(p);
buf->writestring(") ");
}
}
if (t->next)
{
t->next->toCBuffer2(buf, hgs, 0);
buf->writeByte(' ');
}
buf->writestring(kind);
Parameter::argsToCBuffer(buf, hgs, t->parameters, t->varargs);
t->attributesToCBuffer(buf, mod);
}
void TypeFunction::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
//printf("TypeFunction::toCBuffer2() this = %p, ref = %d\n", this, isref);
if (inuse)
{ inuse = 2; // flag error to caller
return;
}
inuse++;
functionToCBuffer2(this, buf, hgs, mod, "function");
inuse--;
}
void TypeFunction::attributesToCBuffer(OutBuffer *buf, int mod)
{
/* Use postfix style for attributes
*/
if (mod != this->mod)
{
modToBuffer(buf);
}
if (purity)
buf->writestring(" pure");
if (isnothrow)
buf->writestring(" nothrow");
if (isproperty)
buf->writestring(" @property");
if (isref)
buf->writestring(" ref");
switch (trust)
{
case TRUSTsystem:
buf->writestring(" @system");
break;
case TRUSTtrusted:
buf->writestring(" @trusted");
break;
case TRUSTsafe:
buf->writestring(" @safe");
break;
default: break;
}
}
Type *TypeFunction::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);
/* 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 = (TypeFunction *)mem.malloc(sizeof(TypeFunction));
memcpy(tf, this, sizeof(TypeFunction));
if (parameters)
{ tf->parameters = (Parameters *)parameters->copy();
for (size_t i = 0; i < parameters->dim; i++)
{ Parameter *arg = (*parameters)[i];
Parameter *cpy = (Parameter *)mem.malloc(sizeof(Parameter));
memcpy(cpy, arg, sizeof(Parameter));
(*tf->parameters)[i] = cpy;
}
}
if (sc->stc & STCpure)
tf->purity = PUREfwdref;
if (sc->stc & STCnothrow)
tf->isnothrow = TRUE;
if (sc->stc & STCref)
tf->isref = 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;
/* 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();
#if !SARRAYVALUE
if (tf->next->toBasetype()->ty == Tsarray)
{ error(loc, "functions cannot return static array %s", tf->next->toChars());
tf->next = Type::terror;
}
#endif
if (tf->next->toBasetype()->ty == Tfunction)
{ error(loc, "functions cannot return a function");
tf->next = Type::terror;
}
if (tf->next->toBasetype()->ty == Ttuple)
{ error(loc, "functions cannot return a tuple");
tf->next = Type::terror;
}
if (tf->next->isscope() && !(sc->flags & SCOPEctor))
error(loc, "functions cannot return scope %s", tf->next->toChars());
if (tf->next->toBasetype()->ty == Tvoid)
tf->isref = FALSE; // rewrite "ref void" as just "void"
if (tf->next->hasWild() &&
!(tf->next->ty == Tpointer && tf->next->nextOf()->ty == Tfunction || tf->next->ty == Tdelegate))
wildreturn = TRUE;
}
bool wildparams = FALSE;
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 = 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--;
fparam->type = fparam->type->addStorageClass(fparam->storageClass);
if (fparam->storageClass & (STCauto | STCalias | STCstatic))
{
if (!fparam->type)
continue;
}
// Possible merge conflict
Type *t = fparam->type->toBasetype();
if (fparam->storageClass & (STCout | STCref | STClazy))
{
//if (t->ty == Tsarray)
//error(loc, "cannot have out or ref parameter of type %s", t->toChars());
if (fparam->storageClass & STCout && fparam->type->mod & (STCconst | STCimmutable))
error(loc, "cannot have const or immutable out parameter of type %s", t->toChars());
}
if (!(fparam->storageClass & STClazy) && t->ty == Tvoid)
error(loc, "cannot have parameter of type %s", fparam->type->toChars());
if (t->hasWild() &&
!(t->ty == Tpointer && t->nextOf()->ty == Tfunction || t->ty == Tdelegate))
{
wildparams = TRUE;
//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;
e = e->inferType(fparam->type);
e = e->semantic(argsc);
e = resolveProperties(argsc, e);
if (e->op == TOKfunction) // see Bugzilla 4820
{ FuncExp *fe = (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, 0);
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 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 = (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;
Parameters *newparams = new Parameters();
newparams->setDim(tdim);
for (size_t j = 0; j < tdim; j++)
{ Parameter *narg = (*tt->arguments)[j];
(*newparams)[j] = new Parameter(narg->storageClass | fparam->storageClass,
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)
{ Expression *farg = (*fargs)[i];
if (farg->isLvalue())
; // ref parameter
else
fparam->storageClass &= ~STCref; // value parameter
}
else
error(loc, "auto can only be used for template function parameters");
}
// Remove redundant storage classes for type, they are already applied
fparam->storageClass &= ~(STC_TYPECTOR | STCin);
}
argsc->pop();
}
if (tf->isWild())
wildparams = TRUE;
if (wildreturn && !wildparams)
error(loc, "inout on return means inout must be on a parameter as well for %s", toChars());
tf->iswild = wildparams;
if (tf->next)
tf->deco = tf->merge()->deco;
if (tf->inuse)
{ error(loc, "recursive type");
tf->inuse = 0;
return terror;
}
if (tf->isproperty && (tf->varargs || Parameter::dim(tf->parameters) > 2))
error(loc, "properties can only have zero, one, or two parameter");
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");
/* Don't return merge(), because arg identifiers and default args
* can be different
* even though the types match
*/
return tf;
}
Type *getIndirection(Type *t)
{
t = t->toBasetype();
if (t->ty == Tsarray)
{ while (t->ty == Tsarray)
t = t->nextOf()->toBasetype();
}
if (t->ty == Tarray || t->ty == Tpointer)
return t->nextOf()->toBasetype();
if (t->ty == Taarray || t->ty == Tclass)
return t;
if (t->ty == Tstruct)
return t->hasPointers() ? t : NULL; // TODO
// should consider TypeDelegate?
return NULL;
}
/********************************************
* Do this lazily, as the parameter types might be forward referenced.
*/
void TypeFunction::purityLevel()
{
//printf("purityLevel(%s)\n", toChars());
TypeFunction *tf = this;
if (tf->purity == PUREfwdref && tf->next)
{ /* Evaluate what kind of purity based on the modifiers for the parameters
*/
enum PURE purity = PUREstrong; // assume strong until something weakens it
size_t dim = Parameter::dim(tf->parameters);
if (dim)
{
Type *tret = tf->next;
assert(tret);
Type *treti = tf->isref ? tret->toBasetype() : getIndirection(tret);
if (treti && (treti->mod & MODimmutable))
treti = NULL; // indirection is immutable
//printf(" tret = %s, treti = %s\n", tret->toChars(), treti ? treti->toChars() : "NULL");
for (size_t i = 0; i < dim; i++)
{ Parameter *fparam = Parameter::getNth(tf->parameters, i);
if (fparam->storageClass & STClazy)
{
purity = PUREweak;
break;
}
if (fparam->storageClass & STCout)
{
purity = PUREweak;
break;
}
if (!fparam->type)
continue;
Type *tprm = fparam->type;
Type *tprmi = fparam->storageClass & STCref ? tprm->toBasetype() : getIndirection(tprm);
//printf(" [%d] tprm = %s, tprmi = %s\n", i, tprm->toChars(), tprmi ? tprmi->toChars() : "NULL");
if (!tprmi || (tprmi->mod & MODimmutable))
continue; // there is no mutable indirection
if (tprmi->isMutable())
{ purity = PUREweak; // indirection is mutable
break;
}
if (!treti)
continue; // mutable indirection is never returned
if (purity < PUREstrong)
continue;
// Determine the parameter is really PUREconst or not
assert(tprmi->mod & (MODconst | MODwild));
if (tprmi->constConv(treti)) // simple case
purity = PUREconst;
else if (tprmi->invariantOf()->equals(treti->invariantOf()))
continue;
else
{
/* The rest of this is little strict; fix later.
* For example:
*
* struct S { immutable* p; }
* pure S foo(const int* p);
*
* which would maintain strong purity.
*/
if (tprmi->hasPointers() || treti->hasPointers())
purity = PUREconst;
}
/* Should catch delegates and function pointers, and fold in their purity
*/
}
}
//printf(" --> purity: %d\n", purity);
tf->purity = purity;
}
}
/********************************************
* FIXME: This function is a workaround for fixing Bugzilla 9210.
* In 2.061, TypeFunction::purityLevel() improved to make more functions
* strong purity, but immutable conversion on return statemet had broken by that.
* Because, it is essentially unrelated to PUREstrong. This function is
* necessary to check the convertibility.
*/
bool TypeFunction::hasMutableIndirectionParams()
{
TypeFunction *tf = this;
size_t dim = Parameter::dim(tf->parameters);
for (size_t i = 0; i < dim; i++)
{
Parameter *fparam = Parameter::getNth(tf->parameters, i);
if (fparam->storageClass & STClazy)
{
return true;
}
if (fparam->storageClass & STCout)
{
return true;
}
if (!fparam->type)
continue;
if (fparam->storageClass & STCref)
{
if (!(fparam->type->mod & (MODconst | MODimmutable | MODwild)))
return true;
if (fparam->type->mod & MODconst)
return true;
}
Type *t = fparam->type->toBasetype();
if (!t->hasPointers())
continue;
if (t->mod & (MODimmutable | MODwild))
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)
return true;
Type *tn = t->nextOf();
if (tn)
{ tn = tn->toBasetype();
if (tn->ty == Tpointer || tn->ty == Tarray)
{ /* Accept immutable(T)* and immutable(T)[] as being strongly pure
*/
if (tn->mod & (MODimmutable | MODwild))
continue;
if (tn->mod & MODconst)
return true;
}
}
/* Should catch delegates and function pointers, and fold in their purity
*/
return true;
}
return false;
}
/********************************
* 'args' are being matched to function 'this'
* Determine match level.
* Input:
* flag 1 performing a partial ordering match
* Returns:
* MATCHxxxx
*/
MATCH TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag)
{
//printf("TypeFunction::callMatch() %s\n", toChars());
MATCH match = MATCHexact; // assume exact match
unsigned wildmatch = 0;
if (ethis)
{ Type *t = ethis->type;
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);
if (!(p->storageClass & STClazy && p->type->ty == Tvoid && arg->type->ty != Tvoid))
{
unsigned mod = arg->type->wildConvTo(p->type);
if (mod)
{
wildmatch |= mod;
}
}
}
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;
// BUG: what about out and ref?
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);
if (arg->op == TOKfunction)
{
arg = ((FuncExp *)arg)->inferType(p->type, 1);
if (!arg)
goto L1; // try typesafe variadics
}
//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)
{ if (m && !arg->isLvalue())
{
Type *ta = targ->aliasthisOf();
if (arg->op == TOKstring && tprm->ty == Tsarray)
{ if (targ->ty != Tsarray)
targ = new TypeSArray(targ->nextOf(),
new IntegerExp(0, ((StringExp *)arg)->len,
Type::tindex));
}
else if (ta && ta->implicitConvTo(tprm))
{
goto Nomatch;
}
else if (arg->op == TOKstructliteral)
{
match = MATCHconvert;
}
else if (arg->op == TOKcall)
{
CallExp *ce = (CallExp *)arg;
if (ce->e1->op == TOKdotvar &&
((DotVarExp *)ce->e1)->var->isCtorDeclaration())
{
match = MATCHconvert;
}
else
goto Nomatch;
}
else
goto Nomatch;
}
Type *targb = targ->toBasetype();
Type *tprmb = tprm->toBasetype();
//printf("%s\n", targb->toChars());
//printf("%s\n", tprmb->toChars());
/* find most derived alias this type being matched.
*/
while (1)
{
Type *tat = targb->aliasthisOf();
if (!tat || !tat->implicitConvTo(tprm))
break;
targb = tat;
}
/* Don't allow static arrays to be passed to mutable references
* to static arrays if the argument cannot be modified.
*/
if (targb->nextOf() && tprmb->ty == Tsarray &&
!MODimplicitConv(targb->nextOf()->mod, tprmb->nextOf()->mod))
goto Nomatch;
// ref variable behaves like head-const reference
if (!targb->constConv(tprmb))
goto Nomatch;
}
else if (p->storageClass & STCout)
{ if (m && !arg->isLvalue())
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 = (TypeSArray *)tb;
sz = tsa->dim->toInteger();
if (sz != nargs - u)
goto Nomatch;
case Tarray:
{ TypeArray *ta = (TypeArray *)tb;
for (; u < nargs; u++)
{
Expression *arg = (*args)[u];
assert(arg);
if (arg->op == TOKfunction)
{
arg = ((FuncExp *)arg)->inferType(tb->nextOf(), 1);
if (!arg)
goto Nomatch;
}
/* 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;
}
Type *TypeFunction::reliesOnTident(TemplateParameters *tparams)
{
size_t dim = Parameter::dim(parameters);
for (size_t i = 0; i < dim; i++)
{ Parameter *fparam = Parameter::getNth(parameters, i);
Type *t = fparam->type->reliesOnTident(tparams);
if (t)
return t;
}
return next ? next->reliesOnTident(tparams) : NULL;
}
/********************************************
* Return TRUE if there are lazy parameters.
*/
bool TypeFunction::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 TypeFunction::parameterEscapes(Parameter *p)
{
/* Scope parameters do not escape.
* Allow 'lazy' to imply 'scope' -
* lazy parameters can be passed along
* as lazy parameters to the next function, but that isn't
* escaping.
*/
if (p->storageClass & (STCscope | STClazy))
return FALSE;
/* If haven't inferred the return type yet, assume it escapes
*/
if (!nextOf())
return TRUE;
if (purity)
{ /* 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;
}
Expression *TypeFunction::defaultInit(Loc loc)
{
error(loc, "function does not have a default initializer");
return new ErrorExp();
}
Type *TypeFunction::addStorageClass(StorageClass stc)
{
TypeFunction *t = (TypeFunction *)Type::addStorageClass(stc);
if ((stc & STCpure && !t->purity) ||
(stc & STCnothrow && !t->isnothrow) ||
(stc & STCsafe && t->trust < TRUSTtrusted))
{
// Klunky to change these
TypeFunction *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->isproperty = t->isproperty;
tf->isref = t->isref;
tf->trust = t->trust;
if (stc & STCpure)
tf->purity = PUREfwdref;
if (stc & STCnothrow)
tf->isnothrow = true;
if (stc & STCsafe)
tf->trust = TRUSTsafe;
#if IN_LLVM
tf->funcdecl = t->funcdecl;
#endif
tf->deco = tf->merge()->deco;
t = tf;
}
return t;
}
/***************************** TypeDelegate *****************************/
TypeDelegate::TypeDelegate(Type *t)
: TypeNext(Tfunction, t)
{
ty = Tdelegate;
}
Type *TypeDelegate::syntaxCopy()
{
Type *t = next->syntaxCopy();
if (t == next)
t = this;
else
{ t = new TypeDelegate(t);
t->mod = mod;
}
return t;
}
Type *TypeDelegate::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);
/* In order to deal with Bugzilla 4028, perhaps default arguments should
* be removed from next before the merge.
*/
#if 1
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;
#endif
}
d_uns64 TypeDelegate::size(Loc loc)
{
return PTRSIZE * 2;
}
// LDC added, no reason to align to 2*PTRSIZE
unsigned TypeDelegate::alignsize()
{
// A Delegate consists of two ptr values, so align it on pointer size
// boundary
return PTRSIZE;
}
MATCH TypeDelegate::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;
#if 1 // not allowing covariant conversions because it interferes with overriding
if (to->ty == Tdelegate && this->nextOf()->covariant(to->nextOf()) == 1)
return MATCHconvert;
#endif
return MATCHnomatch;
}
void TypeDelegate::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
functionToCBuffer2((TypeFunction *)next, buf, hgs, mod, "delegate");
}
Expression *TypeDelegate::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeDelegate::defaultInit() '%s'\n", toChars());
#endif
return new NullExp(loc, this);
}
int TypeDelegate::isZeroInit(Loc loc)
{
return 1;
}
int TypeDelegate::checkBoolean()
{
return TRUE;
}
Expression *TypeDelegate::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
printf("TypeDelegate::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
if (ident == Id::ptr)
{
e = new GEPExp(e->loc, e, ident, 0);
e->type = tvoidptr;
return e;
}
else if (ident == Id::funcptr)
{
e = new GEPExp(e->loc, e, ident, 1);
e->type = next->pointerTo();
return e;
}
else
{
e = Type::dotExp(sc, e, ident);
}
return e;
}
int TypeDelegate::hasPointers()
{
return TRUE;
}
/***************************** TypeQualified *****************************/
TypeQualified::TypeQualified(TY ty, Loc loc)
: Type(ty)
{
this->loc = loc;
}
void TypeQualified::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++)
{
Identifier *id = t->idents[i];
if (id->dyncast() == DYNCAST_DSYMBOL)
{
TemplateInstance *ti = (TemplateInstance *)id;
ti = (TemplateInstance *)ti->syntaxCopy(NULL);
id = (Identifier *)ti;
}
idents[i] = id;
}
}
void TypeQualified::addIdent(Identifier *ident)
{
idents.push(ident);
}
void TypeQualified::toCBuffer2Helper(OutBuffer *buf, HdrGenState *hgs)
{
for (size_t i = 0; i < idents.dim; i++)
{ Identifier *id = idents[i];
buf->writeByte('.');
if (id->dyncast() == DYNCAST_DSYMBOL)
{
TemplateInstance *ti = (TemplateInstance *)id;
ti->toCBuffer(buf, hgs);
}
else
buf->writestring(id->toChars());
}
}
d_uns64 TypeQualified::size(Loc loc)
{
error(this->loc, "size of type %s is not known", toChars());
return 1;
}
/*************************************
* 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
*/
void TypeQualified::resolveHelper(Loc loc, Scope *sc,
Dsymbol *s, Dsymbol *scopesym,
Expression **pe, Type **pt, Dsymbol **ps)
{
VarDeclaration *v;
EnumMember *em;
Expression *e;
#if 0
printf("TypeQualified::resolveHelper(sc = %p, idents = '%s')\n", sc, toChars());
if (scopesym)
printf("\tscopesym = '%s'\n", scopesym->toChars());
#endif
*pe = NULL;
*pt = NULL;
*ps = NULL;
if (s)
{
//printf("\t1: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind());
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++)
{
Identifier *id = idents[i];
Dsymbol *sm = s->searchX(loc, sc, id);
//printf("\t3: s = '%s' %p, kind = '%s'\n",s->toChars(), s, s->kind());
//printf("\tgetType = '%s'\n", s->getType()->toChars());
if (!sm)
{ Type *t;
v = s->isVarDeclaration();
if (v && id == Id::length)
{
e = new VarExp(loc, v);
t = e->type;
if (!t)
goto Lerror;
goto L3;
}
else if (v && (id == Id::stringof || id == Id::offsetof))
{
e = new DsymbolExp(loc, s, 0);
do
{
id = idents[i];
e = new DotIdExp(loc, e, id);
} while (++i < idents.dim);
e = e->semantic(sc);
*pe = e;
return;
}
t = s->getType();
if (!t && s->isDeclaration())
{ t = s->isDeclaration()->type;
if (!t && s->isTupleDeclaration())
{
e = new TupleExp(loc, s->isTupleDeclaration());
e = e->semantic(sc);
t = e->type;
}
}
if (t)
{
sm = t->toDsymbol(sc);
if (sm)
{ sm = sm->search(loc, id, 0);
if (sm)
goto L2;
}
//e = t->getProperty(loc, id);
e = new TypeExp(loc, t);
e = t->dotExp(sc, e, id);
i++;
L3:
for (; i < idents.dim; i++)
{
id = idents[i];
//printf("e: '%s', id: '%s', type = %s\n", e->toChars(), id->toChars(), e->type->toChars());
e = new DotIdExp(e->loc, e, id);
e = e->semantic(sc);
}
if (e->op == TOKtype)
*pt = e->type;
else
*pe = e;
}
else
{
Lerror:
if (id->dyncast() == DYNCAST_DSYMBOL)
{ // searchX already handles errors for template instances
assert(global.errors);
}
else
{
sm = s->search_correct(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();
}
v = s->isVarDeclaration();
if (v)
{
*pe = new VarExp(loc, v);
return;
}
#if 0
fd = s->isFuncDeclaration();
if (fd)
{
*pe = new DsymbolExp(loc, fd, 1);
return;
}
#endif
em = s->isEnumMember();
if (em)
{
// It's not a type, it's an expression
*pe = em->value->copy();
return;
}
L1:
Type *t = s->getType();
if (!t)
{
// If the symbol is an import, try looking inside the import
Import *si;
si = s->isImport();
if (si)
{
s = si->search(loc, s->ident, 0);
if (s && s != si)
goto L1;
s = si;
}
*ps = s;
return;
}
if (t->ty == Tinstance && t != this && !t->deco)
{ error(loc, "forward reference to '%s'", t->toChars());
return;
}
if (t != this)
{
if (t->reliesOnTident())
{
if (s->scope)
t = t->semantic(loc, s->scope);
else
{
/* Attempt to find correct scope in which to evaluate t.
* Not sure if this is right or not, or if we should just
* give forward reference error if s->scope is not set.
*/
for (Scope *scx = sc; 1; scx = scx->enclosing)
{
if (!scx)
{ error(loc, "forward reference to '%s'", t->toChars());
return;
}
if (scx->scopesym == scopesym)
{
t = t->semantic(loc, scx);
break;
}
}
}
}
}
if (t->ty == Ttuple)
*pt = t;
else
*pt = t->merge();
}
if (!s)
{
const char *p = toChars();
const char *n = importHint(p);
if (n)
error(loc, "'%s' is not defined, perhaps you need to import %s; ?", p, n);
else
{
Identifier *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;
}
}
/***************************** TypeIdentifier *****************************/
TypeIdentifier::TypeIdentifier(Loc loc, Identifier *ident)
: TypeQualified(Tident, loc)
{
this->ident = ident;
}
Type *TypeIdentifier::syntaxCopy()
{
TypeIdentifier *t;
t = new TypeIdentifier(loc, ident);
t->syntaxCopyHelper(this);
t->mod = mod;
return t;
}
void TypeIdentifier::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
Type::toDecoBuffer(buf, flag, mangle);
const char *name = ident->toChars();
size_t len = strlen(name);
buf->printf("%u%s", (unsigned)len, name);
}
void TypeIdentifier::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
buf->writestring(this->ident->toChars());
toCBuffer2Helper(buf, hgs);
}
/*************************************
* 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
*/
void TypeIdentifier::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
Dsymbol *scopesym;
//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;
}
}
}
Dsymbol *s = sc->search(loc, ident, &scopesym);
resolveHelper(loc, sc, s, scopesym, pe, pt, ps);
if (*pt)
(*pt) = (*pt)->addMod(mod);
}
/*****************************************
* See if type resolves to a symbol, if so,
* return that symbol.
*/
Dsymbol *TypeIdentifier::toDsymbol(Scope *sc)
{
//printf("TypeIdentifier::toDsymbol('%s')\n", toChars());
if (!sc)
return NULL;
//printf("ident = '%s'\n", ident->toChars());
Dsymbol *scopesym;
Dsymbol *s = sc->search(loc, ident, &scopesym);
if (s)
{
for (size_t i = 0; i < idents.dim; i++)
{
Identifier *id = idents[i];
s = s->searchX(loc, sc, id);
if (!s) // failed to find a symbol
{ //printf("\tdidn't find a symbol\n");
break;
}
}
}
return s;
}
Type *TypeIdentifier::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);
if (t->ty == Ttypedef)
{ TypeTypedef *tt = (TypeTypedef *)t;
if (tt->sym->sem == SemanticIn)
error(loc, "circular reference of typedef %s", tt->toChars());
}
t = t->addMod(mod);
}
else
{
if (s)
{
s->error(loc, "is used as a type");
//halt();
}
else
error(loc, "%s is used as a type", toChars());
t = terror;
}
//t->print();
return t;
}
Type *TypeIdentifier::reliesOnTident(TemplateParameters *tparams)
{
if (tparams)
{
if (idents.dim == 0)
{
for (size_t i = 0; i < tparams->dim; i++)
{ TemplateParameter *tp = (*tparams)[i];
if (tp->ident->equals(ident))
return this;
}
}
return NULL;
}
else
return this;
}
Expression *TypeIdentifier::toExpression()
{
Expression *e = new IdentifierExp(loc, ident);
for (size_t i = 0; i < idents.dim; i++)
{
Identifier *id = idents[i];
e = new DotIdExp(loc, e, id);
}
return e;
}
/***************************** TypeInstance *****************************/
TypeInstance::TypeInstance(Loc loc, TemplateInstance *tempinst)
: TypeQualified(Tinstance, loc)
{
this->tempinst = tempinst;
}
Type *TypeInstance::syntaxCopy()
{
//printf("TypeInstance::syntaxCopy() %s, %d\n", toChars(), idents.dim);
TypeInstance *t;
t = new TypeInstance(loc, (TemplateInstance *)tempinst->syntaxCopy(NULL));
t->syntaxCopyHelper(this);
t->mod = mod;
return t;
}
void TypeInstance::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
tempinst->toCBuffer(buf, hgs);
toCBuffer2Helper(buf, hgs);
}
void TypeInstance::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
// Note close similarity to TypeIdentifier::resolve()
Dsymbol *s;
*pe = NULL;
*pt = NULL;
*ps = NULL;
#if 0
if (!idents.dim)
{
error(loc, "template instance '%s' has no identifier", toChars());
return;
}
#endif
//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);
}
resolveHelper(loc, sc, s, NULL, pe, pt, ps);
if (*pt)
*pt = (*pt)->addMod(mod);
//printf("pt = '%s'\n", (*pt)->toChars());
}
Type *TypeInstance::semantic(Loc loc, Scope *sc)
{
Type *t;
Expression *e;
Dsymbol *s;
//printf("TypeInstance::semantic(%p, %s)\n", this, toChars());
if (sc->parameterSpecialization)
{
unsigned errors = global.startGagging();
resolve(loc, sc, &e, &t, &s);
if (global.endGagging(errors))
{
return this;
}
}
else
resolve(loc, sc, &e, &t, &s);
if (!t)
{
error(loc, "%s is used as a type", toChars());
t = terror;
}
return t;
}
Dsymbol *TypeInstance::toDsymbol(Scope *sc)
{
Type *t;
Expression *e;
Dsymbol *s;
//printf("TypeInstance::semantic(%s)\n", toChars());
if (sc->parameterSpecialization)
{
unsigned errors = global.startGagging();
resolve(loc, sc, &e, &t, &s);
if (global.endGagging(errors))
return NULL;
}
else
resolve(loc, sc, &e, &t, &s);
return s;
}
Type *TypeInstance::reliesOnTident(TemplateParameters *tparams)
{
if (tparams)
{
for (size_t i = 0; i < tparams->dim; i++)
{
TemplateParameter *tp = (*tparams)[i];
if (tempinst->name == tp->ident)
return this;
}
if (!tempinst->tiargs)
return NULL;
for (size_t i = 0; i < tempinst->tiargs->dim; i++)
{
Type *t = isType((*tempinst->tiargs)[i]);
t = t ? t->reliesOnTident(tparams) : NULL;
if (t)
return t;
}
return NULL;
}
else
{
return Type::reliesOnTident(tparams);
}
}
/***************************** TypeTypeof *****************************/
TypeTypeof::TypeTypeof(Loc loc, Expression *exp)
: TypeQualified(Ttypeof, loc)
{
this->exp = exp;
inuse = 0;
}
Type *TypeTypeof::syntaxCopy()
{
//printf("TypeTypeof::syntaxCopy() %s\n", toChars());
TypeTypeof *t;
t = new TypeTypeof(loc, exp->syntaxCopy());
t->syntaxCopyHelper(this);
t->mod = mod;
return t;
}
Dsymbol *TypeTypeof::toDsymbol(Scope *sc)
{
Type *t;
t = semantic(loc, sc);
if (t == this)
return NULL;
return t->toDsymbol(sc);
}
void TypeTypeof::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
buf->writestring("typeof(");
exp->toCBuffer(buf, hgs);
buf->writeByte(')');
toCBuffer2Helper(buf, hgs);
}
Type *TypeTypeof::semantic(Loc loc, Scope *sc)
{
Type *t;
//printf("TypeTypeof::semantic() %s\n", toChars());
//static int nest; if (++nest == 50) *(char*)0=0;
if (inuse)
{
inuse = 2;
error(loc, "circular typeof definition");
return Type::terror;
}
inuse++;
#if 0
/* Special case for typeof(this) and typeof(super) since both
* should work even if they are not inside a non-static member function
*/
if (exp->op == TOKthis || exp->op == TOKsuper)
{
// Find enclosing struct or class
for (Dsymbol *s = sc->parent; 1; s = s->parent)
{
ClassDeclaration *cd;
StructDeclaration *sd;
if (!s)
{
error(loc, "%s is not in a struct or class scope", exp->toChars());
goto Lerr;
}
cd = s->isClassDeclaration();
if (cd)
{
if (exp->op == TOKsuper)
{
cd = cd->baseClass;
if (!cd)
{ error(loc, "class %s has no 'super'", s->toChars());
goto Lerr;
}
}
t = cd->type;
break;
}
sd = s->isStructDeclaration();
if (sd)
{
if (exp->op == TOKsuper)
{
error(loc, "struct %s has no 'super'", sd->toChars());
goto Lerr;
}
t = sd->type->pointerTo();
break;
}
}
}
else
#endif
{
Scope *sc2 = sc->push();
sc2->intypeof++;
sc2->speculative = true;
sc2->ignoreTemplates++;
sc2->flags |= sc->flags & SCOPEstaticif;
unsigned oldspecgag = global.speculativeGag;
if (global.gag)
global.speculativeGag = global.gag;
exp = exp->semantic(sc2);
global.speculativeGag = oldspecgag;
#if DMDV2
if (exp->type && exp->type->ty == Tfunction &&
((TypeFunction *)exp->type)->isproperty)
exp = resolveProperties(sc2, exp);
#endif
sc2->pop();
if (exp->op == TOKtype)
{
error(loc, "argument %s to typeof is not an expression", exp->toChars());
goto Lerr;
}
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;
}
t = t->addMod(mod);
/* typeof should reflect the true type,
* not what 'auto' would have gotten us.
*/
//t = t->toHeadMutable();
}
if (idents.dim)
{
Dsymbol *s = t->toDsymbol(sc);
for (size_t i = 0; i < idents.dim; i++)
{
if (!s)
break;
Identifier *id = idents[i];
s = s->searchX(loc, sc, id);
}
if (s)
{
t = s->getType();
if (!t)
{ error(loc, "%s is not a type", s->toChars());
goto Lerr;
}
}
else
{ error(loc, "cannot resolve .property for %s", toChars());
goto Lerr;
}
}
inuse--;
return t;
Lerr:
inuse--;
return terror;
}
d_uns64 TypeTypeof::size(Loc loc)
{
if (exp->type)
return exp->type->size(loc);
else
return TypeQualified::size(loc);
}
/***************************** TypeReturn *****************************/
TypeReturn::TypeReturn(Loc loc)
: TypeQualified(Treturn, loc)
{
}
Type *TypeReturn::syntaxCopy()
{
TypeReturn *t = new TypeReturn(loc);
t->syntaxCopyHelper(this);
t->mod = mod;
return t;
}
Dsymbol *TypeReturn::toDsymbol(Scope *sc)
{
Type *t = semantic(0, sc);
if (t == this)
return NULL;
return t->toDsymbol(sc);
}
Type *TypeReturn::semantic(Loc loc, Scope *sc)
{
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;
}
t = t->addMod(mod);
if (idents.dim)
{
Dsymbol *s = t->toDsymbol(sc);
for (size_t i = 0; i < idents.dim; i++)
{
if (!s)
break;
Identifier *id = idents[i];
s = s->searchX(loc, sc, id);
}
if (s)
{
t = s->getType();
if (!t)
{ error(loc, "%s is not a type", s->toChars());
goto Lerr;
}
}
else
{ error(loc, "cannot resolve .property for %s", toChars());
goto Lerr;
}
}
return t;
Lerr:
return terror;
}
void TypeReturn::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
buf->writestring("typeof(return)");
toCBuffer2Helper(buf, hgs);
}
/***************************** TypeEnum *****************************/
TypeEnum::TypeEnum(EnumDeclaration *sym)
: Type(Tenum)
{
this->sym = sym;
}
char *TypeEnum::toChars()
{
if (mod)
return Type::toChars();
return sym->toChars();
}
Type *TypeEnum::syntaxCopy()
{
return this;
}
Type *TypeEnum::semantic(Loc loc, Scope *sc)
{
//printf("TypeEnum::semantic() %s\n", toChars());
//sym->semantic(sc);
return merge();
}
d_uns64 TypeEnum::size(Loc loc)
{
if (!sym->memtype)
{
error(loc, "enum %s is forward referenced", sym->toChars());
return 4;
}
return sym->memtype->size(loc);
}
unsigned TypeEnum::alignsize()
{
if (!sym->memtype)
{
error(0, "enum %s is forward referenced", sym->toChars());
return 4;
}
return sym->memtype->alignsize();
}
Dsymbol *TypeEnum::toDsymbol(Scope *sc)
{
return sym;
}
Type *TypeEnum::toBasetype()
{
if (sym->scope)
{ // Enum is forward referenced. We don't need to resolve the whole thing,
// just the base type
if (sym->memtype)
{ sym->memtype = sym->memtype->semantic(sym->loc, sym->scope);
}
else
{ if (!sym->isAnonymous())
sym->memtype = Type::tint32;
}
}
if (!sym->memtype)
{
error(sym->loc, "enum %s is forward referenced", sym->toChars());
return tint32;
}
return sym->memtype->toBasetype();
}
void TypeEnum::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
const char *name = sym->mangle();
Type::toDecoBuffer(buf, flag, mangle);
buf->printf("%s", name);
}
void TypeEnum::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
buf->writestring(sym->toChars());
}
Expression *TypeEnum::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
printf("TypeEnum::dotExp(e = '%s', ident = '%s') '%s'\n", e->toChars(), ident->toChars(), toChars());
#endif
Dsymbol *s = sym->search(e->loc, ident, 0);
if (!s)
{
if (ident == Id::max ||
ident == Id::min ||
ident == Id::init ||
ident == Id::mangleof ||
!sym->memtype
)
{
return getProperty(e->loc, ident);
}
return sym->memtype->dotExp(sc, e, ident);
}
EnumMember *m = s->isEnumMember();
Expression *em = m->value->copy();
em->loc = e->loc;
return em;
}
Expression *TypeEnum::getProperty(Loc loc, Identifier *ident)
{ Expression *e;
if (ident == Id::max)
{
if (!sym->maxval)
goto Lfwd;
e = sym->maxval;
}
else if (ident == Id::min)
{
if (!sym->minval)
goto Lfwd;
e = sym->minval;
}
else if (ident == Id::init)
{
e = defaultInitLiteral(loc);
}
else if (ident == Id::stringof)
{ char *s = toChars();
e = new StringExp(loc, s, strlen(s), 'c');
Scope sc;
e = e->semantic(&sc);
}
else if (ident == Id::mangleof)
{
e = Type::getProperty(loc, ident);
}
else
{
e = toBasetype()->getProperty(loc, ident);
}
return e;
Lfwd:
error(loc, "forward reference of %s.%s", toChars(), ident->toChars());
return new ErrorExp();
}
int TypeEnum::isintegral()
{
return sym->memtype->isintegral();
}
int TypeEnum::isfloating()
{
return sym->memtype->isfloating();
}
int TypeEnum::isreal()
{
return sym->memtype->isreal();
}
int TypeEnum::isimaginary()
{
return sym->memtype->isimaginary();
}
int TypeEnum::iscomplex()
{
return sym->memtype->iscomplex();
}
int TypeEnum::isunsigned()
{
return sym->memtype->isunsigned();
}
int TypeEnum::isscalar()
{
return sym->memtype->isscalar();
}
int TypeEnum::isAssignable(int blit)
{
return sym->memtype->isAssignable(blit);
}
int TypeEnum::checkBoolean()
{
return sym->memtype->checkBoolean();
}
int TypeEnum::needsDestruction()
{
return sym->memtype->needsDestruction();
}
bool TypeEnum::needsNested()
{
return sym->memtype ? sym->memtype->needsNested() : false;
}
MATCH TypeEnum::implicitConvTo(Type *to)
{ MATCH m;
//printf("TypeEnum::implicitConvTo()\n");
if (ty == to->ty && sym == ((TypeEnum *)to)->sym)
m = (mod == to->mod) ? MATCHexact : MATCHconst;
else if (sym->memtype->implicitConvTo(to))
m = MATCHconvert; // match with conversions
else
m = MATCHnomatch; // no match
return m;
}
MATCH TypeEnum::constConv(Type *to)
{
if (equals(to))
return MATCHexact;
if (ty == to->ty && sym == ((TypeEnum *)to)->sym &&
MODimplicitConv(mod, to->mod))
return MATCHconst;
return MATCHnomatch;
}
Expression *TypeEnum::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeEnum::defaultInit() '%s'\n", toChars());
#endif
// Initialize to first member of enum
//printf("%s\n", sym->defaultval->type->toChars());
if (!sym->defaultval)
{
error(loc, "forward reference of %s.init", toChars());
return new ErrorExp();
}
Expression *e = sym->defaultval;
e = e->copy();
e->type = this;
return e;
}
int TypeEnum::isZeroInit(Loc loc)
{
if (!sym->defaultval && sym->scope)
{ // Enum is forward referenced. We need to resolve the whole thing.
sym->semantic(NULL);
}
if (!sym->defaultval)
{
error(loc, "enum %s is forward referenced", sym->toChars());
return 0;
}
return sym->defaultval->isBool(FALSE);
}
int TypeEnum::hasPointers()
{
return toBasetype()->hasPointers();
}
/***************************** TypeTypedef *****************************/
TypeTypedef::TypeTypedef(TypedefDeclaration *sym)
: Type(Ttypedef)
{
this->sym = sym;
}
Type *TypeTypedef::syntaxCopy()
{
return this;
}
char *TypeTypedef::toChars()
{
return Type::toChars();
}
Type *TypeTypedef::semantic(Loc loc, Scope *sc)
{
//printf("TypeTypedef::semantic(%s), sem = %d\n", toChars(), sym->sem);
int errors = global.errors;
sym->semantic(sc);
if (errors != global.errors)
return terror;
return merge();
}
d_uns64 TypeTypedef::size(Loc loc)
{
return sym->basetype->size(loc);
}
unsigned TypeTypedef::alignsize()
{
return sym->basetype->alignsize();
}
Dsymbol *TypeTypedef::toDsymbol(Scope *sc)
{
return sym;
}
void TypeTypedef::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
Type::toDecoBuffer(buf, flag, mangle);
const char *name = sym->mangle();
buf->printf("%s", name);
}
void TypeTypedef::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
//printf("TypeTypedef::toCBuffer2() '%s'\n", sym->toChars());
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
buf->writestring(sym->toChars());
}
Expression *TypeTypedef::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
#if LOGDOTEXP
printf("TypeTypedef::dotExp(e = '%s', ident = '%s') '%s'\n", e->toChars(), ident->toChars(), toChars());
#endif
if (ident == Id::init)
{
return Type::dotExp(sc, e, ident);
}
return sym->basetype->dotExp(sc, e, ident);
}
structalign_t TypeTypedef::alignment()
{
if (sym->inuse)
{
sym->error("circular definition");
sym->basetype = Type::terror;
return STRUCTALIGN_DEFAULT;
}
sym->inuse = 1;
structalign_t a = sym->basetype->alignment();
sym->inuse = 0;
return a;
}
Expression *TypeTypedef::getProperty(Loc loc, Identifier *ident)
{
#if LOGDOTEXP
printf("TypeTypedef::getProperty(ident = '%s') '%s'\n", ident->toChars(), toChars());
#endif
if (ident == Id::init)
{
return Type::getProperty(loc, ident);
}
return sym->basetype->getProperty(loc, ident);
}
int TypeTypedef::isintegral()
{
//printf("TypeTypedef::isintegral()\n");
//printf("sym = '%s'\n", sym->toChars());
//printf("basetype = '%s'\n", sym->basetype->toChars());
return sym->basetype->isintegral();
}
int TypeTypedef::isfloating()
{
return sym->basetype->isfloating();
}
int TypeTypedef::isreal()
{
return sym->basetype->isreal();
}
int TypeTypedef::isimaginary()
{
return sym->basetype->isimaginary();
}
int TypeTypedef::iscomplex()
{
return sym->basetype->iscomplex();
}
int TypeTypedef::isunsigned()
{
return sym->basetype->isunsigned();
}
int TypeTypedef::isscalar()
{
return sym->basetype->isscalar();
}
int TypeTypedef::isAssignable(int blit)
{
return sym->basetype->isAssignable(blit);
}
int TypeTypedef::checkBoolean()
{
return sym->basetype->checkBoolean();
}
int TypeTypedef::needsDestruction()
{
return sym->basetype->needsDestruction();
}
bool TypeTypedef::needsNested()
{
return sym->basetype->needsNested();
}
Type *TypeTypedef::toBasetype()
{
if (sym->inuse)
{
sym->error("circular definition");
sym->basetype = Type::terror;
return Type::terror;
}
sym->inuse = 1;
Type *t = sym->basetype->toBasetype();
sym->inuse = 0;
t = t->addMod(mod);
return t;
}
MATCH TypeTypedef::implicitConvTo(Type *to)
{ MATCH m;
//printf("TypeTypedef::implicitConvTo(to = %s) %s\n", to->toChars(), toChars());
if (equals(to))
m = MATCHexact; // exact match
else if (sym->basetype->implicitConvTo(to))
m = MATCHconvert; // match with conversions
else if (ty == to->ty && sym == ((TypeTypedef *)to)->sym)
{
m = constConv(to);
}
else
m = MATCHnomatch; // no match
return m;
}
MATCH TypeTypedef::constConv(Type *to)
{
if (equals(to))
return MATCHexact;
if (ty == to->ty && sym == ((TypeTypedef *)to)->sym)
return sym->basetype->implicitConvTo(((TypeTypedef *)to)->sym->basetype);
return MATCHnomatch;
}
Type *TypeTypedef::toHeadMutable()
{
if (!mod)
return this;
Type *tb = toBasetype();
Type *t = tb->toHeadMutable();
if (t->equals(tb))
return this;
else
return mutableOf();
}
Expression *TypeTypedef::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeTypedef::defaultInit() '%s'\n", toChars());
#endif
if (sym->init)
{
//sym->init->toExpression()->print();
return sym->init->toExpression();
}
Type *bt = sym->basetype;
Expression *e = bt->defaultInit(loc);
e->type = this;
while (bt->ty == Tsarray)
{ TypeSArray *tsa = (TypeSArray *)bt;
e->type = tsa->next;
bt = tsa->next->toBasetype();
}
return e;
}
Expression *TypeTypedef::defaultInitLiteral(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeTypedef::defaultInitLiteral() '%s'\n", toChars());
#endif
if (sym->init)
{
//sym->init->toExpression()->print();
return sym->init->toExpression();
}
Type *bt = sym->basetype;
Expression *e = bt->defaultInitLiteral(loc);
e->type = this;
return e;
}
int TypeTypedef::isZeroInit(Loc loc)
{
if (sym->init)
{
if (sym->init->isVoidInitializer())
return 1; // initialize voids to 0
Expression *e = sym->init->toExpression();
if (e && e->isBool(FALSE))
return 1;
return 0; // assume not
}
if (sym->inuse)
{
sym->error("circular definition");
sym->basetype = Type::terror;
}
sym->inuse = 1;
int result = sym->basetype->isZeroInit(loc);
sym->inuse = 0;
return result;
}
int TypeTypedef::hasPointers()
{
return toBasetype()->hasPointers();
}
int TypeTypedef::hasWild()
{
assert(toBasetype());
return mod & MODwild || toBasetype()->hasWild();
}
/***************************** TypeStruct *****************************/
TypeStruct::TypeStruct(StructDeclaration *sym)
: Type(Tstruct)
{
this->sym = sym;
// LDC
this->unaligned = 0;
}
char *TypeStruct::toChars()
{
//printf("sym.parent: %s, deco = %s\n", sym->parent->toChars(), deco);
if (mod)
return Type::toChars();
TemplateInstance *ti = sym->parent->isTemplateInstance();
if (ti && ti->toAlias() == sym)
{
return ti->toChars();
}
return sym->toChars();
}
Type *TypeStruct::syntaxCopy()
{
return this;
}
Type *TypeStruct::semantic(Loc loc, Scope *sc)
{
//printf("TypeStruct::semantic('%s')\n", sym->toChars());
/* Cannot do semantic for sym because scope chain may not
* be right.
*/
//sym->semantic(sc);
return merge();
}
d_uns64 TypeStruct::size(Loc loc)
{
return sym->size(loc);
}
unsigned TypeStruct::alignsize()
{
sym->size(0); // give error for forward references
return sym->alignsize;
}
Dsymbol *TypeStruct::toDsymbol(Scope *sc)
{
return sym;
}
void TypeStruct::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
const char *name = sym->mangle();
//printf("TypeStruct::toDecoBuffer('%s') = '%s'\n", toChars(), name);
Type::toDecoBuffer(buf, flag, mangle);
buf->printf("%s", name);
}
void TypeStruct::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
TemplateInstance *ti = sym->parent->isTemplateInstance();
if (ti && ti->toAlias() == sym)
buf->writestring(ti->toChars());
else
buf->writestring(sym->toChars());
}
Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
VarDeclaration *v;
Dsymbol *s;
DotVarExp *de;
Declaration *d;
#if LOGDOTEXP
printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars());
#endif
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
Expressions *exps = new Expressions;
exps->reserve(sym->fields.dim);
Expression *ev = e;
for (size_t i = 0; i < sym->fields.dim; i++)
{ VarDeclaration *v = sym->fields[i];
Expression *fe;
if (i == 0 && sc->func && sym->fields.dim > 1 &&
e->hasSideEffect())
{
Identifier *id = Lexer::uniqueId("__tup");
ExpInitializer *ei = new ExpInitializer(e->loc, e);
VarDeclaration *vd = new VarDeclaration(e->loc, NULL, id, ei);
vd->storage_class |= STCctfe | STCref | STCforeach;
ev = new VarExp(e->loc, vd);
fe = new CommaExp(e->loc, new DeclarationExp(e->loc, vd), ev);
fe = new DotVarExp(e->loc, fe, v);
}
else
fe = new DotVarExp(ev->loc, ev, v);
exps->push(fe);
}
e = new TupleExp(e->loc, exps);
sc = sc->push();
sc->noaccesscheck = 1;
e = e->semantic(sc);
sc->pop();
return e;
}
if (e->op == TOKdotexp)
{ DotExp *de = (DotExp *)e;
if (de->e1->op == TOKimport)
{
assert(0); // cannot find a case where this happens; leave
// assert in until we do
ScopeExp *se = (ScopeExp *)de->e1;
s = se->sds->search(e->loc, ident, 0);
e = de->e1;
goto L1;
}
}
s = sym->search(e->loc, ident, 0);
L1:
if (!s)
{
if (sym->scope) // it's a fwd ref, maybe we can resolve it
{
sym->semantic(NULL);
s = sym->search(e->loc, ident, 0);
if (!s)
return noMember(sc, e, ident);
}
else
return noMember(sc, e, ident);
}
if (!s->isFuncDeclaration()) // because of overloading
s->checkDeprecated(e->loc, sc);
s = s->toAlias();
v = s->isVarDeclaration();
if (v && !v->isDataseg())
{
Expression *ei = v->getConstInitializer();
if (ei)
{ e = ei->copy(); // need to copy it if it's a StringExp
e = e->semantic(sc);
return e;
}
}
if (s->getType())
{
return new TypeExp(e->loc, s->getType());
}
EnumMember *em = s->isEnumMember();
if (em)
{
assert(em->value);
return em->value->copy();
}
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)
{
e = new DotTemplateExp(e->loc, e, td);
e = e->semantic(sc);
return e;
}
TemplateInstance *ti = s->isTemplateInstance();
if (ti)
{ if (!ti->semanticRun)
{
if (global.errors)
return new ErrorExp(); // TemplateInstance::semantic() will fail anyway
ti->semantic(sc);
}
s = ti->inst->toAlias();
if (!s->isTemplateInstance())
goto L1;
Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, ti));
de->type = e->type;
return de;
}
if (s->isImport() || s->isModule() || s->isPackage())
{
e = new DsymbolExp(e->loc, s, 0);
e = e->semantic(sc);
return e;
}
OverloadSet *o = s->isOverloadSet();
if (o)
{
OverExp *oe = new OverExp(o);
if (e->op == TOKtype)
return oe;
return new DotExp(e->loc, e, oe);
}
d = s->isDeclaration();
#ifdef DEBUG
if (!d)
printf("d = %s '%s'\n", s->kind(), s->toChars());
#endif
assert(d);
if (e->op == TOKtype)
{ FuncDeclaration *fd = sc->func;
if (d->isTupleDeclaration())
{
e = new TupleExp(e->loc, d->isTupleDeclaration());
e = e->semantic(sc);
return e;
}
else if (d->needThis() && fd && fd->vthis)
{
e = new DotVarExp(e->loc, new ThisExp(e->loc), d);
e = e->semantic(sc);
return e;
}
accessCheck(e->loc, sc, e, d);
VarExp *ve = new VarExp(e->loc, d, 1);
if (d->isVarDeclaration() && d->needThis())
ve->type = d->type->addMod(e->type->mod);
return ve;
}
if (d->isDataseg())
{
// (e, d)
VarExp *ve;
accessCheck(e->loc, sc, e, d);
ve = new VarExp(e->loc, d);
e = new CommaExp(e->loc, e, ve);
e = e->semantic(sc);
return e;
}
if (v)
{
if (v->toParent() != sym)
sym->error(e->loc, "'%s' is not a member", v->toChars());
// *(&e + offset)
accessCheck(e->loc, sc, e, d);
#if 0
Expression *b = new AddrExp(e->loc, e);
b->type = e->type->pointerTo();
b = new AddExp(e->loc, b, new IntegerExp(e->loc, v->offset, Type::tint32));
b->type = v->type->pointerTo();
b = new PtrExp(e->loc, b);
b->type = v->type->addMod(e->type->mod);
return b;
#endif
}
de = new DotVarExp(e->loc, e, d);
return de->semantic(sc);
}
structalign_t TypeStruct::alignment()
{
if (sym->alignment == 0)
sym->size(0);
return sym->alignment;
}
Expression *TypeStruct::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeStruct::defaultInit() '%s'\n", toChars());
#endif
#if IN_LLVM
Declaration *d = new StaticStructInitDeclaration(sym->loc, sym);
#else
Symbol *s = sym->toInitializer();
Declaration *d = new SymbolDeclaration(sym->loc, s, sym);
#endif
assert(d);
d->type = this;
return new VarExp(sym->loc, d);
}
/***************************************
* Use when we prefer the default initializer to be a literal,
* rather than a global immutable variable.
*/
Expression *TypeStruct::defaultInitLiteral(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars());
#endif
//if (sym->isNested())
// return defaultInit(loc);
Expressions *structelems = new Expressions();
structelems->setDim(sym->fields.dim - sym->isnested);
for (size_t j = 0; j < structelems->dim; j++)
{
VarDeclaration *vd = sym->fields[j];
Expression *e;
if (vd->init)
{ if (vd->init->isVoidInitializer())
e = NULL;
else
e = vd->init->toExpression();
}
else
e = vd->type->defaultInitLiteral(loc);
if (e && vd->scope)
e = e->semantic(vd->scope);
(*structelems)[j] = e;
}
StructLiteralExp *structinit = new StructLiteralExp(loc, (StructDeclaration *)sym, structelems);
#if IN_DMD
/* Copy from the initializer symbol for larger symbols,
* otherwise the literals expressed as code get excessively large.
*/
if (size(loc) > PTRSIZE * 4 && !needsNested())
structinit->sinit = sym->toInitializer();
#endif
structinit->type = this;
return structinit;
}
int TypeStruct::isZeroInit(Loc loc)
{
return sym->zeroInit;
}
int TypeStruct::checkBoolean()
{
return FALSE;
}
int TypeStruct::needsDestruction()
{
return sym->dtor != NULL;
}
bool TypeStruct::needsNested()
{
if (sym->isnested)
return true;
for (size_t i = 0; i < sym->fields.dim; i++)
{
Dsymbol *s = sym->fields[i];
VarDeclaration *vd = s->isVarDeclaration();
if (vd && !vd->isDataseg() && vd->type->needsNested())
return true;
}
return false;
}
int TypeStruct::isAssignable(int blit)
{
if (!blit)
{
if (sym->hasIdentityAssign)
return TRUE;
// has non-identity opAssign
if (search_function(sym, Id::assign))
return FALSE;
}
int assignable = TRUE;
unsigned offset;
/* If any of the fields are const or invariant,
* 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(blit);
offset = v->offset;
//printf(" -> assignable = %d\n", assignable);
}
return assignable;
}
int TypeStruct::hasPointers()
{
// Probably should cache this information in sym rather than recompute
StructDeclaration *s = sym;
sym->size(0); // give error for forward references
for (size_t i = 0; i < s->fields.dim; i++)
{
Dsymbol *sm = s->fields[i];
Declaration *d = sm->isDeclaration();
if (d->storage_class & STCref || d->hasPointers())
return TRUE;
}
return FALSE;
}
MATCH TypeStruct::implicitConvTo(Type *to)
{ MATCH m;
//printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to->toChars());
if (to->ty == Taarray && sym->ident == Id::AssociativeArray)
{
/* If there is an error instantiating AssociativeArray!(), it shouldn't
* be reported -- it just means implicit conversion is impossible.
*/
int errs = global.startGagging();
to = ((TypeAArray*)to)->getImpl()->type;
if (global.endGagging(errs))
{
return MATCHnomatch;
}
}
if (ty == to->ty && sym == ((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.
*/
for (size_t i = 0; i < sym->fields.dim; i++)
{ Dsymbol *s = sym->fields[i];
VarDeclaration *v = s->isVarDeclaration();
assert(v && v->storage_class & STCfield);
// 'from' type
Type *tvf = v->type->addMod(mod);
// 'to' type
Type *tv = v->type->castMod(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;
}
}
}
}
else if (sym->aliasthis)
m = aliasthisOf()->implicitConvTo(to);
else
m = MATCHnomatch; // no match
return m;
}
MATCH TypeStruct::constConv(Type *to)
{
if (equals(to))
return MATCHexact;
if (ty == to->ty && sym == ((TypeStruct *)to)->sym &&
MODimplicitConv(mod, to->mod))
return MATCHconst;
return MATCHnomatch;
}
unsigned TypeStruct::wildConvTo(Type *tprm)
{
if (ty == tprm->ty && sym == ((TypeStruct *)tprm)->sym)
return Type::wildConvTo(tprm);
if (sym->aliasthis)
{ Type *t = aliasthisOf();
assert(t);
return t->wildConvTo(tprm);
}
return 0;
}
Type *TypeStruct::toHeadMutable()
{
return this;
}
/***************************** TypeClass *****************************/
TypeClass::TypeClass(ClassDeclaration *sym)
: Type(Tclass)
{
this->sym = sym;
}
char *TypeClass::toChars()
{
if (mod)
return Type::toChars();
return (char *)sym->toPrettyChars();
}
Type *TypeClass::syntaxCopy()
{
return this;
}
Type *TypeClass::semantic(Loc loc, Scope *sc)
{
//printf("TypeClass::semantic(%s)\n", sym->toChars());
if (deco)
return this;
//printf("\t%s\n", merge()->deco);
return merge();
}
d_uns64 TypeClass::size(Loc loc)
{
return PTRSIZE;
}
Dsymbol *TypeClass::toDsymbol(Scope *sc)
{
return sym;
}
void TypeClass::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
const char *name = sym->mangle();
//printf("TypeClass::toDecoBuffer('%s' flag=%d mod=%x) = '%s'\n", toChars(), flag, mod, name);
Type::toDecoBuffer(buf, flag, mangle);
buf->printf("%s", name);
}
void TypeClass::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
buf->writestring(sym->toChars());
}
Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident)
{
VarDeclaration *v;
Dsymbol *s;
#if LOGDOTEXP
printf("TypeClass::dotExp(e='%s', ident='%s')\n", e->toChars(), ident->toChars());
#endif
if (e->op == TOKdotexp)
{ DotExp *de = (DotExp *)e;
if (de->e1->op == TOKimport)
{
ScopeExp *se = (ScopeExp *)de->e1;
s = se->sds->search(e->loc, ident, 0);
e = de->e1;
goto L1;
}
}
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);
Expressions *exps = new Expressions;
exps->reserve(sym->fields.dim);
Expression *ev = e;
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 *fe;
if (i == 0 && sc->func && sym->fields.dim > 1 &&
e->hasSideEffect())
{
Identifier *id = Lexer::uniqueId("__tup");
ExpInitializer *ei = new ExpInitializer(e->loc, e);
VarDeclaration *vd = new VarDeclaration(e->loc, NULL, id, ei);
vd->storage_class |= STCctfe | STCref | STCforeach;
ev = new VarExp(e->loc, vd);
fe = new CommaExp(e->loc, new DeclarationExp(e->loc, vd), ev);
fe = new DotVarExp(e->loc, fe, v);
}
else
fe = new DotVarExp(e->loc, ev, v);
exps->push(fe);
}
e = new TupleExp(e->loc, exps);
sc = sc->push();
sc->noaccesscheck = 1;
e = e->semantic(sc);
sc->pop();
return e;
}
s = sym->search(e->loc, ident, 0);
L1:
if (!s)
{
// See if it's 'this' class or a base class
if (e->op != TOKtype)
{
Dsymbol *cbase = sym->ident == ident ?
sym : sym->searchBase(e->loc, ident);
if (cbase)
{
e = new DotTypeExp(0, e, cbase);
return e;
}
}
if (ident == Id::classinfo)
{
assert(ClassDeclaration::classinfo);
Type *t = ClassDeclaration::classinfo->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(sc);
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[]
*/
#if IN_LLVM
Type* ct;
if (sym->isInterfaceDeclaration()) {
ct = t->pointerTo()->pointerTo()->pointerTo();
}
else {
ct = t->pointerTo()->pointerTo();
}
e = e->castTo(sc, ct);
e = new PtrExp(e->loc, e);
e->type = ct->nextOf();
e = new PtrExp(e->loc, e);
e->type = ct->nextOf()->nextOf();
if (sym->isInterfaceDeclaration())
{
if (sym->isCOMinterface())
{ /* COM interface vtbl[]s are different in that the
* first entry is always pointer to QueryInterface().
* We can't get a .classinfo for it.
*/
error(e->loc, "no .classinfo for COM 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 = new PtrExp(e->loc, e);
e->type = ct->nextOf()->nextOf()->nextOf();
}
}
#else
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);
}
#endif // !LDC
return e;
}
if (ident == Id::__vptr)
{ /* The pointer to the vtbl[]
* *cast(invariant(void*)**)e
*/
e = e->castTo(sc, tvoidptr->invariantOf()->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)
*/
#if IN_LLVM
e = e->castTo(sc, tint8->pointerTo()->pointerTo());
e = new AddExp(e->loc, e, new IntegerExp(1));
e->type = tint8->pointerTo();
e = e->castTo(sc, tvoidptr->pointerTo());
e = new PtrExp(e->loc, e);
#else
e = e->castTo(sc, tvoidptr->pointerTo());
e = new AddExp(e->loc, e, new IntegerExp(1));
e = new PtrExp(e->loc, e);
#endif
e = e->semantic(sc);
return e;
}
if (ident == Id::typeinfo)
{
error(e->loc, ".typeinfo deprecated, use typeid(type)");
return getTypeInfo(sc);
}
if (ident == Id::outer && sym->vthis)
{
s = sym->vthis;
}
else
{
return noMember(sc, e, ident);
}
}
if (!s->isFuncDeclaration()) // because of overloading
s->checkDeprecated(e->loc, sc);
s = s->toAlias();
v = s->isVarDeclaration();
if (v && !v->isDataseg())
{ Expression *ei = v->getConstInitializer();
if (ei)
{ e = ei->copy(); // need to copy it if it's a StringExp
e = e->semantic(sc);
return e;
}
}
if (s->getType())
{
return new TypeExp(e->loc, s->getType());
}
EnumMember *em = s->isEnumMember();
if (em)
{
assert(em->value);
return em->value->copy();
}
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)
{
e = new DotTemplateExp(e->loc, e, td);
e = e->semantic(sc);
return e;
}
TemplateInstance *ti = s->isTemplateInstance();
if (ti)
{ if (!ti->semanticRun)
{
if (global.errors)
return new ErrorExp(); // TemplateInstance::semantic() will fail anyway
ti->semantic(sc);
}
s = ti->inst->toAlias();
if (!s->isTemplateInstance())
goto L1;
Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, ti));
de->type = e->type;
return de;
}
if (s->isImport() || s->isModule() || s->isPackage())
{
e = new DsymbolExp(e->loc, s, 0);
e = e->semantic(sc);
return e;
}
OverloadSet *o = s->isOverloadSet();
if (o)
{
OverExp *oe = new OverExp(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 (d->isTupleDeclaration())
{
e = new TupleExp(e->loc, d->isTupleDeclaration());
e = e->semantic(sc);
return e;
}
#if 1 // Workaround for Bugzilla 9213
FuncDeclaration *fd = sc->func;
if (d->needThis() && d->isVarDeclaration() && fd && fd->vthis)
{
e = new DotVarExp(e->loc, new ThisExp(e->loc), d);
e = e->semantic(sc);
return e;
}
#endif
FuncDeclaration *fdthis = hasThis(sc);
if (d->needThis() && fdthis)
{
if (d->isFuncDeclaration())
{
// This is almost same as getRightThis() in expression.c
Expression *e1 = new VarExp(e->loc, fdthis->vthis);
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;
Dsymbol *s;
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, 1);
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;
}
}
else
{
/* Rewrite as:
* this.d
*/
DotVarExp *de = new DotVarExp(e->loc, new ThisExp(e->loc), d);
e = de->semantic(sc);
return e;
}
}
accessCheck(e->loc, sc, e, d);
VarExp *ve = new VarExp(e->loc, d, 1);
if (d->isVarDeclaration() && d->needThis())
ve->type = d->type->addMod(e->type->mod);
return ve;
}
if (d->isDataseg())
{
// (e, d)
VarExp *ve;
accessCheck(e->loc, sc, e, d);
ve = new VarExp(e->loc, d);
e = new CommaExp(e->loc, e, ve);
e = e->semantic(sc);
return e;
}
if (d->parent && d->toParent()->isModule())
{
// (e, d)
VarExp *ve = new VarExp(e->loc, d, 1);
e = new CommaExp(e->loc, e, ve);
e->type = d->type;
return e;
}
DotVarExp *de = new DotVarExp(e->loc, e, d, d->hasOverloads());
return de->semantic(sc);
}
ClassDeclaration *TypeClass::isClassHandle()
{
return sym;
}
int TypeClass::isscope()
{
return sym->isscope;
}
int TypeClass::isBaseOf(Type *t, int *poffset)
{
if (t->ty == Tclass)
{ ClassDeclaration *cd;
cd = ((TypeClass *)t)->sym;
if (sym->isBaseOf(cd, poffset))
return 1;
}
return 0;
}
MATCH TypeClass::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)
{
if (cdto->scope)
cdto->semantic(NULL);
if (cdto->isBaseOf(sym, NULL) && MODimplicitConv(mod, to->mod))
{ //printf("'to' is base\n");
return MATCHconvert;
}
}
if (global.params.Dversion == 1)
{
// Allow conversion to (void *)
if (to->ty == Tpointer && ((TypePointer *)to)->next->ty == Tvoid)
return MATCHconvert;
}
m = MATCHnomatch;
if (sym->aliasthis)
m = aliasthisOf()->implicitConvTo(to);
return m;
}
MATCH TypeClass::constConv(Type *to)
{
if (equals(to))
return MATCHexact;
if (ty == to->ty && sym == ((TypeClass *)to)->sym &&
MODimplicitConv(mod, to->mod))
return MATCHconst;
/* Conversion derived to const(base)
*/
int offset = 0;
if (to->isBaseOf(this, &offset) && offset == 0 && !to->isMutable())
return MATCHconvert;
return MATCHnomatch;
}
unsigned TypeClass::wildConvTo(Type *tprm)
{
Type *tcprm = tprm->substWildTo(MODconst);
if (constConv(tcprm))
return Type::wildConvTo(tprm);
ClassDeclaration *cdprm = tcprm->isClassHandle();
if (cdprm && cdprm->isBaseOf(sym, NULL))
return Type::wildConvTo(tprm);
if (sym->aliasthis)
return aliasthisOf()->wildConvTo(tprm);
return 0;
}
Type *TypeClass::toHeadMutable()
{
return this;
}
Expression *TypeClass::defaultInit(Loc loc)
{
#if LOGDEFAULTINIT
printf("TypeClass::defaultInit() '%s'\n", toChars());
#endif
return new NullExp(loc, this);
}
int TypeClass::isZeroInit(Loc loc)
{
return 1;
}
int TypeClass::checkBoolean()
{
return TRUE;
}
int TypeClass::hasPointers()
{
return TRUE;
}
/***************************** TypeTuple *****************************/
TypeTuple::TypeTuple(Parameters *arguments)
: Type(Ttuple)
{
//printf("TypeTuple(this = %p)\n", this);
this->arguments = arguments;
//printf("TypeTuple() %p, %s\n", this, toChars());
#ifdef DEBUG
if (arguments)
{
for (size_t i = 0; i < arguments->dim; i++)
{
Parameter *arg = (*arguments)[i];
assert(arg && arg->type);
}
}
#endif
}
/****************
* Form TypeTuple from the types of the expressions.
* Assume exps[] is already tuple expanded.
*/
TypeTuple::TypeTuple(Expressions *exps)
: Type(Ttuple)
{
Parameters *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");
Parameter *arg = new Parameter(STCundefined, e->type, NULL, NULL);
(*arguments)[i] = arg;
}
}
this->arguments = arguments;
//printf("TypeTuple() %p, %s\n", this, toChars());
}
/*******************************************
* Type tuple with 0, 1 or 2 types in it.
*/
TypeTuple::TypeTuple()
: Type(Ttuple)
{
arguments = new Parameters();
}
TypeTuple::TypeTuple(Type *t1)
: Type(Ttuple)
{
arguments = new Parameters();
arguments->push(new Parameter(0, t1, NULL, NULL));
}
TypeTuple::TypeTuple(Type *t1, Type *t2)
: Type(Ttuple)
{
arguments = new Parameters();
arguments->push(new Parameter(0, t1, NULL, NULL));
arguments->push(new Parameter(0, t2, NULL, NULL));
}
Type *TypeTuple::syntaxCopy()
{
Parameters *args = Parameter::arraySyntaxCopy(arguments);
Type *t = new TypeTuple(args);
t->mod = mod;
return t;
}
Type *TypeTuple::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;
}
int TypeTuple::equals(Object *o)
{ Type *t;
t = (Type *)o;
//printf("TypeTuple::equals(%s, %s)\n", toChars(), t->toChars());
if (this == t)
{
return 1;
}
if (t->ty == Ttuple)
{ TypeTuple *tt = (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 0;
}
return 1;
}
}
return 0;
}
Type *TypeTuple::reliesOnTident(TemplateParameters *tparams)
{
if (arguments)
{
for (size_t i = 0; i < arguments->dim; i++)
{
Parameter *arg = (*arguments)[i];
Type *t = arg->type->reliesOnTident(tparams);
if (t)
return t;
}
}
return NULL;
}
#if 0
Type *TypeTuple::makeConst()
{
//printf("TypeTuple::makeConst() %s\n", toChars());
if (cto)
return cto;
TypeTuple *t = (TypeTuple *)Type::makeConst();
t->arguments = new Parameters();
t->arguments->setDim(arguments->dim);
for (size_t i = 0; i < arguments->dim; i++)
{ Parameter *arg = (*arguments)[i];
Parameter *narg = new Parameter(arg->storageClass, arg->type->constOf(), arg->ident, arg->defaultArg);
(*t->arguments)[i] = (Parameter *)narg;
}
return t;
}
#endif
void TypeTuple::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
Parameter::argsToCBuffer(buf, hgs, arguments, 0);
}
void TypeTuple::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
//printf("TypeTuple::toDecoBuffer() this = %p, %s\n", this, toChars());
Type::toDecoBuffer(buf, flag, mangle);
OutBuffer buf2;
Parameter::argsToDecoBuffer(&buf2, arguments, mangle);
int len = (int)buf2.offset;
buf->printf("%d%.*s", len, len, (char *)buf2.extractData());
}
Expression *TypeTuple::getProperty(Loc loc, Identifier *ident)
{ Expression *e;
#if LOGDOTEXP
printf("TypeTuple::getProperty(type = '%s', ident = '%s')\n", toChars(), ident->toChars());
#endif
if (ident == Id::length)
{
e = new IntegerExp(loc, arguments->dim, Type::tsize_t);
}
else if (ident == Id::init)
{
e = defaultInitLiteral(loc);
}
else
{
error(loc, "no property '%s' for tuple '%s'", ident->toChars(), toChars());
e = new ErrorExp();
}
return e;
}
Expression *TypeTuple::defaultInit(Loc loc)
{
Expressions *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);
}
/***************************** TypeSlice *****************************/
/* This is so we can slice a TypeTuple */
TypeSlice::TypeSlice(Type *next, Expression *lwr, Expression *upr)
: TypeNext(Tslice, next)
{
//printf("TypeSlice[%s .. %s]\n", lwr->toChars(), upr->toChars());
this->lwr = lwr;
this->upr = upr;
}
Type *TypeSlice::syntaxCopy()
{
Type *t = new TypeSlice(next->syntaxCopy(), lwr->syntaxCopy(), upr->syntaxCopy());
t->mod = mod;
return t;
}
Type *TypeSlice::semantic(Loc loc, Scope *sc)
{
//printf("TypeSlice::semantic() %s\n", toChars());
next = next->semantic(loc, sc);
transitive();
//printf("next: %s\n", next->toChars());
Type *tbn = next->toBasetype();
if (tbn->ty != Ttuple)
{ error(loc, "can only slice tuple types, not %s", tbn->toChars());
return Type::terror;
}
TypeTuple *tt = (TypeTuple *)tbn;
lwr = semanticLength(sc, tbn, lwr);
lwr = lwr->ctfeInterpret();
uinteger_t i1 = lwr->toUInteger();
upr = semanticLength(sc, tbn, upr);
upr = upr->ctfeInterpret();
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;
}
Parameters *args = new Parameters;
args->reserve(i2 - i1);
for (size_t i = i1; i < i2; i++)
{ Parameter *arg = (*tt->arguments)[i];
args->push(arg);
}
Type *t = (new TypeTuple(args))->semantic(loc, sc);
return t;
}
void TypeSlice::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps)
{
next->resolve(loc, sc, pe, pt, ps);
if (*pe)
{ // It's really a slice expression
Expression *e;
e = new SliceExp(loc, *pe, lwr, upr);
*pe = e;
}
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);
lwr = lwr->semantic(sc);
lwr = lwr->ctfeInterpret();
uinteger_t i1 = lwr->toUInteger();
upr = upr->semantic(sc);
upr = upr->ctfeInterpret();
uinteger_t i2 = upr->toUInteger();
sc = sc->pop();
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.
*/
Objects *objects = new Objects;
objects->setDim(i2 - i1);
for (size_t i = 0; i < objects->dim; i++)
{
(*objects)[i] = (*td->objects)[(size_t)i1 + i];
}
TupleDeclaration *tds = new TupleDeclaration(loc, td->ident, objects);
*ps = tds;
}
else
goto Ldefault;
}
else
{
Ldefault:
Type::resolve(loc, sc, pe, pt, ps);
}
}
void TypeSlice::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
{
if (mod != this->mod)
{ toCBuffer3(buf, hgs, mod);
return;
}
next->toCBuffer2(buf, hgs, this->mod);
buf->printf("[%s .. ", lwr->toChars());
buf->printf("%s]", upr->toChars());
}
/***************************** TypeNull *****************************/
TypeNull::TypeNull()
: Type(Tnull)
{
}
Type *TypeNull::syntaxCopy()
{
// No semantic analysis done, no need to copy
return this;
}
MATCH TypeNull::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)
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 == Tpointer || tb->ty == Tarray ||
tb->ty == Taarray || tb->ty == Tclass ||
tb->ty == Tdelegate)
return MATCHconst;
}
return MATCHnomatch;
}
void TypeNull::toDecoBuffer(OutBuffer *buf, int flag, bool mangle)
{
//tvoidptr->toDecoBuffer(buf, flag);
Type::toDecoBuffer(buf, flag, mangle);
}
void TypeNull::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs)
{
buf->writestring("typeof(null)");
}
d_uns64 TypeNull::size(Loc loc) { return tvoidptr->size(loc); }
//Expression *TypeNull::getProperty(Loc loc, Identifier *ident) { return new ErrorExp(); }
//Expression *TypeNull::dotExp(Scope *sc, Expression *e, Identifier *ident) { return new ErrorExp(); }
Expression *TypeNull::defaultInit(Loc loc) { return new NullExp(0, Type::tnull); }
//Expression *TypeNull::defaultInitLiteral(Loc loc) { return new ErrorExp(); }
/***************************** Parameter *****************************/
Parameter::Parameter(StorageClass storageClass, Type *type, Identifier *ident, Expression *defaultArg)
{
this->type = type;
this->ident = ident;
this->storageClass = storageClass;
this->defaultArg = defaultArg;
}
Parameter *Parameter::syntaxCopy()
{
Parameter *a = new Parameter(storageClass,
type ? type->syntaxCopy() : NULL,
ident,
defaultArg ? defaultArg->syntaxCopy() : NULL);
return a;
}
Parameters *Parameter::arraySyntaxCopy(Parameters *args)
{ Parameters *a = NULL;
if (args)
{
a = new Parameters();
a->setDim(args->dim);
for (size_t i = 0; i < a->dim; i++)
{ Parameter *arg = (*args)[i];
arg = arg->syntaxCopy();
(*a)[i] = arg;
}
}
return a;
}
char *Parameter::argsTypesToChars(Parameters *args, int varargs)
{
OutBuffer *buf = new OutBuffer();
HdrGenState hgs;
argsToCBuffer(buf, &hgs, args, varargs);
return buf->toChars();
}
void Parameter::argsToCBuffer(OutBuffer *buf, HdrGenState *hgs, Parameters *arguments, int varargs)
{
buf->writeByte('(');
if (arguments)
{
OutBuffer argbuf;
size_t dim = Parameter::dim(arguments);
for (size_t i = 0; i < dim; i++)
{
if (i)
buf->writestring(", ");
Parameter *arg = Parameter::getNth(arguments, i);
if (arg->storageClass & STCauto)
buf->writestring("auto ");
if (arg->storageClass & STCout)
buf->writestring("out ");
else if (arg->storageClass & STCref)
buf->writestring((global.params.Dversion == 1)
? "inout " : "ref ");
else if (arg->storageClass & STCin)
buf->writestring("in ");
else if (arg->storageClass & STClazy)
buf->writestring("lazy ");
else if (arg->storageClass & STCalias)
buf->writestring("alias ");
StorageClass stc = arg->storageClass;
if (arg->type && arg->type->mod & MODshared)
stc &= ~STCshared;
StorageClassDeclaration::stcToCBuffer(buf,
stc & (STCconst | STCimmutable | STCshared | STCscope));
argbuf.reset();
if (arg->storageClass & STCalias)
{ if (arg->ident)
argbuf.writestring(arg->ident->toChars());
}
else
arg->type->toCBuffer(&argbuf, arg->ident, hgs);
if (arg->defaultArg)
{
argbuf.writestring(" = ");
arg->defaultArg->toCBuffer(&argbuf, hgs);
}
buf->write(&argbuf);
}
if (varargs)
{
if (arguments->dim && varargs == 1)
buf->writestring(", ");
buf->writestring("...");
}
}
buf->writeByte(')');
}
static int argsToDecoBufferDg(void *ctx, size_t n, Parameter *arg)
{
#if IN_LLVM
arg->toDecoBuffer((OutBuffer *)ctx, false);
#else
arg->toDecoBuffer((OutBuffer *)ctx);
#endif
return 0;
}
#if IN_LLVM
static int argsToDecoBufferDg2(void *ctx, size_t n, Parameter *arg)
{
arg->toDecoBuffer((OutBuffer *)ctx, true);
return 0;
}
#endif
void Parameter::argsToDecoBuffer(OutBuffer *buf, Parameters *arguments, bool mangle)
{
//printf("Parameter::argsToDecoBuffer()\n");
// Write argument types
#if IN_LLVM
foreach(arguments, mangle ? &argsToDecoBufferDg2 : &argsToDecoBufferDg, buf);
#else
foreach(arguments, &argsToDecoBufferDg, buf);
#endif
}
/****************************************
* Determine if parameter list is really a template parameter list
* (i.e. it has auto or alias parameters)
*/
static int isTPLDg(void *ctx, size_t n, Parameter *arg)
{
if (arg->storageClass & (STCalias | STCauto | STCstatic))
return 1;
return 0;
}
int Parameter::isTPL(Parameters *arguments)
{
//printf("Parameter::isTPL()\n");
return foreach(arguments, &isTPLDg, NULL);
}
/****************************************************
* Determine if parameter is a lazy array of delegates.
* If so, return the return type of those delegates.
* If not, return NULL.
*/
Type *Parameter::isLazyArray()
{
// if (inout == Lazy)
{
Type *tb = type->toBasetype();
if (tb->ty == Tsarray || tb->ty == Tarray)
{
Type *tel = ((TypeArray *)tb)->next->toBasetype();
if (tel->ty == Tdelegate)
{
TypeDelegate *td = (TypeDelegate *)tel;
TypeFunction *tf = (TypeFunction *)td->next;
if (!tf->varargs && Parameter::dim(tf->parameters) == 0)
{
return tf->next; // return type of delegate
}
}
}
}
return NULL;
}
void Parameter::toDecoBuffer(OutBuffer *buf, bool mangle)
{
if (storageClass & STCscope)
buf->writeByte('M');
switch (storageClass & (STCin | STCout | STCref | STClazy))
{ case 0:
case STCin:
break;
case STCout:
buf->writeByte('J');
break;
case STCref:
buf->writeByte('K');
break;
case STClazy:
buf->writeByte('L');
break;
default:
#ifdef DEBUG
printf("storageClass = x%llx\n", storageClass & (STCin | STCout | STCref | STClazy));
halt();
#endif
assert(0);
}
#if 0
int mod = 0x100;
if (type->toBasetype()->ty == Tclass)
mod = 0;
type->toDecoBuffer(buf, mod);
#else
//type->toHeadMutable()->toDecoBuffer(buf, 0);
type->toDecoBuffer(buf, 0, mangle);
#endif
}
/***************************************
* Determine number of arguments, folding in tuples.
*/
static int dimDg(void *ctx, size_t n, Parameter *)
{
++*(size_t *)ctx;
return 0;
}
size_t Parameter::dim(Parameters *args)
{
size_t n = 0;
foreach(args, &dimDg, &n);
return n;
}
/***************************************
* Get nth Parameter, folding in tuples.
* Returns:
* Parameter* nth Parameter
* NULL not found, *pn gets incremented by the number
* of Parameters
*/
struct GetNthParamCtx
{
size_t nth;
Parameter *arg;
};
static int getNthParamDg(void *ctx, size_t n, Parameter *arg)
{
GetNthParamCtx *p = (GetNthParamCtx *)ctx;
if (n == p->nth)
{ p->arg = arg;
return 1;
}
return 0;
}
Parameter *Parameter::getNth(Parameters *args, size_t nth, size_t *pn)
{
GetNthParamCtx ctx = { nth, NULL };
int res = foreach(args, &getNthParamDg, &ctx);
return res ? ctx.arg : NULL;
}
/***************************************
* 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.
*/
int Parameter::foreach(Parameters *args, Parameter::ForeachDg dg, void *ctx, size_t *pn)
{
assert(dg);
if (!args)
return 0;
size_t n = pn ? *pn : 0; // take over index
int result = 0;
for (size_t i = 0; i < args->dim; i++)
{ Parameter *arg = (*args)[i];
Type *t = arg->type->toBasetype();
if (t->ty == Ttuple)
{ TypeTuple *tu = (TypeTuple *)t;
result = foreach(tu->arguments, dg, ctx, &n);
}
else
result = dg(ctx, n++, arg);
if (result)
break;
}
if (pn)
*pn = n; // update index
return result;
}