view dmd/TraitsExp.d @ 135:af1bebfd96a4 dmd2037

dmd 2.038
author Eldar Insafutdinov <e.insafutdinov@gmail.com>
date Mon, 13 Sep 2010 22:19:42 +0100
parents e28b18c23469
children 80f4806ffa13
line wrap: on
line source

module dmd.TraitsExp;

import dmd.common;
import dmd.Expression;
import dmd.Identifier;
import dmd.ArrayTypes;
import dmd.OutBuffer;
import dmd.Loc;
import dmd.Scope;
import dmd.HdrGenState;
import dmd.TOK;
import dmd.TY;
import dmd.STC;
import dmd.WANT;
import dmd.Id;
import dmd.Global;
import dmd.Lexer;
import dmd.ArrayLiteralExp;
import dmd.VarExp;
import dmd.StringExp;
import dmd.DotIdExp;
import dmd.DotVarExp;
import dmd.IntegerExp;
import dmd.TupleExp;
import dmd.Type;
import dmd.Dsymbol;
import dmd.DsymbolExp;
import dmd.ScopeDsymbol;
import dmd.FuncDeclaration;
import dmd.ClassDeclaration;
import dmd.TemplateDeclaration;
import dmd.TemplateInstance;
import dmd.TypeClass;
import dmd.Declaration;
import dmd.Util;
import dmd.expression.Util;

import core.stdc.string : strcmp;

/************************************************
 * Delegate to be passed to overloadApply() that looks
 * for virtual functions.
 */

struct Pvirtuals
{
	Expression e1;
	Expressions exps;
	int fpvirtuals(void* param, FuncDeclaration f)
	{   Pvirtuals p = *cast(Pvirtuals*)param;

		if (f.isVirtual())
		{	Expression e;

			if (p.e1.op == TOK.TOKdotvar)
			{   DotVarExp dve = cast(DotVarExp)p.e1;
				e = new DotVarExp(Loc(0), dve.e1, f);
			}
			else
				e = new DsymbolExp(Loc(0), f);
			p.exps.push(e);
		}
		return 0;
	}
}



class TraitsExp : Expression
{
	Identifier ident;

	Objects args;

	this(Loc loc, Identifier ident, Objects args)
	{
		super(loc, TOK.TOKtraits, this.sizeof);
		this.ident = ident;
		this.args = args;
	}

	override Expression syntaxCopy()
	{
		return new TraitsExp(loc, ident, TemplateInstance.arraySyntaxCopy(args));
	}

