mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-08 11:56:12 +03:00
6395 lines
138 KiB
C
6395 lines
138 KiB
C
|
|
// Compiler implementation of the D programming language
|
|
// Copyright (c) 1999-2008 by Digital Mars
|
|
// All Rights Reserved
|
|
// written by Walter Bright
|
|
// http://www.digitalmars.com
|
|
// 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 __USE_ISOC99 1 // so signbit() gets defined
|
|
#include <cmath>
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <float.h>
|
|
|
|
#ifdef __DMC__
|
|
#include <fp.h>
|
|
#endif
|
|
|
|
#if _MSC_VER
|
|
#include <malloc.h>
|
|
#include <complex>
|
|
#include <limits>
|
|
#elif __DMC__
|
|
#include <complex.h>
|
|
#elif __MINGW32__
|
|
#include <malloc.h>
|
|
#else
|
|
//#define signbit 56
|
|
#endif
|
|
|
|
#if __APPLE__
|
|
#include <cmath>
|
|
static double zero = 0;
|
|
#elif __MINGW32__
|
|
#include <cmath>
|
|
static double zero = 0;
|
|
#elif __GNUC__
|
|
#include <cmath>
|
|
#include <bits/nan.h>
|
|
#include <bits/mathdef.h>
|
|
static double zero = 0;
|
|
#endif
|
|
|
|
#include "mem.h"
|
|
|
|
#include "dsymbol.h"
|
|
#include "mtype.h"
|
|
#include "scope.h"
|
|
#include "init.h"
|
|
#include "expression.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"
|
|
|
|
#include "gen/tollvm.h"
|
|
|
|
FuncDeclaration *hasThis(Scope *sc);
|
|
|
|
|
|
#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;
|
|
#if IN_LLVM
|
|
int REALSIZE = 8;
|
|
int REALPAD = 0;
|
|
#elif TARGET_LINUX
|
|
int REALSIZE = 12;
|
|
int REALPAD = 2;
|
|
#else
|
|
int REALSIZE = 10;
|
|
int REALPAD = 0;
|
|
#endif
|
|
int Tsize_t = Tuns32;
|
|
int Tptrdiff_t = Tint32;
|
|
|
|
/***************************** 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::typeinfoenum;
|
|
ClassDeclaration *Type::typeinfofunction;
|
|
ClassDeclaration *Type::typeinfodelegate;
|
|
ClassDeclaration *Type::typeinfotypelist;
|
|
ClassDeclaration *Type::typeinfoconst;
|
|
ClassDeclaration *Type::typeinfoinvariant;
|
|
|
|
Type *Type::tvoidptr;
|
|
Type *Type::basic[TMAX];
|
|
unsigned char Type::mangleChar[TMAX];
|
|
unsigned char Type::sizeTy[TMAX];
|
|
StringTable Type::stringtable;
|
|
|
|
|
|
Type::Type(TY ty)
|
|
{
|
|
this->ty = ty;
|
|
this->mod = 0;
|
|
this->deco = NULL;
|
|
#if DMDV2
|
|
this->cto = NULL;
|
|
this->ito = NULL;
|
|
#endif
|
|
this->pto = NULL;
|
|
this->rto = NULL;
|
|
this->arrayof = NULL;
|
|
this->vtinfo = NULL;
|
|
this->ctype = NULL;
|
|
}
|
|
|
|
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'
|
|
}
|
|
|
|
void Type::init()
|
|
{ int i;
|
|
int j;
|
|
|
|
Lexer::initKeywords();
|
|
|
|
for (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);
|
|
|
|
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';
|
|
|
|
mangleChar[Tbit] = '@';
|
|
mangleChar[Tinstance] = '@';
|
|
mangleChar[Terror] = '@';
|
|
mangleChar[Ttypeof] = '@';
|
|
mangleChar[Ttuple] = 'B';
|
|
mangleChar[Tslice] = '@';
|
|
mangleChar[Treturn] = '@';
|
|
|
|
for (i = 0; i < TMAX; i++)
|
|
{ if (!mangleChar[i])
|
|
fprintf(stdmsg, "ty = %d\n", i);
|
|
assert(mangleChar[i]);
|
|
}
|
|
|
|
// Set basic types
|
|
static TY basetab[] =
|
|
{ Tvoid, Tint8, Tuns8, Tint16, Tuns16, Tint32, Tuns32, Tint64, Tuns64,
|
|
Tfloat32, Tfloat64, Tfloat80,
|
|
Timaginary32, Timaginary64, Timaginary80,
|
|
Tcomplex32, Tcomplex64, Tcomplex80,
|
|
Tbool,
|
|
Tascii, Twchar, Tdchar };
|
|
|
|
for (i = 0; i < sizeof(basetab) / sizeof(basetab[0]); i++)
|
|
{ Type *t = new TypeBasic(basetab[i]);
|
|
t = t->merge();
|
|
basic[basetab[i]] = t;
|
|
}
|
|
basic[Terror] = basic[Tint32];
|
|
|
|
tvoidptr = tvoid->pointerTo();
|
|
|
|
// set size_t / ptrdiff_t types and pointer size
|
|
if (global.params.is64bit)
|
|
{
|
|
Tsize_t = Tuns64;
|
|
Tptrdiff_t = Tint64;
|
|
PTRSIZE = 8;
|
|
}
|
|
else
|
|
{
|
|
Tsize_t = Tuns32;
|
|
Tptrdiff_t = Tint32;
|
|
PTRSIZE = 4;
|
|
}
|
|
|
|
// set real size and padding
|
|
if (global.params.cpu == ARCHx86)
|
|
{
|
|
REALSIZE = 12;
|
|
REALPAD = 2;
|
|
}
|
|
else if (global.params.cpu == ARCHx86_64)
|
|
{
|
|
REALSIZE = 16;
|
|
REALPAD = 6;
|
|
}
|
|
else
|
|
{
|
|
REALSIZE = 8;
|
|
REALPAD = 0;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
return merge();
|
|
}
|
|
|
|
/*******************************
|
|
* Determine if converting 'this' to 'to' is an identity operation,
|
|
* a conversion to const operation, or the types aren't the same.
|
|
* Returns:
|
|
* MATCHequal 'this' == 'to'
|
|
* MATCHconst 'to' is const
|
|
* MATCHnomatch conversion to mutable or invariant
|
|
*/
|
|
|
|
MATCH Type::constConv(Type *to)
|
|
{
|
|
if (equals(to))
|
|
return MATCHexact;
|
|
if (ty == to->ty && to->mod == MODconst)
|
|
return MATCHconst;
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
Type *Type::constOf()
|
|
{
|
|
//printf("Type::constOf() %p %s\n", this, toChars());
|
|
if (isConst())
|
|
return this;
|
|
if (cto)
|
|
return cto;
|
|
Type *t = makeConst();
|
|
t = t->merge();
|
|
cto = t;
|
|
if (ito)
|
|
ito->cto = t;
|
|
//if (t->nextOf()) assert(t->nextOf()->isConst());
|
|
//printf("-Type::constOf() %p %s\n", t, toChars());
|
|
return t;
|
|
}
|
|
|
|
Type *Type::invariantOf()
|
|
{
|
|
//printf("Type::invariantOf() %p %s\n", this, toChars());
|
|
if (isInvariant())
|
|
{
|
|
return this;
|
|
}
|
|
if (ito)
|
|
{
|
|
//if (!ito->isInvariant()) printf("\tito is %p %s\n", ito, ito->toChars());
|
|
assert(ito->isInvariant());
|
|
return ito;
|
|
}
|
|
Type *t = makeInvariant();
|
|
t = t->merge();
|
|
ito = t;
|
|
if (cto)
|
|
cto->ito = t;
|
|
#if 0 // fails for function types
|
|
if (t->nextOf() && !t->nextOf()->isInvariant())
|
|
{
|
|
assert(0);
|
|
}
|
|
#endif
|
|
//printf("\t%p\n", t);
|
|
return t;
|
|
}
|
|
|
|
Type *Type::mutableOf()
|
|
{
|
|
//printf("Type::mutableOf() %p, %s\n", this, toChars());
|
|
Type *t = this;
|
|
if (isConst())
|
|
{ t = cto;
|
|
assert(!t || t->isMutable());
|
|
}
|
|
else if (isInvariant())
|
|
{ t = ito;
|
|
assert(!t || t->isMutable());
|
|
}
|
|
if (!t)
|
|
{
|
|
unsigned sz = sizeTy[ty];
|
|
t = (Type *)mem.malloc(sz);
|
|
memcpy(t, this, sz);
|
|
t->mod = 0;
|
|
t->deco = NULL;
|
|
t->arrayof = NULL;
|
|
t->pto = NULL;
|
|
t->rto = NULL;
|
|
t->cto = NULL;
|
|
t->ito = NULL;
|
|
t->vtinfo = NULL;
|
|
if (ty == Tsarray)
|
|
{ TypeSArray *ta = (TypeSArray *)t;
|
|
//ta->next = ta->next->mutableOf();
|
|
}
|
|
t = t->merge();
|
|
if (isConst())
|
|
{ cto = t;
|
|
t->cto = this;
|
|
if (ito)
|
|
ito->cto = this;
|
|
}
|
|
else if (isInvariant())
|
|
{ ito = t;
|
|
t->ito = this;
|
|
if (cto)
|
|
cto->ito = this;
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
|
|
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->vtinfo = NULL;
|
|
//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 = MODinvariant;
|
|
t->deco = NULL;
|
|
t->arrayof = NULL;
|
|
t->pto = NULL;
|
|
t->rto = NULL;
|
|
t->cto = NULL;
|
|
t->ito = NULL;
|
|
t->vtinfo = NULL;
|
|
return t;
|
|
}
|
|
|
|
/**************************
|
|
* Return type with the top level of it being mutable.
|
|
*/
|
|
Type *Type::toHeadMutable()
|
|
{
|
|
if (!mod)
|
|
return this;
|
|
return mutableOf();
|
|
}
|
|
|
|
Type *Type::pointerTo()
|
|
{
|
|
if (!pto)
|
|
{ Type *t;
|
|
|
|
t = new TypePointer(this);
|
|
pto = t->merge();
|
|
}
|
|
return pto;
|
|
}
|
|
|
|
Type *Type::referenceTo()
|
|
{
|
|
if (!rto)
|
|
{ Type *t;
|
|
|
|
t = new TypeReference(this);
|
|
rto = t->merge();
|
|
}
|
|
return rto;
|
|
}
|
|
|
|
Type *Type::arrayOf()
|
|
{
|
|
if (!arrayof)
|
|
{ Type *t;
|
|
|
|
t = new TypeDArray(this);
|
|
arrayof = t->merge();
|
|
}
|
|
return arrayof;
|
|
}
|
|
|
|
Dsymbol *Type::toDsymbol(Scope *sc)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/*******************************
|
|
* If this is a shell around another type,
|
|
* get that other type.
|
|
*/
|
|
|
|
Type *Type::toBasetype()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
/********************************
|
|
* Name mangling.
|
|
* Input:
|
|
* flag 0x100 do not do const/invariant
|
|
*/
|
|
|
|
void Type::toDecoBuffer(OutBuffer *buf, int flag)
|
|
{
|
|
if (flag != mod && flag != 0x100)
|
|
{
|
|
if (mod & MODshared)
|
|
buf->writeByte('O');
|
|
|
|
if (mod & MODconst)
|
|
buf->writeByte('x');
|
|
else if (mod & MODinvariant)
|
|
buf->writeByte('y');
|
|
|
|
// Cannot be both const and invariant
|
|
assert((mod & (MODconst | MODinvariant)) != (MODconst | MODinvariant));
|
|
}
|
|
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)
|
|
{ const char *p;
|
|
|
|
if (mod & MODshared)
|
|
buf->writestring("shared(");
|
|
switch (this->mod & (MODconst | MODinvariant))
|
|
{
|
|
case 0:
|
|
toCBuffer2(buf, hgs, this->mod);
|
|
break;
|
|
case MODconst:
|
|
p = "const(";
|
|
goto L1;
|
|
case MODinvariant:
|
|
p = "invariant(";
|
|
L1: buf->writestring(p);
|
|
toCBuffer2(buf, hgs, this->mod);
|
|
buf->writeByte(')');
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
if (mod & MODshared)
|
|
buf->writeByte(')');
|
|
}
|
|
}
|
|
|
|
/************************************
|
|
*/
|
|
|
|
Type *Type::merge()
|
|
{ Type *t;
|
|
|
|
//printf("merge(%s)\n", toChars());
|
|
t = this;
|
|
assert(t);
|
|
if (!deco)
|
|
{
|
|
OutBuffer buf;
|
|
StringValue *sv;
|
|
|
|
//if (next)
|
|
//next = next->merge();
|
|
toDecoBuffer(&buf);
|
|
sv = stringtable.update((char *)buf.data, buf.offset);
|
|
if (sv->ptrvalue)
|
|
{ t = (Type *) sv->ptrvalue;
|
|
assert(t->deco);
|
|
//printf("old value, deco = '%s' %p\n", t->deco, t->deco);
|
|
}
|
|
else
|
|
{
|
|
sv->ptrvalue = this;
|
|
deco = sv->lstring.string;
|
|
//printf("new value, deco = '%s' %p\n", t->deco, t->deco);
|
|
}
|
|
}
|
|
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::isauto()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int Type::isString()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/**************************
|
|
* Given:
|
|
* T a, b;
|
|
* Can we assign:
|
|
* a = b;
|
|
* ?
|
|
*/
|
|
int Type::isAssignable()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
int Type::checkBoolean()
|
|
{
|
|
return isscalar();
|
|
}
|
|
|
|
/*********************************
|
|
* 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;
|
|
}
|
|
|
|
int Type::isZeroInit()
|
|
{
|
|
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:
|
|
* 0 can't convert
|
|
* 1 can convert using implicit conversions
|
|
* 2 this and to are the same type
|
|
*/
|
|
|
|
MATCH Type::implicitConvTo(Type *to)
|
|
{
|
|
//printf("Type::implicitConvTo(this=%p, to=%p)\n", this, to);
|
|
if (this == to)
|
|
return MATCHexact;
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
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::size)
|
|
{
|
|
error(loc, ".size property should be replaced with .sizeof");
|
|
e = new IntegerExp(loc, size(loc), Type::tsize_t);
|
|
}
|
|
else if (ident == Id::alignof)
|
|
{
|
|
e = new IntegerExp(loc, alignsize(), Type::tsize_t);
|
|
}
|
|
else if (ident == Id::typeinfo)
|
|
{
|
|
if (!global.params.useDeprecated)
|
|
error(loc, ".typeinfo deprecated, use typeid(type)");
|
|
e = getTypeInfo(NULL);
|
|
}
|
|
else if (ident == Id::init)
|
|
{
|
|
if (ty == Tvoid)
|
|
error(loc, "void does not have an initializer");
|
|
e = defaultInit(loc);
|
|
}
|
|
else if (ident == Id::mangleof)
|
|
{
|
|
assert(deco);
|
|
e = new StringExp(loc, deco, strlen(deco), '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
|
|
{
|
|
error(loc, "no property '%s' for type '%s'", ident->toChars(), toChars());
|
|
e = new IntegerExp(loc, 1, Type::tint32);
|
|
}
|
|
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)
|
|
{
|
|
if (!global.params.useDeprecated)
|
|
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 0
|
|
if (v->init)
|
|
{
|
|
if (v->init->isVoidInitializer())
|
|
error(e->loc, "%s.init is void", v->toChars());
|
|
else
|
|
{ Loc loc = e->loc;
|
|
e = v->init->toExpression();
|
|
if (e->op == TOKassign || e->op == TOKconstruct || e->op == TOKblit)
|
|
{
|
|
e = ((AssignExp *)e)->e2;
|
|
|
|
/* Take care of case where we used a 0
|
|
* to initialize the struct.
|
|
*/
|
|
if (e->type == Type::tint32 &&
|
|
e->isBool(0) &&
|
|
v->type->toBasetype()->ty == Tstruct)
|
|
{
|
|
e = v->type->defaultInit(e->loc);
|
|
}
|
|
}
|
|
e = e->optimize(WANTvalue | WANTinterpret);
|
|
// if (!e->isConst())
|
|
// error(loc, ".init cannot be evaluated at compile time");
|
|
}
|
|
return e;
|
|
}
|
|
#endif
|
|
Expression *ex = defaultInit(e->loc);
|
|
return ex;
|
|
}
|
|
}
|
|
if (ident == Id::typeinfo)
|
|
{
|
|
if (!global.params.useDeprecated)
|
|
error(e->loc, ".typeinfo deprecated, use typeid(type)");
|
|
e = getTypeInfo(sc);
|
|
return e;
|
|
}
|
|
if (ident == Id::stringof)
|
|
{ char *s = e->toChars();
|
|
e = new StringExp(e->loc, s, strlen(s), 'c');
|
|
Scope sc;
|
|
e = e->semantic(&sc);
|
|
return e;
|
|
}
|
|
return getProperty(e->loc, ident);
|
|
}
|
|
|
|
unsigned Type::memalign(unsigned salign)
|
|
{
|
|
return salign;
|
|
}
|
|
|
|
void Type::error(Loc loc, const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
::verror(loc, format, ap);
|
|
va_end( ap );
|
|
}
|
|
|
|
Identifier *Type::getTypeInfoIdent(int internal)
|
|
{
|
|
// _init_10TypeInfo_%s
|
|
OutBuffer buf;
|
|
Identifier *id;
|
|
char *name;
|
|
int len;
|
|
|
|
if (internal)
|
|
{ buf.writeByte(mangleChar[ty]);
|
|
if (ty == Tarray)
|
|
buf.writeByte(mangleChar[((TypeArray *)this)->next->ty]);
|
|
}
|
|
else
|
|
toDecoBuffer(&buf);
|
|
len = buf.offset;
|
|
name = (char *)alloca(19 + sizeof(len) * 3 + len + 1);
|
|
buf.writeByte(0);
|
|
sprintf(name, "_D%dTypeInfo_%s6__initZ", 9 + len, buf.data);
|
|
// LDC
|
|
// it is not clear where the underscore that's stripped here is added back in
|
|
// if (global.params.isWindows)
|
|
// name++; // C mangling will add it back in
|
|
//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;
|
|
}
|
|
|
|
/*******************************
|
|
* If one of the subtypes of this type is a TypeIdentifier,
|
|
* i.e. it's an unresolved type, return that type.
|
|
*/
|
|
|
|
Type *Type::reliesOnTident()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/********************************
|
|
* 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()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/*************************************
|
|
* If this is a type of something, return that something.
|
|
*/
|
|
|
|
Type *Type::nextOf()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* ============================= TypeNext =========================== */
|
|
|
|
TypeNext::TypeNext(TY ty, Type *next)
|
|
: Type(ty)
|
|
{
|
|
this->next = next;
|
|
}
|
|
|
|
void TypeNext::toDecoBuffer(OutBuffer *buf, int flag)
|
|
{
|
|
Type::toDecoBuffer(buf, flag);
|
|
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);
|
|
}
|
|
|
|
void TypeNext::checkDeprecated(Loc loc, Scope *sc)
|
|
{
|
|
Type::checkDeprecated(loc, sc);
|
|
next->checkDeprecated(loc, sc);
|
|
}
|
|
|
|
|
|
Type *TypeNext::reliesOnTident()
|
|
{
|
|
return next->reliesOnTident();
|
|
}
|
|
|
|
Type *TypeNext::nextOf()
|
|
{
|
|
return next;
|
|
}
|
|
|
|
Type *TypeNext::makeConst()
|
|
{
|
|
//printf("TypeNext::makeConst() %p, %s\n", this, toChars());
|
|
if (cto)
|
|
return cto;
|
|
TypeNext *t = (TypeNext *)Type::makeConst();
|
|
if (ty != Tfunction && ty != Tdelegate && next->deco &&
|
|
!next->isInvariant())
|
|
t->next = next->constOf();
|
|
//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->isInvariant());
|
|
return ito;
|
|
}
|
|
TypeNext *t = (TypeNext *)Type::makeInvariant();
|
|
if (ty != Tfunction && ty != Tdelegate && next->deco)
|
|
{ t->next = next->invariantOf();
|
|
}
|
|
return t;
|
|
}
|
|
|
|
MATCH TypeNext::constConv(Type *to)
|
|
{ MATCH m = Type::constConv(to);
|
|
|
|
if (m == MATCHconst &&
|
|
next->constConv(((TypeNext *)to)->next) == MATCHnomatch)
|
|
m = MATCHnomatch;
|
|
return m;
|
|
}
|
|
|
|
|
|
/* ============================= 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
|
|
|
|
flags = 0;
|
|
switch (ty)
|
|
{
|
|
case Tvoid: d = Token::toChars(TOKvoid);
|
|
break;
|
|
|
|
case Tint8: d = Token::toChars(TOKint8);
|
|
flags |= TFLAGSintegral;
|
|
break;
|
|
|
|
case Tuns8: d = Token::toChars(TOKuns8);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
|
|
case Tint16: d = Token::toChars(TOKint16);
|
|
flags |= TFLAGSintegral;
|
|
break;
|
|
|
|
case Tuns16: d = Token::toChars(TOKuns16);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
|
|
case Tint32: d = Token::toChars(TOKint32);
|
|
flags |= TFLAGSintegral;
|
|
break;
|
|
|
|
case Tuns32: d = Token::toChars(TOKuns32);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
|
|
case Tfloat32: d = Token::toChars(TOKfloat32);
|
|
flags |= TFLAGSfloating | TFLAGSreal;
|
|
break;
|
|
|
|
case Tint64: d = Token::toChars(TOKint64);
|
|
flags |= TFLAGSintegral;
|
|
break;
|
|
|
|
case Tuns64: d = Token::toChars(TOKuns64);
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
|
|
case Tfloat64: d = Token::toChars(TOKfloat64);
|
|
flags |= TFLAGSfloating | TFLAGSreal;
|
|
break;
|
|
|
|
case Tfloat80: d = Token::toChars(TOKfloat80);
|
|
flags |= TFLAGSfloating | TFLAGSreal;
|
|
break;
|
|
|
|
case Timaginary32: d = Token::toChars(TOKimaginary32);
|
|
flags |= TFLAGSfloating | TFLAGSimaginary;
|
|
break;
|
|
|
|
case Timaginary64: d = Token::toChars(TOKimaginary64);
|
|
flags |= TFLAGSfloating | TFLAGSimaginary;
|
|
break;
|
|
|
|
case Timaginary80: d = Token::toChars(TOKimaginary80);
|
|
flags |= TFLAGSfloating | TFLAGSimaginary;
|
|
break;
|
|
|
|
case Tcomplex32: d = Token::toChars(TOKcomplex32);
|
|
flags |= TFLAGSfloating | TFLAGScomplex;
|
|
break;
|
|
|
|
case Tcomplex64: d = Token::toChars(TOKcomplex64);
|
|
flags |= TFLAGSfloating | TFLAGScomplex;
|
|
break;
|
|
|
|
case Tcomplex80: d = Token::toChars(TOKcomplex80);
|
|
flags |= TFLAGSfloating | TFLAGScomplex;
|
|
break;
|
|
|
|
case Tbool: d = "bool";
|
|
flags |= TFLAGSintegral | TFLAGSunsigned;
|
|
break;
|
|
|
|
case 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:
|
|
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 getABITypeAlign(DtoType(this));
|
|
}
|
|
|
|
|
|
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 = 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: 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:
|
|
{
|
|
#if IN_GCC
|
|
// mode doesn't matter, will be converted in RealExp anyway
|
|
fvalue = real_t::getnan(real_t::LongDouble);
|
|
#elif __GNUC__
|
|
// gcc nan's have the sign bit set by default, so turn it off
|
|
// Need the volatile to prevent gcc from doing incorrect
|
|
// constant folding.
|
|
volatile d_float80 foo;
|
|
foo = NAN;
|
|
if (std::signbit(foo)) // signbit sometimes, not always, set
|
|
foo = -foo; // turn off sign bit
|
|
fvalue = foo;
|
|
#elif _MSC_VER
|
|
unsigned long nan[2]= { 0xFFFFFFFF, 0x7FFFFFFF };
|
|
fvalue = *(double*)nan;
|
|
#else
|
|
fvalue = NAN;
|
|
#endif
|
|
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:
|
|
#if IN_GCC
|
|
fvalue = real_t::getinfinity();
|
|
#elif __GNUC__
|
|
fvalue = 1 / zero;
|
|
#elif _MSC_VER
|
|
fvalue = std::numeric_limits<long double>::infinity();
|
|
#else
|
|
fvalue = INFINITY;
|
|
#endif
|
|
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;
|
|
}
|
|
}
|
|
|
|
Ldefault:
|
|
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(0, 0.0, t);
|
|
break;
|
|
|
|
default:
|
|
return Type::getProperty(e->loc, ident);
|
|
}
|
|
}
|
|
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(0, 0.0, this);
|
|
break;
|
|
|
|
default:
|
|
return Type::getProperty(e->loc, ident);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return Type::dotExp(sc, e, ident);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
Expression *TypeBasic::defaultInit(Loc loc)
|
|
{ integer_t value = 0;
|
|
|
|
#if LOGDEFAULTINIT
|
|
printf("TypeBasic::defaultInit() '%s'\n", toChars());
|
|
#endif
|
|
switch (ty)
|
|
{
|
|
case Tvoid:
|
|
return new IntegerExp(loc, value, Type::tbool);
|
|
|
|
case Tchar:
|
|
value = 0xFF;
|
|
break;
|
|
|
|
case Twchar:
|
|
case Tdchar:
|
|
value = 0xFFFF;
|
|
break;
|
|
|
|
case Timaginary32:
|
|
case Timaginary64:
|
|
case Timaginary80:
|
|
case Tfloat32:
|
|
case Tfloat64:
|
|
case Tfloat80:
|
|
case Tcomplex32:
|
|
case Tcomplex64:
|
|
case Tcomplex80:
|
|
return getProperty(loc, Id::nan);
|
|
}
|
|
return new IntegerExp(loc, value, this);
|
|
}
|
|
|
|
int TypeBasic::isZeroInit()
|
|
{
|
|
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 (ty == to->ty)
|
|
{
|
|
return (mod == to->mod) ? MATCHexact : MATCHconst;
|
|
}
|
|
|
|
if (ty == Tvoid || to->ty == Tvoid)
|
|
return MATCHnomatch;
|
|
if (1 || global.params.Dversion == 1)
|
|
{
|
|
if (to->ty == Tbool)
|
|
return MATCHnomatch;
|
|
}
|
|
else
|
|
{
|
|
if (ty == Tbool || to->ty == Tbool)
|
|
return MATCHnomatch;
|
|
}
|
|
if (!to->isTypeBasic())
|
|
return MATCHnomatch;
|
|
|
|
TypeBasic *tob = (TypeBasic *)to;
|
|
if (flags & TFLAGSintegral)
|
|
{
|
|
// Disallow implicit conversion of integers to imaginary or complex
|
|
if (tob->flags & (TFLAGSimaginary | TFLAGScomplex))
|
|
return MATCHnomatch;
|
|
|
|
// If converting to integral
|
|
if (0 && global.params.Dversion > 1 && tob->flags & TFLAGSintegral)
|
|
{ d_uns64 sz = size(0);
|
|
d_uns64 tosz = tob->size(0);
|
|
|
|
/* Can't convert to smaller size or, if same size, change sign
|
|
*/
|
|
if (sz > tosz)
|
|
return MATCHnomatch;
|
|
|
|
/*if (sz == tosz && (flags ^ tob->flags) & TFLAGSunsigned)
|
|
return MATCHnomatch;*/
|
|
}
|
|
}
|
|
else if (flags & TFLAGSfloating)
|
|
{
|
|
// Disallow implicit conversion of floating point to integer
|
|
if (tob->flags & TFLAGSintegral)
|
|
return MATCHnomatch;
|
|
|
|
assert(tob->flags & TFLAGSfloating);
|
|
|
|
// 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;
|
|
}
|
|
|
|
/***************************** 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 (ident == Id::reverse && (n->ty == Tchar || n->ty == Twchar))
|
|
{
|
|
Expression *ec;
|
|
Expressions *arguments;
|
|
|
|
//LDC: Build arguments.
|
|
static FuncDeclaration *adReverseChar_fd = NULL;
|
|
if(!adReverseChar_fd) {
|
|
Arguments* args = new Arguments;
|
|
Type* arrty = Type::tchar->arrayOf();
|
|
args->push(new Argument(STCin, arrty, NULL, NULL));
|
|
adReverseChar_fd = FuncDeclaration::genCfunc(args, arrty, "_adReverseChar");
|
|
}
|
|
static FuncDeclaration *adReverseWchar_fd = NULL;
|
|
if(!adReverseWchar_fd) {
|
|
Arguments* args = new Arguments;
|
|
Type* arrty = Type::twchar->arrayOf();
|
|
args->push(new Argument(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) {
|
|
Arguments* args = new Arguments;
|
|
Type* arrty = Type::tchar->arrayOf();
|
|
args->push(new Argument(STCin, arrty, NULL, NULL));
|
|
adSortChar_fd = FuncDeclaration::genCfunc(args, arrty, "_adSortChar");
|
|
}
|
|
static FuncDeclaration *adSortWchar_fd = NULL;
|
|
if(!adSortWchar_fd) {
|
|
Arguments* args = new Arguments;
|
|
Type* arrty = Type::twchar->arrayOf();
|
|
args->push(new Argument(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;
|
|
|
|
assert(size);
|
|
dup = (ident == Id::dup || ident == Id::idup);
|
|
//LDC: Build arguments.
|
|
static FuncDeclaration *adDup_fd = NULL;
|
|
if(!adDup_fd) {
|
|
Arguments* args = new Arguments;
|
|
args->push(new Argument(STCin, Type::typeinfo->type, NULL, NULL));
|
|
args->push(new Argument(STCin, Type::tvoid->arrayOf(), NULL, NULL));
|
|
adDup_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::adDup);
|
|
}
|
|
static FuncDeclaration *adReverse_fd = NULL;
|
|
if(!adReverse_fd) {
|
|
Arguments* args = new Arguments;
|
|
args->push(new Argument(STCin, Type::tvoid->arrayOf(), NULL, NULL));
|
|
args->push(new Argument(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) {
|
|
e = new CastExp(e->loc, e, e->type);
|
|
e->type = Type::tvoid->arrayOf();
|
|
}
|
|
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 invariant", next->toChars());
|
|
e->type = einv->arrayOf();
|
|
}
|
|
else
|
|
e->type = next->mutableOf()->arrayOf();
|
|
}
|
|
else if (ident == Id::sort)
|
|
{
|
|
Expression *ec;
|
|
Expressions *arguments;
|
|
bool isBit = (n->ty == Tbit);
|
|
|
|
//LDC: Build arguments.
|
|
static FuncDeclaration *adSort_fd = NULL;
|
|
if(!adSort_fd) {
|
|
Arguments* args = new Arguments;
|
|
args->push(new Argument(STCin, Type::tvoid->arrayOf(), NULL, NULL));
|
|
args->push(new Argument(STCin, Type::typeinfo->type, NULL, NULL));
|
|
adSort_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), "_adSort");
|
|
}
|
|
static FuncDeclaration *adSortBit_fd = NULL;
|
|
if(!adSortBit_fd) {
|
|
Arguments* args = new Arguments;
|
|
args->push(new Argument(STCin, Type::tvoid->arrayOf(), NULL, NULL));
|
|
args->push(new Argument(STCin, Type::typeinfo->type, NULL, NULL));
|
|
adSortBit_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), "_adSortBit");
|
|
}
|
|
|
|
if(isBit)
|
|
ec = new VarExp(0, adSortBit_fd);
|
|
else
|
|
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) {
|
|
e = new CastExp(e->loc, e, e->type);
|
|
e->type = Type::tvoid->arrayOf();
|
|
}
|
|
arguments->push(e);
|
|
|
|
arguments->push(n->getTypeInfo(sc)); // LDC, we don't support the getInternalTypeInfo
|
|
// optimization arbitrarily, not yet at least...
|
|
e = new CallExp(e->loc, ec, arguments);
|
|
e->type = next->arrayOf();
|
|
}
|
|
else
|
|
{
|
|
e = Type::dotExp(sc, e, ident);
|
|
}
|
|
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)
|
|
{ integer_t sz;
|
|
|
|
if (!dim)
|
|
return Type::size(loc);
|
|
sz = dim->toInteger();
|
|
|
|
{ integer_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
|
|
Expression *e = new IndexExp(loc, *pe, dim);
|
|
*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->optimize(WANTvalue | WANTinterpret);
|
|
uinteger_t d = dim->toUInteger();
|
|
|
|
sc = sc->pop();
|
|
|
|
if (d >= td->objects->dim)
|
|
{ error(loc, "tuple index %llu exceeds %u", d, td->objects->dim);
|
|
goto Ldefault;
|
|
}
|
|
Object *o = (Object *)td->objects->data[(size_t)d];
|
|
if (o->dyncast() == DYNCAST_DSYMBOL)
|
|
{
|
|
*ps = (Dsymbol *)o;
|
|
return;
|
|
}
|
|
if (o->dyncast() == DYNCAST_EXPRESSION)
|
|
{
|
|
*ps = NULL;
|
|
*pe = (Expression *)o;
|
|
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->data[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->optimize(WANTvalue | WANTinterpret);
|
|
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 = (Object *)sd->objects->data[(size_t)d];
|
|
if (o->dyncast() != DYNCAST_TYPE)
|
|
{ error(loc, "%s is not a type", toChars());
|
|
return Type::terror;
|
|
}
|
|
t = (Type *)o;
|
|
return t;
|
|
}
|
|
|
|
next = next->semantic(loc,sc);
|
|
if (mod == MODconst && !next->isInvariant())
|
|
next = next->constOf();
|
|
else if (mod == MODinvariant)
|
|
next = next->invariantOf();
|
|
|
|
Type *tbn = next->toBasetype();
|
|
|
|
if (dim)
|
|
{ integer_t n, n2;
|
|
|
|
dim = semanticLength(sc, tbn, dim);
|
|
|
|
dim = dim->optimize(WANTvalue | WANTinterpret);
|
|
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;
|
|
}
|
|
integer_t d1 = dim->toInteger();
|
|
dim = dim->castTo(sc, tsize_t);
|
|
dim = dim->optimize(WANTvalue);
|
|
integer_t d2 = dim->toInteger();
|
|
|
|
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);
|
|
dim = new IntegerExp(0, 1, tsize_t);
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
return Type::terror;
|
|
}
|
|
Argument *arg = (Argument *)tt->arguments->data[(size_t)d];
|
|
return arg->type;
|
|
}
|
|
case Tfunction:
|
|
case Tnone:
|
|
error(loc, "can't have array of %s", tbn->toChars());
|
|
tbn = next = tint32;
|
|
break;
|
|
}
|
|
if (tbn->isauto())
|
|
error(loc, "cannot have array of auto %s", tbn->toChars());
|
|
return merge();
|
|
}
|
|
|
|
void TypeSArray::toDecoBuffer(OutBuffer *buf, int flag)
|
|
{
|
|
Type::toDecoBuffer(buf, flag);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
int TypeSArray::isString()
|
|
{
|
|
TY nty = next->toBasetype()->ty;
|
|
return nty == Tchar || nty == Twchar || nty == Tdchar;
|
|
}
|
|
|
|
unsigned TypeSArray::memalign(unsigned salign)
|
|
{
|
|
return next->memalign(salign);
|
|
}
|
|
|
|
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 (next->mod != tp->next->mod && tp->next->mod != MODconst)
|
|
return MATCHnomatch;
|
|
|
|
if (tp->next->ty == Tvoid || next->constConv(tp->next) != MATCHnomatch)
|
|
{
|
|
return MATCHconvert;
|
|
}
|
|
return MATCHnomatch;
|
|
}
|
|
if (to->ty == Tarray)
|
|
{ int offset = 0;
|
|
TypeDArray *ta = (TypeDArray *)to;
|
|
|
|
if (next->mod != ta->next->mod && ta->next->mod != MODconst)
|
|
return MATCHnomatch;
|
|
|
|
if (next->equals(ta->next) ||
|
|
next->implicitConvTo(ta->next) >= MATCHconst ||
|
|
(ta->next->isBaseOf(next, &offset) && offset == 0) ||
|
|
ta->next->ty == Tvoid)
|
|
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()
|
|
{
|
|
return next->isZeroInit();
|
|
}
|
|
|
|
|
|
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()
|
|
{
|
|
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;
|
|
|
|
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());
|
|
tn = next = tint32;
|
|
break;
|
|
}
|
|
if (tn->isauto())
|
|
error(loc, "cannot have array of auto %s", tn->toChars());
|
|
|
|
if (mod == MODconst && !tn->isInvariant())
|
|
tn = tn->constOf();
|
|
else if (mod == MODinvariant)
|
|
tn = tn->invariantOf();
|
|
|
|
next = tn;
|
|
return merge();
|
|
}
|
|
|
|
void TypeDArray::toDecoBuffer(OutBuffer *buf, int flag)
|
|
{
|
|
Type::toDecoBuffer(buf, flag);
|
|
if (next)
|
|
next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod);
|
|
}
|
|
|
|
void TypeDArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
|
|
{
|
|
if (mod != this->mod)
|
|
{ toCBuffer3(buf, hgs, mod);
|
|
return;
|
|
}
|
|
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);
|
|
}
|
|
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 &&
|
|
(next->mod == tp->next->mod || tp->next->mod == MODconst))
|
|
{
|
|
return MATCHconvert;
|
|
}
|
|
|
|
return next->constConv(to);
|
|
}
|
|
|
|
if (to->ty == Tarray)
|
|
{ int offset = 0;
|
|
TypeDArray *ta = (TypeDArray *)to;
|
|
|
|
if (!(next->mod == ta->next->mod || ta->next->mod == MODconst))
|
|
return MATCHnomatch; // not const-compatible
|
|
|
|
/* Allow conversion to void[]
|
|
*/
|
|
if (next->ty != Tvoid && ta->next->ty == Tvoid)
|
|
{
|
|
return MATCHconvert;
|
|
}
|
|
|
|
MATCH m = next->constConv(ta->next);
|
|
if (m != MATCHnomatch)
|
|
{
|
|
if (m == MATCHexact && mod != to->mod)
|
|
m = MATCHconst;
|
|
return m;
|
|
}
|
|
|
|
/* Allow conversions of T[][] to const(T)[][]
|
|
*/
|
|
if (mod == ta->mod && next->ty == Tarray && ta->next->ty == Tarray)
|
|
{
|
|
m = next->implicitConvTo(ta->next);
|
|
if (m == MATCHconst)
|
|
return m;
|
|
}
|
|
|
|
/* Conversion of array of derived to array of base
|
|
*/
|
|
if (ta->next->isBaseOf(next, &offset) && offset == 0)
|
|
return MATCHconvert;
|
|
}
|
|
return Type::implicitConvTo(to);
|
|
}
|
|
|
|
Expression *TypeDArray::defaultInit(Loc loc)
|
|
{
|
|
#if LOGDEFAULTINIT
|
|
printf("TypeDArray::defaultInit() '%s'\n", toChars());
|
|
#endif
|
|
Expression *e;
|
|
e = new NullExp(loc);
|
|
e->type = this;
|
|
return e;
|
|
}
|
|
|
|
int TypeDArray::isZeroInit()
|
|
{
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
// 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");
|
|
}
|
|
else
|
|
index = index->semantic(loc,sc);
|
|
|
|
if (index->nextOf() && !index->nextOf()->isInvariant())
|
|
{
|
|
index = index->constOf()->mutableOf();
|
|
}
|
|
|
|
switch (index->toBasetype()->ty)
|
|
{
|
|
case Tbool:
|
|
case Tfunction:
|
|
case Tvoid:
|
|
case Tnone:
|
|
error(loc, "can't have associative array key of %s", index->toBasetype()->toChars());
|
|
break;
|
|
}
|
|
next = next->semantic(loc,sc);
|
|
if (mod == MODconst && !next->isInvariant())
|
|
next = next->constOf();
|
|
else if (mod == MODinvariant)
|
|
next = next->invariantOf();
|
|
|
|
switch (next->toBasetype()->ty)
|
|
{
|
|
case Tfunction:
|
|
case Tnone:
|
|
error(loc, "can't have associative array of %s", next->toChars());
|
|
break;
|
|
}
|
|
if (next->isauto())
|
|
error(loc, "cannot have array of auto %s", next->toChars());
|
|
|
|
return merge();
|
|
}
|
|
|
|
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->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 (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 = ((TypeFunction *)aaLen_fd->type)->next;
|
|
}
|
|
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));
|
|
e = new CallExp(e->loc, ec, arguments);
|
|
e->type = this;
|
|
}
|
|
else
|
|
{
|
|
e = Type::dotExp(sc, e, ident);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
void TypeAArray::toDecoBuffer(OutBuffer *buf, int flag)
|
|
{
|
|
Type::toDecoBuffer(buf, flag);
|
|
index->toDecoBuffer(buf);
|
|
next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod);
|
|
}
|
|
|
|
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
|
|
Expression *e;
|
|
e = new NullExp(loc);
|
|
e->type = this;
|
|
return e;
|
|
}
|
|
|
|
int TypeAArray::isZeroInit()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
int TypeAArray::checkBoolean()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
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 (!(next->mod == ta->next->mod || ta->next->mod == MODconst))
|
|
return MATCHnomatch; // not const-compatible
|
|
|
|
if (!(index->mod == ta->index->mod || ta->index->mod == MODconst))
|
|
return MATCHnomatch; // not const-compatible
|
|
|
|
MATCH m = next->constConv(ta->next);
|
|
MATCH mi = index->constConv(ta->index);
|
|
if (m != MATCHnomatch && mi != MATCHnomatch)
|
|
{
|
|
if (m == MATCHexact && mod != to->mod)
|
|
m = MATCHconst;
|
|
if (mi < m)
|
|
m = mi;
|
|
return m;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
else
|
|
return Type::constConv(to);
|
|
}
|
|
|
|
/***************************** 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()\n");
|
|
Type *n = next->semantic(loc, sc);
|
|
switch (n->toBasetype()->ty)
|
|
{
|
|
case Ttuple:
|
|
error(loc, "can't have pointer to %s", n->toChars());
|
|
n = tint32;
|
|
break;
|
|
}
|
|
if (n != next)
|
|
deco = NULL;
|
|
next = n;
|
|
if (mod == MODconst && !next->isInvariant())
|
|
next = next->constOf();
|
|
else if (mod == MODinvariant)
|
|
next = next->invariantOf();
|
|
return merge();
|
|
}
|
|
|
|
|
|
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 (to->ty == Tpointer)
|
|
{ TypePointer *tp = (TypePointer *)to;
|
|
assert(tp->next);
|
|
|
|
if (!(next->mod == tp->next->mod || tp->next->mod == MODconst))
|
|
return MATCHnomatch; // not const-compatible
|
|
|
|
/* Alloc conversion to void[]
|
|
*/
|
|
if (next->ty != Tvoid && tp->next->ty == Tvoid)
|
|
{
|
|
return MATCHconvert;
|
|
}
|
|
|
|
MATCH m = next->constConv(tp->next);
|
|
if (m != MATCHnomatch)
|
|
{
|
|
if (m == MATCHexact && mod != to->mod)
|
|
m = MATCHconst;
|
|
return m;
|
|
}
|
|
|
|
/* Conversion of ptr to derived to ptr to base
|
|
*/
|
|
int offset = 0;
|
|
if (tp->next->isBaseOf(next, &offset) && offset == 0)
|
|
return MATCHconvert;
|
|
}
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
int TypePointer::isscalar()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
Expression *TypePointer::defaultInit(Loc loc)
|
|
{
|
|
#if LOGDEFAULTINIT
|
|
printf("TypePointer::defaultInit() '%s'\n", toChars());
|
|
#endif
|
|
Expression *e;
|
|
e = new NullExp(loc);
|
|
e->type = this;
|
|
return e;
|
|
}
|
|
|
|
int TypePointer::isZeroInit()
|
|
{
|
|
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;
|
|
if (mod == MODconst && !next->isInvariant())
|
|
next = next->constOf();
|
|
else if (mod == MODinvariant)
|
|
next = next->invariantOf();
|
|
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
|
|
Expression *e = new NullExp(loc);
|
|
e->type = this;
|
|
return e;
|
|
}
|
|
|
|
int TypeReference::isZeroInit()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
|
|
/***************************** TypeFunction *****************************/
|
|
|
|
TypeFunction::TypeFunction(Arguments *parameters, Type *treturn, int varargs, enum LINK linkage)
|
|
: 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->ispure = false;
|
|
this->isref = false;
|
|
|
|
// LDC
|
|
this->fty = NULL;
|
|
}
|
|
|
|
Type *TypeFunction::syntaxCopy()
|
|
{
|
|
Type *treturn = next ? next->syntaxCopy() : NULL;
|
|
Arguments *params = Argument::arraySyntaxCopy(parameters);
|
|
TypeFunction *t = new TypeFunction(params, treturn, varargs, linkage);
|
|
t->mod = mod;
|
|
t->isnothrow = isnothrow;
|
|
t->ispure = ispure;
|
|
t->isref = isref;
|
|
return t;
|
|
}
|
|
|
|
/*******************************
|
|
* 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
|
|
*/
|
|
|
|
int Type::covariant(Type *t)
|
|
{
|
|
#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);
|
|
#endif
|
|
|
|
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 = Argument::dim(t1->parameters);
|
|
if (dim != Argument::dim(t2->parameters))
|
|
goto Ldistinct;
|
|
|
|
for (size_t i = 0; i < dim; i++)
|
|
{ Argument *arg1 = Argument::getNth(t1->parameters, i);
|
|
Argument *arg2 = Argument::getNth(t2->parameters, i);
|
|
|
|
if (!arg1->type->equals(arg2->type))
|
|
goto Ldistinct;
|
|
if ((arg1->storageClass & ~STCscope) != (arg2->storageClass & ~STCscope))
|
|
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)
|
|
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->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 &&
|
|
t2n->mod == MODconst)
|
|
goto Lcovariant;
|
|
|
|
// If t1n is forward referenced:
|
|
ClassDeclaration *cd = ((TypeClass *)t1n)->sym;
|
|
if (!cd->baseClass && cd->baseclasses.dim && !cd->isInterfaceDeclaration())
|
|
{
|
|
return 3;
|
|
}
|
|
}
|
|
if (t1n->implicitConvTo(t2n))
|
|
goto Lcovariant;
|
|
}
|
|
goto Lnotcovariant;
|
|
|
|
Lcovariant:
|
|
/* Can convert pure to impure, and nothrow to throw
|
|
*/
|
|
if (!t1->ispure && t2->ispure)
|
|
goto Lnotcovariant;
|
|
|
|
if (!t1->isnothrow && t2->isnothrow)
|
|
goto Lnotcovariant;
|
|
|
|
if (t1->isref != t2->isref)
|
|
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)
|
|
{ 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++;
|
|
#if 1
|
|
if (mod & MODshared)
|
|
buf->writeByte('O');
|
|
if (mod & MODconst)
|
|
buf->writeByte('x');
|
|
else if (mod & MODinvariant)
|
|
buf->writeByte('y');
|
|
#endif
|
|
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 (ispure || isnothrow || isref)
|
|
{
|
|
if (ispure)
|
|
buf->writestring("Na");
|
|
if (isnothrow)
|
|
buf->writestring("Nb");
|
|
if (isref)
|
|
buf->writestring("Nc");
|
|
}
|
|
// Write argument types
|
|
Argument::argsToDecoBuffer(buf, parameters);
|
|
//if (buf->data[buf->offset - 1] == '@') halt();
|
|
buf->writeByte('Z' - varargs); // mark end of arg list
|
|
next->toDecoBuffer(buf);
|
|
inuse--;
|
|
}
|
|
|
|
void TypeFunction::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs)
|
|
{
|
|
//printf("TypeFunction::toCBuffer() this = %p %s\n", this, toChars());
|
|
const char *p = NULL;
|
|
|
|
if (inuse)
|
|
{ inuse = 2; // flag error to caller
|
|
return;
|
|
}
|
|
inuse++;
|
|
|
|
/* Use 'storage class' style for attributes
|
|
*/
|
|
if (mod & MODconst)
|
|
buf->writestring("const ");
|
|
if (mod & MODinvariant)
|
|
buf->writestring("invariant ");
|
|
if (mod & MODshared)
|
|
buf->writestring("shared ");
|
|
|
|
if (ispure)
|
|
buf->writestring("pure ");
|
|
if (isnothrow)
|
|
buf->writestring("nothrow ");
|
|
if (isref)
|
|
buf->writestring("ref ");
|
|
|
|
if (next && (!ident || ident->toHChars2() == ident->toChars()))
|
|
next->toCBuffer2(buf, hgs, 0);
|
|
if (hgs->ddoc != 1)
|
|
{
|
|
switch (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(p);
|
|
if (ident)
|
|
{ buf->writeByte(' ');
|
|
buf->writestring(ident->toHChars2());
|
|
}
|
|
Argument::argsToCBuffer(buf, hgs, parameters, varargs);
|
|
inuse--;
|
|
}
|
|
|
|
void TypeFunction::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
|
|
{
|
|
//printf("TypeFunction::toCBuffer2() this = %p %s\n", this, toChars());
|
|
const char *p = NULL;
|
|
|
|
if (inuse)
|
|
{ inuse = 2; // flag error to caller
|
|
return;
|
|
}
|
|
inuse++;
|
|
if (next)
|
|
next->toCBuffer2(buf, hgs, 0);
|
|
if (hgs->ddoc != 1)
|
|
{
|
|
switch (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(p);
|
|
buf->writestring(" function");
|
|
Argument::argsToCBuffer(buf, hgs, parameters, varargs);
|
|
|
|
/* Use postfix style for attributes
|
|
*/
|
|
if (mod != this->mod)
|
|
{
|
|
if (mod & MODconst)
|
|
buf->writestring(" const");
|
|
if (mod & MODinvariant)
|
|
buf->writestring(" invariant");
|
|
if (mod & MODshared)
|
|
buf->writestring(" shared");
|
|
}
|
|
if (ispure)
|
|
buf->writestring(" pure");
|
|
if (isnothrow)
|
|
buf->writestring(" nothrow");
|
|
if (isref)
|
|
buf->writestring(" ref");
|
|
|
|
inuse--;
|
|
}
|
|
|
|
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);
|
|
|
|
TypeFunction *tf = (TypeFunction *)mem.malloc(sizeof(TypeFunction));
|
|
memcpy(tf, this, sizeof(TypeFunction));
|
|
if (parameters)
|
|
{ tf->parameters = (Arguments *)parameters->copy();
|
|
for (size_t i = 0; i < parameters->dim; i++)
|
|
{ Argument *arg = (Argument *)parameters->data[i];
|
|
Argument *cpy = (Argument *)mem.malloc(sizeof(Argument));
|
|
memcpy(cpy, arg, sizeof(Argument));
|
|
tf->parameters->data[i] = (void *)cpy;
|
|
}
|
|
}
|
|
|
|
if (sc->stc & STCpure)
|
|
tf->ispure = TRUE;
|
|
if (sc->stc & STCnothrow)
|
|
tf->isnothrow = TRUE;
|
|
if (sc->stc & STCref)
|
|
tf->isref = TRUE;
|
|
|
|
tf->linkage = sc->linkage;
|
|
if (!tf->next)
|
|
{
|
|
assert(global.errors);
|
|
tf->next = tvoid;
|
|
}
|
|
tf->next = tf->next->semantic(loc,sc);
|
|
if (tf->next->toBasetype()->ty == Tsarray)
|
|
{ error(loc, "functions cannot return static array %s", tf->next->toChars());
|
|
tf->next = Type::terror;
|
|
}
|
|
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->isauto() && !(sc->flags & SCOPEctor))
|
|
error(loc, "functions cannot return auto %s", tf->next->toChars());
|
|
|
|
if (tf->parameters)
|
|
{ size_t dim = Argument::dim(tf->parameters);
|
|
|
|
for (size_t i = 0; i < dim; i++)
|
|
{ Argument *arg = Argument::getNth(tf->parameters, i);
|
|
|
|
tf->inuse++;
|
|
arg->type = arg->type->semantic(loc,sc);
|
|
if (tf->inuse == 1) tf->inuse--;
|
|
|
|
if (arg->storageClass & (STCconst | STCin))
|
|
{
|
|
if (!arg->type->isInvariant())
|
|
arg->type = arg->type->constOf();
|
|
}
|
|
else if (arg->storageClass & STCinvariant)
|
|
arg->type = arg->type->invariantOf();
|
|
|
|
if (arg->storageClass & (STCauto | STCalias | STCstatic))
|
|
{
|
|
if (!arg->type)
|
|
continue;
|
|
}
|
|
|
|
Type *t = arg->type->toBasetype();
|
|
|
|
if (arg->storageClass & (STCout | STCref | STClazy))
|
|
{
|
|
if (t->ty == Tsarray)
|
|
error(loc, "cannot have out or ref parameter of type %s", t->toChars());
|
|
if (arg->storageClass & STCout && arg->type->mod)
|
|
error(loc, "cannot have const/invariant out parameter of type %s", t->toChars());
|
|
}
|
|
if (!(arg->storageClass & STClazy) && t->ty == Tvoid)
|
|
error(loc, "cannot have parameter of type %s", arg->type->toChars());
|
|
|
|
if (arg->defaultArg)
|
|
{
|
|
arg->defaultArg = arg->defaultArg->semantic(sc);
|
|
arg->defaultArg = resolveProperties(sc, arg->defaultArg);
|
|
arg->defaultArg = arg->defaultArg->implicitCastTo(sc, arg->type);
|
|
}
|
|
|
|
/* If arg turns out to be a tuple, the number of parameters may
|
|
* change.
|
|
*/
|
|
if (t->ty == Ttuple)
|
|
{ dim = Argument::dim(tf->parameters);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
tf->deco = tf->merge()->deco;
|
|
|
|
if (tf->inuse)
|
|
{ error(loc, "recursive type");
|
|
tf->inuse = 0;
|
|
return terror;
|
|
}
|
|
|
|
if (tf->varargs == 1 && tf->linkage != LINKd && Argument::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;
|
|
}
|
|
|
|
/********************************
|
|
* 'args' are being matched to function 'this'
|
|
* Determine match level.
|
|
* Returns:
|
|
* MATCHxxxx
|
|
*/
|
|
|
|
int TypeFunction::callMatch(Expression *ethis, Expressions *args)
|
|
{
|
|
//printf("TypeFunction::callMatch() %s\n", toChars());
|
|
MATCH match = MATCHexact; // assume exact match
|
|
|
|
if (ethis)
|
|
{ Type *t = ethis->type;
|
|
if (t->toBasetype()->ty == Tpointer)
|
|
t = t->toBasetype()->nextOf(); // change struct* to struct
|
|
if (t->mod != mod)
|
|
{
|
|
if (mod == MODconst)
|
|
match = MATCHconst;
|
|
else
|
|
return MATCHnomatch;
|
|
}
|
|
}
|
|
|
|
size_t nparams = Argument::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 < nparams; u++)
|
|
{ MATCH m;
|
|
Expression *arg;
|
|
|
|
// BUG: what about out and ref?
|
|
|
|
Argument *p = Argument::getNth(parameters, u);
|
|
assert(p);
|
|
if (u >= nargs)
|
|
{
|
|
if (p->defaultArg)
|
|
continue;
|
|
if (varargs == 2 && u + 1 == nparams)
|
|
goto L1;
|
|
goto Nomatch; // not enough arguments
|
|
}
|
|
arg = (Expression *)args->data[u];
|
|
assert(arg);
|
|
|
|
// Non-lvalues do not match ref or out parameters
|
|
if (p->storageClass & (STCref | STCout) && !arg->isLvalue())
|
|
goto Nomatch;
|
|
|
|
if (p->storageClass & STClazy && p->type->ty == Tvoid &&
|
|
arg->type->ty != Tvoid)
|
|
m = MATCHconvert;
|
|
else
|
|
m = arg->implicitConvTo(p->type);
|
|
//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;
|
|
integer_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++)
|
|
{
|
|
arg = (Expression *)args->data[u];
|
|
assert(arg);
|
|
#if 1
|
|
/* 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
|
|
{
|
|
m = arg->implicitConvTo(tret);
|
|
if (m == MATCHnomatch)
|
|
{
|
|
if (tret->toBasetype()->ty == Tvoid)
|
|
m = MATCHconvert;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
m = arg->implicitConvTo(ta->next);
|
|
#else
|
|
m = arg->implicitConvTo(ta->next);
|
|
#endif
|
|
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()
|
|
{
|
|
if (parameters)
|
|
{
|
|
for (size_t i = 0; i < parameters->dim; i++)
|
|
{ Argument *arg = (Argument *)parameters->data[i];
|
|
Type *t = arg->type->reliesOnTident();
|
|
if (t)
|
|
return t;
|
|
}
|
|
}
|
|
return next->reliesOnTident();
|
|
}
|
|
|
|
/***************************
|
|
* Examine function signature for parameter p and see if
|
|
* p can 'escape' the scope of the function.
|
|
*/
|
|
|
|
bool TypeFunction::parameterEscapes(Argument *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 (ispure)
|
|
{ /* 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;
|
|
}
|
|
|
|
/***************************** 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)
|
|
{
|
|
if (deco) // if semantic() already run
|
|
{
|
|
//printf("already done\n");
|
|
return this;
|
|
}
|
|
next = next->semantic(loc,sc);
|
|
return merge();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void TypeDelegate::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
|
|
{
|
|
if (mod != this->mod)
|
|
{ toCBuffer3(buf, hgs, mod);
|
|
return;
|
|
}
|
|
TypeFunction *tf = (TypeFunction *)next;
|
|
|
|
tf->next->toCBuffer2(buf, hgs, 0);
|
|
buf->writestring(" delegate");
|
|
Argument::argsToCBuffer(buf, hgs, tf->parameters, tf->varargs);
|
|
}
|
|
|
|
Expression *TypeDelegate::defaultInit(Loc loc)
|
|
{
|
|
#if LOGDEFAULTINIT
|
|
printf("TypeDelegate::defaultInit() '%s'\n", toChars());
|
|
#endif
|
|
Expression *e;
|
|
e = new NullExp(loc);
|
|
e->type = this;
|
|
return e;
|
|
}
|
|
|
|
int TypeDelegate::isZeroInit()
|
|
{
|
|
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 = tvoidptr;
|
|
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 (int i = 0; i < idents.dim; i++)
|
|
{
|
|
Identifier *id = (Identifier *)t->idents.data[i];
|
|
if (id->dyncast() == DYNCAST_DSYMBOL)
|
|
{
|
|
TemplateInstance *ti = (TemplateInstance *)id;
|
|
|
|
ti = (TemplateInstance *)ti->syntaxCopy(NULL);
|
|
id = (Identifier *)ti;
|
|
}
|
|
idents.data[i] = id;
|
|
}
|
|
}
|
|
|
|
|
|
void TypeQualified::addIdent(Identifier *ident)
|
|
{
|
|
idents.push(ident);
|
|
}
|
|
|
|
void TypeQualified::toCBuffer2Helper(OutBuffer *buf, HdrGenState *hgs)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < idents.dim; i++)
|
|
{ Identifier *id = (Identifier *)idents.data[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;
|
|
TupleDeclaration *td;
|
|
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 (int i = 0; i < idents.dim; i++)
|
|
{
|
|
Identifier *id = (Identifier *)idents.data[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 = v->getConstInitializer();
|
|
if (!e)
|
|
e = new VarExp(loc, v);
|
|
t = e->type;
|
|
if (!t)
|
|
goto Lerror;
|
|
goto L3;
|
|
}
|
|
t = s->getType();
|
|
if (!t && s->isDeclaration())
|
|
t = s->isDeclaration()->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 = (Identifier *)idents.data[i];
|
|
//printf("e: '%s', id: '%s', type = %p\n", e->toChars(), id->toChars(), e->type);
|
|
if (id == Id::offsetof)
|
|
{ e = new DotIdExp(e->loc, e, id);
|
|
e = e->semantic(sc);
|
|
}
|
|
else
|
|
e = e->type->dotExp(sc, e, id);
|
|
}
|
|
*pe = e;
|
|
}
|
|
else
|
|
Lerror:
|
|
error(loc, "identifier '%s' of '%s' is not defined", id->toChars(), toChars());
|
|
return;
|
|
}
|
|
L2:
|
|
s = sm->toAlias();
|
|
}
|
|
|
|
v = s->isVarDeclaration();
|
|
if (v)
|
|
{
|
|
#if 0
|
|
// It's not a type, it's an expression
|
|
Expression *e = v->getConstInitializer();
|
|
if (e)
|
|
{
|
|
*pe = e->copy(); // make copy so we can change loc
|
|
(*pe)->loc = loc;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#if 0
|
|
WithScopeSymbol *withsym;
|
|
if (scopesym && (withsym = scopesym->isWithScopeSymbol()) != NULL)
|
|
{
|
|
// Same as wthis.ident
|
|
e = new VarExp(loc, withsym->withstate->wthis);
|
|
e = new DotIdExp(loc, e, ident);
|
|
//assert(0); // BUG: should handle this
|
|
}
|
|
else
|
|
#endif
|
|
*pe = new VarExp(loc, v);
|
|
}
|
|
return;
|
|
}
|
|
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())
|
|
{
|
|
Scope *scx;
|
|
|
|
for (scx = sc; 1; scx = scx->enclosing)
|
|
{
|
|
if (!scx)
|
|
{ error(loc, "forward reference to '%s'", t->toChars());
|
|
return;
|
|
}
|
|
if (scx->scopesym == scopesym)
|
|
break;
|
|
}
|
|
t = t->semantic(loc, scx);
|
|
//((TypeIdentifier *)t)->resolve(loc, scx, pe, &t, ps);
|
|
}
|
|
}
|
|
if (t->ty == Ttuple)
|
|
*pt = t->syntaxCopy();
|
|
else
|
|
*pt = t->merge();
|
|
}
|
|
if (!s)
|
|
{
|
|
error(loc, "identifier '%s' is not defined", toChars());
|
|
}
|
|
}
|
|
|
|
/***************************** 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)
|
|
{ unsigned len;
|
|
char *name;
|
|
|
|
Type::toDecoBuffer(buf, flag);
|
|
name = ident->toChars();
|
|
len = strlen(name);
|
|
buf->printf("%d%s", 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 *s;
|
|
Dsymbol *scopesym;
|
|
|
|
//printf("TypeIdentifier::resolve(sc = %p, idents = '%s')\n", sc, toChars());
|
|
s = sc->search(loc, ident, &scopesym);
|
|
resolveHelper(loc, sc, s, scopesym, pe, pt, ps);
|
|
if (*pt && mod)
|
|
{
|
|
if (mod & MODconst)
|
|
*pt = (*pt)->constOf();
|
|
else if (mod & MODinvariant)
|
|
*pt = (*pt)->invariantOf();
|
|
}
|
|
}
|
|
|
|
/*****************************************
|
|
* 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 (int i = 0; i < idents.dim; i++)
|
|
{
|
|
Identifier *id = (Identifier *)idents.data[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 == 1)
|
|
error(loc, "circular reference of typedef %s", tt->toChars());
|
|
}
|
|
if (isConst())
|
|
t = t->constOf();
|
|
else if (isInvariant())
|
|
t = t->invariantOf();
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
if (!global.gag)
|
|
printf("1: ");
|
|
#endif
|
|
if (s)
|
|
{
|
|
s->error(loc, "is used as a type");
|
|
//halt();
|
|
}
|
|
else
|
|
error(loc, "%s is used as a type", toChars());
|
|
t = tvoid;
|
|
}
|
|
//t->print();
|
|
return t;
|
|
}
|
|
|
|
Type *TypeIdentifier::reliesOnTident()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
Expression *TypeIdentifier::toExpression()
|
|
{
|
|
Expression *e = new IdentifierExp(loc, ident);
|
|
for (int i = 0; i < idents.dim; i++)
|
|
{
|
|
Identifier *id = (Identifier *)idents.data[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)
|
|
s->semantic(sc);
|
|
resolveHelper(loc, sc, s, NULL, pe, pt, ps);
|
|
if (*pt && mod)
|
|
{
|
|
if (mod & MODconst)
|
|
*pt = (*pt)->constOf();
|
|
else if (mod & MODinvariant)
|
|
*pt = (*pt)->invariantOf();
|
|
}
|
|
//printf("pt = '%s'\n", (*pt)->toChars());
|
|
}
|
|
|
|
Type *TypeInstance::semantic(Loc loc, Scope *sc)
|
|
{
|
|
Type *t;
|
|
Expression *e;
|
|
Dsymbol *s;
|
|
|
|
//printf("TypeInstance::semantic(%s)\n", toChars());
|
|
|
|
if (sc->parameterSpecialization)
|
|
{
|
|
unsigned errors = global.errors;
|
|
global.gag++;
|
|
|
|
resolve(loc, sc, &e, &t, &s);
|
|
|
|
global.gag--;
|
|
if (errors != global.errors)
|
|
{ if (global.gag == 0)
|
|
global.errors = errors;
|
|
return this;
|
|
}
|
|
}
|
|
else
|
|
resolve(loc, sc, &e, &t, &s);
|
|
|
|
if (!t)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("2: ");
|
|
#endif
|
|
error(loc, "%s is used as a type", toChars());
|
|
t = tvoid;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
|
|
/***************************** TypeTypeof *****************************/
|
|
|
|
TypeTypeof::TypeTypeof(Loc loc, Expression *exp)
|
|
: TypeQualified(Ttypeof, loc)
|
|
{
|
|
this->exp = exp;
|
|
}
|
|
|
|
Type *TypeTypeof::syntaxCopy()
|
|
{
|
|
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)
|
|
{ Expression *e;
|
|
Type *t;
|
|
|
|
//printf("TypeTypeof::semantic() %p\n", this);
|
|
|
|
//static int nest; if (++nest == 50) *(char*)0=0;
|
|
|
|
#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
|
|
{
|
|
sc->intypeof++;
|
|
exp = exp->semantic(sc);
|
|
sc->intypeof--;
|
|
if (exp->op == TOKtype)
|
|
{
|
|
error(loc, "argument %s to typeof is not an expression", exp->toChars());
|
|
}
|
|
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());
|
|
|
|
/* 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 = (Identifier *)idents.data[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 tvoid;
|
|
}
|
|
|
|
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;
|
|
if (!sc->func)
|
|
{ error(loc, "typeof(return) must be inside function");
|
|
goto Lerr;
|
|
}
|
|
t = sc->func->type->nextOf();
|
|
|
|
if (mod & MODinvariant)
|
|
t = t->invariantOf();
|
|
else if (mod & MODconst)
|
|
t = t->constOf();
|
|
|
|
if (idents.dim)
|
|
{
|
|
Dsymbol *s = t->toDsymbol(sc);
|
|
for (size_t i = 0; i < idents.dim; i++)
|
|
{
|
|
if (!s)
|
|
break;
|
|
Identifier *id = (Identifier *)idents.data[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)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("1: ");
|
|
#endif
|
|
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->memtype)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("2: ");
|
|
#endif
|
|
error(sym->loc, "enum %s is forward referenced", sym->toChars());
|
|
return tint32;
|
|
}
|
|
return sym->memtype->toBasetype();
|
|
}
|
|
|
|
void TypeEnum::toDecoBuffer(OutBuffer *buf, int flag)
|
|
{
|
|
const char *name = sym->mangle();
|
|
Type::toDecoBuffer(buf, flag);
|
|
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)
|
|
{
|
|
return getProperty(e->loc, 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 = defaultInit(loc);
|
|
}
|
|
else if (ident == Id::stringof)
|
|
{ char *s = toChars();
|
|
e = new StringExp(loc, s, strlen(s), 'c');
|
|
Scope sc;
|
|
e = e->semantic(&sc);
|
|
}
|
|
else
|
|
{
|
|
e = toBasetype()->getProperty(loc, ident);
|
|
}
|
|
return e;
|
|
|
|
Lfwd:
|
|
error(loc, "forward reference of %s.%s", toChars(), ident->toChars());
|
|
return new IntegerExp(0, 0, this);
|
|
}
|
|
|
|
int TypeEnum::isintegral()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int TypeEnum::isfloating()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int TypeEnum::isunsigned()
|
|
{
|
|
return sym->memtype->isunsigned();
|
|
}
|
|
|
|
int TypeEnum::isscalar()
|
|
{
|
|
return 1;
|
|
//return sym->memtype->isscalar();
|
|
}
|
|
|
|
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 &&
|
|
to->mod == MODconst)
|
|
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 IntegerExp(0, 0, this);
|
|
}
|
|
return sym->defaultval;
|
|
}
|
|
|
|
int TypeEnum::isZeroInit()
|
|
{
|
|
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);
|
|
sym->semantic(sc);
|
|
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;
|
|
}
|
|
|
|
Type *TypeTypedef::toHeadMutable()
|
|
{
|
|
if (!mod)
|
|
return this;
|
|
|
|
Type *tb = toBasetype();
|
|
Type *t = tb->toHeadMutable();
|
|
if (t->equals(tb))
|
|
return this;
|
|
else
|
|
return mutableOf();
|
|
}
|
|
|
|
void TypeTypedef::toDecoBuffer(OutBuffer *buf, int flag)
|
|
{
|
|
Type::toDecoBuffer(buf, flag);
|
|
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);
|
|
}
|
|
|
|
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()
|
|
{
|
|
return sym->basetype->isAssignable();
|
|
}
|
|
|
|
int TypeTypedef::checkBoolean()
|
|
{
|
|
return sym->basetype->checkBoolean();
|
|
}
|
|
|
|
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;
|
|
if (mod == MODconst && !t->isInvariant())
|
|
t = t->constOf();
|
|
else if (mod == MODinvariant)
|
|
t = t->invariantOf();
|
|
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;
|
|
}
|
|
|
|
|
|
Expression *TypeTypedef::defaultInit(Loc loc)
|
|
{ Expression *e;
|
|
Type *bt;
|
|
|
|
#if LOGDEFAULTINIT
|
|
printf("TypeTypedef::defaultInit() '%s'\n", toChars());
|
|
#endif
|
|
if (sym->init)
|
|
{
|
|
//sym->init->toExpression()->print();
|
|
return sym->init->toExpression();
|
|
}
|
|
bt = sym->basetype;
|
|
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;
|
|
}
|
|
|
|
int TypeTypedef::isZeroInit()
|
|
{
|
|
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();
|
|
sym->inuse = 0;
|
|
return result;
|
|
}
|
|
|
|
int TypeTypedef::hasPointers()
|
|
{
|
|
return toBasetype()->hasPointers();
|
|
}
|
|
|
|
/***************************** 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()
|
|
{ unsigned sz;
|
|
|
|
sym->size(0); // give error for forward references
|
|
sz = sym->alignsize;
|
|
if (sz > sym->structalign)
|
|
sz = sym->structalign;
|
|
return sz;
|
|
}
|
|
|
|
Dsymbol *TypeStruct::toDsymbol(Scope *sc)
|
|
{
|
|
return sym;
|
|
}
|
|
|
|
void TypeStruct::toDecoBuffer(OutBuffer *buf, int flag)
|
|
{
|
|
const char *name = sym->mangle();
|
|
//printf("TypeStruct::toDecoBuffer('%s') = '%s'\n", toChars(), name);
|
|
Type::toDecoBuffer(buf, flag);
|
|
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)
|
|
{ unsigned offset;
|
|
|
|
Expression *b;
|
|
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 IntegerExp(e->loc, 0, Type::tint32);
|
|
}
|
|
|
|
/* 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
|
|
Expressions *exps = new Expressions;
|
|
exps->reserve(sym->fields.dim);
|
|
for (size_t i = 0; i < sym->fields.dim; i++)
|
|
{ VarDeclaration *v = (VarDeclaration *)sym->fields.data[i];
|
|
Expression *fe = new DotVarExp(e->loc, e, 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 (ident != Id::__sizeof &&
|
|
ident != Id::alignof &&
|
|
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.opId().ident
|
|
*/
|
|
e = build_overload(e->loc, sc, e, NULL, fd->ident);
|
|
e = new DotIdExp(e->loc, e, ident);
|
|
return e->semantic(sc);
|
|
}
|
|
}
|
|
return Type::dotExp(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 DotTypeExp(e->loc, e, s);
|
|
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->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
TemplateInstance *ti = s->isTemplateInstance();
|
|
if (ti)
|
|
{ if (!ti->semanticdone)
|
|
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;
|
|
}
|
|
|
|
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->needThis() && fd && fd->vthis)
|
|
{
|
|
e = new DotVarExp(e->loc, new ThisExp(e->loc), d);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
if (d->isTupleDeclaration())
|
|
{
|
|
e = new TupleExp(e->loc, d->isTupleDeclaration());
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
return new VarExp(e->loc, d, 1);
|
|
}
|
|
|
|
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->type = d->type;
|
|
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);
|
|
|
|
// LDC we don't want dot exprs turned into pointer arithmetic. it complicates things for no apparent gain
|
|
#ifndef IN_LLVM
|
|
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;
|
|
if (e->type->isConst())
|
|
b->type = b->type->constOf();
|
|
else if (e->type->isInvariant())
|
|
b->type = b->type->invariantOf();
|
|
return b;
|
|
#endif
|
|
}
|
|
|
|
de = new DotVarExp(e->loc, e, d);
|
|
return de->semantic(sc);
|
|
}
|
|
|
|
unsigned TypeStruct::memalign(unsigned salign)
|
|
{
|
|
sym->size(0); // give error for forward references
|
|
return sym->structalign;
|
|
}
|
|
|
|
Expression *TypeStruct::defaultInit(Loc loc)
|
|
{ Declaration *d;
|
|
|
|
#if LOGDEFAULTINIT
|
|
printf("TypeStruct::defaultInit() '%s'\n", toChars());
|
|
#endif
|
|
d = new StaticStructInitDeclaration(sym->loc, sym);
|
|
assert(d);
|
|
d->type = this;
|
|
return new VarExp(sym->loc, d);
|
|
}
|
|
|
|
int TypeStruct::isZeroInit()
|
|
{
|
|
return sym->zeroInit;
|
|
}
|
|
|
|
int TypeStruct::checkBoolean()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int TypeStruct::isAssignable()
|
|
{
|
|
/* 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 = (VarDeclaration *)sym->fields.data[i];
|
|
if (v->isConst() || v->isInvariant())
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
int TypeStruct::hasPointers()
|
|
{
|
|
StructDeclaration *s = sym;
|
|
|
|
sym->size(0); // give error for forward references
|
|
for (size_t i = 0; i < s->fields.dim; i++)
|
|
{
|
|
Dsymbol *sm = (Dsymbol *)s->fields.data[i];
|
|
if (sm->hasPointers())
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
MATCH TypeStruct::implicitConvTo(Type *to)
|
|
{ MATCH m;
|
|
|
|
//printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to->toChars());
|
|
if (ty == to->ty && sym == ((TypeStruct *)to)->sym)
|
|
{ m = MATCHexact; // exact match
|
|
if (mod != to->mod)
|
|
{
|
|
if (to->mod == MODconst)
|
|
m = MATCHconst;
|
|
else
|
|
{ /* Check all the fields. If they can all be converted,
|
|
* allow the conversion.
|
|
*/
|
|
for (int i = 0; i < sym->fields.dim; i++)
|
|
{ Dsymbol *s = (Dsymbol *)sym->fields.data[i];
|
|
VarDeclaration *v = s->isVarDeclaration();
|
|
assert(v && v->storage_class & STCfield);
|
|
|
|
// 'from' type
|
|
Type *tvf = v->type;
|
|
if (mod == MODconst)
|
|
tvf = tvf->constOf();
|
|
else if (mod == MODinvariant)
|
|
tvf = tvf->invariantOf();
|
|
|
|
// 'to' type
|
|
Type *tv = v->type;
|
|
if (to->mod == 0)
|
|
tv = tv->mutableOf();
|
|
else
|
|
{ assert(to->mod == MODinvariant);
|
|
tv = tv->invariantOf();
|
|
}
|
|
|
|
//printf("\t%s => %s, match = %d\n", v->type->toChars(), tv->toChars(), tvf->implicitConvTo(tv));
|
|
if (tvf->implicitConvTo(tv) < MATCHconst)
|
|
return MATCHnomatch;
|
|
}
|
|
m = MATCHconst;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
m = MATCHnomatch; // no match
|
|
return m;
|
|
}
|
|
|
|
Type *TypeStruct::toHeadMutable()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
MATCH TypeStruct::constConv(Type *to)
|
|
{
|
|
if (equals(to))
|
|
return MATCHexact;
|
|
if (ty == to->ty && sym == ((TypeStruct *)to)->sym &&
|
|
to->mod == MODconst)
|
|
return MATCHconst;
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
|
|
/***************************** TypeClass *****************************/
|
|
|
|
TypeClass::TypeClass(ClassDeclaration *sym)
|
|
: Type(Tclass)
|
|
{
|
|
this->sym = sym;
|
|
}
|
|
|
|
char *TypeClass::toChars()
|
|
{
|
|
if (mod)
|
|
return Type::toChars();
|
|
return sym->toPrettyChars();
|
|
}
|
|
|
|
Type *TypeClass::syntaxCopy()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
Type *TypeClass::semantic(Loc loc, Scope *sc)
|
|
{
|
|
//printf("TypeClass::semantic(%s)\n", sym->toChars());
|
|
if (sym->scope)
|
|
sym->semantic(sym->scope);
|
|
return merge();
|
|
}
|
|
|
|
d_uns64 TypeClass::size(Loc loc)
|
|
{
|
|
return PTRSIZE;
|
|
}
|
|
|
|
Dsymbol *TypeClass::toDsymbol(Scope *sc)
|
|
{
|
|
return sym;
|
|
}
|
|
|
|
void TypeClass::toDecoBuffer(OutBuffer *buf, int flag)
|
|
{
|
|
const char *name = sym->mangle();
|
|
//printf("TypeClass::toDecoBuffer('%s' flag=%d mod=%x) = '%s'\n", toChars(), flag, mod, name);
|
|
Type::toDecoBuffer(buf, flag);
|
|
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)
|
|
{ unsigned offset;
|
|
|
|
Expression *b;
|
|
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
|
|
Expressions *exps = new Expressions;
|
|
exps->reserve(sym->fields.dim);
|
|
for (size_t i = 0; i < sym->fields.dim; i++)
|
|
{ VarDeclaration *v = (VarDeclaration *)sym->fields.data[i];
|
|
Expression *fe = new DotVarExp(e->loc, e, 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 a base class
|
|
ClassDeclaration *cbase;
|
|
for (cbase = sym->baseClass; cbase; cbase = cbase->baseClass)
|
|
{
|
|
if (cbase->ident->equals(ident))
|
|
{
|
|
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 ClassInfoDeclaration(sym);
|
|
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)
|
|
*/
|
|
e = e->castTo(sc, tvoidptr->pointerTo());
|
|
e = new AddExp(e->loc, e, new IntegerExp(1));
|
|
e = new PtrExp(e->loc, e);
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
if (ident == Id::typeinfo)
|
|
{
|
|
if (!global.params.useDeprecated)
|
|
error(e->loc, ".typeinfo deprecated, use typeid(type)");
|
|
return getTypeInfo(sc);
|
|
}
|
|
if (ident == Id::outer && sym->vthis)
|
|
{
|
|
s = sym->vthis;
|
|
}
|
|
else
|
|
{
|
|
|
|
if (ident != Id::__sizeof &&
|
|
ident != Id::alignof &&
|
|
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.opId().ident
|
|
*/
|
|
e = build_overload(e->loc, sc, e, NULL, fd->ident);
|
|
e = new DotIdExp(e->loc, e, ident);
|
|
return e->semantic(sc);
|
|
}
|
|
}
|
|
|
|
return Type::dotExp(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())
|
|
{
|
|
// if (e->op == TOKtype)
|
|
return new TypeExp(e->loc, s->getType());
|
|
// return new DotTypeExp(e->loc, e, s);
|
|
}
|
|
|
|
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->semantic(sc);
|
|
return e;
|
|
}
|
|
|
|
TemplateInstance *ti = s->isTemplateInstance();
|
|
if (ti)
|
|
{ if (!ti->semanticdone)
|
|
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;
|
|
}
|
|
|
|
Declaration *d = s->isDeclaration();
|
|
if (!d)
|
|
{
|
|
e->error("%s.%s is not a declaration", e->toChars(), ident->toChars());
|
|
return new IntegerExp(e->loc, 1, Type::tint32);
|
|
}
|
|
|
|
if (e->op == TOKtype)
|
|
{
|
|
/* It's:
|
|
* Class.d
|
|
*/
|
|
if (d->needThis() && (hasThis(sc) || !d->isFuncDeclaration()))
|
|
{
|
|
if (sc->func)
|
|
{
|
|
ClassDeclaration *thiscd;
|
|
thiscd = sc->func->toParent()->isClassDeclaration();
|
|
|
|
if (thiscd)
|
|
{
|
|
ClassDeclaration *cd = e->type->isClassHandle();
|
|
|
|
if (cd == thiscd)
|
|
{
|
|
e = new ThisExp(e->loc);
|
|
e = new DotTypeExp(e->loc, e, cd);
|
|
DotVarExp *de = new DotVarExp(e->loc, e, d);
|
|
e = de->semantic(sc);
|
|
return e;
|
|
}
|
|
else if ((!cd || !cd->isBaseOf(thiscd, NULL)) &&
|
|
!d->isFuncDeclaration())
|
|
e->error("'this' is required, but %s is not a base class of %s", e->type->toChars(), thiscd->toChars());
|
|
}
|
|
}
|
|
|
|
/* Rewrite as:
|
|
* this.d
|
|
*/
|
|
DotVarExp *de = new DotVarExp(e->loc, new ThisExp(e->loc), d);
|
|
e = de->semantic(sc);
|
|
return e;
|
|
}
|
|
else if (d->isTupleDeclaration())
|
|
{
|
|
e = new TupleExp(e->loc, d->isTupleDeclaration());
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
else
|
|
{
|
|
VarExp *ve = new VarExp(e->loc, d, 1);
|
|
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->type = d->type;
|
|
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);
|
|
return de->semantic(sc);
|
|
}
|
|
|
|
ClassDeclaration *TypeClass::isClassHandle()
|
|
{
|
|
return sym;
|
|
}
|
|
|
|
int TypeClass::isauto()
|
|
{
|
|
return sym->isauto;
|
|
}
|
|
|
|
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 && cdto->isBaseOf(sym, NULL))
|
|
{ //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;
|
|
}
|
|
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
MATCH TypeClass::constConv(Type *to)
|
|
{
|
|
if (equals(to))
|
|
return MATCHexact;
|
|
if (ty == to->ty && sym == ((TypeClass *)to)->sym &&
|
|
to->mod == MODconst)
|
|
return MATCHconst;
|
|
return MATCHnomatch;
|
|
}
|
|
|
|
Type *TypeClass::toHeadMutable()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
Expression *TypeClass::defaultInit(Loc loc)
|
|
{
|
|
#if LOGDEFAULTINIT
|
|
printf("TypeClass::defaultInit() '%s'\n", toChars());
|
|
#endif
|
|
Expression *e;
|
|
e = new NullExp(loc);
|
|
e->type = this;
|
|
return e;
|
|
}
|
|
|
|
int TypeClass::isZeroInit()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int TypeClass::checkBoolean()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
int TypeClass::hasPointers()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************** TypeTuple *****************************/
|
|
|
|
TypeTuple::TypeTuple(Arguments *arguments)
|
|
: Type(Ttuple)
|
|
{
|
|
//printf("TypeTuple(this = %p)\n", this);
|
|
this->arguments = arguments;
|
|
//printf("TypeTuple() %s\n", toChars());
|
|
#ifdef DEBUG
|
|
if (arguments)
|
|
{
|
|
for (size_t i = 0; i < arguments->dim; i++)
|
|
{
|
|
Argument *arg = (Argument *)arguments->data[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)
|
|
{
|
|
Arguments *arguments = new Arguments;
|
|
if (exps)
|
|
{
|
|
arguments->setDim(exps->dim);
|
|
for (size_t i = 0; i < exps->dim; i++)
|
|
{ Expression *e = (Expression *)exps->data[i];
|
|
if (e->type->ty == Ttuple)
|
|
e->error("cannot form tuple of tuples");
|
|
Argument *arg = new Argument(STCundefined, e->type, NULL, NULL);
|
|
arguments->data[i] = (void *)arg;
|
|
}
|
|
}
|
|
this->arguments = arguments;
|
|
}
|
|
|
|
Type *TypeTuple::syntaxCopy()
|
|
{
|
|
Arguments *args = Argument::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() %s\n", 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++)
|
|
{ Argument *arg1 = (Argument *)arguments->data[i];
|
|
Argument *arg2 = (Argument *)tt->arguments->data[i];
|
|
|
|
if (!arg1->type->equals(arg2->type))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Type *TypeTuple::reliesOnTident()
|
|
{
|
|
if (arguments)
|
|
{
|
|
for (size_t i = 0; i < arguments->dim; i++)
|
|
{
|
|
Argument *arg = (Argument *)arguments->data[i];
|
|
Type *t = arg->type->reliesOnTident();
|
|
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 Arguments();
|
|
t->arguments->setDim(arguments->dim);
|
|
for (size_t i = 0; i < arguments->dim; i++)
|
|
{ Argument *arg = (Argument *)arguments->data[i];
|
|
Argument *narg = new Argument(arg->storageClass, arg->type->constOf(), arg->ident, arg->defaultArg);
|
|
t->arguments->data[i] = (Argument *)narg;
|
|
}
|
|
return t;
|
|
}
|
|
#endif
|
|
|
|
void TypeTuple::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod)
|
|
{
|
|
Argument::argsToCBuffer(buf, hgs, arguments, 0);
|
|
}
|
|
|
|
void TypeTuple::toDecoBuffer(OutBuffer *buf, int flag)
|
|
{
|
|
//printf("TypeTuple::toDecoBuffer() this = %p, %s\n", this, toChars());
|
|
Type::toDecoBuffer(buf, flag);
|
|
OutBuffer buf2;
|
|
Argument::argsToDecoBuffer(&buf2, arguments);
|
|
unsigned len = 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
|
|
{
|
|
error(loc, "no property '%s' for tuple '%s'", ident->toChars(), toChars());
|
|
e = new IntegerExp(loc, 1, Type::tint32);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/***************************** 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);
|
|
if (mod == MODconst && !next->isInvariant())
|
|
next = next->constOf();
|
|
else if (mod == MODinvariant)
|
|
next = next->invariantOf();
|
|
//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->optimize(WANTvalue);
|
|
uinteger_t i1 = lwr->toUInteger();
|
|
|
|
upr = semanticLength(sc, tbn, upr);
|
|
upr = upr->optimize(WANTvalue);
|
|
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;
|
|
}
|
|
|
|
Arguments *args = new Arguments;
|
|
args->reserve(i2 - i1);
|
|
for (size_t i = i1; i < i2; i++)
|
|
{ Argument *arg = (Argument *)tt->arguments->data[i];
|
|
args->push(arg);
|
|
}
|
|
|
|
return new TypeTuple(args);
|
|
}
|
|
|
|
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->optimize(WANTvalue);
|
|
uinteger_t i1 = lwr->toUInteger();
|
|
|
|
upr = upr->semantic(sc);
|
|
upr = upr->optimize(WANTvalue);
|
|
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->data[i] = td->objects->data[(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());
|
|
}
|
|
|
|
/***************************** Argument *****************************/
|
|
|
|
Argument::Argument(unsigned storageClass, Type *type, Identifier *ident, Expression *defaultArg)
|
|
{
|
|
this->type = type;
|
|
this->ident = ident;
|
|
this->storageClass = storageClass;
|
|
this->defaultArg = defaultArg;
|
|
}
|
|
|
|
Argument *Argument::syntaxCopy()
|
|
{
|
|
Argument *a = new Argument(storageClass,
|
|
type ? type->syntaxCopy() : NULL,
|
|
ident,
|
|
defaultArg ? defaultArg->syntaxCopy() : NULL);
|
|
return a;
|
|
}
|
|
|
|
Arguments *Argument::arraySyntaxCopy(Arguments *args)
|
|
{ Arguments *a = NULL;
|
|
|
|
if (args)
|
|
{
|
|
a = new Arguments();
|
|
a->setDim(args->dim);
|
|
for (size_t i = 0; i < a->dim; i++)
|
|
{ Argument *arg = (Argument *)args->data[i];
|
|
|
|
arg = arg->syntaxCopy();
|
|
a->data[i] = (void *)arg;
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
|
|
char *Argument::argsTypesToChars(Arguments *args, int varargs)
|
|
{
|
|
OutBuffer *buf = new OutBuffer();
|
|
|
|
#if 1
|
|
HdrGenState hgs;
|
|
argsToCBuffer(buf, &hgs, args, varargs);
|
|
#else
|
|
buf->writeByte('(');
|
|
if (args)
|
|
{ OutBuffer argbuf;
|
|
HdrGenState hgs;
|
|
|
|
for (int i = 0; i < args->dim; i++)
|
|
{ if (i)
|
|
buf->writeByte(',');
|
|
Argument *arg = (Argument *)args->data[i];
|
|
argbuf.reset();
|
|
arg->type->toCBuffer2(&argbuf, &hgs, 0);
|
|
buf->write(&argbuf);
|
|
}
|
|
if (varargs)
|
|
{
|
|
if (i && varargs == 1)
|
|
buf->writeByte(',');
|
|
buf->writestring("...");
|
|
}
|
|
}
|
|
buf->writeByte(')');
|
|
#endif
|
|
return buf->toChars();
|
|
}
|
|
|
|
void Argument::argsToCBuffer(OutBuffer *buf, HdrGenState *hgs, Arguments *arguments, int varargs)
|
|
{
|
|
buf->writeByte('(');
|
|
if (arguments)
|
|
{ int i;
|
|
OutBuffer argbuf;
|
|
|
|
for (i = 0; i < arguments->dim; i++)
|
|
{
|
|
if (i)
|
|
buf->writestring(", ");
|
|
Argument *arg = (Argument *)arguments->data[i];
|
|
|
|
if (arg->storageClass & STCout)
|
|
buf->writestring("out ");
|
|
else if (arg->storageClass & STCref)
|
|
buf->writestring((global.params.Dversion == 1)
|
|
? (char *)"inout " : (char *)"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 ");
|
|
else if (arg->storageClass & STCauto)
|
|
buf->writestring("auto ");
|
|
|
|
if (arg->storageClass & STCscope)
|
|
buf->writestring("scope ");
|
|
|
|
if (arg->storageClass & STCconst)
|
|
buf->writestring("const ");
|
|
if (arg->storageClass & STCinvariant)
|
|
buf->writestring("invariant ");
|
|
if (arg->storageClass & STCshared)
|
|
buf->writestring("shared ");
|
|
|
|
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 (i && varargs == 1)
|
|
buf->writeByte(',');
|
|
buf->writestring("...");
|
|
}
|
|
}
|
|
buf->writeByte(')');
|
|
}
|
|
|
|
|
|
void Argument::argsToDecoBuffer(OutBuffer *buf, Arguments *arguments)
|
|
{
|
|
//printf("Argument::argsToDecoBuffer()\n");
|
|
|
|
// Write argument types
|
|
if (arguments)
|
|
{
|
|
size_t dim = Argument::dim(arguments);
|
|
for (size_t i = 0; i < dim; i++)
|
|
{
|
|
Argument *arg = Argument::getNth(arguments, i);
|
|
arg->toDecoBuffer(buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************
|
|
* Determine if parameter list is really a template parameter list
|
|
* (i.e. it has auto or alias parameters)
|
|
*/
|
|
|
|
int Argument::isTPL(Arguments *arguments)
|
|
{
|
|
//printf("Argument::isTPL()\n");
|
|
|
|
if (arguments)
|
|
{
|
|
size_t dim = Argument::dim(arguments);
|
|
for (size_t i = 0; i < dim; i++)
|
|
{
|
|
Argument *arg = Argument::getNth(arguments, i);
|
|
if (arg->storageClass & (STCalias | STCauto | STCstatic))
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************
|
|
* Determine if parameter is a lazy array of delegates.
|
|
* If so, return the return type of those delegates.
|
|
* If not, return NULL.
|
|
*/
|
|
|
|
Type *Argument::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 && Argument::dim(tf->parameters) == 0)
|
|
{
|
|
return tf->next; // return type of delegate
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void Argument::toDecoBuffer(OutBuffer *buf)
|
|
{
|
|
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
|
|
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);
|
|
#endif
|
|
}
|
|
|
|
/***************************************
|
|
* Determine number of arguments, folding in tuples.
|
|
*/
|
|
|
|
size_t Argument::dim(Arguments *args)
|
|
{
|
|
size_t n = 0;
|
|
if (args)
|
|
{
|
|
for (size_t i = 0; i < args->dim; i++)
|
|
{ Argument *arg = (Argument *)args->data[i];
|
|
Type *t = arg->type->toBasetype();
|
|
|
|
if (t->ty == Ttuple)
|
|
{ TypeTuple *tu = (TypeTuple *)t;
|
|
n += dim(tu->arguments);
|
|
}
|
|
else
|
|
n++;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/***************************************
|
|
* Get nth Argument, folding in tuples.
|
|
* Returns:
|
|
* Argument* nth Argument
|
|
* NULL not found, *pn gets incremented by the number
|
|
* of Arguments
|
|
*/
|
|
|
|
Argument *Argument::getNth(Arguments *args, size_t nth, size_t *pn)
|
|
{
|
|
if (!args)
|
|
return NULL;
|
|
|
|
size_t n = 0;
|
|
for (size_t i = 0; i < args->dim; i++)
|
|
{ Argument *arg = (Argument *)args->data[i];
|
|
Type *t = arg->type->toBasetype();
|
|
|
|
if (t->ty == Ttuple)
|
|
{ TypeTuple *tu = (TypeTuple *)t;
|
|
arg = getNth(tu->arguments, nth - n, &n);
|
|
if (arg)
|
|
return arg;
|
|
}
|
|
else if (n == nth)
|
|
return arg;
|
|
else
|
|
n++;
|
|
}
|
|
|
|
if (pn)
|
|
*pn += n;
|
|
return NULL;
|
|
}
|