view dmd2/statement.c @ 945:03d7c4aac654

SWITCHED TO LLVM 2.5 ! Applied patch from ticket #129 to compile against latest LLVM. Thanks Frits van Bommel. Fixed implicit return by asm block at the end of a function on x86-32. Other architectures will produce an error at the moment. Adding support for new targets is fairly simple. Fixed return calling convention for complex numbers, ST and ST(1) were switched around. Added some testcases. I've run a dstress test and there are no regressions. However, the runtime does not seem to compile with symbolic debug information. -O3 -release -inline works well and is what I used for the dstress run. Tango does not compile, a small workaround is needed in tango.io.digest.Digest.Digest.hexDigest. See ticket #206 .
author Tomas Lindquist Olsen <tomas.l.olsen@gmail.com>
date Sun, 08 Feb 2009 05:26:54 +0100
parents 356e65836fb5
children 5fa3e0ea06e9
line wrap: on
line source


// 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.

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "mem.h"

#include "statement.h"
#include "expression.h"
#include "cond.h"
#include "init.h"
#include "staticassert.h"
#include "mtype.h"
#include "scope.h"
#include "declaration.h"
#include "aggregate.h"
#include "id.h"
#include "hdrgen.h"
#include "parse.h"
#include "template.h"

/******************************** Statement ***************************/

Statement::Statement(Loc loc)
    : loc(loc)
{
#ifdef _DH
    // If this is an in{} contract scope statement (skip for determining
    //  inlineStatus of a function body for header content)
    incontract = 0;
#endif
}

Statement *Statement::syntaxCopy()
{
    assert(0);
    return NULL;
}

void Statement::print()
{
    fprintf(stdmsg, "%s\n", toChars());
    fflush(stdmsg);
}

char *Statement::toChars()
{   OutBuffer *buf;
    HdrGenState hgs;

    buf = new OutBuffer();
    toCBuffer(buf, &hgs);
    return buf->toChars();
}

void Statement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->printf("Statement::toCBuffer()");
    buf->writenl();
}

Statement *Statement::semantic(Scope *sc)
{
    return this;
}

// Same as semantic(), but do create a new scope

Statement *Statement::semanticScope(Scope *sc, Statement *sbreak, Statement *scontinue)
{   Scope *scd;
    Statement *s;

    scd = sc->push();
    if (sbreak)
	scd->sbreak = sbreak;
    if (scontinue)
	scd->scontinue = scontinue;
    s = semantic(scd);
    scd->pop();
    return s;
}

void Statement::error(const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    ::verror(loc, format, ap);
    va_end( ap );
}

int Statement::hasBreak()
{
    //printf("Statement::hasBreak()\n");
    return FALSE;
}

int Statement::hasContinue()
{
    return FALSE;
}

// TRUE if statement uses exception handling

int Statement::usesEH()
{
    return FALSE;
}

/* Only valid after semantic analysis
 */
int Statement::blockExit()
{
    printf("Statement::blockExit(%p)\n", this);
    printf("%s\n", toChars());
    assert(0);
    return BEany;
}

// TRUE if statement may fall off the end without a throw or return

int Statement::fallOffEnd()
{
    return TRUE;
}

// TRUE if statement 'comes from' somewhere else, like a goto

int Statement::comeFrom()
{
    //printf("Statement::comeFrom()\n");
    return FALSE;
}

/****************************************
 * If this statement has code that needs to run in a finally clause
 * at the end of the current scope, return that code in the form of
 * a Statement.
 * Output:
 *	*sentry		code executed upon entry to the scope
 *	*sexception	code executed upon exit from the scope via exception
 *	*sfinally	code executed in finally block
 */

void Statement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally)
{
    //printf("Statement::scopeCode()\n");
    //print();
    *sentry = NULL;
    *sexception = NULL;
    *sfinally = NULL;
}

/*********************************
 * Flatten out the scope by presenting the statement
 * as an array of statements.
 * Returns NULL if no flattening necessary.
 */

Statements *Statement::flatten(Scope *sc)
{
    return NULL;
}


/******************************** ExpStatement ***************************/

ExpStatement::ExpStatement(Loc loc, Expression *exp)
    : Statement(loc)
{
    this->exp = exp;
}

Statement *ExpStatement::syntaxCopy()
{
    Expression *e = exp ? exp->syntaxCopy() : NULL;
    ExpStatement *es = new ExpStatement(loc, e);
    return es;
}

void ExpStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    if (exp)
	exp->toCBuffer(buf, hgs);
    buf->writeByte(';');
    if (!hgs->FLinit.init)
        buf->writenl();
}

Statement *ExpStatement::semantic(Scope *sc)
{
    if (exp)
    {
	//printf("ExpStatement::semantic() %s\n", exp->toChars());
	exp = exp->semantic(sc);
	exp = resolveProperties(sc, exp);
	exp->checkSideEffect(0);
	exp = exp->optimize(0);
	if (exp->op == TOKdeclaration && !isDeclarationStatement())
	{   Statement *s = new DeclarationStatement(loc, exp);
	    return s;
	}
	//exp = exp->optimize(isDeclarationStatement() ? WANTvalue : 0);
    }
    return this;
}

int ExpStatement::blockExit()
{   int result = BEfallthru;

    if (exp)
    {
	if (exp->op == TOKhalt)
	    return BEhalt;
	if (exp->op == TOKassert)
	{   AssertExp *a = (AssertExp *)exp;

	    if (a->e1->isBool(FALSE))	// if it's an assert(0)
		return BEhalt;
	}
	if (exp->canThrow())
	    result |= BEthrow;
    }
    return result;
}

int ExpStatement::fallOffEnd()
{
    if (exp)
    {
	if (exp->op == TOKassert)
	{   AssertExp *a = (AssertExp *)exp;

	    if (a->e1->isBool(FALSE))	// if it's an assert(0)
		return FALSE;
	}
	else if (exp->op == TOKhalt)
	    return FALSE;
    }
    return TRUE;
}

/******************************** CompileStatement ***************************/

CompileStatement::CompileStatement(Loc loc, Expression *exp)
    : Statement(loc)
{
    this->exp = exp;
}

Statement *CompileStatement::syntaxCopy()
{
    Expression *e = exp->syntaxCopy();
    CompileStatement *es = new CompileStatement(loc, e);
    return es;
}

void CompileStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("mixin(");
    exp->toCBuffer(buf, hgs);
    buf->writestring(");");
    if (!hgs->FLinit.init)
        buf->writenl();
}

Statements *CompileStatement::flatten(Scope *sc)
{
    //printf("CompileStatement::flatten() %s\n", exp->toChars());
    exp = exp->semantic(sc);
    exp = resolveProperties(sc, exp);
    exp = exp->optimize(WANTvalue | WANTinterpret);
    if (exp->op != TOKstring)
    {	error("argument to mixin must be a string, not (%s)", exp->toChars());
	return NULL;
    }
    StringExp *se = (StringExp *)exp;
    se = se->toUTF8(sc);
    Parser p(sc->module, (unsigned char *)se->string, se->len, 0);
    p.loc = loc;
    p.nextToken();

    Statements *a = new Statements();
    while (p.token.value != TOKeof)
    {
	Statement *s = p.parseStatement(PSsemi | PScurlyscope);
	a->push(s);
    }
    return a;
}

Statement *CompileStatement::semantic(Scope *sc)
{
    //printf("CompileStatement::semantic() %s\n", exp->toChars());
    Statements *a = flatten(sc);
    if (!a)
	return NULL;
    Statement *s = new CompoundStatement(loc, a);
    return s->semantic(sc);
}


/******************************** DeclarationStatement ***************************/

DeclarationStatement::DeclarationStatement(Loc loc, Dsymbol *declaration)
    : ExpStatement(loc, new DeclarationExp(loc, declaration))
{
}

DeclarationStatement::DeclarationStatement(Loc loc, Expression *exp)
    : ExpStatement(loc, exp)
{
}

Statement *DeclarationStatement::syntaxCopy()
{
    DeclarationStatement *ds = new DeclarationStatement(loc, exp->syntaxCopy());
    return ds;
}

void DeclarationStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally)
{
    //printf("DeclarationStatement::scopeCode()\n");
    //print();

    *sentry = NULL;
    *sexception = NULL;
    *sfinally = NULL;

    if (exp)
    {
	if (exp->op == TOKdeclaration)
	{
	    DeclarationExp *de = (DeclarationExp *)(exp);
	    VarDeclaration *v = de->declaration->isVarDeclaration();
	    if (v)
	    {	Expression *e;

		e = v->callAutoDtor(sc);
		if (e)
		{
		    //printf("dtor is: "); e->print();
		    *sfinally = new ExpStatement(loc, e);
		}
	    }
	}
    }
}

void DeclarationStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    exp->toCBuffer(buf, hgs);
}


/******************************** CompoundStatement ***************************/

CompoundStatement::CompoundStatement(Loc loc, Statements *s)
    : Statement(loc)
{
    statements = s;
}

CompoundStatement::CompoundStatement(Loc loc, Statement *s1, Statement *s2)
    : Statement(loc)
{
    statements = new Statements();
    statements->reserve(2);
    statements->push(s1);
    statements->push(s2);
}

Statement *CompoundStatement::syntaxCopy()
{
    Statements *a = new Statements();
    a->setDim(statements->dim);
    for (size_t i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *)statements->data[i];
	if (s)
	    s = s->syntaxCopy();
	a->data[i] = s;
    }
    CompoundStatement *cs = new CompoundStatement(loc, a);
    return cs;
}


Statement *CompoundStatement::semantic(Scope *sc)
{   Statement *s;

    //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", this, sc);

    for (size_t i = 0; i < statements->dim; )
    {
	s = (Statement *) statements->data[i];
	if (s)
	{   Statements *a = s->flatten(sc);

	    if (a)
	    {
		statements->remove(i);
		statements->insert(i, a);
		continue;
	    }
	    s = s->semantic(sc);
	    statements->data[i] = s;
	    if (s)
	    {
		Statement *sentry;
		Statement *sexception;
		Statement *sfinally;

		s->scopeCode(sc, &sentry, &sexception, &sfinally);
		if (sentry)
		{
		    sentry = sentry->semantic(sc);
		    statements->data[i] = sentry;
		}
		if (sexception)
		{
		    if (i + 1 == statements->dim && !sfinally)
		    {
#if 1
			sexception = sexception->semantic(sc);
#else
			statements->push(sexception);
			if (sfinally)
			    // Assume sexception does not throw
			    statements->push(sfinally);
#endif
		    }
		    else
		    {
			/* Rewrite:
			 *	s; s1; s2;
			 * As:
			 *	s;
			 *	try { s1; s2; }
			 *	catch (Object __o)
			 *	{ sexception; throw __o; }
			 */
			Statement *body;
			Statements *a = new Statements();

			for (int j = i + 1; j < statements->dim; j++)
			{
			    a->push(statements->data[j]);
			}
			body = new CompoundStatement(0, a);
			body = new ScopeStatement(0, body);

			Identifier *id = Lexer::uniqueId("__o");

			Statement *handler = new ThrowStatement(0, new IdentifierExp(0, id));
			handler = new CompoundStatement(0, sexception, handler);

			Array *catches = new Array();
			Catch *ctch = new Catch(0, NULL, id, handler);
			catches->push(ctch);
			s = new TryCatchStatement(0, body, catches);

			if (sfinally)
			    s = new TryFinallyStatement(0, s, sfinally);
			s = s->semantic(sc);
			statements->setDim(i + 1);
			statements->push(s);
			break;
		    }
		}
		else if (sfinally)
		{
		    if (0 && i + 1 == statements->dim)
		    {
			statements->push(sfinally);
		    }
		    else
		    {
			/* Rewrite:
			 *	s; s1; s2;
			 * As:
			 *	s; try { s1; s2; } finally { sfinally; }
			 */
			Statement *body;
			Statements *a = new Statements();

			for (int j = i + 1; j < statements->dim; j++)
			{
			    a->push(statements->data[j]);
			}
			body = new CompoundStatement(0, a);
			s = new TryFinallyStatement(0, body, sfinally);
			s = s->semantic(sc);
			statements->setDim(i + 1);
			statements->push(s);
			break;
		    }
		}
	    }
	}
	i++;
    }
    if (statements->dim == 1 && !isAsmBlockStatement())
    {
	return (Statement *)statements->data[0];
    }
    return this;
}

Statements *CompoundStatement::flatten(Scope *sc)
{
    return statements;
}

ReturnStatement *CompoundStatement::isReturnStatement()
{
    ReturnStatement *rs = NULL;

    for (int i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *) statements->data[i];
	if (s)
	{
	    rs = s->isReturnStatement();
	    if (rs)
		break;
	}
    }
    return rs;
}

void CompoundStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    for (int i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *) statements->data[i];
	if (s)
	    s->toCBuffer(buf, hgs);
    }
}

int CompoundStatement::usesEH()
{
    for (int i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *) statements->data[i];
	if (s && s->usesEH())
	    return TRUE;
    }
    return FALSE;
}

int CompoundStatement::blockExit()
{
    //printf("CompoundStatement::blockExit(%p) %d\n", this, statements->dim);
    int result = BEfallthru;
    for (size_t i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *) statements->data[i];
	if (s)
	{
//printf("result = x%x\n", result);
//printf("%s\n", s->toChars());
	    if (!(result & BEfallthru) && !s->comeFrom())
	    {
		if (global.params.warnings)
		{   fprintf(stdmsg, "warning - ");
		    s->error("statement is not reachable");
		}
	    }

	    result &= ~BEfallthru;
	    result |= s->blockExit();
	}
    }
    return result;
}

int CompoundStatement::fallOffEnd()
{   int falloff = TRUE;

    //printf("CompoundStatement::fallOffEnd() %s\n", toChars());
    for (int i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *)statements->data[i];

	if (!s)
	    continue;

#if 0
	if (!falloff && global.params.warnings && !s->comeFrom())
	{
	    warning("%s: statement is not reachable", s->loc.toChars());
	}
#endif
	falloff = s->fallOffEnd();
    }
    return falloff;
}