	override Expression semantic(Scope sc)
	{
		version (LOGSEMANTIC) {
			printf("TraitsExp.semantic() %s\n", toChars());
		}
		if (ident != Id.compiles && ident != Id.isSame)
			TemplateInstance.semanticTiargs(loc, sc, args, 1);
		size_t dim = args ? args.dim : 0;
		Object o;
        Declaration d;
		FuncDeclaration f;

		string ISTYPE(string cond)
		{
			return `
				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;
			`;
		}

		string ISDSYMBOL(string cond)
		{
			return `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)
		{
			mixin(ISTYPE(`t.isintegral() || t.isfloating()`));
		}
		else if (ident == Id.isFloating)
		{
			mixin(ISTYPE(q{t.isfloating()}));
		}
		else if (ident == Id.isIntegral)
		{
			mixin(ISTYPE(q{t.isintegral()}));
		}
		else if (ident == Id.isScalar)
		{
			mixin(ISTYPE(q{t.isscalar()}));
		}
		else if (ident == Id.isUnsigned)
		{
			mixin(ISTYPE(q{t.isunsigned()}));
		}
		else if (ident == Id.isAssociativeArray)
		{
			mixin(ISTYPE(q{t.toBasetype().ty == TY.Taarray}));
		}
		else if (ident == Id.isStaticArray)
		{
			mixin(ISTYPE(q{t.toBasetype().ty == TY.Tsarray}));
		}
		else if (ident == Id.isAbstractClass)
		{
			mixin(ISTYPE(q{t.toBasetype().ty == TY.Tclass && (cast(TypeClass)t.toBasetype()).sym.isAbstract()}));
		}
		else if (ident == Id.isFinalClass)
		{
			mixin(ISTYPE(q{t.toBasetype().ty == TY.Tclass && (cast(TypeClass)t.toBasetype()).sym.storage_class & STC.STCfinal}));
		}
		else if (ident == Id.isAbstractFunction)
		{
			mixin(ISDSYMBOL(q{(f = s.isFuncDeclaration()) !is null && f.isAbstract()}));
		}
		else if (ident == Id.isVirtualFunction)
		{
			mixin(ISDSYMBOL(q{(f = s.isFuncDeclaration()) !is null && f.isVirtual()}));
		}
		else if (ident == Id.isFinalFunction)
		{
			mixin(ISDSYMBOL(q{(f = s.isFuncDeclaration()) !is null && f.isFinal()}));
		}
//version(DMDV2) {
        else if (ident == Id.isRef)
        {
	        mixin(ISDSYMBOL(q{(d = s.isDeclaration()) !is null && d.isRef()}));
        }
        else if (ident == Id.isOut)
        {
            mixin(ISDSYMBOL(q{(d = s.isDeclaration()) !is null && d.isOut()}));
        }
        else if (ident == Id.isLazy)
        {
	        mixin(ISDSYMBOL(q{(d = s.isDeclaration()) !is null && d.storage_class & STClazy}));
        }
//}
		else if (ident == Id.hasMember ||
				ident == Id.getMember ||
				ident == Id.getVirtualFunctions)
		{
			if (dim != 2)
				goto Ldimerror;
			auto 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.optimize(WANT.WANTvalue | WANT.WANTinterpret);
			if (e.op != TOKstring)
			{   error("string expected as second argument of __traits %s instead of %s", ident.toChars(), e.toChars());
				goto Lfalse;
			}
			auto se = cast(StringExp)e;
			se = se.toUTF8(sc);
			if (se.sz != 1)
			{   error("string must be chars");
				goto Lfalse;
			}
			Identifier id = Lexer.idPool(fromStringz(cast(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)
			{   /* Take any errors as meaning it wasn't found
			     */
				e = e.trySemantic(sc);
				if (!e)
				{	if (global.gag)
					global.errors++;
					goto Lfalse;
				}
				else
					goto Ltrue;
			}
			else if (ident == Id.getMember)
			{
				e = e.semantic(sc);
				return e;
			}
			else if (ident == Id.getVirtualFunctions)
			{
				uint errors = global.errors;
				Expression ex = e;
				e = e.semantic(sc);
				if (errors < global.errors)
					error("%s cannot be resolved", ex.toChars());

				/* Create tuple of virtual function overloads of e
				 */
				//e.dump(0);
				Expressions exps = new Expressions();
				FuncDeclaration f_;
				if (e.op == TOKvar)
				{	VarExp ve = cast(VarExp)e;
					f_ = ve.var.isFuncDeclaration();
				}
				else if (e.op == TOKdotvar)
				{	DotVarExp dve = cast(DotVarExp)e;
					f_ = dve.var.isFuncDeclaration();
				}
				else
					f_ = null;
				Pvirtuals p;
				p.exps = exps;
				p.e1 = e;
				overloadApply(f_, &p.fpvirtuals, &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()) is null)
			{
				error("first argument is not a class");
				goto Lfalse;
			}
			return new IntegerExp(loc, cd.structsize, Type.tsize_t);
		}
		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()) is null)
			{
				error("%s %s has no members", s.kind(), s.toChars());
				goto Lfalse;
			}
			Expressions exps = new Expressions;
			while (1)
			{   size_t dim_ = ScopeDsymbol.dim(sd.members);
				for (size_t i = 0; i < dim_; i++)
				{
					Dsymbol sm = ScopeDsymbol.getNth(sd.members, i);
					//printf("\t[%i] %s %s\n", i, sm.kind(), sm.toChars());
					if (sm.ident)
					{
						//printf("\t%s\n", sm.ident.toChars());
						auto str = sm.ident.toChars();

						/* Skip if already present in exps[]
						 */
						for (size_t j = 0; j < exps.dim; j++)
						{   auto se2 = cast(StringExp)exps[j];
							if (strcmp(toStringz(str), cast(char*)se2.string_) == 0)
								goto Lnext;
						}

						auto se = new StringExp(loc, str);
						exps.push(se);
					}
Lnext:
					;
				}
				ClassDeclaration cd = sd.isClassDeclaration();
				if (cd && cd.baseClass && ident == Id.allMembers)
					sd = cd.baseClass;	// do again with base class
				else
					break;
			}
			Expression e = new ArrayLiteralExp(loc, exps);
			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;

				uint errors = global.errors;
				global.gag++;

				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.semantic(sc);
				}
				else
				{	e = isExpression(o);
					if (e)
						e.semantic(sc);
				}

				global.gag--;
				if (errors != global.errors)
				{   if (global.gag == 0)
					global.errors = 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);

			// writef("isSame: %s, %s\n", o1.toChars(), o2.toChars());
			static if (0)
			{
				writef("o1: %p\n", o1);
				writef("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());
			}
			if (!s1 && !s2)
			{   Expression ea1 = isExpression(o1);
				Expression ea2 = isExpression(o2);
				if (ea1 && ea2 && ea1.equals(ea2))
					goto Ltrue;
			}

			if (!s1 || !s2)
				goto Lfalse;

			s1 = s1.toAlias();
			s2 = s2.toAlias();

			if (s1 == s2)
				goto Ltrue;
			else
				goto Lfalse;
		}
		else
		{	error("unrecognized trait %s", ident.toChars());
			goto Lfalse;
		}

		return null;

Lnottype:
		error("%s is not a type", o/*.toChars()*/); // BUG: o is Object, no member toChars()
		goto Lfalse;

Ldimerror:
		error("wrong number of arguments %d", dim);
		goto Lfalse;


Lfalse:
		return new IntegerExp(loc, 0, Type.tbool);

Ltrue:
		return new IntegerExp(loc, 1, Type.tbool);
	}

	override void toCBuffer(OutBuffer buf, HdrGenState* hgs)
	{
		buf.writestring("__traits(");
		buf.writestring(ident.toChars());
		if (args)
		{
			for (int i = 0; i < args.dim; i++)
			{
				buf.writeByte(',');
				Object oarg = args[i];
				ObjectToCBuffer(buf, hgs, oarg);
			}
		}
		buf.writeByte(')');
	}
}