view dmd/EnumDeclaration.d @ 178:e3afd1303184

Many small bugs fixed Made all classes derive from TObject to detect memory leaks (functionality is disabled for now) Began work on overriding backend memory allocations (to avoid memory leaks)
author korDen
date Sun, 17 Oct 2010 07:42:00 +0400
parents 90821c10b6a7
children cd48cb899aee
line wrap: on
line source

module dmd.EnumDeclaration;

import dmd.common;
import dmd.ScopeDsymbol;
import dmd.AddExp;
import dmd.Type;
import dmd.CmpExp;
import dmd.IntegerExp;
import dmd.EqualExp;
import dmd.TOK;
import dmd.Id;
import dmd.TY;
import dmd.DsymbolTable;
import dmd.STC;
import dmd.Expression;
import dmd.Identifier;
import dmd.Dsymbol;
import dmd.Scope;
import dmd.OutBuffer;
import dmd.HdrGenState;
import dmd.Global;
import dmd.Loc;
import dmd.Module;
import dmd.TypeEnum;
import dmd.EnumMember;
import dmd.DYNCAST;
import dmd.WANT;
import dmd.Id;
import dmd.Json;
import dmd.Lexer;

import dmd.backend.SC;
import dmd.backend.FL;
import dmd.backend.Util;
import dmd.backend.Symbol;
import dmd.backend.Classsym;
import dmd.backend.SFL;
import dmd.backend.LIST;
import dmd.codegen.Util;

import std.stdio : writef;

class EnumDeclaration : ScopeDsymbol
{   /* enum ident : memtype { ... }
     */
    Type type;			// the TypeEnum
    Type memtype;		// type of the members

version (DMDV1)
{
    ulong maxval;
    ulong minval;
    ulong defaultval;	// default initializer
}
else
{
    Expression maxval;
    Expression minval;
    Expression defaultval;	// default initializer
}
	bool isdeprecated = false;
	bool isdone = false;	// 0: not done
							// 1: semantic() successfully completed
    
    this(Loc loc, Identifier id, Type memtype)
	{
		register();
		super(id);
		this.loc = loc;
		type = new TypeEnum(this);
		this.memtype = memtype;
	}
	
    override Dsymbol syntaxCopy(Dsymbol s)
	{
	    Type t = null;
	    if (memtype)
		t = memtype.syntaxCopy();

	    EnumDeclaration ed;
	    if (s)
	    {	ed = cast(EnumDeclaration)s;
		ed.memtype = t;
	    }
	    else
		ed = new EnumDeclaration(loc, ident, t);
	    ScopeDsymbol.syntaxCopy(ed);
	    return ed;
	}
	