int CompoundStatement::comeFrom()
{   int comefrom = FALSE;

    //printf("CompoundStatement::comeFrom()\n");
    for (int i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *)statements->data[i];

	if (!s)
	    continue;

	comefrom |= s->comeFrom();
    }
    return comefrom;
}


/**************************** UnrolledLoopStatement ***************************/

UnrolledLoopStatement::UnrolledLoopStatement(Loc loc, Statements *s)
    : Statement(loc)
{
    statements = s;
    enclosinghandler = NULL;
}

Statement *UnrolledLoopStatement::syntaxCopy()
{
    Statements *a = new Statements();
    a->setDim(statements->dim);
    for (size_t i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *)statements->data[i];
	if (s)
	    s = s->syntaxCopy();
	a->data[i] = s;
    }
    UnrolledLoopStatement *cs = new UnrolledLoopStatement(loc, a);
    return cs;
}


Statement *UnrolledLoopStatement::semantic(Scope *sc)
{
    //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", this, sc);

    enclosinghandler = sc->tfOfTry;

    sc->noctor++;
    Scope *scd = sc->push();
    scd->sbreak = this;
    scd->scontinue = this;

    for (size_t i = 0; i < statements->dim; i++)
    {
	Statement *s = (Statement *) statements->data[i];
	if (s)
	{
	    s = s->semantic(scd);
	    statements->data[i] = s;
	}
    }

    scd->pop();
    sc->noctor--;
    return this;
}

void UnrolledLoopStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("unrolled {");
    buf->writenl();

    for (size_t i = 0; i < statements->dim; i++)
    {	Statement *s;

	s = (Statement *) statements->data[i];
	if (s)
	    s->toCBuffer(buf, hgs);
    }

    buf->writeByte('}');
    buf->writenl();
}

int UnrolledLoopStatement::hasBreak()
{
    return TRUE;
}

int UnrolledLoopStatement::hasContinue()
{
    return TRUE;
}

int UnrolledLoopStatement::usesEH()
{
    for (size_t i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *) statements->data[i];
	if (s && s->usesEH())
	    return TRUE;
    }
    return FALSE;
}

int UnrolledLoopStatement::blockExit()
{
    int result = BEfallthru;
    for (size_t i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *) statements->data[i];
	if (s)
	{
	    int r = s->blockExit();
	    result |= r & ~(BEbreak | BEcontinue);
	}
    }
    return result;
}

int UnrolledLoopStatement::fallOffEnd()
{
    //printf("UnrolledLoopStatement::fallOffEnd()\n");
    for (size_t i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *)statements->data[i];

	if (s)
	    s->fallOffEnd();
    }
    return TRUE;
}

int UnrolledLoopStatement::comeFrom()
{   int comefrom = FALSE;

    //printf("UnrolledLoopStatement::comeFrom()\n");
    for (size_t i = 0; i < statements->dim; i++)
    {	Statement *s = (Statement *)statements->data[i];

	if (!s)
	    continue;

	comefrom |= s->comeFrom();
    }
    return comefrom;
}


/******************************** ScopeStatement ***************************/

ScopeStatement::ScopeStatement(Loc loc, Statement *s)
    : Statement(loc)
{
    this->statement = s;
}

Statement *ScopeStatement::syntaxCopy()
{
    Statement *s;

    s = statement ? statement->syntaxCopy() : NULL;
    s = new ScopeStatement(loc, s);
    return s;
}


Statement *ScopeStatement::semantic(Scope *sc)
{   ScopeDsymbol *sym;

    //printf("ScopeStatement::semantic(sc = %p)\n", sc);
    if (statement)
    {	Statements *a;

	sym = new ScopeDsymbol();
	sym->parent = sc->scopesym;
	sc = sc->push(sym);

	a = statement->flatten(sc);
	if (a)
	{
	    statement = new CompoundStatement(loc, a);
	}

	statement = statement->semantic(sc);
	if (statement)
	{
	    Statement *sentry;
	    Statement *sexception;
	    Statement *sfinally;

	    statement->scopeCode(sc, &sentry, &sexception, &sfinally);
	    if (sfinally)
	    {
		//printf("adding sfinally\n");
		statement = new CompoundStatement(loc, statement, sfinally);
	    }
	}

	sc->pop();
    }
    return this;
}

int ScopeStatement::hasBreak()
{
    //printf("ScopeStatement::hasBreak() %s\n", toChars());
    return statement ? statement->hasBreak() : FALSE;
}

int ScopeStatement::hasContinue()
{
    return statement ? statement->hasContinue() : FALSE;
}

int ScopeStatement::usesEH()
{
    return statement ? statement->usesEH() : FALSE;
}

int ScopeStatement::blockExit()
{
    //printf("ScopeStatement::blockExit(%p)\n", statement);
    return statement ? statement->blockExit() : BEfallthru;
}

int ScopeStatement::fallOffEnd()
{
    return statement ? statement->fallOffEnd() : TRUE;
}

int ScopeStatement::comeFrom()
{
    //printf("ScopeStatement::comeFrom()\n");
    return statement ? statement->comeFrom() : FALSE;
}

void ScopeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writeByte('{');
    buf->writenl();

    if (statement)
	statement->toCBuffer(buf, hgs);

    buf->writeByte('}');
    buf->writenl();
}

/******************************** WhileStatement ***************************/

WhileStatement::WhileStatement(Loc loc, Expression *c, Statement *b)
    : Statement(loc)
{
    condition = c;
    body = b;
    enclosinghandler = NULL;
}

Statement *WhileStatement::syntaxCopy()
{
    WhileStatement *s = new WhileStatement(loc, condition->syntaxCopy(), body ? body->syntaxCopy() : NULL);
    return s;
}


Statement *WhileStatement::semantic(Scope *sc)
{
#if 0
    if (condition->op == TOKmatch)
    {
	/* Rewrite while (condition) body as:
	 *   if (condition)
	 *     do
	 *       body
	 *     while ((_match = _match.opNext), _match);
	 */

	Expression *ew = new IdentifierExp(0, Id::_match);
	ew = new DotIdExp(0, ew, Id::next);
	ew = new AssignExp(0, new IdentifierExp(0, Id::_match), ew);
	////ew = new EqualExp(TOKnotequal, 0, ew, new NullExp(0));
	Expression *ev = new IdentifierExp(0, Id::_match);
	//ev = new CastExp(0, ev, Type::tvoidptr);
	ew = new CommaExp(0, ew, ev);
	Statement *sw = new DoStatement(loc, body, ew);
	Statement *si = new IfStatement(loc, condition, sw, NULL);
	return si->semantic(sc);
    }
#endif

    enclosinghandler = sc->tfOfTry;

    condition = condition->semantic(sc);
    condition = resolveProperties(sc, condition);
    condition = condition->optimize(WANTvalue);
    condition = condition->checkToBoolean();

    sc->noctor++;

    Scope *scd = sc->push();
    scd->sbreak = this;
    scd->scontinue = this;
    if (body)
	body = body->semantic(scd);
    scd->pop();

    sc->noctor--;

    return this;
}

int WhileStatement::hasBreak()
{
    return TRUE;
}

int WhileStatement::hasContinue()
{
    return TRUE;
}

int WhileStatement::usesEH()
{
    return body ? body->usesEH() : 0;
}

int WhileStatement::blockExit()
{
    //printf("WhileStatement::blockExit(%p)\n", this);

    int result = BEnone;
    if (condition->canThrow())
	result |= BEthrow;
    if (condition->isBool(TRUE))
    {
	if (body)
	{   result |= body->blockExit();
	    if (result & BEbreak)
		result |= BEfallthru;
	}
    }
    else if (condition->isBool(FALSE))
    {
	result |= BEfallthru;
    }
    else
    {
	if (body)
	    result |= body->blockExit();
	result |= BEfallthru;
    }
    result &= ~(BEbreak | BEcontinue);
    return result;
}

int WhileStatement::fallOffEnd()
{
    if (body)
	body->fallOffEnd();
    return TRUE;
}

int WhileStatement::comeFrom()
{
    if (body)
	return body->comeFrom();
    return FALSE;
}

void WhileStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("while (");
    condition->toCBuffer(buf, hgs);
    buf->writebyte(')');
    buf->writenl();
    if (body)
	body->toCBuffer(buf, hgs);
}

/******************************** DoStatement ***************************/

DoStatement::DoStatement(Loc loc, Statement *b, Expression *c)
    : Statement(loc)
{
    body = b;
    condition = c;
    enclosinghandler = NULL;
}

Statement *DoStatement::syntaxCopy()
{
    DoStatement *s = new DoStatement(loc, body ? body->syntaxCopy() : NULL, condition->syntaxCopy());
    return s;
}


Statement *DoStatement::semantic(Scope *sc)
{
    enclosinghandler = sc->tfOfTry;

    sc->noctor++;
    if (body)
	body = body->semanticScope(sc, this, this);
    sc->noctor--;
    condition = condition->semantic(sc);
    condition = resolveProperties(sc, condition);
    condition = condition->optimize(WANTvalue);

    condition = condition->checkToBoolean();

    return this;
}

int DoStatement::hasBreak()
{
    return TRUE;
}

int DoStatement::hasContinue()
{
    return TRUE;
}

int DoStatement::usesEH()
{
    return body ? body->usesEH() : 0;
}

int DoStatement::blockExit()
{   int result;

    if (body)
    {	result = body->blockExit();
	if (result == BEbreak)
	    return BEfallthru;
	if (result & BEcontinue)
	    result |= BEfallthru;
    }
    else
	result = BEfallthru;
    if (result & BEfallthru)
    {	if (condition->canThrow())
	    result |= BEthrow;
	if (!(result & BEbreak) && condition->isBool(TRUE))
	    result &= ~BEfallthru;
    }
    result &= ~(BEbreak | BEcontinue);
    return result;
}

int DoStatement::fallOffEnd()
{
    if (body)
	body->fallOffEnd();
    return TRUE;
}

int DoStatement::comeFrom()
{
    if (body)
	return body->comeFrom();
    return FALSE;
}

void DoStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("do");
    buf->writenl();
    if (body)
	body->toCBuffer(buf, hgs);
    buf->writestring("while (");
    condition->toCBuffer(buf, hgs);
    buf->writebyte(')');
}

/******************************** ForStatement ***************************/

ForStatement::ForStatement(Loc loc, Statement *init, Expression *condition, Expression *increment, Statement *body)
    : Statement(loc)
{
    this->init = init;
    this->condition = condition;
    this->increment = increment;
    this->body = body;
    this->enclosinghandler = NULL;
}

Statement *ForStatement::syntaxCopy()
{
    Statement *i = NULL;
    if (init)
	i = init->syntaxCopy();
    Expression *c = NULL;
    if (condition)
	c = condition->syntaxCopy();
    Expression *inc = NULL;
    if (increment)
	inc = increment->syntaxCopy();
    ForStatement *s = new ForStatement(loc, i, c, inc, body->syntaxCopy());
    return s;
}

Statement *ForStatement::semantic(Scope *sc)
{
    enclosinghandler = sc->tfOfTry;

    ScopeDsymbol *sym = new ScopeDsymbol();
    sym->parent = sc->scopesym;
    sc = sc->push(sym);
    if (init)
	init = init->semantic(sc);
    sc->noctor++;
    if (condition)
    {
	condition = condition->semantic(sc);
	condition = resolveProperties(sc, condition);
	condition = condition->optimize(WANTvalue);
	condition = condition->checkToBoolean();
    }
    if (increment)
    {	increment = increment->semantic(sc);
	increment = resolveProperties(sc, increment);
    }

    sc->sbreak = this;
    sc->scontinue = this;
    body = body->semantic(sc);
    sc->noctor--;

    sc->pop();
    return this;
}

void ForStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally)
{
    //printf("ForStatement::scopeCode()\n");
    //print();
    if (init)
	init->scopeCode(sc, sentry, sexception, sfinally);
    else
	Statement::scopeCode(sc, sentry, sexception, sfinally);
}

int ForStatement::hasBreak()
{
    //printf("ForStatement::hasBreak()\n");
    return TRUE;
}

int ForStatement::hasContinue()
{
    return TRUE;
}

int ForStatement::usesEH()
{
    return (init && init->usesEH()) || body->usesEH();
}

int ForStatement::blockExit()
{   int result = BEfallthru;

    if (init)
    {	result = init->blockExit();
	if (!(result & BEfallthru))
	    return result;
    }
    if (condition)
    {	if (condition->canThrow())
	    result |= BEthrow;
    }
    else
	result &= ~BEfallthru;	// the body must do the exiting
    if (body)
    {	int r = body->blockExit();
	if (r & BEbreak)
	    result |= BEfallthru;
	result |= r & ~(BEbreak | BEcontinue);
    }
    if (increment && increment->canThrow())
	result |= BEthrow;
    return result;
}

int ForStatement::fallOffEnd()
{
    if (body)
	body->fallOffEnd();
    return TRUE;
}

int ForStatement::comeFrom()
{
    //printf("ForStatement::comeFrom()\n");
    if (body)
    {	int result = body->comeFrom();
	//printf("result = %d\n", result);
	return result;
    }
    return FALSE;
}

void ForStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("for (");
    if (init)
    {
        hgs->FLinit.init++;
        hgs->FLinit.decl = 0;
        init->toCBuffer(buf, hgs);
        if (hgs->FLinit.decl > 0)
            buf->writebyte(';');
        hgs->FLinit.decl = 0;
        hgs->FLinit.init--;
    }
    else
        buf->writebyte(';');
    if (condition)
    {   buf->writebyte(' ');
        condition->toCBuffer(buf, hgs);
    }
    buf->writebyte(';');
    if (increment)
    {   buf->writebyte(' ');
        increment->toCBuffer(buf, hgs);
    }
    buf->writebyte(')');
    buf->writenl();
    buf->writebyte('{');
    buf->writenl();
    body->toCBuffer(buf, hgs);
    buf->writebyte('}');
    buf->writenl();
}

/******************************** ForeachStatement ***************************/

