mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-08 03:46:02 +03:00
4426 lines
99 KiB
C
4426 lines
99 KiB
C
|
|
// 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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// LDC
|
|
else if (ident == Id::allow_inline)
|
|
{
|
|
sc->func->allowInlining = true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|