    override void semantic(Scope sc)
	{
		Type t;
		Scope sce;

		//writef("EnumDeclaration.semantic(sd = %p, '%s') %s\n", sc.scopesym, sc.scopesym.toChars(), toChars());
		//writef("EnumDeclaration.semantic() %s\n", toChars());
		if (!members)		// enum ident;
			return;

		if (!memtype && !isAnonymous())
		{	
			// Set memtype if we can to reduce fwd reference errors
			memtype = Type.tint32;	// case 1)  enum ident { ... }
		}

		if (symtab)			// if already done
		{	
			if (isdone || !scope_)
				return;		// semantic() already completed
		}
		else
			symtab = new DsymbolTable();

		Scope scx = null;
		if (scope_)
		{   sc = scope_;
			scx = scope_;            // save so we don't make redundant copies
			scope_ = null;
		}

		if (sc.stc & STC.STCdeprecated)
			isdeprecated = true;

		parent = sc.parent;

		/* The separate, and distinct, cases are:
		 *  1. enum { ... }
		 *  2. enum : memtype { ... }
		 *  3. enum ident { ... }
		 *  4. enum ident : memtype { ... }
		 */

		if (memtype)
		{
			memtype = memtype.semantic(loc, sc);

			/* Check to see if memtype is forward referenced
			 */
			if (memtype.ty == TY.Tenum)
			{   EnumDeclaration sym = cast(EnumDeclaration)memtype.toDsymbol(sc);
				if (!sym.memtype || !sym.members || !sym.symtab || sym.scope_)
				{	
					// memtype is forward referenced, so try again later
					scope_ = scx ? scx : sc.clone();
					scope_.setNoFree();
					scope_.module_.addDeferredSemantic(this);
					writef("\tdeferring %s\n", toChars());
					return;
				}
			}
static if (false)
{
		// Decided to abandon this restriction for D 2.0
			if (!memtype.isintegral())
			{   error("base type must be of integral type, not %s", memtype.toChars());
				memtype = Type.tint32;
			}
}
		}

		isdone = true;

		type = type.semantic(loc, sc);
		if (isAnonymous())
			sce = sc;
		else
		{	sce = sc.push(this);
		sce.parent = this;
		}
		if (members.dim == 0)
		error("enum %s must have at least one member", toChars());
		int first = 1;
		Expression elast = null;
		foreach (Dsymbol s; members)
		{
		EnumMember em = s.isEnumMember();
		Expression e;

		if (!em)
			/* The e.semantic(sce) can insert other symbols, such as
			 * template instances and function literals.
			 */
			continue;

		//printf("  Enum member '%s'\n",em.toChars());
		if (em.type)
			em.type = em.type.semantic(em.loc, sce);
		e = em.value;
		if (e)
		{
			assert(e.dyncast() == DYNCAST.DYNCAST_EXPRESSION);
			e = e.semantic(sce);
			e = e.optimize(WANT.WANTvalue | WANT.WANTinterpret);
			if (memtype)
			{
			e = e.implicitCastTo(sce, memtype);
			e = e.optimize(WANT.WANTvalue | WANT.WANTinterpret);
			if (!isAnonymous())
				e = e.castTo(sce, type);
			t = memtype;
			}
			else if (em.type)
			{
			e = e.implicitCastTo(sce, em.type);
			e = e.optimize(WANT.WANTvalue | WANT.WANTinterpret);
			assert(isAnonymous());
			t = e.type;
			}
			else
			t = e.type;
		}
		else if (first)
		{
			if (memtype)
			t = memtype;
			else if (em.type)
			t = em.type;
			else
			t = Type.tint32;
			e = new IntegerExp(em.loc, 0, Type.tint32);
			e = e.implicitCastTo(sce, t);
			e = e.optimize(WANT.WANTvalue | WANT.WANTinterpret);
			if (!isAnonymous())
			e = e.castTo(sce, type);
		}
		else
		{
			// Set value to (elast + 1).
			// But first check that (elast != t.max)
			assert(elast);
			e = new EqualExp(TOK.TOKequal, em.loc, elast, t.getProperty(Loc(0), Id.max));
			e = e.semantic(sce);
			e = e.optimize(WANT.WANTvalue | WANT.WANTinterpret);
			if (e.toInteger())
			error("overflow of enum value %s", elast.toChars());

			// Now set e to (elast + 1)
			e = new AddExp(em.loc, elast, new IntegerExp(em.loc, 1, Type.tint32));
			e = e.semantic(sce);
			e = e.castTo(sce, elast.type);
			e = e.optimize(WANT.WANTvalue | WANT.WANTinterpret);
		}
		elast = e;
		em.value = e;

		// Add to symbol table only after evaluating 'value'
		if (isAnonymous())
		{
			/* Anonymous enum members get added to enclosing scope.
			 */
			for (Scope scxx = sce; scxx; scxx = scxx.enclosing)
			{
				if (scxx.scopesym)
				{
					if (!scxx.scopesym.symtab)
						scxx.scopesym.symtab = new DsymbolTable();
					em.addMember(sce, scxx.scopesym, true);
					break;
				}
			}
		}
		else
			em.addMember(sc, this, true);

		/* Compute .min, .max and .default values.
		 * If enum doesn't have a name, we can never identify the enum type,
		 * so there is no purpose for a .min, .max or .default
		 */
		if (!isAnonymous())
		{
			if (first)
			{	defaultval = e;
			minval = e;
			maxval = e;
			}
			else
			{	Expression ec;

			/* In order to work successfully with UDTs,
			 * build expressions to do the comparisons,
			 * and let the semantic analyzer and constant
			 * folder give us the result.
			 */

			// Compute if(e < minval)
			ec = new CmpExp(TOK.TOKlt, em.loc, e, minval);
			ec = ec.semantic(sce);
			ec = ec.optimize(WANT.WANTvalue | WANT.WANTinterpret);
			if (ec.toInteger())
				minval = e;

			ec = new CmpExp(TOK.TOKgt, em.loc, e, maxval);
			ec = ec.semantic(sce);
			ec = ec.optimize(WANT.WANTvalue | WANT.WANTinterpret);
			if (ec.toInteger())
				maxval = e;
			}
		}
		first = 0;
		}
		//printf("defaultval = %lld\n", defaultval);

		//if (defaultval) printf("defaultval: %s %s\n", defaultval.toChars(), defaultval.type.toChars());
		if (sc != sce)
		sce.pop();
		//members.print();
	}
	
    override bool oneMember(Dsymbol* ps)
	{
    		if (isAnonymous())
			return Dsymbol.oneMembers(members, ps);
	    	return Dsymbol.oneMember(ps);
	}
	