ForeachStatement::ForeachStatement(Loc loc, enum TOK op, Arguments *arguments,
	Expression *aggr, Statement *body)
    : Statement(loc)
{
    this->op = op;
    this->arguments = arguments;
    this->aggr = aggr;
    this->body = body;
    this->enclosinghandler = NULL;

    this->key = NULL;
    this->value = NULL;

    this->func = NULL;
}

Statement *ForeachStatement::syntaxCopy()
{
    Arguments *args = Argument::arraySyntaxCopy(arguments);
    Expression *exp = aggr->syntaxCopy();
    ForeachStatement *s = new ForeachStatement(loc, op, args, exp,
	body ? body->syntaxCopy() : NULL);
    return s;
}

Statement *ForeachStatement::semantic(Scope *sc)
{
    //printf("ForeachStatement::semantic() %p\n", this);
    ScopeDsymbol *sym;
    Statement *s = this;
    size_t dim = arguments->dim;
    TypeAArray *taa = NULL;

    Type *tn = NULL;
    Type *tnv = NULL;

    enclosinghandler = sc->tfOfTry;

    func = sc->func;
    if (func->fes)
	func = func->fes->func;

    aggr = aggr->semantic(sc);
    aggr = resolveProperties(sc, aggr);
    aggr = aggr->optimize(WANTvalue);
    if (!aggr->type)
    {
	error("invalid foreach aggregate %s", aggr->toChars());
	return this;
    }

    inferApplyArgTypes(op, arguments, aggr);

    /* Check for inference errors
     */
    if (dim != arguments->dim)
    {
	//printf("dim = %d, arguments->dim = %d\n", dim, arguments->dim);
	error("cannot uniquely infer foreach argument types");
	return this;
    }

    Type *tab = aggr->type->toBasetype();

    if (tab->ty == Ttuple)	// don't generate new scope for tuple loops
    {
	if (dim < 1 || dim > 2)
	{
	    error("only one (value) or two (key,value) arguments for tuple foreach");
	    return s;
	}

	TypeTuple *tuple = (TypeTuple *)tab;
	Statements *statements = new Statements();
	//printf("aggr: op = %d, %s\n", aggr->op, aggr->toChars());
	size_t n;
	TupleExp *te = NULL;
	if (aggr->op == TOKtuple)	// expression tuple
	{   te = (TupleExp *)aggr;
	    n = te->exps->dim;
	}
	else if (aggr->op == TOKtype)	// type tuple
	{
	    n = Argument::dim(tuple->arguments);
	}
	else
	    assert(0);
	for (size_t j = 0; j < n; j++)
	{   size_t k = (op == TOKforeach) ? j : n - 1 - j;
	    Expression *e;
	    Type *t;
	    if (te)
		e = (Expression *)te->exps->data[k];
	    else
		t = Argument::getNth(tuple->arguments, k)->type;
	    Argument *arg = (Argument *)arguments->data[0];
	    Statements *st = new Statements();

	    if (dim == 2)
	    {   // Declare key
		if (arg->storageClass & (STCout | STCref | STClazy))
		    error("no storage class for key %s", arg->ident->toChars());
		TY keyty = arg->type->ty;
        if (global.params.is64bit)
        {
            if (keyty != Tint32 && keyty != Tuns32 && keyty != Tint64 && keyty != Tuns64)
            {
                error("foreach: key type must be int, uint, long or ulong, not %s", key->type->toChars());
            }
        }
        else if (keyty != Tint32 && keyty != Tuns32)
        {
            error("foreach: key type must be int or uint, not %s", key->type->toChars());
        }
		Initializer *ie = new ExpInitializer(0, new IntegerExp(k));
		VarDeclaration *var = new VarDeclaration(loc, arg->type, arg->ident, ie);
		var->storage_class |= STCmanifest;
		DeclarationExp *de = new DeclarationExp(loc, var);
		st->push(new ExpStatement(loc, de));
		arg = (Argument *)arguments->data[1];	// value
	    }
	    // Declare value
	    if (arg->storageClass & (STCout | STCref | STClazy))
		error("no storage class for value %s", arg->ident->toChars());
	    Dsymbol *var;
	    if (te)
	    {	Type *tb = e->type->toBasetype();
		if ((tb->ty == Tfunction || tb->ty == Tsarray) && e->op == TOKvar)
		{   VarExp *ve = (VarExp *)e;
		    var = new AliasDeclaration(loc, arg->ident, ve->var);
		}
		else
		{
		    arg->type = e->type;
		    Initializer *ie = new ExpInitializer(0, e);
		    VarDeclaration *v = new VarDeclaration(loc, arg->type, arg->ident, ie);
		    if (e->isConst())
			v->storage_class |= STCconst;
		    var = v;
		}
	    }
	    else
	    {
		var = new AliasDeclaration(loc, arg->ident, t);
	    }
	    DeclarationExp *de = new DeclarationExp(loc, var);
	    st->push(new ExpStatement(loc, de));

	    st->push(body->syntaxCopy());
	    s = new CompoundStatement(loc, st);
	    s = new ScopeStatement(loc, s);
	    statements->push(s);
	}

	s = new UnrolledLoopStatement(loc, statements);
	s = s->semantic(sc);
	return s;
    }

    sym = new ScopeDsymbol();
    sym->parent = sc->scopesym;
    sc = sc->push(sym);

    sc->noctor++;

    switch (tab->ty)
    {
	case Tarray:
	case Tsarray:
	    if (!checkForArgTypes())
		return this;

	    if (dim < 1 || dim > 2)
	    {
		error("only one or two arguments for array foreach");
		break;
	    }

	    /* Look for special case of parsing char types out of char type
	     * array.
	     */
	    tn = tab->nextOf()->toBasetype();
	    if (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar)
	    {	Argument *arg;

		int i = (dim == 1) ? 0 : 1;	// index of value
		arg = (Argument *)arguments->data[i];
		arg->type = arg->type->semantic(loc, sc);
		tnv = arg->type->toBasetype();
		if (tnv->ty != tn->ty &&
		    (tnv->ty == Tchar || tnv->ty == Twchar || tnv->ty == Tdchar))
		{
		    if (arg->storageClass & STCref)
			error("foreach: value of UTF conversion cannot be ref");
		    if (dim == 2)
		    {	arg = (Argument *)arguments->data[0];
			if (arg->storageClass & STCref)
			    error("foreach: key cannot be ref");
		    }
		    goto Lapply;
		}
	    }

	    for (size_t i = 0; i < dim; i++)
	    {	// Declare args
		Argument *arg = (Argument *)arguments->data[i];
		VarDeclaration *var;

		var = new VarDeclaration(loc, arg->type, arg->ident, NULL);
		var->storage_class |= STCforeach;
		var->storage_class |= arg->storageClass & (STCin | STCout | STCref | STCconst | STCinvariant);
		if (dim == 2 && i == 0)
		{   key = var;
		    //var->storage_class |= STCfinal;
		}
		else
		{
		    value = var;
		    /* Reference to immutable data should be marked as const
		     */
		    if (var->storage_class & STCref && !tn->isMutable())
		    {
			var->storage_class |= STCconst;
		    }
		}
#if 1
		DeclarationExp *de = new DeclarationExp(loc, var);
		de->semantic(sc);
#else
		var->semantic(sc);
		if (!sc->insert(var))
		    error("%s already defined", var->ident->toChars());
#endif
	    }

	    sc->sbreak = this;
	    sc->scontinue = this;
	    body = body->semantic(sc);

	    if (tab->nextOf()->implicitConvTo(value->type) < MATCHconst)
	    {
		if (aggr->op == TOKstring)
		    aggr = aggr->implicitCastTo(sc, value->type->arrayOf());
		else
		    error("foreach: %s is not an array of %s",
			tab->toChars(), value->type->toChars());
	    }

        if (key)
        {
            if (global.params.is64bit)
            {
                if (key->type->ty != Tint32 && key->type->ty != Tuns32 && key->type->ty != Tint64 && key->type->ty != Tuns64)
                {
                    error("foreach: key type must be int, uint, long or ulong, not %s", key->type->toChars());
                }
            }
            else if (key->type->ty != Tint32 && key->type->ty != Tuns32)
            {
                error("foreach: key type must be int or uint, not %s", key->type->toChars());
            }
        }

	    if (key && key->storage_class & (STCout | STCref))
		error("foreach: key cannot be out or ref");
	    break;

	case Taarray:
	    if (!checkForArgTypes())
		return this;

	    taa = (TypeAArray *)tab;
	    if (dim < 1 || dim > 2)
	    {
		error("only one or two arguments for associative array foreach");
		break;
	    }
	    if (op == TOKforeach_reverse)
	    {
		error("no reverse iteration on associative arrays");
	    }
	    goto Lapply;

	case Tclass:
	case Tstruct:
#if DMDV2
	{   /* Look for range iteration, i.e. the properties
	     * .empty, .next, .retreat, .head and .rear
	     *    foreach (e; range) { ... }
	     * translates to:
	     *    for (auto __r = range; !__r.empty; __r.next)
	     *    {   auto e = __r.head;
	     *        ...
	     *    }
	     */
	    if (dim != 1)	// only one argument allowed with ranges
		goto Lapply;
	    AggregateDeclaration *ad = (tab->ty == Tclass)
			? (AggregateDeclaration *)((TypeClass  *)tab)->sym
			: (AggregateDeclaration *)((TypeStruct *)tab)->sym;
	    Identifier *idhead;
	    Identifier *idnext;
	    if (op == TOKforeach)
	    {	idhead = Id::Fhead;
		idnext = Id::Fnext;
	    }
	    else
	    {	idhead = Id::Ftoe;
		idnext = Id::Fretreat;
	    }
	    Dsymbol *shead = search_function(ad, idhead);
	    if (!shead)
		goto Lapply;

	    /* Generate a temporary __r and initialize it with the aggregate.
	     */
	    Identifier *id = Identifier::generateId("__r");
	    VarDeclaration *r = new VarDeclaration(loc, NULL, id, new ExpInitializer(loc, aggr));
	    r->semantic(sc);
	    Statement *init = new DeclarationStatement(loc, r);

	    // !__r.empty
	    Expression *e = new VarExp(loc, r);
	    e = new DotIdExp(loc, e, Id::Fempty);
	    Expression *condition = new NotExp(loc, e);

	    // __r.next
	    e = new VarExp(loc, r);
	    Expression *increment = new DotIdExp(loc, e, idnext);

	    /* Declaration statement for e:
	     *    auto e = __r.idhead;
	     */
	    e = new VarExp(loc, r);
	    Expression *einit = new DotIdExp(loc, e, idhead);
	    einit = einit->semantic(sc);
	    Argument *arg = (Argument *)arguments->data[0];
	    VarDeclaration *ve = new VarDeclaration(loc, arg->type, arg->ident, new ExpInitializer(loc, einit));
	    ve->storage_class |= STCforeach;
	    ve->storage_class |= arg->storageClass & (STCin | STCout | STCref | STCconst | STCinvariant);

	    DeclarationExp *de = new DeclarationExp(loc, ve);

	    Statement *body = new CompoundStatement(loc,
		new DeclarationStatement(loc, de), this->body);

	    s = new ForStatement(loc, init, condition, increment, body);
	    s = s->semantic(sc);
	    break;
	}
#endif
	case Tdelegate:
	Lapply:
	{   FuncDeclaration *fdapply;
	    Arguments *args;
	    Expression *ec;
	    Expression *e;
	    FuncLiteralDeclaration *fld;
	    Argument *a;
	    Type *t;
	    Expression *flde;
	    Identifier *id;
	    Type *tret;
        TypeDelegate* dgty;
        TypeDelegate* dgty2;
        TypeDelegate* fldeTy;

	    if (!checkForArgTypes())
		return this;

	    tret = func->type->nextOf();

	    // Need a variable to hold value from any return statements in body.
	    if (!sc->func->vresult && tret && tret != Type::tvoid)
	    {	VarDeclaration *v;

		v = new VarDeclaration(loc, tret, Id::result, NULL);
		v->noauto = 1;
		v->semantic(sc);
		if (!sc->insert(v))
		    assert(0);
		v->parent = sc->func;
		sc->func->vresult = v;
	    }

	    /* Turn body into the function literal:
	     *	int delegate(ref T arg) { body }
	     */
	    args = new Arguments();
	    for (size_t i = 0; i < dim; i++)
	    {	Argument *arg = (Argument *)arguments->data[i];

		arg->type = arg->type->semantic(loc, sc);
		if (arg->storageClass & STCref)
		    id = arg->ident;
		else
		{   // Make a copy of the ref argument so it isn't
		    // a reference.
		    VarDeclaration *v;
		    Initializer *ie;

		    id = Lexer::uniqueId("__applyArg", i);

		    ie = new ExpInitializer(0, new IdentifierExp(0, id));
		    v = new VarDeclaration(0, arg->type, arg->ident, ie);
		    s = new DeclarationStatement(0, v);
		    body = new CompoundStatement(loc, s, body);
		}
		a = new Argument(STCref, arg->type, id, NULL);
		args->push(a);
	    }
	    t = new TypeFunction(args, Type::tint32, 0, LINKd);
	    fld = new FuncLiteralDeclaration(loc, 0, t, TOKdelegate, this);
	    fld->fbody = body;
	    flde = new FuncExp(loc, fld);
	    flde = flde->semantic(sc);
	    fld->tookAddressOf = 0;

	    // Resolve any forward referenced goto's
	    for (int i = 0; i < gotos.dim; i++)
	    {	CompoundStatement *cs = (CompoundStatement *)gotos.data[i];
		GotoStatement *gs = (GotoStatement *)cs->statements->data[0];

		if (!gs->label->statement)
		{   // 'Promote' it to this scope, and replace with a return
		    cases.push(gs);
		    s = new ReturnStatement(0, new IntegerExp(cases.dim + 1));
		    cs->statements->data[0] = (void *)s;
		}
	    }

	    if (tab->ty == Taarray)
	    {
		// Check types
		Argument *arg = (Argument *)arguments->data[0];
		if (dim == 2)
		{
		    if (arg->storageClass & STCref)
			error("foreach: index cannot be ref");
		    if (!arg->type->equals(taa->index))
			error("foreach: index must be type %s, not %s", taa->index->toChars(), arg->type->toChars());
		    arg = (Argument *)arguments->data[1];
		}
		if (!arg->type->equals(taa->nextOf()))
		    error("foreach: value must be type %s, not %s", taa->nextOf()->toChars(), arg->type->toChars());

		/* Call:
		 *	_aaApply(aggr, keysize, flde)
		 */
		//LDC: Build arguments.
		static FuncDeclaration *aaApply2_fd = NULL;
        static TypeDelegate* aaApply2_dg;
		if(!aaApply2_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));
		    Arguments* dgargs = new Arguments;
		    dgargs->push(new Argument(STCin, Type::tvoidptr, NULL, NULL));
		    dgargs->push(new Argument(STCin, Type::tvoidptr, NULL, NULL));
		    aaApply2_dg = new TypeDelegate(new TypeFunction(dgargs, Type::tindex, 0, LINKd));
		    args->push(new Argument(STCin, aaApply2_dg, NULL, NULL));
		    aaApply2_fd = FuncDeclaration::genCfunc(args, Type::tindex, "_aaApply2");
		}
		static FuncDeclaration *aaApply_fd = NULL;
        static TypeDelegate* aaApply_dg;
		if(!aaApply_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));
		    Arguments* dgargs = new Arguments;
		    dgargs->push(new Argument(STCin, Type::tvoidptr, NULL, NULL));
		    aaApply_dg = new TypeDelegate(new TypeFunction(dgargs, Type::tindex, 0, LINKd));
		    args->push(new Argument(STCin, aaApply_dg, NULL, NULL));
		    aaApply_fd = FuncDeclaration::genCfunc(args, Type::tindex, "_aaApply");
		}
		if (dim == 2) {
		    fdapply = aaApply2_fd;
            fldeTy = aaApply2_dg;
		} else {
		    fdapply = aaApply_fd;
            fldeTy = aaApply_dg;
		}
		ec = new VarExp(0, fdapply);
		Expressions *exps = new Expressions();
		exps->push(aggr);
		size_t keysize = taa->index->size();
		keysize = (keysize + (PTRSIZE-1)) & ~(PTRSIZE-1);
		exps->push(new IntegerExp(0, keysize, Type::tsize_t));

        // LDC paint delegate argument to the type runtime expects
        if (!fldeTy->equals(flde->type))
        {
            flde = new CastExp(loc, flde, flde->type);
            flde->type = fldeTy;
        }
		exps->push(flde);

		e = new CallExp(loc, ec, exps);
		e->type = Type::tindex;	// don't run semantic() on e
	    }
	    else if (tab->ty == Tarray || tab->ty == Tsarray)
	    {
		/* Call:
		 *	_aApply(aggr, flde)
		 */
		static char fntab[9][3] =
		{ "cc","cw","cd",
		  "wc","cc","wd",
		  "dc","dw","dd"
		};
		char fdname[7+1+2+ sizeof(dim)*3 + 1];
		int flag;

		switch (tn->ty)
		{
		    case Tchar:		flag = 0; break;
		    case Twchar:	flag = 3; break;
		    case Tdchar:	flag = 6; break;
		    default:		assert(0);
		}
		switch (tnv->ty)
		{
		    case Tchar:		flag += 0; break;
		    case Twchar:	flag += 1; break;
		    case Tdchar:	flag += 2; break;
		    default:		assert(0);
		}
		const char *r = (op == TOKforeach_reverse) ? "R" : "";
		int j = sprintf(fdname, "_aApply%s%.*s%d", r, 2, fntab[flag], dim);
		assert(j < sizeof(fdname));
		//LDC: Build arguments.
		Arguments* args = new Arguments;
		args->push(new Argument(STCin, tn->arrayOf(), NULL, NULL));
		if (dim == 2) {
		    Arguments* dgargs = new Arguments;
		    dgargs->push(new Argument(STCin, Type::tvoidptr, NULL, NULL));
		    dgargs->push(new Argument(STCin, Type::tvoidptr, NULL, NULL));
		    dgty = new TypeDelegate(new TypeFunction(dgargs, Type::tindex, 0, LINKd));
		    args->push(new Argument(STCin, dgty, NULL, NULL));
		    fdapply = FuncDeclaration::genCfunc(args, Type::tindex, fdname);
		} else {
		    Arguments* dgargs = new Arguments;
		    dgargs->push(new Argument(STCin, Type::tvoidptr, NULL, NULL));
		    dgty = new TypeDelegate(new TypeFunction(dgargs, Type::tindex, 0, LINKd));
		    args->push(new Argument(STCin, dgty, NULL, NULL));
		    fdapply = FuncDeclaration::genCfunc(args, Type::tindex, fdname);
		}

		ec = new VarExp(0, fdapply);
		Expressions *exps = new Expressions();
		if (tab->ty == Tsarray)
		   aggr = aggr->castTo(sc, tn->arrayOf());
        exps->push(aggr);

        // LDC paint delegate argument to the type runtime expects
        if (!dgty->equals(flde->type))
        {
            flde = new CastExp(loc, flde, flde->type);
            flde->type = dgty;
        }
		exps->push(flde);

		e = new CallExp(loc, ec, exps);
		e->type = Type::tindex;	// don't run semantic() on e
	    }
	    else if (tab->ty == Tdelegate)
	    {
		/* Call:
		 *	aggr(flde)
		 */
		Expressions *exps = new Expressions();
		exps->push(flde);
		e = new CallExp(loc, aggr, exps);
		e = e->semantic(sc);
		if (e->type != Type::tint32)
		    error("opApply() function for %s must return an int", tab->toChars());
	    }
	    else
	    {
		assert(tab->ty == Tstruct || tab->ty == Tclass);
		Identifier *idapply = (op == TOKforeach_reverse)
				? Id::applyReverse : Id::apply;
		Dsymbol *sapply = search_function((AggregateDeclaration *)tab->toDsymbol(sc), idapply);
	        Expressions *exps = new Expressions();
#if 0
		TemplateDeclaration *td;
		if (sapply &&
		    (td = sapply->isTemplateDeclaration()) != NULL)
		{   /* Call:
		     *	aggr.apply!(fld)()
		     */
		    TemplateInstance *ti = new TemplateInstance(loc, idapply);
		    Objects *tiargs = new Objects();
		    tiargs->push(fld);
		    ti->tiargs = tiargs;
		    ec = new DotTemplateInstanceExp(loc, aggr, ti);
		}
		else
#endif
		{
		    /* Call:
		     *	aggr.apply(flde)
		     */
		    ec = new DotIdExp(loc, aggr, idapply);
		    exps->push(flde);
		}
		e = new CallExp(loc, ec, exps);
		e = e->semantic(sc);
		if (e->type != Type::tint32)
		    error("opApply() function for %s must return an int", tab->toChars());
	    }

	    if (!cases.dim)
		// Easy case, a clean exit from the loop
		s = new ExpStatement(loc, e);
	    else
	    {	// Construct a switch statement around the return value
		// of the apply function.
		Statements *a = new Statements();

		// default: break; takes care of cases 0 and 1
		s = new BreakStatement(0, NULL);
		s = new DefaultStatement(0, s);
		a->push(s);

		// cases 2...
		for (int i = 0; i < cases.dim; i++)
		{
		    s = (Statement *)cases.data[i];
		    s = new CaseStatement(0, new IntegerExp(i + 2), s);
		    a->push(s);
		}

		s = new CompoundStatement(loc, a);
		s = new SwitchStatement(loc, e, s);
		s = s->semantic(sc);
	    }
	    break;
	}

	default:
	    error("foreach: %s is not an aggregate type", aggr->type->toChars());
	    break;
    }
    sc->noctor--;
    sc->pop();
    return s;
}

