mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-11 18:33:14 +01:00
9916 lines
257 KiB
C
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;
|
|
}
|