mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-11 18:33:14 +01:00
638 lines
18 KiB
C
638 lines
18 KiB
C
|
|
// Compiler implementation of the D programming language
|
|
// Copyright (c) 1999-2012 by Digital Mars
|
|
// All Rights Reserved
|
|
// written by Walter Bright
|
|
// http://www.digitalmars.com
|
|
// 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.
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
|
|
#include "rmem.h"
|
|
|
|
//#include "port.h"
|
|
#include "mtype.h"
|
|
#include "init.h"
|
|
#include "expression.h"
|
|
#include "template.h"
|
|
#include "utf.h"
|
|
#include "enum.h"
|
|
#include "scope.h"
|
|
#include "statement.h"
|
|
#include "declaration.h"
|
|
#include "aggregate.h"
|
|
#include "import.h"
|
|
#include "id.h"
|
|
#include "dsymbol.h"
|
|
#include "module.h"
|
|
#include "attrib.h"
|
|
#include "hdrgen.h"
|
|
#include "parse.h"
|
|
|
|
#define LOGSEMANTIC 0
|
|
|
|
#if DMDV2
|
|
|
|
/************************************************
|
|
* Delegate to be passed to overloadApply() that looks
|
|
* for functions matching a trait.
|
|
*/
|
|
|
|
struct Ptrait
|
|
{
|
|
Expression *e1;
|
|
Expressions *exps; // collected results
|
|
Identifier *ident; // which trait we're looking for
|
|
};
|
|
|
|
static int fptraits(void *param, FuncDeclaration *f)
|
|
{ Ptrait *p = (Ptrait *)param;
|
|
|
|
if (p->ident == Id::getVirtualFunctions && !f->isVirtual())
|
|
return 0;
|
|
|
|
if (p->ident == Id::getVirtualMethods && !f->isVirtualMethod())
|
|
return 0;
|
|
|
|
Expression *e;
|
|
if (p->e1)
|
|
e = new DotVarExp(0, p->e1, new FuncAliasDeclaration(f, 0));
|
|
else
|
|
e = new DsymbolExp(0, new FuncAliasDeclaration(f, 0));
|
|
p->exps->push(e);
|
|
return 0;
|
|
}
|
|
|
|
/************************ TraitsExp ************************************/
|
|
|
|
Expression *TraitsExp::semantic(Scope *sc)
|
|
{
|
|
#if LOGSEMANTIC
|
|
printf("TraitsExp::semantic() %s\n", toChars());
|
|
#endif
|
|
if (ident != Id::compiles && ident != Id::isSame &&
|
|
ident != Id::identifier)
|
|
{
|
|
TemplateInstance::semanticTiargs(loc, sc, args, 1);
|
|
}
|
|
size_t dim = args ? args->dim : 0;
|
|
Declaration *d;
|
|
|
|
#define ISTYPE(cond) \
|
|
for (size_t i = 0; i < dim; i++) \
|
|
{ Type *t = getType((*args)[i]); \
|
|
if (!t) \
|
|
goto Lfalse; \
|
|
if (!(cond)) \
|
|
goto Lfalse; \
|
|
} \
|
|
if (!dim) \
|
|
goto Lfalse; \
|
|
goto Ltrue;
|
|
|
|
#define ISDSYMBOL(cond) \
|
|
for (size_t i = 0; i < dim; i++) \
|
|
{ Dsymbol *s = getDsymbol((*args)[i]); \
|
|
if (!s) \
|
|
goto Lfalse; \
|
|
if (!(cond)) \
|
|
goto Lfalse; \
|
|
} \
|
|
if (!dim) \
|
|
goto Lfalse; \
|
|
goto Ltrue;
|
|
|
|
|
|
|
|
if (ident == Id::isArithmetic)
|
|
{
|
|
ISTYPE(t->isintegral() || t->isfloating())
|
|
}
|
|
else if (ident == Id::isFloating)
|
|
{
|
|
ISTYPE(t->isfloating())
|
|
}
|
|
else if (ident == Id::isIntegral)
|
|
{
|
|
ISTYPE(t->isintegral())
|
|
}
|
|
else if (ident == Id::isScalar)
|
|
{
|
|
ISTYPE(t->isscalar())
|
|
}
|
|
else if (ident == Id::isUnsigned)
|
|
{
|
|
ISTYPE(t->isunsigned())
|
|
}
|
|
else if (ident == Id::isAssociativeArray)
|
|
{
|
|
ISTYPE(t->toBasetype()->ty == Taarray)
|
|
}
|
|
else if (ident == Id::isStaticArray)
|
|
{
|
|
ISTYPE(t->toBasetype()->ty == Tsarray)
|
|
}
|
|
else if (ident == Id::isAbstractClass)
|
|
{
|
|
ISTYPE(t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->isAbstract())
|
|
}
|
|
else if (ident == Id::isFinalClass)
|
|
{
|
|
ISTYPE(t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal)
|
|
}
|
|
else if (ident == Id::isPOD)
|
|
{
|
|
if (dim != 1)
|
|
goto Ldimerror;
|
|
Object *o = (*args)[0];
|
|
Type *t = isType(o);
|
|
StructDeclaration *sd;
|
|
if (!t)
|
|
{
|
|
error("type expected as second argument of __traits %s instead of %s", ident->toChars(), o->toChars());
|
|
goto Lfalse;
|
|
}
|
|
if (t->toBasetype()->ty == Tstruct
|
|
&& ((sd = (StructDeclaration *)(((TypeStruct *)t->toBasetype())->sym)) != NULL))
|
|
{
|
|
if (sd->isPOD())
|
|
goto Ltrue;
|
|
else
|
|
goto Lfalse;
|
|
}
|
|
goto Ltrue;
|
|
}
|
|
else if (ident == Id::isAbstractFunction)
|
|
{
|
|
FuncDeclaration *f;
|
|
ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isAbstract())
|
|
}
|
|
else if (ident == Id::isVirtualFunction)
|
|
{
|
|
FuncDeclaration *f;
|
|
ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isVirtual())
|
|
}
|
|
else if (ident == Id::isVirtualMethod)
|
|
{
|
|
FuncDeclaration *f;
|
|
ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isVirtualMethod())
|
|
}
|
|
else if (ident == Id::isFinalFunction)
|
|
{
|
|
FuncDeclaration *f;
|
|
ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && f->isFinal())
|
|
}
|
|
#if DMDV2
|
|
else if (ident == Id::isStaticFunction)
|
|
{
|
|
FuncDeclaration *f;
|
|
ISDSYMBOL((f = s->isFuncDeclaration()) != NULL && !f->needThis() && !f->isNested())
|
|
}
|
|
else if (ident == Id::isRef)
|
|
{
|
|
ISDSYMBOL((d = s->isDeclaration()) != NULL && d->isRef())
|
|
}
|
|
else if (ident == Id::isOut)
|
|
{
|
|
ISDSYMBOL((d = s->isDeclaration()) != NULL && d->isOut())
|
|
}
|
|
else if (ident == Id::isLazy)
|
|
{
|
|
ISDSYMBOL((d = s->isDeclaration()) != NULL && d->storage_class & STClazy)
|
|
}
|
|
else if (ident == Id::identifier)
|
|
{ // Get identifier for symbol as a string literal
|
|
|
|
/* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that
|
|
* a symbol should not be folded to a constant.
|
|
* Bit 1 means don't convert Parameter to Type if Parameter has an identifier
|
|
*/
|
|
TemplateInstance::semanticTiargs(loc, sc, args, 2);
|
|
|
|
if (dim != 1)
|
|
goto Ldimerror;
|
|
Object *o = (*args)[0];
|
|
Parameter *po = isParameter(o);
|
|
Identifier *id;
|
|
if (po)
|
|
{ id = po->ident;
|
|
assert(id);
|
|
}
|
|
else
|
|
{
|
|
Dsymbol *s = getDsymbol(o);
|
|
if (!s || !s->ident)
|
|
{
|
|
error("argument %s has no identifier", o->toChars());
|
|
goto Lfalse;
|
|
}
|
|
id = s->ident;
|
|
}
|
|
StringExp *se = new StringExp(loc, id->toChars());
|
|
return se->semantic(sc);
|
|
}
|
|
else if (ident == Id::getProtection)
|
|
{
|
|
if (dim != 1)
|
|
goto Ldimerror;
|
|
Object *o = (*args)[0];
|
|
Dsymbol *s = getDsymbol(o);
|
|
if (!s)
|
|
{
|
|
if (!isError(o))
|
|
error("argument %s has no protection", o->toChars());
|
|
goto Lfalse;
|
|
}
|
|
|
|
PROT protection = s->prot();
|
|
|
|
const char *protName = Pprotectionnames[protection];
|
|
|
|
StringExp *se = new StringExp(loc, (char *) protName);
|
|
return se->semantic(sc);
|
|
}
|
|
else if (ident == Id::parent)
|
|
{
|
|
if (dim != 1)
|
|
goto Ldimerror;
|
|
Object *o = (*args)[0];
|
|
Dsymbol *s = getDsymbol(o);
|
|
if (s)
|
|
{
|
|
if (FuncDeclaration *fd = s->isFuncDeclaration()) // Bugzilla 8943
|
|
s = fd->toAliasFunc();
|
|
s = s->toParent();
|
|
}
|
|
if (!s)
|
|
{
|
|
error("argument %s has no parent", o->toChars());
|
|
goto Lfalse;
|
|
}
|
|
return (new DsymbolExp(loc, s))->semantic(sc);
|
|
}
|
|
#endif
|
|
else if (ident == Id::hasMember ||
|
|
ident == Id::getMember ||
|
|
ident == Id::getOverloads ||
|
|
ident == Id::getVirtualMethods ||
|
|
ident == Id::getVirtualFunctions)
|
|
{
|
|
if (dim != 2)
|
|
goto Ldimerror;
|
|
Object *o = (*args)[0];
|
|
Expression *e = isExpression((*args)[1]);
|
|
if (!e)
|
|
{ error("expression expected as second argument of __traits %s", ident->toChars());
|
|
goto Lfalse;
|
|
}
|
|
e = e->ctfeInterpret();
|
|
StringExp *se = e->toString();
|
|
if (!se || se->length() == 0)
|
|
{ error("string expected as second argument of __traits %s instead of %s", ident->toChars(), e->toChars());
|
|
goto Lfalse;
|
|
}
|
|
se = se->toUTF8(sc);
|
|
if (se->sz != 1)
|
|
{ error("string must be chars");
|
|
goto Lfalse;
|
|
}
|
|
Identifier *id = Lexer::idPool((char *)se->string);
|
|
|
|
Type *t = isType(o);
|
|
e = isExpression(o);
|
|
Dsymbol *s = isDsymbol(o);
|
|
if (t)
|
|
e = typeDotIdExp(loc, t, id);
|
|
else if (e)
|
|
e = new DotIdExp(loc, e, id);
|
|
else if (s)
|
|
{ e = new DsymbolExp(loc, s);
|
|
e = new DotIdExp(loc, e, id);
|
|
}
|
|
else
|
|
{ error("invalid first argument");
|
|
goto Lfalse;
|
|
}
|
|
|
|
if (ident == Id::hasMember)
|
|
{
|
|
if (t)
|
|
{
|
|
Dsymbol *sym = t->toDsymbol(sc);
|
|
if (sym)
|
|
{
|
|
Dsymbol *sm = sym->search(loc, id, 0);
|
|
if (sm)
|
|
goto Ltrue;
|
|
}
|
|
}
|
|
|
|
/* Take any errors as meaning it wasn't found
|
|
*/
|
|
Scope *sc2 = sc->push();
|
|
//sc2->inHasMember++;
|
|
e = e->trySemantic(sc2);
|
|
sc2->pop();
|
|
if (!e)
|
|
goto Lfalse;
|
|
else
|
|
goto Ltrue;
|
|
}
|
|
else if (ident == Id::getMember)
|
|
{
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
else if (ident == Id::getVirtualFunctions ||
|
|
ident == Id::getVirtualMethods ||
|
|
ident == Id::getOverloads)
|
|
{
|
|
unsigned errors = global.errors;
|
|
Expression *ex = e;
|
|
e = e->semantic(sc);
|
|
if (errors < global.errors)
|
|
error("%s cannot be resolved", ex->toChars());
|
|
|
|
/* Create tuple of functions of e
|
|
*/
|
|
//e->dump(0);
|
|
Expressions *exps = new Expressions();
|
|
FuncDeclaration *f;
|
|
if (e->op == TOKvar)
|
|
{ VarExp *ve = (VarExp *)e;
|
|
f = ve->var->isFuncDeclaration();
|
|
e = NULL;
|
|
}
|
|
else if (e->op == TOKdotvar)
|
|
{ DotVarExp *dve = (DotVarExp *)e;
|
|
f = dve->var->isFuncDeclaration();
|
|
if (dve->e1->op == TOKdottype || dve->e1->op == TOKthis)
|
|
e = NULL;
|
|
else
|
|
e = dve->e1;
|
|
}
|
|
else
|
|
f = NULL;
|
|
Ptrait p;
|
|
p.exps = exps;
|
|
p.e1 = e;
|
|
p.ident = ident;
|
|
overloadApply(f, fptraits, &p);
|
|
|
|
TupleExp *tup = new TupleExp(loc, exps);
|
|
return tup->semantic(sc);
|
|
}
|
|
else
|
|
assert(0);
|
|
}
|
|
else if (ident == Id::classInstanceSize)
|
|
{
|
|
if (dim != 1)
|
|
goto Ldimerror;
|
|
Object *o = (*args)[0];
|
|
Dsymbol *s = getDsymbol(o);
|
|
ClassDeclaration *cd;
|
|
if (!s || (cd = s->isClassDeclaration()) == NULL)
|
|
{
|
|
error("first argument is not a class");
|
|
goto Lfalse;
|
|
}
|
|
return new IntegerExp(loc, cd->structsize, Type::tsize_t);
|
|
}
|
|
else if (ident == Id::getAttributes)
|
|
{
|
|
if (dim != 1)
|
|
goto Ldimerror;
|
|
Object *o = (*args)[0];
|
|
Dsymbol *s = getDsymbol(o);
|
|
if (!s)
|
|
{
|
|
error("first argument is not a symbol");
|
|
goto Lfalse;
|
|
}
|
|
//printf("getAttributes %s, %p\n", s->toChars(), s->userAttributes);
|
|
if (!s->userAttributes)
|
|
s->userAttributes = new Expressions();
|
|
TupleExp *tup = new TupleExp(loc, s->userAttributes);
|
|
return tup->semantic(sc);
|
|
}
|
|
else if (ident == Id::allMembers || ident == Id::derivedMembers)
|
|
{
|
|
if (dim != 1)
|
|
goto Ldimerror;
|
|
Object *o = (*args)[0];
|
|
Dsymbol *s = getDsymbol(o);
|
|
ScopeDsymbol *sd;
|
|
if (!s)
|
|
{
|
|
error("argument has no members");
|
|
goto Lfalse;
|
|
}
|
|
if ((sd = s->isScopeDsymbol()) == NULL)
|
|
{
|
|
error("%s %s has no members", s->kind(), s->toChars());
|
|
goto Lfalse;
|
|
}
|
|
|
|
// use a struct as local function
|
|
struct PushIdentsDg
|
|
{
|
|
static int dg(void *ctx, size_t n, Dsymbol *sm)
|
|
{
|
|
if (!sm)
|
|
return 1;
|
|
//printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars());
|
|
if (sm->ident)
|
|
{
|
|
//printf("\t%s\n", sm->ident->toChars());
|
|
Identifiers *idents = (Identifiers *)ctx;
|
|
|
|
/* Skip if already present in idents[]
|
|
*/
|
|
for (size_t j = 0; j < idents->dim; j++)
|
|
{ Identifier *id = (*idents)[j];
|
|
if (id == sm->ident)
|
|
return 0;
|
|
#ifdef DEBUG
|
|
// Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop.
|
|
assert(strcmp(id->toChars(), sm->ident->toChars()) != 0);
|
|
#endif
|
|
}
|
|
|
|
idents->push(sm->ident);
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
Identifiers *idents = new Identifiers;
|
|
|
|
ScopeDsymbol::foreach(sc, sd->members, &PushIdentsDg::dg, idents);
|
|
|
|
ClassDeclaration *cd = sd->isClassDeclaration();
|
|
if (cd && ident == Id::allMembers)
|
|
{
|
|
struct PushBaseMembers
|
|
{
|
|
static void dg(ClassDeclaration *cd, Identifiers *idents)
|
|
{
|
|
for (size_t i = 0; i < cd->baseclasses->dim; i++)
|
|
{ ClassDeclaration *cb = (*cd->baseclasses)[i]->base;
|
|
ScopeDsymbol::foreach(NULL, cb->members, &PushIdentsDg::dg, idents);
|
|
if (cb->baseclasses->dim)
|
|
dg(cb, idents);
|
|
}
|
|
}
|
|
};
|
|
PushBaseMembers::dg(cd, idents);
|
|
}
|
|
|
|
// Turn Identifiers into StringExps reusing the allocated array
|
|
assert(sizeof(Expressions) == sizeof(Identifiers));
|
|
Expressions *exps = (Expressions *)idents;
|
|
for (size_t i = 0; i < idents->dim; i++)
|
|
{ Identifier *id = (*idents)[i];
|
|
StringExp *se = new StringExp(loc, id->toChars());
|
|
(*exps)[i] = se;
|
|
}
|
|
|
|
#if DMDV1
|
|
Expression *e = new ArrayLiteralExp(loc, exps);
|
|
#endif
|
|
#if DMDV2
|
|
/* Making this a tuple is more flexible, as it can be statically unrolled.
|
|
* To make an array literal, enclose __traits in [ ]:
|
|
* [ __traits(allMembers, ...) ]
|
|
*/
|
|
Expression *e = new TupleExp(loc, exps);
|
|
#endif
|
|
e = e->semantic(sc);
|
|
return e;
|
|
}
|
|
else if (ident == Id::compiles)
|
|
{
|
|
/* Determine if all the objects - types, expressions, or symbols -
|
|
* compile without error
|
|
*/
|
|
if (!dim)
|
|
goto Lfalse;
|
|
|
|
for (size_t i = 0; i < dim; i++)
|
|
{ Object *o = (*args)[i];
|
|
Expression *e;
|
|
|
|
unsigned errors = global.startGagging();
|
|
unsigned oldspec = global.speculativeGag;
|
|
global.speculativeGag = global.gag;
|
|
bool scSpec = sc->speculative;
|
|
sc->speculative = true;
|
|
|
|
Type *t = isType(o);
|
|
if (t)
|
|
{ Dsymbol *s;
|
|
t->resolve(loc, sc, &e, &t, &s);
|
|
if (t)
|
|
t->semantic(loc, sc);
|
|
else if (e)
|
|
{ e = e->semantic(sc);
|
|
e = e->optimize(WANTvalue);
|
|
}
|
|
}
|
|
else
|
|
{ e = isExpression(o);
|
|
if (e)
|
|
{ e = e->semantic(sc);
|
|
e = e->optimize(WANTvalue);
|
|
}
|
|
}
|
|
|
|
sc->speculative = scSpec;
|
|
global.speculativeGag = oldspec;
|
|
if (global.endGagging(errors))
|
|
{
|
|
goto Lfalse;
|
|
}
|
|
}
|
|
goto Ltrue;
|
|
}
|
|
else if (ident == Id::isSame)
|
|
{ /* Determine if two symbols are the same
|
|
*/
|
|
if (dim != 2)
|
|
goto Ldimerror;
|
|
TemplateInstance::semanticTiargs(loc, sc, args, 0);
|
|
Object *o1 = (*args)[0];
|
|
Object *o2 = (*args)[1];
|
|
Dsymbol *s1 = getDsymbol(o1);
|
|
Dsymbol *s2 = getDsymbol(o2);
|
|
|
|
//printf("isSame: %s, %s\n", o1->toChars(), o2->toChars());
|
|
#if 0
|
|
printf("o1: %p\n", o1);
|
|
printf("o2: %p\n", o2);
|
|
if (!s1)
|
|
{ Expression *ea = isExpression(o1);
|
|
if (ea)
|
|
printf("%s\n", ea->toChars());
|
|
Type *ta = isType(o1);
|
|
if (ta)
|
|
printf("%s\n", ta->toChars());
|
|
goto Lfalse;
|
|
}
|
|
else
|
|
printf("%s %s\n", s1->kind(), s1->toChars());
|
|
#endif
|
|
if (!s1 && !s2)
|
|
{ Expression *ea1 = isExpression(o1);
|
|
Expression *ea2 = isExpression(o2);
|
|
if (ea1 && ea2)
|
|
{
|
|
if (ea1->equals(ea2))
|
|
goto Ltrue;
|
|
}
|
|
}
|
|
|
|
if (!s1 || !s2)
|
|
goto Lfalse;
|
|
|
|
s1 = s1->toAlias();
|
|
s2 = s2->toAlias();
|
|
|
|
if (s1->isFuncAliasDeclaration())
|
|
s1 = ((FuncAliasDeclaration *)s1)->toAliasFunc();
|
|
if (s2->isFuncAliasDeclaration())
|
|
s2 = ((FuncAliasDeclaration *)s2)->toAliasFunc();
|
|
|
|
if (s1 == s2)
|
|
goto Ltrue;
|
|
else
|
|
goto Lfalse;
|
|
}
|
|
else
|
|
{ error("unrecognized trait %s", ident->toChars());
|
|
goto Lfalse;
|
|
}
|
|
|
|
return NULL;
|
|
|
|
Ldimerror:
|
|
error("wrong number of arguments %d", (int)dim);
|
|
goto Lfalse;
|
|
|
|
|
|
Lfalse:
|
|
return new IntegerExp(loc, 0, Type::tbool);
|
|
|
|
Ltrue:
|
|
return new IntegerExp(loc, 1, Type::tbool);
|
|
}
|
|
|
|
|
|
#endif
|