bool ForeachStatement::checkForArgTypes()
{
    for (size_t i = 0; i < arguments->dim; i++)
    {	Argument *arg = (Argument *)arguments->data[i];
	if (!arg->type)
	{
	    error("cannot infer type for %s", arg->ident->toChars());
	    return FALSE;
	}
    }
    return TRUE;
}

int ForeachStatement::hasBreak()
{
    return TRUE;
}

int ForeachStatement::hasContinue()
{
    return TRUE;
}

int ForeachStatement::usesEH()
{
    return body->usesEH();
}

int ForeachStatement::blockExit()
{   int result = BEfallthru;

    if (aggr->canThrow())
	result |= BEthrow;

    if (body)
    {
	result |= body->blockExit() & ~(BEbreak | BEcontinue);
    }
    return result;
}

int ForeachStatement::fallOffEnd()
{
    if (body)
	body->fallOffEnd();
    return TRUE;
}

int ForeachStatement::comeFrom()
{
    if (body)
	return body->comeFrom();
    return FALSE;
}

void ForeachStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring(Token::toChars(op));
    buf->writestring(" (");
    for (int i = 0; i < arguments->dim; i++)
    {
	Argument *a = (Argument *)arguments->data[i];
	if (i)
	    buf->writestring(", ");
	if (a->storageClass & STCref)
	    buf->writestring((global.params.Dversion == 1)
		? (char*)"inout " : (char*)"ref ");
	if (a->type)
	    a->type->toCBuffer(buf, a->ident, hgs);
	else
	    buf->writestring(a->ident->toChars());
    }
    buf->writestring("; ");
    aggr->toCBuffer(buf, hgs);
    buf->writebyte(')');
    buf->writenl();
    buf->writebyte('{');
    buf->writenl();
    if (body)
	body->toCBuffer(buf, hgs);
    buf->writebyte('}');
    buf->writenl();
}

/**************************** ForeachRangeStatement ***************************/

#if DMDV2

ForeachRangeStatement::ForeachRangeStatement(Loc loc, enum TOK op, Argument *arg,
	Expression *lwr, Expression *upr, Statement *body)
    : Statement(loc)
{
    this->op = op;
    this->arg = arg;
    this->lwr = lwr;
    this->upr = upr;
    this->body = body;

    this->enclosinghandler = NULL;

    this->key = NULL;
}

Statement *ForeachRangeStatement::syntaxCopy()
{
    ForeachRangeStatement *s = new ForeachRangeStatement(loc, op,
	arg->syntaxCopy(),
	lwr->syntaxCopy(),
	upr->syntaxCopy(),
	body ? body->syntaxCopy() : NULL);
    return s;
}

Statement *ForeachRangeStatement::semantic(Scope *sc)
{
    //printf("ForeachRangeStatement::semantic() %p\n", this);
    ScopeDsymbol *sym;
    Statement *s = this;

    enclosinghandler = sc->tfOfTry;

    lwr = lwr->semantic(sc);
    lwr = resolveProperties(sc, lwr);
    lwr = lwr->optimize(WANTvalue);
    if (!lwr->type)
    {
	error("invalid range lower bound %s", lwr->toChars());
	return this;
    }

    upr = upr->semantic(sc);
    upr = resolveProperties(sc, upr);
    upr = upr->optimize(WANTvalue);
    if (!upr->type)
    {
	error("invalid range upper bound %s", upr->toChars());
	return this;
    }

    if (arg->type)
    {
	lwr = lwr->implicitCastTo(sc, arg->type);
	upr = upr->implicitCastTo(sc, arg->type);
    }
    else
    {
	/* Must infer types from lwr and upr
	 */
	AddExp ea(loc, lwr, upr);
	ea.typeCombine(sc);
	arg->type = ea.type->mutableOf();
	lwr = ea.e1;
	upr = ea.e2;
    }
    if (!arg->type->isscalar())
	error("%s is not a scalar type", arg->type->toChars());

    sym = new ScopeDsymbol();
    sym->parent = sc->scopesym;
    sc = sc->push(sym);

    sc->noctor++;

    key = new VarDeclaration(loc, arg->type, arg->ident, NULL);
    DeclarationExp *de = new DeclarationExp(loc, key);
    de->semantic(sc);

    if (key->storage_class)
	error("foreach range: key cannot have storage class");

    sc->sbreak = this;
    sc->scontinue = this;
    body = body->semantic(sc);

    sc->noctor--;
    sc->pop();
    return s;
}

int ForeachRangeStatement::hasBreak()
{
    return TRUE;
}

int ForeachRangeStatement::hasContinue()
{
    return TRUE;
}

int ForeachRangeStatement::usesEH()
{
    return body->usesEH();
}

int ForeachRangeStatement::blockExit()
{   int result = BEfallthru;

    if (lwr && lwr->canThrow())
	result |= BEthrow;
    else if (upr && upr->canThrow())
	result |= BEthrow;

    if (body)
    {
	result |= body->blockExit() & ~(BEbreak | BEcontinue);
    }
    return result;
}

int ForeachRangeStatement::fallOffEnd()
{
    if (body)
	body->fallOffEnd();
    return TRUE;
}

int ForeachRangeStatement::comeFrom()
{
    if (body)
	return body->comeFrom();
    return FALSE;
}

void ForeachRangeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring(Token::toChars(op));
    buf->writestring(" (");

    if (arg->type)
	arg->type->toCBuffer(buf, arg->ident, hgs);
    else
	buf->writestring(arg->ident->toChars());

    buf->writestring("; ");
    lwr->toCBuffer(buf, hgs);
    buf->writestring(" .. ");
    upr->toCBuffer(buf, hgs);
    buf->writebyte(')');
    buf->writenl();
    buf->writebyte('{');
    buf->writenl();
    if (body)
	body->toCBuffer(buf, hgs);
    buf->writebyte('}');
    buf->writenl();
}

#endif

/******************************** IfStatement ***************************/

IfStatement::IfStatement(Loc loc, Argument *arg, Expression *condition, Statement *ifbody, Statement *elsebody)
    : Statement(loc)
{
    this->arg = arg;
    this->condition = condition;
    this->ifbody = ifbody;
    this->elsebody = elsebody;
    this->match = NULL;
}

