Files
ldc/dmd2/traits.c
2013-01-04 06:22:53 +01:00

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