    override void toCBuffer(OutBuffer buf, HdrGenState* hgs)
	{
	    buf.writestring("enum ");
	    if (ident)
	    {	buf.writestring(ident.toChars());
		buf.writeByte(' ');
	    }
	    if (memtype)
	    {
		buf.writestring(": ");
		memtype.toCBuffer(buf, null, hgs);
	    }
	    if (!members)
	    {
		buf.writeByte(';');
		buf.writenl();
		return;
	    }
	    buf.writenl();
	    buf.writeByte('{');
	    buf.writenl();
	    foreach(Dsymbol s; members)
	    {
		EnumMember em = s.isEnumMember();
		if (!em)
		    continue;
		//buf.writestring("    ");
		em.toCBuffer(buf, hgs);
		buf.writeByte(',');
		buf.writenl();
	    }
	    buf.writeByte('}');
	    buf.writenl();
	}
	
    override Type getType()
	{
		return type;
	}
	
    override string kind()
	{
		return "enum";
	}
	
version (DMDV2) {
    override Dsymbol search(Loc, Identifier ident, int flags)
	{
		//printf("%s.EnumDeclaration.search('%s')\n", toChars(), ident.toChars());
		if (scope_)
			// Try one last time to resolve this enum
			semantic(scope_);

		if (!members || !symtab || scope_)
		{   
			error("is forward referenced when looking for '%s'", ident.toChars());
			//*(char*)0=0;
			return null;
		}

		return ScopeDsymbol.search(loc, ident, flags);
	}
}
    override bool isDeprecated()			// is Dsymbol deprecated?
	{
		return isdeprecated;
	}

    override void emitComment(Scope sc)
	{
		assert(false);
	}

	override void toJsonBuffer(OutBuffer buf)
	{
		//writef("EnumDeclaration.toJsonBuffer()\n");
		if (isAnonymous())
		{
			if (members)
			{
				foreach (Dsymbol s; members)
				{
					s.toJsonBuffer(buf);
					buf.writestring(",\n");
				}
				JsonRemoveComma(buf);
			}
			return;
		}
	
		buf.writestring("{\n");
	
		JsonProperty(buf, Pname, toChars());
		JsonProperty(buf, Pkind, kind());
		if (comment)
			JsonProperty(buf, Pcomment, comment);
	
		if (loc.linnum)
			JsonProperty(buf, Pline, loc.linnum);
	
		if (memtype)
			JsonProperty(buf, "base", memtype.toChars());
	
		JsonString(buf, Pmembers);
		buf.writestring(" : [\n");
		size_t offset = buf.offset;
		foreach (Dsymbol s; members)
		{
			if (offset != buf.offset)
			{
				buf.writestring(",\n");
				offset = buf.offset;
			}
			s.toJsonBuffer(buf);
		}
		JsonRemoveComma(buf);
		buf.writestring("]\n");
	
		buf.writestring("}\n");
	}

    override void toDocBuffer(OutBuffer buf)
	{
		assert(false);
	}

    override EnumDeclaration isEnumDeclaration() { return this; }

    override void toObjFile(int multiobj)			// compile to .obj file
	{
		//printf("EnumDeclaration.toObjFile('%s')\n", toChars());
	version (DMDV2) {
		if (isAnonymous())
			return;
	}

		if (global.params.symdebug)
			toDebug();

		type.getTypeInfo(null);	// generate TypeInfo

		TypeEnum tc = cast(TypeEnum)type;
		if (!tc.sym.defaultval || type.isZeroInit(Loc(0))) {
			;
		} else {
			SC scclass = SCglobal;
			if (inTemplateInstance())
				scclass = SCcomdat;

			// Generate static initializer
			toInitializer();
			sinit.Sclass = scclass;
			sinit.Sfl = FLdata;
		version (ELFOBJ) { // Burton
			sinit.Sseg = Segment.CDATA;
		}
		version (MACHOBJ) {
			sinit.Sseg = Segment.DATA;
		}
		version (DMDV1) {
			dtnbytes(&sinit.Sdt, tc.size(0), cast(char*)&tc.sym.defaultval);
			//sinit.Sdt = tc.sym.init.toDt();
		}
		version (DMDV2) {
			tc.sym.defaultval.toDt(&sinit.Sdt);
		}
			outdata(sinit);
		}
	}
	
    void toDebug()
	{
		assert(false);
	}
	
    override int cvMember(ubyte* p)
	{
		assert(false);
	}

    Symbol* sinit;

    Symbol* toInitializer()
	{
		Symbol* s;
		Classsym* stag;

		if (!sinit)
		{
			stag = fake_classsym(Id.ClassInfo);
			Identifier ident_save = ident;
			if (!ident)
				ident = Lexer.uniqueId("__enum");
			s = toSymbolX("__init", SCextern, stag.Stype, "Z");
			ident = ident_save;
			s.Sfl = FLextern;
			s.Sflags |= SFLnodebug;
			slist_add(s);
			sinit = s;
		}

		return sinit;
	}
};