Statement *IfStatement::syntaxCopy()
{
    Statement *i = NULL;
    if (ifbody)
        i = ifbody->syntaxCopy();

    Statement *e = NULL;
    if (elsebody)
	e = elsebody->syntaxCopy();

    Argument *a = arg ? arg->syntaxCopy() : NULL;
    IfStatement *s = new IfStatement(loc, a, condition->syntaxCopy(), i, e);
    return s;
}

Statement *IfStatement::semantic(Scope *sc)
{
    condition = condition->semantic(sc);
    condition = resolveProperties(sc, condition);
    condition = condition->checkToBoolean();

    // If we can short-circuit evaluate the if statement, don't do the
    // semantic analysis of the skipped code.
    // This feature allows a limited form of conditional compilation.
    condition = condition->optimize(WANTflags);

    // Evaluate at runtime
    unsigned cs0 = sc->callSuper;
    unsigned cs1;

    Scope *scd;
    if (arg)
    {	/* Declare arg, which we will set to be the
	 * result of condition.
	 */
	ScopeDsymbol *sym = new ScopeDsymbol();
	sym->parent = sc->scopesym;
	scd = sc->push(sym);

	Type *t = arg->type ? arg->type : condition->type;
	match = new VarDeclaration(loc, t, arg->ident, NULL);
	match->noauto = 1;
	match->semantic(scd);
	if (!scd->insert(match))
	    assert(0);
	match->parent = sc->func;

	/* Generate:
	 *  (arg = condition)
	 */
	VarExp *v = new VarExp(0, match);
	condition = new AssignExp(loc, v, condition);
	condition = condition->semantic(scd);
    }

    // LDC
    else if (ident == Id::allow_inline)
    {
        sc->func->allowInlining = true;
    }

    else
	scd = sc->push();
    ifbody = ifbody->semantic(scd);
    scd->pop();

    cs1 = sc->callSuper;
    sc->callSuper = cs0;
    if (elsebody)
	elsebody = elsebody->semanticScope(sc, NULL, NULL);
    sc->mergeCallSuper(loc, cs1);

    return this;
}

int IfStatement::usesEH()
{
    return (ifbody && ifbody->usesEH()) || (elsebody && elsebody->usesEH());
}

int IfStatement::blockExit()
{
    //printf("IfStatement::blockExit(%p)\n", this);

    int result = BEnone;
    if (condition->canThrow())
	result |= BEthrow;
    if (condition->isBool(TRUE))
    {
	if (ifbody)
	    result |= ifbody->blockExit();
	else
	    result |= BEfallthru;
    }
    else if (condition->isBool(FALSE))
    {
	if (elsebody)
	    result |= elsebody->blockExit();
	else
	    result |= BEfallthru;
    }
    else
    {
	if (ifbody)
	    result |= ifbody->blockExit();
	else
	    result |= BEfallthru;
	if (elsebody)
	    result |= elsebody->blockExit();
	else
	    result |= BEfallthru;
    }
    //printf("IfStatement::blockExit(%p) = x%x\n", this, result);
    return result;
}

int IfStatement::fallOffEnd()
{
    if (!ifbody || ifbody->fallOffEnd() ||
	!elsebody || elsebody->fallOffEnd())
	return TRUE;
    return FALSE;
}


void IfStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("if (");
    if (arg)
    {
	if (arg->type)
	    arg->type->toCBuffer(buf, arg->ident, hgs);
	else
	{   buf->writestring("auto ");
	    buf->writestring(arg->ident->toChars());
	}
	buf->writestring(" = ");
    }
    condition->toCBuffer(buf, hgs);
    buf->writebyte(')');
    buf->writenl();
    ifbody->toCBuffer(buf, hgs);
    if (elsebody)
    {   buf->writestring("else");
        buf->writenl();
        elsebody->toCBuffer(buf, hgs);
    }
}

/******************************** ConditionalStatement ***************************/

ConditionalStatement::ConditionalStatement(Loc loc, Condition *condition, Statement *ifbody, Statement *elsebody)
    : Statement(loc)
{
    this->condition = condition;
    this->ifbody = ifbody;
    this->elsebody = elsebody;
}

Statement *ConditionalStatement::syntaxCopy()
{
    Statement *e = NULL;
    if (elsebody)
	e = elsebody->syntaxCopy();
    ConditionalStatement *s = new ConditionalStatement(loc,
		condition->syntaxCopy(), ifbody->syntaxCopy(), e);
    return s;
}

Statement *ConditionalStatement::semantic(Scope *sc)
{
    //printf("ConditionalStatement::semantic()\n");

    // If we can short-circuit evaluate the if statement, don't do the
    // semantic analysis of the skipped code.
    // This feature allows a limited form of conditional compilation.
    if (condition->include(sc, NULL))
    {
	ifbody = ifbody->semantic(sc);
	return ifbody;
    }
    else
    {
	if (elsebody)
	    elsebody = elsebody->semantic(sc);
	return elsebody;
    }
}

Statements *ConditionalStatement::flatten(Scope *sc)
{
    Statement *s;

    if (condition->include(sc, NULL))
	s = ifbody;
    else
	s = elsebody;

    Statements *a = new Statements();
    a->push(s);
    return a;
}

int ConditionalStatement::usesEH()
{
    return (ifbody && ifbody->usesEH()) || (elsebody && elsebody->usesEH());
}

int ConditionalStatement::blockExit()
{
    int result = ifbody->blockExit();
    if (elsebody)
	result |= elsebody->blockExit();
    return result;
}

void ConditionalStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    condition->toCBuffer(buf, hgs);
    buf->writenl();
    buf->writeByte('{');
    buf->writenl();
    if (ifbody)
	ifbody->toCBuffer(buf, hgs);
    buf->writeByte('}');
    buf->writenl();
    if (elsebody)
    {
	buf->writestring("else");
	buf->writenl();
	buf->writeByte('{');
	buf->writenl();
	elsebody->toCBuffer(buf, hgs);
	buf->writeByte('}');
	buf->writenl();
    }
    buf->writenl();
}


/******************************** PragmaStatement ***************************/

PragmaStatement::PragmaStatement(Loc loc, Identifier *ident, Expressions *args, Statement *body)
    : Statement(loc)
{
    this->ident = ident;
    this->args = args;
    this->body = body;
}

Statement *PragmaStatement::syntaxCopy()
{
    Statement *b = NULL;
    if (body)
	b = body->syntaxCopy();
    PragmaStatement *s = new PragmaStatement(loc,
		ident, Expression::arraySyntaxCopy(args), b);
    return s;
}

Statement *PragmaStatement::semantic(Scope *sc)
{   // Should be merged with PragmaDeclaration
    //printf("PragmaStatement::semantic() %s\n", toChars());
    //printf("body = %p\n", body);
    if (ident == Id::msg)
    {
        if (args)
        {
            for (size_t i = 0; i < args->dim; i++)
            {
                Expression *e = (Expression *)args->data[i];

                e = e->semantic(sc);
		e = e->optimize(WANTvalue | WANTinterpret);
                if (e->op == TOKstring)
                {
                    StringExp *se = (StringExp *)e;
                    fprintf(stdmsg, "%.*s", (int)se->len, se->string);
                }
                else
		    error("string expected for message, not '%s'", e->toChars());
            }
            fprintf(stdmsg, "\n");
        }
    }
    else if (ident == Id::lib)
    {
#if 1
	/* Should this be allowed?
	 */
	error("pragma(lib) not allowed as statement");
#else
	if (!args || args->dim != 1)
	    error("string expected for library name");
	else
	{
	    Expression *e = (Expression *)args->data[0];

	    e = e->semantic(sc);
	    e = e->optimize(WANTvalue | WANTinterpret);
	    args->data[0] = (void *)e;
	    if (e->op != TOKstring)
		error("string expected for library name, not '%s'", e->toChars());
	    else if (global.params.verbose)
	    {
		StringExp *se = (StringExp *)e;
		char *name = (char *)mem.malloc(se->len + 1);
		memcpy(name, se->string, se->len);
		name[se->len] = 0;
		printf("library   %s\n", name);
		mem.free(name);
	    }
	}
#endif
    }
    else if (ident == Id::startaddress)
    {
	if (!args || args->dim != 1)
	    error("function name expected for start address");
	else
	{
	    Expression *e = (Expression *)args->data[0];
	    e = e->semantic(sc);
	    e = e->optimize(WANTvalue | WANTinterpret);
	    args->data[0] = (void *)e;
	    Dsymbol *sa = getDsymbol(e);
	    if (!sa || !sa->isFuncDeclaration())
		error("function name expected for start address, not '%s'", e->toChars());
	    if (body)
	    {
		body = body->semantic(sc);
	    }
	    return this;
	}
    }
    else
        error("unrecognized pragma(%s)", ident->toChars());

    if (body)
    {
	body = body->semantic(sc);
    }
    return body;
}

int PragmaStatement::usesEH()
{
    return body && body->usesEH();
}

int PragmaStatement::blockExit()
{
    int result = BEfallthru;
#if 0 // currently, no code is generated for Pragma's, so it's just fallthru
    if (arrayExpressionCanThrow(args))
	result |= BEthrow;
    if (body)
	result |= body->blockExit();
#endif
    return result;
}

int PragmaStatement::fallOffEnd()
{
    if (body)
	return body->fallOffEnd();
    return TRUE;
}

void PragmaStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("pragma (");
    buf->writestring(ident->toChars());
    if (args && args->dim)
    {
	buf->writestring(", ");
	argsToCBuffer(buf, args, hgs);
    }
    buf->writeByte(')');
    if (body)
    {
	buf->writenl();
	buf->writeByte('{');
	buf->writenl();

	body->toCBuffer(buf, hgs);

	buf->writeByte('}');
	buf->writenl();
    }
    else
    {
	buf->writeByte(';');
	buf->writenl();
    }
}


/******************************** StaticAssertStatement ***************************/

StaticAssertStatement::StaticAssertStatement(StaticAssert *sa)
    : Statement(sa->loc)
{
    this->sa = sa;
}

Statement *StaticAssertStatement::syntaxCopy()
{
    StaticAssertStatement *s = new StaticAssertStatement((StaticAssert *)sa->syntaxCopy(NULL));
    return s;
}

Statement *StaticAssertStatement::semantic(Scope *sc)
{
    sa->semantic2(sc);
    return NULL;
}

void StaticAssertStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    sa->toCBuffer(buf, hgs);
}


/******************************** SwitchStatement ***************************/

SwitchStatement::SwitchStatement(Loc loc, Expression *c, Statement *b)
    : Statement(loc)
{
    condition = c;
    body = b;
    sdefault = NULL;
    tf = NULL;
    cases = NULL;
    hasNoDefault = 0;
    hasVars = 0;
    // LDC
    enclosinghandler = NULL;
}

Statement *SwitchStatement::syntaxCopy()
{
    SwitchStatement *s = new SwitchStatement(loc,
	condition->syntaxCopy(), body->syntaxCopy());
    return s;
}

Statement *SwitchStatement::semantic(Scope *sc)
{
    //printf("SwitchStatement::semantic(%p)\n", this);
    tf = sc->tf;
    assert(!cases);		// ensure semantic() is only run once

    enclosinghandler = sc->tfOfTry;

    condition = condition->semantic(sc);
    condition = resolveProperties(sc, condition);
    if (condition->type->isString())
    {
	// If it's not an array, cast it to one
	if (condition->type->ty != Tarray)
	{
	    condition = condition->implicitCastTo(sc, condition->type->nextOf()->arrayOf());
	}
	condition->type = condition->type->constOf();
    }
    else
    {	condition = condition->integralPromotions(sc);
	condition->checkIntegral();
    }
    condition = condition->optimize(WANTvalue);

    sc = sc->push();
    sc->sbreak = this;
    sc->sw = this;

    cases = new Array();
    sc->noctor++;	// BUG: should use Scope::mergeCallSuper() for each case instead
    body = body->semantic(sc);
    sc->noctor--;

    // Resolve any goto case's with exp
    for (int i = 0; i < gotoCases.dim; i++)
    {
	GotoCaseStatement *gcs = (GotoCaseStatement *)gotoCases.data[i];

	if (!gcs->exp)
	{
	    gcs->error("no case statement following goto case;");
	    break;
	}

	for (Scope *scx = sc; scx; scx = scx->enclosing)
	{
	    if (!scx->sw)
		continue;
	    for (int j = 0; j < scx->sw->cases->dim; j++)
	    {
		CaseStatement *cs = (CaseStatement *)scx->sw->cases->data[j];

		if (cs->exp->equals(gcs->exp))
		{
		    gcs->cs = cs;
		    goto Lfoundcase;
		}
	    }
	}
	gcs->error("case %s not found", gcs->exp->toChars());

     Lfoundcase:
	;
    }

    if (!sc->sw->sdefault)
    {	hasNoDefault = 1;

	if (global.params.warnings)
	{   warning("%s: switch statement has no default", loc.toChars());
	}

	// Generate runtime error if the default is hit
	Statements *a = new Statements();
	CompoundStatement *cs;
	Statement *s;

	if (global.params.useSwitchError)
	    s = new SwitchErrorStatement(loc);
	else
	{   Expression *e = new HaltExp(loc);
	    s = new ExpStatement(loc, e);
	}

	a->reserve(4);
	a->push(body);
	a->push(new BreakStatement(loc, NULL));
	sc->sw->sdefault = new DefaultStatement(loc, s);
	a->push(sc->sw->sdefault);
	cs = new CompoundStatement(loc, a);
	body = cs;
    }

    sc->pop();
    return this;
}

int SwitchStatement::hasBreak()
{
    return TRUE;
}

int SwitchStatement::usesEH()
{
    return body ? body->usesEH() : 0;
}

int SwitchStatement::blockExit()
{   int result = BEnone;
    if (condition->canThrow())
	result |= BEthrow;

    if (body)
    {	result |= body->blockExit();
	if (result & BEbreak)
	{   result |= BEfallthru;
	    result &= ~BEbreak;
	}
    }
    else
	result |= BEfallthru;

    return result;
}

int SwitchStatement::fallOffEnd()
{
    if (body)
	body->fallOffEnd();
    return TRUE;	// need to do this better
}

void SwitchStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("switch (");
    condition->toCBuffer(buf, hgs);
    buf->writebyte(')');
    buf->writenl();
    if (body)
    {
	if (!body->isScopeStatement())
        {   buf->writebyte('{');
            buf->writenl();
            body->toCBuffer(buf, hgs);
            buf->writebyte('}');
            buf->writenl();
        }
        else
        {
            body->toCBuffer(buf, hgs);
        }
    }
}

/******************************** CaseStatement ***************************/

CaseStatement::CaseStatement(Loc loc, Expression *exp, Statement *s)
    : Statement(loc)
{
    this->exp = exp;
    this->statement = s;
    index = 0;
    cblock = NULL;
    bodyBB = NULL;
    llvmIdx = NULL;
}

Statement *CaseStatement::syntaxCopy()
{
    CaseStatement *s = new CaseStatement(loc, exp->syntaxCopy(), statement->syntaxCopy());
    return s;
}

Statement *CaseStatement::semantic(Scope *sc)
{   SwitchStatement *sw = sc->sw;

    //printf("CaseStatement::semantic() %s\n", toChars());
    exp = exp->semantic(sc);
    if (sw)
    {
	exp = exp->implicitCastTo(sc, sw->condition->type);
	exp = exp->optimize(WANTvalue | WANTinterpret);

	/* This is where variables are allowed as case expressions.
	 */
	if (exp->op == TOKvar)
	{   VarExp *ve = (VarExp *)exp;
	    VarDeclaration *v = ve->var->isVarDeclaration();
	    Type *t = exp->type->toBasetype();
	    if (v && (t->isintegral() || t->ty == Tclass))
	    {	/* Flag that we need to do special code generation
		 * for this, i.e. generate a sequence of if-then-else
		 */
		sw->hasVars = 1;
		goto L1;
	    }
	}

	if (exp->op != TOKstring && exp->op != TOKint64)
	{
	    error("case must be a string or an integral constant, not %s", exp->toChars());
	    exp = new IntegerExp(0);
	}

    L1:
	for (int i = 0; i < sw->cases->dim; i++)
	{
	    CaseStatement *cs = (CaseStatement *)sw->cases->data[i];

	    //printf("comparing '%s' with '%s'\n", exp->toChars(), cs->exp->toChars());
	    if (cs->exp->equals(exp))
	    {	error("duplicate case %s in switch statement", exp->toChars());
		break;
	    }
	}

	sw->cases->push(this);

	// Resolve any goto case's with no exp to this case statement
	for (int i = 0; i < sw->gotoCases.dim; i++)
	{
	    GotoCaseStatement *gcs = (GotoCaseStatement *)sw->gotoCases.data[i];

	    if (!gcs->exp)
	    {
		gcs->cs = this;
		sw->gotoCases.remove(i);	// remove from array
	    }
	}

	if (sc->sw->tf != sc->tf)
	    error("switch and case are in different finally blocks");
    }
    else
	error("case not in switch statement");
    statement = statement->semantic(sc);
    return this;
}

int CaseStatement::compare(Object *obj)
{
    // Sort cases so we can do an efficient lookup
    CaseStatement *cs2 = (CaseStatement *)(obj);

    return exp->compare(cs2->exp);
}

int CaseStatement::usesEH()
{
    return statement->usesEH();
}

int CaseStatement::blockExit()
{
    return statement->blockExit();
}

int CaseStatement::fallOffEnd()
{
    return statement->fallOffEnd();
}

int CaseStatement::comeFrom()
{
    return TRUE;
}

void CaseStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("case ");
    exp->toCBuffer(buf, hgs);
    buf->writebyte(':');
    buf->writenl();
    statement->toCBuffer(buf, hgs);
}

/******************************** DefaultStatement ***************************/

DefaultStatement::DefaultStatement(Loc loc, Statement *s)
    : Statement(loc)
{
    this->statement = s;
#if IN_GCC
+    cblock = NULL;
#endif
    bodyBB = NULL;
}

Statement *DefaultStatement::syntaxCopy()
{
    DefaultStatement *s = new DefaultStatement(loc, statement->syntaxCopy());
    return s;
}

Statement *DefaultStatement::semantic(Scope *sc)
{
    //printf("DefaultStatement::semantic()\n");
    if (sc->sw)
    {
	if (sc->sw->sdefault)
	{
	    error("switch statement already has a default");
	}
	sc->sw->sdefault = this;

	if (sc->sw->tf != sc->tf)
	    error("switch and default are in different finally blocks");
    }
    else
	error("default not in switch statement");
    statement = statement->semantic(sc);
    return this;
}

int DefaultStatement::usesEH()
{
    return statement->usesEH();
}

int DefaultStatement::blockExit()
{
    return statement->blockExit();
}

int DefaultStatement::fallOffEnd()
{
    return statement->fallOffEnd();
}

int DefaultStatement::comeFrom()
{
    return TRUE;
}

void DefaultStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("default:\n");
    statement->toCBuffer(buf, hgs);
}

/******************************** GotoDefaultStatement ***************************/

GotoDefaultStatement::GotoDefaultStatement(Loc loc)
    : Statement(loc)
{
    sw = NULL;
    enclosinghandler = NULL;
}

Statement *GotoDefaultStatement::syntaxCopy()
{
    GotoDefaultStatement *s = new GotoDefaultStatement(loc);
    return s;
}

Statement *GotoDefaultStatement::semantic(Scope *sc)
{
    enclosinghandler = sc->tfOfTry;
    sw = sc->sw;
    if (!sw)
	error("goto default not in switch statement");
    return this;
}

int GotoDefaultStatement::blockExit()
{
    return BEgoto;
}

int GotoDefaultStatement::fallOffEnd()
{
    return FALSE;
}

void GotoDefaultStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("goto default;\n");
}

/******************************** GotoCaseStatement ***************************/

GotoCaseStatement::GotoCaseStatement(Loc loc, Expression *exp)
    : Statement(loc)
{
    cs = NULL;
    this->exp = exp;
    enclosinghandler = NULL;
    sw = NULL;
}

Statement *GotoCaseStatement::syntaxCopy()
{
    Expression *e = exp ? exp->syntaxCopy() : NULL;
    GotoCaseStatement *s = new GotoCaseStatement(loc, e);
    return s;
}

Statement *GotoCaseStatement::semantic(Scope *sc)
{
    enclosinghandler = sc->tfOfTry;
    if (exp)
	exp = exp->semantic(sc);

    if (!sc->sw)
	error("goto case not in switch statement");
    else
    {
	sw = sc->sw;
	sc->sw->gotoCases.push(this);
	if (exp)
	{
	    exp = exp->implicitCastTo(sc, sc->sw->condition->type);
	    exp = exp->optimize(WANTvalue);
	}
    }
    return this;
}

int GotoCaseStatement::blockExit()
{
    return BEgoto;
}

int GotoCaseStatement::fallOffEnd()
{
    return FALSE;
}

void GotoCaseStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("goto case");
    if (exp)
    {   buf->writebyte(' ');
        exp->toCBuffer(buf, hgs);
    }
    buf->writebyte(';');
    buf->writenl();
}

/******************************** SwitchErrorStatement ***************************/

SwitchErrorStatement::SwitchErrorStatement(Loc loc)
    : Statement(loc)
{
}

int SwitchErrorStatement::blockExit()
{
    return BEthrow;
}

int SwitchErrorStatement::fallOffEnd()
{
    return FALSE;
}

void SwitchErrorStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("SwitchErrorStatement::toCBuffer()");
    buf->writenl();
}

/******************************** ReturnStatement ***************************/

ReturnStatement::ReturnStatement(Loc loc, Expression *exp)
    : Statement(loc)
{
    this->exp = exp;
    this->enclosinghandler = NULL;
}

Statement *ReturnStatement::syntaxCopy()
{
    Expression *e = NULL;
    if (exp)
	e = exp->syntaxCopy();
    ReturnStatement *s = new ReturnStatement(loc, e);
    return s;
}

Statement *ReturnStatement::semantic(Scope *sc)
{
    //printf("ReturnStatement::semantic() %s\n", toChars());
    this->enclosinghandler = sc->tfOfTry;

    FuncDeclaration *fd = sc->parent->isFuncDeclaration();
    Scope *scx = sc;
    int implicit0 = 0;

    if (sc->fes)
    {
	// Find scope of function foreach is in
	for (; 1; scx = scx->enclosing)
	{
	    assert(scx);
	    if (scx->func != fd)
	    {	fd = scx->func;		// fd is now function enclosing foreach
		break;
	    }
	}
    }

    Type *tret = fd->type->nextOf();
    if (fd->tintro)
	/* We'll be implicitly casting the return expression to tintro
	 */
	tret = fd->tintro->nextOf();
    Type *tbret = NULL;

    if (tret)
	tbret = tret->toBasetype();

    // main() returns 0, even if it returns void
    if (!exp && (!tbret || tbret->ty == Tvoid) && fd->isMain())
    {	implicit0 = 1;
	exp = new IntegerExp(0);
    }

    if (sc->incontract || scx->incontract)
	error("return statements cannot be in contracts");
    if (sc->tf || scx->tf)
	error("return statements cannot be in finally, scope(exit) or scope(success) bodies");

    if (fd->isCtorDeclaration())
    {
	// Constructors implicitly do:
	//	return this;
	if (exp && exp->op != TOKthis)
	    error("cannot return expression from constructor");
	exp = new ThisExp(0);
    }

    if (!exp)
	fd->nrvo_can = 0;

    if (exp)
    {
	fd->hasReturnExp |= 1;

	exp = exp->semantic(sc);
	exp = resolveProperties(sc, exp);
	exp = exp->optimize(WANTvalue);

	if (fd->nrvo_can && exp->op == TOKvar)
	{   VarExp *ve = (VarExp *)exp;
	    VarDeclaration *v = ve->var->isVarDeclaration();

	    if (((TypeFunction *)fd->type)->isref)
		// Function returns a reference
		fd->nrvo_can = 0;
	    else if (!v || v->isOut() || v->isRef())
		fd->nrvo_can = 0;
	    else if (tbret->ty == Tstruct && ((TypeStruct *)tbret)->sym->dtor)
		// Struct being returned has destructors
		fd->nrvo_can = 0;
	    else if (fd->nrvo_var == NULL)
	    {	if (!v->isDataseg() && !v->isParameter() && v->toParent2() == fd)
		{   //printf("Setting nrvo to %s\n", v->toChars());
		    fd->nrvo_var = v;
		}
		else
		    fd->nrvo_can = 0;
	    }
	    else if (fd->nrvo_var != v)
		fd->nrvo_can = 0;
	}
	else
	    fd->nrvo_can = 0;

	if (fd->returnLabel && tbret->ty != Tvoid)
	{
	}
	else if (fd->inferRetType)
	{
	    if (fd->type->nextOf())
	    {
		if (!exp->type->equals(fd->type->nextOf()))
		    error("mismatched function return type inference of %s and %s",
			exp->type->toChars(), fd->type->nextOf()->toChars());
	    }
	    else
	    {
		((TypeFunction *)fd->type)->next = exp->type;
		fd->type = fd->type->semantic(loc, sc);
		if (!fd->tintro)
		{   tret = fd->type->nextOf();
		    tbret = tret->toBasetype();
		}
	    }
	}
	else if (tbret->ty != Tvoid)
	{
	    exp = exp->implicitCastTo(sc, tret);
	}
    }
    else if (fd->inferRetType)
    {
	if (fd->type->nextOf())
	{
	    if (fd->type->nextOf()->ty != Tvoid)
		error("mismatched function return type inference of void and %s",
		    fd->type->nextOf()->toChars());
	}
	else
	{
	    ((TypeFunction *)fd->type)->next = Type::tvoid;
	    fd->type = fd->type->semantic(loc, sc);
	    if (!fd->tintro)
	    {   tret = Type::tvoid;
		tbret = tret;
	    }
	}
    }
    else if (tbret->ty != Tvoid)	// if non-void return
	error("return expression expected");

    if (sc->fes)
    {
	Statement *s;

	if (exp && !implicit0)
	{
	    exp = exp->implicitCastTo(sc, tret);
	}
	if (!exp || exp->op == TOKint64 || exp->op == TOKfloat64 ||
	    exp->op == TOKimaginary80 || exp->op == TOKcomplex80 ||
	    exp->op == TOKthis || exp->op == TOKsuper || exp->op == TOKnull ||
	    exp->op == TOKstring)
	{
	    sc->fes->cases.push(this);
	    // Construct: return cases.dim+1;
	    s = new ReturnStatement(0, new IntegerExp(sc->fes->cases.dim + 1));
	}
	else if (fd->type->nextOf()->toBasetype() == Type::tvoid)
	{
	    s = new ReturnStatement(0, NULL);
	    sc->fes->cases.push(s);

	    // Construct: { exp; return cases.dim + 1; }
	    Statement *s1 = new ExpStatement(loc, exp);
	    Statement *s2 = new ReturnStatement(0, new IntegerExp(sc->fes->cases.dim + 1));
	    s = new CompoundStatement(loc, s1, s2);
	}
	else
	{
	    // Construct: return vresult;
	    if (!fd->vresult)
	    {	// Declare vresult
		VarDeclaration *v = new VarDeclaration(loc, tret, Id::result, NULL);
		v->noauto = 1;
		v->semantic(scx);
		if (!scx->insert(v))
		    assert(0);
		v->parent = fd;
		fd->vresult = v;
	    }

	    s = new ReturnStatement(0, new VarExp(0, fd->vresult));
	    sc->fes->cases.push(s);

	    // Construct: { vresult = exp; return cases.dim + 1; }
	    exp = new AssignExp(loc, new VarExp(0, fd->vresult), exp);
	    exp = exp->semantic(sc);
	    Statement *s1 = new ExpStatement(loc, exp);
	    Statement *s2 = new ReturnStatement(0, new IntegerExp(sc->fes->cases.dim + 1));
	    s = new CompoundStatement(loc, s1, s2);
	}
	return s;
    }

    if (exp)
    {
	if (fd->returnLabel && tbret->ty != Tvoid)
	{
	    assert(fd->vresult);
	    VarExp *v = new VarExp(0, fd->vresult);

	    exp = new AssignExp(loc, v, exp);
	    exp = exp->semantic(sc);
	}

	if (((TypeFunction *)fd->type)->isref)
	{   // Function returns a reference
	    if (tbret->isMutable())
		exp = exp->modifiableLvalue(sc, exp);
	    else
		exp = exp->toLvalue(sc, exp);

	    if (exp->op == TOKvar)
	    {	VarExp *ve = (VarExp *)exp;
		VarDeclaration *v = ve->var->isVarDeclaration();
		if (v && !v->isDataseg() && !(v->storage_class & (STCref | STCout)))
		    error("escaping reference to local variable %s", v->toChars());
	    }
	}

	//exp->dump(0);
	//exp->print();
	exp->checkEscape();
    }

    /* BUG: need to issue an error on:
     *	this
     *	{   if (x) return;
     *	    super();
     *	}
     */

    if (sc->callSuper & CSXany_ctor &&
	!(sc->callSuper & (CSXthis_ctor | CSXsuper_ctor)))
	error("return without calling constructor");

    sc->callSuper |= CSXreturn;

    // See if all returns are instead to be replaced with a goto returnLabel;
    if (fd->returnLabel)
    {
	GotoStatement *gs = new GotoStatement(loc, Id::returnLabel);

	gs->label = fd->returnLabel;
	if (exp)
	{   /* Replace: return exp;
	     * with:    exp; goto returnLabel;
	     */
	    Statement *s = new ExpStatement(0, exp);
	    return new CompoundStatement(loc, s, gs);
	}
	return gs;
    }

    if (exp && tbret->ty == Tvoid && !fd->isMain())
    {
	/* Replace:
	 *	return exp;
	 * with:
	 *	exp; return;
	 */
	Statement *s = new ExpStatement(loc, exp);
	loc = 0;
	exp = NULL;
	return new CompoundStatement(loc, s, this);
    }

    return this;
}

int ReturnStatement::blockExit()
{   int result = BEreturn;

    if (exp && exp->canThrow())
	result |= BEthrow;
    return result;
}

int ReturnStatement::fallOffEnd()
{
    return FALSE;
}

void ReturnStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->printf("return ");
    if (exp)
	exp->toCBuffer(buf, hgs);
    buf->writeByte(';');
    buf->writenl();
}

/******************************** BreakStatement ***************************/

BreakStatement::BreakStatement(Loc loc, Identifier *ident)
    : Statement(loc)
{
    this->ident = ident;
    this->enclosinghandler = NULL;
}

Statement *BreakStatement::syntaxCopy()
{
    BreakStatement *s = new BreakStatement(loc, ident);
    return s;
}

Statement *BreakStatement::semantic(Scope *sc)
{
    //printf("BreakStatement::semantic()\n");
    enclosinghandler = sc->tfOfTry;
    // If:
    //	break Identifier;
    if (ident)
    {
	Scope *scx;
	FuncDeclaration *thisfunc = sc->func;

	for (scx = sc; scx; scx = scx->enclosing)
	{
	    LabelStatement *ls;

	    if (scx->func != thisfunc)	// if in enclosing function
	    {
		if (sc->fes)		// if this is the body of a foreach
		{
		    /* Post this statement to the fes, and replace
		     * it with a return value that caller will put into
		     * a switch. Caller will figure out where the break
		     * label actually is.
		     * Case numbers start with 2, not 0, as 0 is continue
		     * and 1 is break.
		     */
		    Statement *s;
		    sc->fes->cases.push(this);
		    s = new ReturnStatement(0, new IntegerExp(sc->fes->cases.dim + 1));
		    return s;
		}
		break;			// can't break to it
	    }

	    ls = scx->slabel;
	    if (ls && ls->ident == ident)
	    {
		Statement *s = ls->statement;

		if (!s->hasBreak())
		    error("label '%s' has no break", ident->toChars());
		if (ls->tf != sc->tf)
		    error("cannot break out of finally block");
		
		this->target = ls;
		return this;
	    }
	}
	error("enclosing label '%s' for break not found", ident->toChars());
    }
    else if (!sc->sbreak)
    {
	if (sc->fes)
	{   Statement *s;

	    // Replace break; with return 1;
	    s = new ReturnStatement(0, new IntegerExp(1));
	    return s;
	}
	error("break is not inside a loop or switch");
    }
    return this;
}

int BreakStatement::blockExit()
{
    //printf("BreakStatement::blockExit(%p) = x%x\n", this, ident ? BEgoto : BEbreak);
    return ident ? BEgoto : BEbreak;
}

int BreakStatement::fallOffEnd()
{
    return FALSE;
}

void BreakStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("break");
    if (ident)
    {   buf->writebyte(' ');
        buf->writestring(ident->toChars());
    }
    buf->writebyte(';');
    buf->writenl();
}

/******************************** ContinueStatement ***************************/

ContinueStatement::ContinueStatement(Loc loc, Identifier *ident)
    : Statement(loc)
{
    this->ident = ident;
    this->enclosinghandler = NULL;
}

Statement *ContinueStatement::syntaxCopy()
{
    ContinueStatement *s = new ContinueStatement(loc, ident);
    return s;
}

Statement *ContinueStatement::semantic(Scope *sc)
{
    enclosinghandler = sc->tfOfTry;
    //printf("ContinueStatement::semantic() %p\n", this);
    if (ident)
    {
	Scope *scx;
	FuncDeclaration *thisfunc = sc->func;

	for (scx = sc; scx; scx = scx->enclosing)
	{
	    LabelStatement *ls;

	    if (scx->func != thisfunc)	// if in enclosing function
	    {
		if (sc->fes)		// if this is the body of a foreach
		{
		    for (; scx; scx = scx->enclosing)
		    {
			ls = scx->slabel;
			if (ls && ls->ident == ident && ls->statement == sc->fes)
			{
			    // Replace continue ident; with return 0;
			    return new ReturnStatement(0, new IntegerExp(0));
			}
		    }

		    /* Post this statement to the fes, and replace
		     * it with a return value that caller will put into
		     * a switch. Caller will figure out where the break
		     * label actually is.
		     * Case numbers start with 2, not 0, as 0 is continue
		     * and 1 is break.
		     */
		    Statement *s;
		    sc->fes->cases.push(this);
		    s = new ReturnStatement(0, new IntegerExp(sc->fes->cases.dim + 1));
		    return s;
		}
		break;			// can't continue to it
	    }

	    ls = scx->slabel;
	    if (ls && ls->ident == ident)
	    {
		Statement *s = ls->statement;

		if (!s->hasContinue())
		    error("label '%s' has no continue", ident->toChars());
		if (ls->tf != sc->tf)
		    error("cannot continue out of finally block");
		
		this->target = ls;
		return this;
	    }
	}
	error("enclosing label '%s' for continue not found", ident->toChars());
    }
    else if (!sc->scontinue)
    {
	if (sc->fes)
	{   Statement *s;

	    // Replace continue; with return 0;
	    s = new ReturnStatement(0, new IntegerExp(0));
	    return s;
	}
	error("continue is not inside a loop");
    }
    return this;
}

int ContinueStatement::blockExit()
{
    return ident ? BEgoto : BEcontinue;
}

int ContinueStatement::fallOffEnd()
{
    return FALSE;
}

void ContinueStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("continue");
    if (ident)
    {   buf->writebyte(' ');
        buf->writestring(ident->toChars());
    }
    buf->writebyte(';');
    buf->writenl();
}

/******************************** SynchronizedStatement ***************************/

SynchronizedStatement::SynchronizedStatement(Loc loc, Expression *exp, Statement *body)
    : Statement(loc)
{
    this->exp = exp;
    this->body = body;
    this->esync = NULL;
    this->enclosinghandler = NULL;
    // LDC
    this->llsync = NULL;
}

SynchronizedStatement::SynchronizedStatement(Loc loc, elem *esync, Statement *body)
    : Statement(loc)
{
    this->exp = NULL;
    this->body = body;
    this->esync = esync;
    this->enclosinghandler = NULL;
    // LDC
    this->llsync = NULL;
}

Statement *SynchronizedStatement::syntaxCopy()
{
    Expression *e = exp ? exp->syntaxCopy() : NULL;
    SynchronizedStatement *s = new SynchronizedStatement(loc, e, body ? body->syntaxCopy() : NULL);
    return s;
}

Statement *SynchronizedStatement::semantic(Scope *sc)
{
    if (exp)
    {	ClassDeclaration *cd;

	exp = exp->semantic(sc);
	exp = resolveProperties(sc, exp);
	cd = exp->type->isClassHandle();
	if (!cd)
	    error("can only synchronize on class objects, not '%s'", exp->type->toChars());
	else if (cd->isInterfaceDeclaration())
	{   Type *t = new TypeIdentifier(0, Id::Object);

	    t = t->semantic(0, sc);
	    exp = new CastExp(loc, exp, t);
	    exp = exp->semantic(sc);
	}
    }
    if (body)
    {
        enclosinghandler = sc->tfOfTry;
        sc->tfOfTry = new EnclosingSynchro(this);
        body = body->semantic(sc);
        sc->tfOfTry = enclosinghandler;
    }
    return this;
}

int SynchronizedStatement::hasBreak()
{
    return FALSE; //TRUE;
}

int SynchronizedStatement::hasContinue()
{
    return FALSE; //TRUE;
}

int SynchronizedStatement::usesEH()
{
    return TRUE;
}

int SynchronizedStatement::blockExit()
{
    return body ? body->blockExit() : BEfallthru;
}

int SynchronizedStatement::fallOffEnd()
{
    return body ? body->fallOffEnd() : TRUE;
}

void SynchronizedStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("synchronized");
    if (exp)
    {   buf->writebyte('(');
	exp->toCBuffer(buf, hgs);
	buf->writebyte(')');
    }
    if (body)
    {
	buf->writebyte(' ');
	body->toCBuffer(buf, hgs);
    }
}

/******************************** WithStatement ***************************/

WithStatement::WithStatement(Loc loc, Expression *exp, Statement *body)
    : Statement(loc)
{
    this->exp = exp;
    this->body = body;
    wthis = NULL;
}

Statement *WithStatement::syntaxCopy()
{
    WithStatement *s = new WithStatement(loc, exp->syntaxCopy(), body ? body->syntaxCopy() : NULL);
    return s;
}

Statement *WithStatement::semantic(Scope *sc)
{   ScopeDsymbol *sym;
    Initializer *init;

    //printf("WithStatement::semantic()\n");
    exp = exp->semantic(sc);
    exp = resolveProperties(sc, exp);
    if (exp->op == TOKimport)
    {	ScopeExp *es = (ScopeExp *)exp;

	sym = es->sds;
    }
    else if (exp->op == TOKtype)
    {	TypeExp *es = (TypeExp *)exp;

	sym = es->type->toDsymbol(sc)->isScopeDsymbol();
	if (!sym)
	{   error("%s has no members", es->toChars());
	    body = body->semantic(sc);
	    return this;
	}
    }
    else
    {	Type *t = exp->type;

	assert(t);
	t = t->toBasetype();
	if (t->isClassHandle())
	{
	    init = new ExpInitializer(loc, exp);
	    wthis = new VarDeclaration(loc, exp->type, Id::withSym, init);
	    wthis->semantic(sc);

	    sym = new WithScopeSymbol(this);
	    sym->parent = sc->scopesym;
	}
	else if (t->ty == Tstruct)
	{
	    Expression *e = exp->addressOf(sc);
	    init = new ExpInitializer(loc, e);
	    wthis = new VarDeclaration(loc, e->type, Id::withSym, init);
	    wthis->semantic(sc);
	    sym = new WithScopeSymbol(this);
	    sym->parent = sc->scopesym;
	}
	else
	{   error("with expressions must be class objects, not '%s'", exp->type->toChars());
	    return NULL;
	}
    }
    sc = sc->push(sym);

    if (body)
	body = body->semantic(sc);

    sc->pop();

    return this;
}

void WithStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("with (");
    exp->toCBuffer(buf, hgs);
    buf->writestring(")\n");
    if (body)
	body->toCBuffer(buf, hgs);
}

int WithStatement::usesEH()
{
    return body ? body->usesEH() : 0;
}

int WithStatement::blockExit()
{
    int result = BEnone;
    if (exp->canThrow())
	result = BEthrow;
    if (body)
	result |= body->blockExit();
    else
	result |= BEfallthru;
    return result;
}

int WithStatement::fallOffEnd()
{
    return body ? body->fallOffEnd() : TRUE;
}

/******************************** TryCatchStatement ***************************/

TryCatchStatement::TryCatchStatement(Loc loc, Statement *body, Array *catches)
    : Statement(loc)
{
    this->body = body;
    this->catches = catches;
}

Statement *TryCatchStatement::syntaxCopy()
{
    Array *a = new Array();
    a->setDim(catches->dim);
    for (int i = 0; i < a->dim; i++)
    {   Catch *c;

	c = (Catch *)catches->data[i];
	c = c->syntaxCopy();
	a->data[i] = c;
    }
    TryCatchStatement *s = new TryCatchStatement(loc, body->syntaxCopy(), a);
    return s;
}

Statement *TryCatchStatement::semantic(Scope *sc)
{
    body = body->semanticScope(sc, NULL /*this*/, NULL);

    /* Even if body is NULL, still do semantic analysis on catches
     */
    for (size_t i = 0; i < catches->dim; i++)
    {   Catch *c = (Catch *)catches->data[i];
	c->semantic(sc);

	// Determine if current catch 'hides' any previous catches
	for (size_t j = 0; j < i; j++)
	{   Catch *cj = (Catch *)catches->data[j];
	    char *si = c->loc.toChars();
	    char *sj = cj->loc.toChars();

	    if (c->type->toBasetype()->implicitConvTo(cj->type->toBasetype()))
		error("catch at %s hides catch at %s", sj, si);
	}
    }

    if (!body)
	return NULL;

    return this;
}

int TryCatchStatement::hasBreak()
{
    return FALSE; //TRUE;
}

int TryCatchStatement::usesEH()
{
    return TRUE;
}

int TryCatchStatement::blockExit()
{   int result;

    assert(body);
    result = body->blockExit();

    for (size_t i = 0; i < catches->dim; i++)
    {
        Catch *c = (Catch *)catches->data[i];
        result |= c->blockExit();
    }
    return result;
}

int TryCatchStatement::fallOffEnd()
{
    int result = FALSE;

    if (body)
	result = body->fallOffEnd();
    for (int i = 0; i < catches->dim; i++)
    {   Catch *c;

	c = (Catch *)catches->data[i];
	if (c->handler)
	    result |= c->handler->fallOffEnd();
    }
    return result;
}

void TryCatchStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("try");
    buf->writenl();
    if (body)
        body->toCBuffer(buf, hgs);
    for (size_t i = 0; i < catches->dim; i++)
    {
        Catch *c = (Catch *)catches->data[i];
        c->toCBuffer(buf, hgs);
    }
}

/******************************** Catch ***************************/

Catch::Catch(Loc loc, Type *t, Identifier *id, Statement *handler)
{
    //printf("Catch(%s, loc = %s)\n", id->toChars(), loc.toChars());
    this->loc = loc;
    this->type = t;
    this->ident = id;
    this->handler = handler;
    var = NULL;
}

Catch *Catch::syntaxCopy()
{
    Catch *c = new Catch(loc,
	(type ? type->syntaxCopy() : NULL),
	ident,
	(handler ? handler->syntaxCopy() : NULL));
    return c;
}

void Catch::semantic(Scope *sc)
{   ScopeDsymbol *sym;

    //printf("Catch::semantic(%s)\n", ident->toChars());

#ifndef IN_GCC
    if (sc->tf)
    {
	/* This is because the _d_local_unwind() gets the stack munged
	 * up on this. The workaround is to place any try-catches into
	 * a separate function, and call that.
	 * To fix, have the compiler automatically convert the finally
	 * body into a nested function.
	 */
	error(loc, "cannot put catch statement inside finally block");
    }
#endif

    sym = new ScopeDsymbol();
    sym->parent = sc->scopesym;
    sc = sc->push(sym);

    if (!type)
	type = new TypeIdentifier(0, Id::Object);
    type = type->semantic(loc, sc);
    if (!type->toBasetype()->isClassHandle())
	error("can only catch class objects, not '%s'", type->toChars());
    else if (ident)
    {
	var = new VarDeclaration(loc, type, ident, NULL);
	var->parent = sc->parent;
	sc->insert(var);
    }
    handler = handler->semantic(sc);

    sc->pop();
}

int Catch::blockExit()
{
    return handler ? handler->blockExit() : BEfallthru;
}

void Catch::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("catch");
    if (type)
    {   buf->writebyte('(');
	type->toCBuffer(buf, ident, hgs);
        buf->writebyte(')');
    }
    buf->writenl();
    buf->writebyte('{');
    buf->writenl();
    if (handler)
	handler->toCBuffer(buf, hgs);
    buf->writebyte('}');
    buf->writenl();
}

/****************************** TryFinallyStatement ***************************/

TryFinallyStatement::TryFinallyStatement(Loc loc, Statement *body, Statement *finalbody)
    : Statement(loc)
{
    this->body = body;
    this->finalbody = finalbody;
    this->enclosinghandler = NULL;
}

Statement *TryFinallyStatement::syntaxCopy()
{
    TryFinallyStatement *s = new TryFinallyStatement(loc,
	body->syntaxCopy(), finalbody->syntaxCopy());
    return s;
}

Statement *TryFinallyStatement::semantic(Scope *sc)
{
    //printf("TryFinallyStatement::semantic()\n");

    enclosinghandler = sc->tfOfTry;
    sc->tfOfTry = new EnclosingTryFinally(this);
    body = body->semantic(sc);
    sc->tfOfTry = enclosinghandler;

    sc = sc->push();
    sc->tf = this;
    sc->sbreak = NULL;
    sc->scontinue = NULL;	// no break or continue out of finally block
    finalbody = finalbody->semantic(sc);
    sc->pop();
    if (!body)
	return finalbody;
    if (!finalbody)
	return body;
    if (body->blockExit() == BEfallthru)
    {	Statement *s = new CompoundStatement(loc, body, finalbody);
	return s;
    }
    return this;
}

void TryFinallyStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->printf("try\n{\n");
    body->toCBuffer(buf, hgs);
    buf->printf("}\nfinally\n{\n");
    finalbody->toCBuffer(buf, hgs);
    buf->writeByte('}');
    buf->writenl();
}

int TryFinallyStatement::hasBreak()
{
    return FALSE; //TRUE;
}

int TryFinallyStatement::hasContinue()
{
    return FALSE; //TRUE;
}

int TryFinallyStatement::usesEH()
{
    return TRUE;
}

int TryFinallyStatement::blockExit()
{
    int result = body->blockExit();
    return result;
}

int TryFinallyStatement::fallOffEnd()
{   int result;

    result = body->fallOffEnd();
//    if (finalbody)
//	result = finalbody->fallOffEnd();
    return result;
}

/****************************** OnScopeStatement ***************************/

OnScopeStatement::OnScopeStatement(Loc loc, TOK tok, Statement *statement)
    : Statement(loc)
{
    this->tok = tok;
    this->statement = statement;
}

Statement *OnScopeStatement::syntaxCopy()
{
    OnScopeStatement *s = new OnScopeStatement(loc,
	tok, statement->syntaxCopy());
    return s;
}

Statement *OnScopeStatement::semantic(Scope *sc)
{
    /* semantic is called on results of scopeCode() */
    return this;
}

int OnScopeStatement::blockExit()
{   // At this point, this statement is just an empty placeholder
    return BEfallthru;
}

void OnScopeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring(Token::toChars(tok));
    buf->writebyte(' ');
    statement->toCBuffer(buf, hgs);
}

int OnScopeStatement::usesEH()
{
    return 1;
}

void OnScopeStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally)
{
    //printf("OnScopeStatement::scopeCode()\n");
    //print();
    *sentry = NULL;
    *sexception = NULL;
    *sfinally = NULL;
    switch (tok)
    {
	case TOKon_scope_exit:
	    *sfinally = statement;
	    break;

	case TOKon_scope_failure:
	    *sexception = statement;
	    break;

	case TOKon_scope_success:
	{
	    /* Create:
	     *	sentry:   int x = 0;
	     *	sexception:    x = 1;
	     *	sfinally: if (!x) statement;
	     */
	    Identifier *id = Lexer::uniqueId("__os");

	    ExpInitializer *ie = new ExpInitializer(loc, new IntegerExp(0));
	    VarDeclaration *v = new VarDeclaration(loc, Type::tint32, id, ie);
	    *sentry = new DeclarationStatement(loc, v);

	    Expression *e = new IntegerExp(1);
	    e = new AssignExp(0, new VarExp(0, v), e);
	    *sexception = new ExpStatement(0, e);

	    e = new VarExp(0, v);
	    e = new NotExp(0, e);
	    *sfinally = new IfStatement(0, NULL, e, statement, NULL);

	    break;
	}

	default:
	    assert(0);
    }
}

/******************************** ThrowStatement ***************************/

ThrowStatement::ThrowStatement(Loc loc, Expression *exp)
    : Statement(loc)
{
    this->exp = exp;
}

Statement *ThrowStatement::syntaxCopy()
{
    ThrowStatement *s = new ThrowStatement(loc, exp->syntaxCopy());
    return s;
}

Statement *ThrowStatement::semantic(Scope *sc)
{
    //printf("ThrowStatement::semantic()\n");

    FuncDeclaration *fd = sc->parent->isFuncDeclaration();
    fd->hasReturnExp |= 2;

    if (sc->incontract)
	error("Throw statements cannot be in contracts");
    exp = exp->semantic(sc);
    exp = resolveProperties(sc, exp);
    if (!exp->type->toBasetype()->isClassHandle())
	error("can only throw class objects, not type %s", exp->type->toChars());
    return this;
}

int ThrowStatement::blockExit()
{
    return BEthrow;  // obviously
}

int ThrowStatement::fallOffEnd()
{
    return FALSE;
}

void ThrowStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->printf("throw ");
    exp->toCBuffer(buf, hgs);
    buf->writeByte(';');
    buf->writenl();
}

/******************************** VolatileStatement **************************/

VolatileStatement::VolatileStatement(Loc loc, Statement *statement)
    : Statement(loc)
{
    this->statement = statement;
    this->enclosinghandler = NULL;
}

Statement *VolatileStatement::syntaxCopy()
{
    VolatileStatement *s = new VolatileStatement(loc,
		statement ? statement->syntaxCopy() : NULL);
    return s;
}

Statement *VolatileStatement::semantic(Scope *sc)
{
    if (statement)
    {
    enclosinghandler = sc->tfOfTry;
    sc->tfOfTry = new EnclosingVolatile(this);
	statement = statement->semantic(sc);
    sc->tfOfTry = enclosinghandler;
    }
    return this;
}

Statements *VolatileStatement::flatten(Scope *sc)
{
    Statements *a;

    a = statement ? statement->flatten(sc) : NULL;
    if (a)
    {	for (int i = 0; i < a->dim; i++)
	{   Statement *s = (Statement *)a->data[i];

	    s = new VolatileStatement(loc, s);
	    a->data[i] = s;
	}
    }

    return a;
}

int VolatileStatement::blockExit()
{
    return statement ? statement->blockExit() : BEfallthru;
}

int VolatileStatement::fallOffEnd()
{
    return statement ? statement->fallOffEnd() : TRUE;
}

void VolatileStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("volatile");
    if (statement)
    {   if (statement->isScopeStatement())
            buf->writenl();
        else
            buf->writebyte(' ');
        statement->toCBuffer(buf, hgs);
    }
}


/******************************** GotoStatement ***************************/

GotoStatement::GotoStatement(Loc loc, Identifier *ident)
    : Statement(loc)
{
    this->ident = ident;
    this->label = NULL;
    this->tf = NULL;
    this->enclosinghandler = NULL;
}

Statement *GotoStatement::syntaxCopy()
{
    GotoStatement *s = new GotoStatement(loc, ident);
    return s;
}

Statement *GotoStatement::semantic(Scope *sc)
{   FuncDeclaration *fd = sc->parent->isFuncDeclaration();

    //printf("GotoStatement::semantic()\n");
    tf = sc->tf;
    enclosinghandler = sc->tfOfTry;
    label = fd->searchLabel(ident);
    if (!label->statement && sc->fes)
    {
	/* Either the goto label is forward referenced or it
	 * is in the function that the enclosing foreach is in.
	 * Can't know yet, so wrap the goto in a compound statement
	 * so we can patch it later, and add it to a 'look at this later'
	 * list.
	 */
	Statements *a = new Statements();
	Statement *s;

	a->push(this);
	s = new CompoundStatement(loc, a);
	sc->fes->gotos.push(s);		// 'look at this later' list
	return s;
    }
    if (label->statement && label->statement->tf != sc->tf)
	error("cannot goto in or out of finally block");
    return this;
}

int GotoStatement::blockExit()
{
    //printf("GotoStatement::blockExit(%p)\n", this);
    return BEgoto;
}

int GotoStatement::fallOffEnd()
{
    return FALSE;
}

void GotoStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("goto ");
    buf->writestring(ident->toChars());
    buf->writebyte(';');
    buf->writenl();
}

/******************************** LabelStatement ***************************/

LabelStatement::LabelStatement(Loc loc, Identifier *ident, Statement *statement)
    : Statement(loc)
{
    this->ident = ident;
    this->statement = statement;
    this->tf = NULL;
    this->enclosinghandler = NULL;
    this->lblock = NULL;
    this->isReturnLabel = 0;
    this->asmLabel = false;
}

Statement *LabelStatement::syntaxCopy()
{
    LabelStatement *s = new LabelStatement(loc, ident, statement->syntaxCopy());
    return s;
}

Statement *LabelStatement::semantic(Scope *sc)
{   LabelDsymbol *ls;
    FuncDeclaration *fd = sc->parent->isFuncDeclaration();

    //printf("LabelStatement::semantic()\n");
    ls = fd->searchLabel(ident);
    if (ls->statement)
	error("Label '%s' already defined", ls->toChars());
    else
	ls->statement = this;
    tf = sc->tf;
    enclosinghandler = sc->tfOfTry;
    sc = sc->push();
    sc->scopesym = sc->enclosing->scopesym;
    sc->callSuper |= CSXlabel;
    sc->slabel = this;
    if (statement)
	statement = statement->semantic(sc);
    sc->pop();

    // LDC put in labmap
    fd->labmap[ident->toChars()] = this;

    return this;
}

Statements *LabelStatement::flatten(Scope *sc)
{
    Statements *a = NULL;

    if (statement)
    {
	a = statement->flatten(sc);
	if (a)
	{
	    if (!a->dim)
	    {
		a->push(new ExpStatement(loc, NULL));
	    }
	    Statement *s = (Statement *)a->data[0];

	    s = new LabelStatement(loc, ident, s);
	    a->data[0] = s;
	}
    }

    return a;
}


int LabelStatement::usesEH()
{
    return statement ? statement->usesEH() : FALSE;
}

int LabelStatement::blockExit()
{
    //printf("LabelStatement::blockExit(%p)\n", this);
    return statement ? statement->blockExit() : BEfallthru;
}

int LabelStatement::fallOffEnd()
{
    return statement ? statement->fallOffEnd() : TRUE;
}

int LabelStatement::comeFrom()
{
    //printf("LabelStatement::comeFrom()\n");
    return TRUE;
}

void LabelStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring(ident->toChars());
    buf->writebyte(':');
    buf->writenl();
    if (statement)
        statement->toCBuffer(buf, hgs);
}


/******************************** LabelDsymbol ***************************/

LabelDsymbol::LabelDsymbol(Identifier *ident)
	: Dsymbol(ident)
{
    statement = NULL;
}

LabelDsymbol *LabelDsymbol::isLabel()		// is this a LabelDsymbol()?
{
    return this;
}