blob: 1f8e5122b1a72c829f686fbecff55406f6035dfc [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/statement.c
*/
#include "root/dsystem.h"
#include "statement.h"
#include "errors.h"
#include "expression.h"
#include "cond.h"
#include "init.h"
#include "staticassert.h"
#include "scope.h"
#include "declaration.h"
#include "aggregate.h"
#include "id.h"
#include "hdrgen.h"
#include "parse.h"
#include "template.h"
#include "attrib.h"
#include "import.h"
bool walkPostorder(Statement *s, StoppableVisitor *v);
StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration *f);
bool checkEscapeRef(Scope *sc, Expression *e, bool gag);
VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e);
Statement *makeTupleForeachStatic(Scope *sc, ForeachStatement *fs, bool needExpansion);
bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps);
Identifier *fixupLabelName(Scope *sc, Identifier *ident)
{
unsigned flags = (sc->flags & SCOPEcontract);
const char *id = ident->toChars();
if (flags && flags != SCOPEinvariant &&
!(id[0] == '_' && id[1] == '_'))
{
/* CTFE requires FuncDeclaration::labtab for the interpretation.
* So fixing the label name inside in/out contracts is necessary
* for the uniqueness in labtab.
*/
const char *prefix = flags == SCOPErequire ? "__in_" : "__out_";
OutBuffer buf;
buf.printf("%s%s", prefix, ident->toChars());
const char *name = buf.extractChars();
ident = Identifier::idPool(name);
}
return ident;
}
LabelStatement *checkLabeledLoop(Scope *sc, Statement *statement)
{
if (sc->slabel && sc->slabel->statement == statement)
{
return sc->slabel;
}
return NULL;
}
/***********************************************************
* Check an assignment is used as a condition.
* Intended to be use before the `semantic` call on `e`.
* Params:
* e = condition expression which is not yet run semantic analysis.
* Returns:
* `e` or ErrorExp.
*/
Expression *checkAssignmentAsCondition(Expression *e)
{
Expression *ec = e;
while (ec->op == TOKcomma)
ec = ((CommaExp *)ec)->e2;
if (ec->op == TOKassign)
{
ec->error("assignment cannot be used as a condition, perhaps == was meant?");
return new ErrorExp();
}
return e;
}
/// Return a type identifier reference to 'object.Throwable'
TypeIdentifier *getThrowable()
{
TypeIdentifier *tid = new TypeIdentifier(Loc(), Id::empty);
tid->addIdent(Id::object);
tid->addIdent(Id::Throwable);
return tid;
}
/******************************** Statement ***************************/
Statement::Statement(Loc loc)
: loc(loc)
{
// If this is an in{} contract scope statement (skip for determining
// inlineStatus of a function body for header content)
}
Statement *Statement::syntaxCopy()
{
assert(0);
return NULL;
}
/*************************************
* Do syntax copy of an array of Statement's.
*/
Statements *Statement::arraySyntaxCopy(Statements *a)
{
Statements *b = NULL;
if (a)
{
b = a->copy();
for (size_t i = 0; i < a->length; i++)
{
Statement *s = (*a)[i];
(*b)[i] = s ? s->syntaxCopy() : NULL;
}
}
return b;
}
void Statement::print()
{
fprintf(stderr, "%s\n", toChars());
fflush(stderr);
}
const char *Statement::toChars()
{
HdrGenState hgs;
OutBuffer buf;
::toCBuffer(this, &buf, &hgs);
return buf.extractChars();
}
void Statement::error(const char *format, ...)
{
va_list ap;
va_start(ap, format);
::verror(loc, format, ap);
va_end( ap );
}
void Statement::warning(const char *format, ...)
{
va_list ap;
va_start(ap, format);
::vwarning(loc, format, ap);
va_end( ap );
}
void Statement::deprecation(const char *format, ...)
{
va_list ap;
va_start(ap, format);
::vdeprecation(loc, format, ap);
va_end( ap );
}
bool Statement::hasBreak()
{
//printf("Statement::hasBreak()\n");
return false;
}
bool Statement::hasContinue()
{
return false;
}
/* ============================================== */
// true if statement uses exception handling
bool Statement::usesEH()
{
class UsesEH : public StoppableVisitor
{
public:
void visit(Statement *) {}
void visit(TryCatchStatement *) { stop = true; }
void visit(TryFinallyStatement *) { stop = true; }
void visit(ScopeGuardStatement *) { stop = true; }
void visit(SynchronizedStatement *) { stop = true; }
};
UsesEH ueh;
return walkPostorder(this, &ueh);
}
/* ============================================== */
// true if statement 'comes from' somewhere else, like a goto
bool Statement::comeFrom()
{
class ComeFrom : public StoppableVisitor
{
public:
void visit(Statement *) {}
void visit(CaseStatement *) { stop = true; }
void visit(DefaultStatement *) { stop = true; }
void visit(LabelStatement *) { stop = true; }
void visit(AsmStatement *) { stop = true; }
};
ComeFrom cf;
return walkPostorder(this, &cf);
}
/* ============================================== */
// Return true if statement has executable code.
bool Statement::hasCode()
{
class HasCode : public StoppableVisitor
{
public:
void visit(Statement *)
{
stop = true;
}
void visit(ExpStatement *s)
{
if (s->exp != NULL)
{
stop = s->exp->hasCode();
}
}
void visit(CompoundStatement *) {}
void visit(ScopeStatement *) {}
void visit(ImportStatement *) {}
};
HasCode hc;
return walkPostorder(this, &hc);
}
Statement *Statement::last()
{
return this;
}
/****************************************
* 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
*/
Statement *Statement::scopeCode(Scope *, Statement **sentry, Statement **sexception, Statement **sfinally)
{
//printf("Statement::scopeCode()\n");
//print();
*sentry = NULL;
*sexception = NULL;
*sfinally = NULL;
return this;
}
/*********************************
* Flatten out the scope by presenting the statement
* as an array of statements.
* Returns NULL if no flattening necessary.
*/
Statements *Statement::flatten(Scope *)
{
return NULL;
}
/******************************** ErrorStatement ***************************/
ErrorStatement::ErrorStatement()
: Statement(Loc())
{
assert(global.gaggedErrors || global.errors);
}
Statement *ErrorStatement::syntaxCopy()
{
return this;
}
/******************************** PeelStatement ***************************/
PeelStatement::PeelStatement(Statement *s)
: Statement(s->loc)
{
this->s = s;
}
/******************************** ExpStatement ***************************/
ExpStatement::ExpStatement(Loc loc, Expression *exp)
: Statement(loc)
{
this->exp = exp;
}
ExpStatement::ExpStatement(Loc loc, Dsymbol *declaration)
: Statement(loc)
{
this->exp = new DeclarationExp(loc, declaration);
}
ExpStatement *ExpStatement::create(Loc loc, Expression *exp)
{
return new ExpStatement(loc, exp);
}
Statement *ExpStatement::syntaxCopy()
{
return new ExpStatement(loc, exp ? exp->syntaxCopy() : NULL);
}
Statement *ExpStatement::scopeCode(Scope *, Statement **sentry, Statement **sexception, Statement **sfinally)
{
//printf("ExpStatement::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 && !v->isDataseg())
{
if (v->needsScopeDtor())
{
//printf("dtor is: "); v->edtor->print();
*sfinally = new DtorExpStatement(loc, v->edtor, v);
v->storage_class |= STCnodtor; // don't add in dtor again
}
}
}
}
return this;
}
/****************************************
* Convert TemplateMixin members (== Dsymbols) to Statements.
*/
Statement *toStatement(Dsymbol *s)
{
class ToStmt : public Visitor
{
public:
Statement *result;
ToStmt()
{
this->result = NULL;
}
Statement *visitMembers(Loc loc, Dsymbols *a)
{
if (!a)
return NULL;
Statements *statements = new Statements();
for (size_t i = 0; i < a->length; i++)
{
statements->push(toStatement((*a)[i]));
}
return new CompoundStatement(loc, statements);
}
void visit(Dsymbol *s)
{
::error(Loc(), "Internal Compiler Error: cannot mixin %s %s\n", s->kind(), s->toChars());
result = new ErrorStatement();
}
void visit(TemplateMixin *tm)
{
Statements *a = new Statements();
for (size_t i = 0; i < tm->members->length; i++)
{
Statement *s = toStatement((*tm->members)[i]);
if (s)
a->push(s);
}
result = new CompoundStatement(tm->loc, a);
}
/* An actual declaration symbol will be converted to DeclarationExp
* with ExpStatement.
*/
Statement *declStmt(Dsymbol *s)
{
DeclarationExp *de = new DeclarationExp(s->loc, s);
de->type = Type::tvoid; // avoid repeated semantic
return new ExpStatement(s->loc, de);
}
void visit(VarDeclaration *d) { result = declStmt(d); }
void visit(AggregateDeclaration *d) { result = declStmt(d); }
void visit(FuncDeclaration *d) { result = declStmt(d); }
void visit(EnumDeclaration *d) { result = declStmt(d); }
void visit(AliasDeclaration *d) { result = declStmt(d); }
void visit(TemplateDeclaration *d) { result = declStmt(d); }
/* All attributes have been already picked by the semantic analysis of
* 'bottom' declarations (function, struct, class, etc).
* So we don't have to copy them.
*/
void visit(StorageClassDeclaration *d) { result = visitMembers(d->loc, d->decl); }
void visit(DeprecatedDeclaration *d) { result = visitMembers(d->loc, d->decl); }
void visit(LinkDeclaration *d) { result = visitMembers(d->loc, d->decl); }
void visit(ProtDeclaration *d) { result = visitMembers(d->loc, d->decl); }
void visit(AlignDeclaration *d) { result = visitMembers(d->loc, d->decl); }
void visit(UserAttributeDeclaration *d) { result = visitMembers(d->loc, d->decl); }
void visit(ForwardingAttribDeclaration *d) { result = visitMembers(d->loc, d->decl); }
void visit(StaticAssert *) {}
void visit(Import *) {}
void visit(PragmaDeclaration *) {}
void visit(ConditionalDeclaration *d)
{
result = visitMembers(d->loc, d->include(NULL));
}
void visit(StaticForeachDeclaration *d)
{
assert(d->sfe && !!d->sfe->aggrfe ^ !!d->sfe->rangefe);
result = visitMembers(d->loc, d->include(NULL));
}
void visit(CompileDeclaration *d)
{
result = visitMembers(d->loc, d->include(NULL));
}
};
if (!s)
return NULL;
ToStmt v;
s->accept(&v);
return v.result;
}
Statements *ExpStatement::flatten(Scope *sc)
{
/* Bugzilla 14243: expand template mixin in statement scope
* to handle variable destructors.
*/
if (exp && exp->op == TOKdeclaration)
{
Dsymbol *d = ((DeclarationExp *)exp)->declaration;
if (TemplateMixin *tm = d->isTemplateMixin())
{
Expression *e = expressionSemantic(exp, sc);
if (e->op == TOKerror || tm->errors)
{
Statements *a = new Statements();
a->push(new ErrorStatement());
return a;
}
assert(tm->members);
Statement *s = toStatement(tm);
Statements *a = new Statements();
a->push(s);
return a;
}
}
return NULL;
}
/******************************** DtorExpStatement ***************************/
DtorExpStatement::DtorExpStatement(Loc loc, Expression *exp, VarDeclaration *v)
: ExpStatement(loc, exp)
{
this->var = v;
}
Statement *DtorExpStatement::syntaxCopy()
{
return new DtorExpStatement(loc, exp ? exp->syntaxCopy() : NULL, var);
}
/******************************** CompileStatement ***************************/
CompileStatement::CompileStatement(Loc loc, Expression *exp)
: Statement(loc)
{
this->exps = new Expressions();
this->exps->push(exp);
}
CompileStatement::CompileStatement(Loc loc, Expressions *exps)
: Statement(loc)
{
this->exps = exps;
}
Statement *CompileStatement::syntaxCopy()
{
return new CompileStatement(loc, Expression::arraySyntaxCopy(exps));
}
static Statements *errorStatements()
{
Statements *a = new Statements();
a->push(new ErrorStatement());
return a;
}
static Statements *compileIt(CompileStatement *cs, Scope *sc)
{
//printf("CompileStatement::compileIt() %s\n", exp->toChars());
OutBuffer buf;
if (expressionsToString(buf, sc, cs->exps))
return errorStatements();
unsigned errors = global.errors;
const size_t len = buf.length();
const char *str = buf.extractChars();
Parser p(cs->loc, sc->_module, (const utf8_t *)str, len, false);
p.nextToken();
Statements *a = new Statements();
while (p.token.value != TOKeof)
{
Statement *s = p.parseStatement(PSsemi | PScurlyscope);
if (!s || global.errors != errors)
return errorStatements();
a->push(s);
}
return a;
}
Statements *CompileStatement::flatten(Scope *sc)
{
//printf("CompileStatement::flatten() %s\n", exp->toChars());
return compileIt(this, sc);
}
/******************************** 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);
}
CompoundStatement::CompoundStatement(Loc loc, Statement *s1)
: Statement(loc)
{
statements = new Statements();
statements->push(s1);
}
CompoundStatement *CompoundStatement::create(Loc loc, Statement *s1, Statement *s2)
{
return new CompoundStatement(loc, s1, s2);
}
Statement *CompoundStatement::syntaxCopy()
{
return new CompoundStatement(loc, Statement::arraySyntaxCopy(statements));
}
Statements *CompoundStatement::flatten(Scope *)
{
return statements;
}
ReturnStatement *CompoundStatement::isReturnStatement()
{
ReturnStatement *rs = NULL;
for (size_t i = 0; i < statements->length; i++)
{
Statement *s = (*statements)[i];
if (s)
{
rs = s->isReturnStatement();
if (rs)
break;
}
}
return rs;
}
Statement *CompoundStatement::last()
{
Statement *s = NULL;
for (size_t i = statements->length; i; --i)
{ s = (*statements)[i - 1];
if (s)
{
s = s->last();
if (s)
break;
}
}
return s;
}
/******************************** CompoundDeclarationStatement ***************************/
CompoundDeclarationStatement::CompoundDeclarationStatement(Loc loc, Statements *s)
: CompoundStatement(loc, s)
{
statements = s;
}
Statement *CompoundDeclarationStatement::syntaxCopy()
{
Statements *a = new Statements();
a->setDim(statements->length);
for (size_t i = 0; i < statements->length; i++)
{
Statement *s = (*statements)[i];
(*a)[i] = s ? s->syntaxCopy() : NULL;
}
return new CompoundDeclarationStatement(loc, a);
}
/**************************** UnrolledLoopStatement ***************************/
UnrolledLoopStatement::UnrolledLoopStatement(Loc loc, Statements *s)
: Statement(loc)
{
statements = s;
}
Statement *UnrolledLoopStatement::syntaxCopy()
{
Statements *a = new Statements();
a->setDim(statements->length);
for (size_t i = 0; i < statements->length; i++)
{
Statement *s = (*statements)[i];
(*a)[i] = s ? s->syntaxCopy() : NULL;
}
return new UnrolledLoopStatement(loc, a);
}
bool UnrolledLoopStatement::hasBreak()
{
return true;
}
bool UnrolledLoopStatement::hasContinue()
{
return true;
}
/******************************** ScopeStatement ***************************/
ScopeStatement::ScopeStatement(Loc loc, Statement *s, Loc endloc)
: Statement(loc)
{
this->statement = s;
this->endloc = endloc;
}
Statement *ScopeStatement::syntaxCopy()
{
return new ScopeStatement(loc, statement ? statement->syntaxCopy() : NULL, endloc);
}
ReturnStatement *ScopeStatement::isReturnStatement()
{
if (statement)
return statement->isReturnStatement();
return NULL;
}
bool ScopeStatement::hasBreak()
{
//printf("ScopeStatement::hasBreak() %s\n", toChars());
return statement ? statement->hasBreak() : false;
}
bool ScopeStatement::hasContinue()
{
return statement ? statement->hasContinue() : false;
}
/******************************** ForwardingStatement **********************/
/* Statement whose symbol table contains foreach index variables in a
* local scope and forwards other members to the parent scope. This
* wraps a statement.
*
* Also see: `ddmd.attrib.ForwardingAttribDeclaration`
*/
ForwardingStatement::ForwardingStatement(Loc loc, ForwardingScopeDsymbol *sym, Statement *s)
: Statement(loc)
{
this->sym = sym;
assert(s);
this->statement = s;
}
ForwardingStatement::ForwardingStatement(Loc loc, Statement *s)
: Statement(loc)
{
this->sym = new ForwardingScopeDsymbol(NULL);
this->sym->symtab = new DsymbolTable();
assert(s);
this->statement = s;
}
Statement *ForwardingStatement::syntaxCopy()
{
return new ForwardingStatement(loc, statement->syntaxCopy());
}
/***********************
* ForwardingStatements are distributed over the flattened
* sequence of statements. This prevents flattening to be
* "blocked" by a ForwardingStatement and is necessary, for
* example, to support generating scope guards with `static
* foreach`:
*
* static foreach(i; 0 .. 10) scope(exit) writeln(i);
* writeln("this is printed first");
* // then, it prints 10, 9, 8, 7, ...
*/
Statements *ForwardingStatement::flatten(Scope *sc)
{
if (!statement)
{
return NULL;
}
sc = sc->push(sym);
Statements *a = statement->flatten(sc);
sc = sc->pop();
if (!a)
{
return a;
}
Statements *b = new Statements();
b->setDim(a->length);
for (size_t i = 0; i < a->length; i++)
{
Statement *s = (*a)[i];
(*b)[i] = s ? new ForwardingStatement(s->loc, sym, s) : NULL;
}
return b;
}
/******************************** WhileStatement ***************************/
WhileStatement::WhileStatement(Loc loc, Expression *c, Statement *b, Loc endloc)
: Statement(loc)
{
condition = c;
_body = b;
this->endloc = endloc;
}
Statement *WhileStatement::syntaxCopy()
{
return new WhileStatement(loc,
condition->syntaxCopy(),
_body ? _body->syntaxCopy() : NULL,
endloc);
}
bool WhileStatement::hasBreak()
{
return true;
}
bool WhileStatement::hasContinue()
{
return true;
}
/******************************** DoStatement ***************************/
DoStatement::DoStatement(Loc loc, Statement *b, Expression *c, Loc endloc)
: Statement(loc)
{
_body = b;
condition = c;
this->endloc = endloc;
}
Statement *DoStatement::syntaxCopy()
{
return new DoStatement(loc,
_body ? _body->syntaxCopy() : NULL,
condition->syntaxCopy(),
endloc);
}
bool DoStatement::hasBreak()
{
return true;
}
bool DoStatement::hasContinue()
{
return true;
}
/******************************** ForStatement ***************************/
ForStatement::ForStatement(Loc loc, Statement *init, Expression *condition, Expression *increment, Statement *body, Loc endloc)
: Statement(loc)
{
this->_init = init;
this->condition = condition;
this->increment = increment;
this->_body = body;
this->endloc = endloc;
this->relatedLabeled = NULL;
}
Statement *ForStatement::syntaxCopy()
{
return new ForStatement(loc,
_init ? _init->syntaxCopy() : NULL,
condition ? condition->syntaxCopy() : NULL,
increment ? increment->syntaxCopy() : NULL,
_body->syntaxCopy(),
endloc);
}
Statement *ForStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally)
{
//printf("ForStatement::scopeCode()\n");
Statement::scopeCode(sc, sentry, sexception, sfinally);
return this;
}
bool ForStatement::hasBreak()
{
//printf("ForStatement::hasBreak()\n");
return true;
}
bool ForStatement::hasContinue()
{
return true;
}
/******************************** ForeachStatement ***************************/
ForeachStatement::ForeachStatement(Loc loc, TOK op, Parameters *parameters,
Expression *aggr, Statement *body, Loc endloc)
: Statement(loc)
{
this->op = op;
this->parameters = parameters;
this->aggr = aggr;
this->_body = body;
this->endloc = endloc;
this->key = NULL;
this->value = NULL;
this->func = NULL;
this->cases = NULL;
this->gotos = NULL;
}
Statement *ForeachStatement::syntaxCopy()
{
return new ForeachStatement(loc, op,
Parameter::arraySyntaxCopy(parameters),
aggr->syntaxCopy(),
_body ? _body->syntaxCopy() : NULL,
endloc);
}
bool ForeachStatement::checkForArgTypes()
{
bool result = false;
for (size_t i = 0; i < parameters->length; i++)
{
Parameter *p = (*parameters)[i];
if (!p->type)
{
error("cannot infer type for %s", p->ident->toChars());
p->type = Type::terror;
result = true;
}
}
return result;
}
bool ForeachStatement::hasBreak()
{
return true;
}
bool ForeachStatement::hasContinue()
{
return true;
}
/**************************** ForeachRangeStatement ***************************/
ForeachRangeStatement::ForeachRangeStatement(Loc loc, TOK op, Parameter *prm,
Expression *lwr, Expression *upr, Statement *body, Loc endloc)
: Statement(loc)
{
this->op = op;
this->prm = prm;
this->lwr = lwr;
this->upr = upr;
this->_body = body;
this->endloc = endloc;
this->key = NULL;
}
Statement *ForeachRangeStatement::syntaxCopy()
{
return new ForeachRangeStatement(loc, op,
prm->syntaxCopy(),
lwr->syntaxCopy(),
upr->syntaxCopy(),
_body ? _body->syntaxCopy() : NULL,
endloc);
}
bool ForeachRangeStatement::hasBreak()
{
return true;
}
bool ForeachRangeStatement::hasContinue()
{
return true;
}
/******************************** IfStatement ***************************/
IfStatement::IfStatement(Loc loc, Parameter *prm, Expression *condition, Statement *ifbody, Statement *elsebody, Loc endloc)
: Statement(loc)
{
this->prm = prm;
this->condition = condition;
this->ifbody = ifbody;
this->elsebody = elsebody;
this->endloc = endloc;
this->match = NULL;
}
Statement *IfStatement::syntaxCopy()
{
return new IfStatement(loc,
prm ? prm->syntaxCopy() : NULL,
condition->syntaxCopy(),
ifbody ? ifbody->syntaxCopy() : NULL,
elsebody ? elsebody->syntaxCopy() : NULL,
endloc);
}
/******************************** 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()
{
return new ConditionalStatement(loc,
condition->syntaxCopy(),
ifbody->syntaxCopy(),
elsebody ? elsebody->syntaxCopy() : NULL);
}
Statements *ConditionalStatement::flatten(Scope *sc)
{
Statement *s;
//printf("ConditionalStatement::flatten()\n");
if (condition->include(sc))
{
DebugCondition *dc = condition->isDebugCondition();
if (dc)
s = new DebugStatement(loc, ifbody);
else
s = ifbody;
}
else
s = elsebody;
Statements *a = new Statements();
a->push(s);
return a;
}
/******************************** StaticForeachStatement ********************/
/* Static foreach statements, like:
* void main()
* {
* static foreach(i; 0 .. 10)
* {
* pragma(msg, i);
* }
* }
*/
StaticForeachStatement::StaticForeachStatement(Loc loc, StaticForeach *sfe)
: Statement(loc)
{
this->sfe = sfe;
}
Statement *StaticForeachStatement::syntaxCopy()
{
return new StaticForeachStatement(loc, sfe->syntaxCopy());
}
Statements *StaticForeachStatement::flatten(Scope *sc)
{
staticForeachPrepare(sfe, sc);
if (staticForeachReady(sfe))
{
Statement *s = makeTupleForeachStatic(sc, sfe->aggrfe, sfe->needExpansion);
Statements *result = s->flatten(sc);
if (result)
{
return result;
}
result = new Statements();
result->push(s);
return result;
}
else
{
Statements *result = new Statements();
result->push(new ErrorStatement());
return result;
}
}
/******************************** 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()
{
return new PragmaStatement(loc, ident,
Expression::arraySyntaxCopy(args),
_body ? _body->syntaxCopy() : NULL);
}
/******************************** StaticAssertStatement ***************************/
StaticAssertStatement::StaticAssertStatement(StaticAssert *sa)
: Statement(sa->loc)
{
this->sa = sa;
}
Statement *StaticAssertStatement::syntaxCopy()
{
return new StaticAssertStatement((StaticAssert *)sa->syntaxCopy(NULL));
}
/******************************** SwitchStatement ***************************/
SwitchStatement::SwitchStatement(Loc loc, Expression *c, Statement *b, bool isFinal)
: Statement(loc)
{
this->condition = c;
this->_body = b;
this->isFinal = isFinal;
sdefault = NULL;
tf = NULL;
cases = NULL;
hasNoDefault = 0;
hasVars = 0;
lastVar = NULL;
}
Statement *SwitchStatement::syntaxCopy()
{
return new SwitchStatement(loc,
condition->syntaxCopy(),
_body->syntaxCopy(),
isFinal);
}
bool SwitchStatement::hasBreak()
{
return true;
}
static bool checkVar(SwitchStatement *s, VarDeclaration *vd)
{
if (!vd || vd->isDataseg() || (vd->storage_class & STCmanifest))
return false;
VarDeclaration *last = s->lastVar;
while (last && last != vd)
last = last->lastVar;
if (last == vd)
{
// All good, the label's scope has no variables
}
else if (vd->storage_class & STCexptemp)
{
// Lifetime ends at end of expression, so no issue with skipping the statement
}
else if (vd->ident == Id::withSym)
{
s->deprecation("`switch` skips declaration of `with` temporary at %s", vd->loc.toChars());
return true;
}
else
{
s->deprecation("`switch` skips declaration of variable %s at %s", vd->toPrettyChars(), vd->loc.toChars());
return true;
}
return false;
}
bool SwitchStatement::checkLabel()
{
const bool error = true;
if (sdefault && checkVar(this, sdefault->lastVar))
return !error; // return error once fully deprecated
for (size_t i = 0; i < cases->length; i++)
{
CaseStatement *scase = (*cases)[i];
if (scase && checkVar(this, scase->lastVar))
return !error; // return error once fully deprecated
}
return !error;
}
/******************************** CaseStatement ***************************/
CaseStatement::CaseStatement(Loc loc, Expression *exp, Statement *s)
: Statement(loc)
{
this->exp = exp;
this->statement = s;
index = 0;
lastVar = NULL;
}
Statement *CaseStatement::syntaxCopy()
{
return new CaseStatement(loc,
exp->syntaxCopy(),
statement->syntaxCopy());
}
int CaseStatement::compare(RootObject *obj)
{
// Sort cases so we can do an efficient lookup
CaseStatement *cs2 = (CaseStatement *)(obj);
return exp->compare(cs2->exp);
}
/******************************** CaseRangeStatement ***************************/
CaseRangeStatement::CaseRangeStatement(Loc loc, Expression *first,
Expression *last, Statement *s)
: Statement(loc)
{
this->first = first;
this->last = last;
this->statement = s;
}
Statement *CaseRangeStatement::syntaxCopy()
{
return new CaseRangeStatement(loc,
first->syntaxCopy(),
last->syntaxCopy(),
statement->syntaxCopy());
}
/******************************** DefaultStatement ***************************/
DefaultStatement::DefaultStatement(Loc loc, Statement *s)
: Statement(loc)
{
this->statement = s;
this->lastVar = NULL;
}
Statement *DefaultStatement::syntaxCopy()
{
return new DefaultStatement(loc, statement->syntaxCopy());
}
/******************************** GotoDefaultStatement ***************************/
GotoDefaultStatement::GotoDefaultStatement(Loc loc)
: Statement(loc)
{
sw = NULL;
}
Statement *GotoDefaultStatement::syntaxCopy()
{
return new GotoDefaultStatement(loc);
}
/******************************** GotoCaseStatement ***************************/
GotoCaseStatement::GotoCaseStatement(Loc loc, Expression *exp)
: Statement(loc)
{
cs = NULL;
this->exp = exp;
}
Statement *GotoCaseStatement::syntaxCopy()
{
return new GotoCaseStatement(loc, exp ? exp->syntaxCopy() : NULL);
}
/******************************** SwitchErrorStatement ***************************/
SwitchErrorStatement::SwitchErrorStatement(Loc loc)
: Statement(loc)
{
}
/******************************** ReturnStatement ***************************/
ReturnStatement::ReturnStatement(Loc loc, Expression *exp)
: Statement(loc)
{
this->exp = exp;
this->caseDim = 0;
}
Statement *ReturnStatement::syntaxCopy()
{
return new ReturnStatement(loc, exp ? exp->syntaxCopy() : NULL);
}
/******************************** BreakStatement ***************************/
BreakStatement::BreakStatement(Loc loc, Identifier *ident)
: Statement(loc)
{
this->ident = ident;
}
Statement *BreakStatement::syntaxCopy()
{
return new BreakStatement(loc, ident);
}
/******************************** ContinueStatement ***************************/
ContinueStatement::ContinueStatement(Loc loc, Identifier *ident)
: Statement(loc)
{
this->ident = ident;
}
Statement *ContinueStatement::syntaxCopy()
{
return new ContinueStatement(loc, ident);
}
/******************************** SynchronizedStatement ***************************/
SynchronizedStatement::SynchronizedStatement(Loc loc, Expression *exp, Statement *body)
: Statement(loc)
{
this->exp = exp;
this->_body = body;
}
Statement *SynchronizedStatement::syntaxCopy()
{
return new SynchronizedStatement(loc,
exp ? exp->syntaxCopy() : NULL,
_body ? _body->syntaxCopy() : NULL);
}
bool SynchronizedStatement::hasBreak()
{
return false; //true;
}
bool SynchronizedStatement::hasContinue()
{
return false; //true;
}
/******************************** WithStatement ***************************/
WithStatement::WithStatement(Loc loc, Expression *exp, Statement *body, Loc endloc)
: Statement(loc)
{
this->exp = exp;
this->_body = body;
this->endloc = endloc;
wthis = NULL;
}
Statement *WithStatement::syntaxCopy()
{
return new WithStatement(loc,
exp->syntaxCopy(),
_body ? _body->syntaxCopy() : NULL, endloc);
}
/******************************** TryCatchStatement ***************************/
TryCatchStatement::TryCatchStatement(Loc loc, Statement *body, Catches *catches)
: Statement(loc)
{
this->_body = body;
this->catches = catches;
}
Statement *TryCatchStatement::syntaxCopy()
{
Catches *a = new Catches();
a->setDim(catches->length);
for (size_t i = 0; i < a->length; i++)
{
Catch *c = (*catches)[i];
(*a)[i] = c->syntaxCopy();
}
return new TryCatchStatement(loc, _body->syntaxCopy(), a);
}
bool TryCatchStatement::hasBreak()
{
return false;
}
/******************************** 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;
errors = false;
internalCatch = false;
}
Catch *Catch::syntaxCopy()
{
Catch *c = new Catch(loc,
type ? type->syntaxCopy() : getThrowable(),
ident,
(handler ? handler->syntaxCopy() : NULL));
c->internalCatch = internalCatch;
return c;
}
/****************************** TryFinallyStatement ***************************/
TryFinallyStatement::TryFinallyStatement(Loc loc, Statement *body, Statement *finalbody)
: Statement(loc)
{
this->_body = body;
this->finalbody = finalbody;
}
TryFinallyStatement *TryFinallyStatement::create(Loc loc, Statement *body, Statement *finalbody)
{
return new TryFinallyStatement(loc, body, finalbody);
}
Statement *TryFinallyStatement::syntaxCopy()
{
return new TryFinallyStatement(loc,
_body->syntaxCopy(), finalbody->syntaxCopy());
}
bool TryFinallyStatement::hasBreak()
{
return false; //true;
}
bool TryFinallyStatement::hasContinue()
{
return false; //true;
}
/****************************** ScopeGuardStatement ***************************/
ScopeGuardStatement::ScopeGuardStatement(Loc loc, TOK tok, Statement *statement)
: Statement(loc)
{
this->tok = tok;
this->statement = statement;
}
Statement *ScopeGuardStatement::syntaxCopy()
{
return new ScopeGuardStatement(loc, tok, statement->syntaxCopy());
}
Statement *ScopeGuardStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally)
{
//printf("ScopeGuardStatement::scopeCode()\n");
//print();
*sentry = NULL;
*sexception = NULL;
*sfinally = NULL;
Statement *s = new PeelStatement(statement);
switch (tok)
{
case TOKon_scope_exit:
*sfinally = s;
break;
case TOKon_scope_failure:
*sexception = s;
break;
case TOKon_scope_success:
{
/* Create:
* sentry: bool x = false;
* sexception: x = true;
* sfinally: if (!x) statement;
*/
VarDeclaration *v = copyToTemp(0, "__os", new IntegerExp(Loc(), 0, Type::tbool));
dsymbolSemantic(v, sc);
*sentry = new ExpStatement(loc, v);
Expression *e = new IntegerExp(Loc(), 1, Type::tbool);
e = new AssignExp(Loc(), new VarExp(Loc(), v), e);
*sexception = new ExpStatement(Loc(), e);
e = new VarExp(Loc(), v);
e = new NotExp(Loc(), e);
*sfinally = new IfStatement(Loc(), NULL, e, s, NULL, Loc());
break;
}
default:
assert(0);
}
return NULL;
}
/******************************** ThrowStatement ***************************/
ThrowStatement::ThrowStatement(Loc loc, Expression *exp)
: Statement(loc)
{
this->exp = exp;
this->internalThrow = false;
}
Statement *ThrowStatement::syntaxCopy()
{
ThrowStatement *s = new ThrowStatement(loc, exp->syntaxCopy());
s->internalThrow = internalThrow;
return s;
}
/******************************** DebugStatement **************************/
DebugStatement::DebugStatement(Loc loc, Statement *statement)
: Statement(loc)
{
this->statement = statement;
}
Statement *DebugStatement::syntaxCopy()
{
return new DebugStatement(loc,
statement ? statement->syntaxCopy() : NULL);
}
Statements *DebugStatement::flatten(Scope *sc)
{
Statements *a = statement ? statement->flatten(sc) : NULL;
if (a)
{
for (size_t i = 0; i < a->length; i++)
{ Statement *s = (*a)[i];
s = new DebugStatement(loc, s);
(*a)[i] = s;
}
}
return a;
}
/******************************** GotoStatement ***************************/
GotoStatement::GotoStatement(Loc loc, Identifier *ident)
: Statement(loc)
{
this->ident = ident;
this->label = NULL;
this->tf = NULL;
this->os = NULL;
this->lastVar = NULL;
}
Statement *GotoStatement::syntaxCopy()
{
return new GotoStatement(loc, ident);
}
bool GotoStatement::checkLabel()
{
if (!label->statement)
{
error("label `%s` is undefined", label->toChars());
return true;
}
if (label->statement->os != os)
{
if (os && os->tok == TOKon_scope_failure && !label->statement->os)
{
// Jump out from scope(failure) block is allowed.
}
else
{
if (label->statement->os)
error("cannot goto in to %s block", Token::toChars(label->statement->os->tok));
else
error("cannot goto out of %s block", Token::toChars(os->tok));
return true;
}
}
if (label->statement->tf != tf)
{
error("cannot goto in or out of finally block");
return true;
}
VarDeclaration *vd = label->statement->lastVar;
if (!vd || vd->isDataseg() || (vd->storage_class & STCmanifest))
return false;
VarDeclaration *last = lastVar;
while (last && last != vd)
last = last->lastVar;
if (last == vd)
{
// All good, the label's scope has no variables
}
else if (vd->ident == Id::withSym)
{
error("goto skips declaration of with temporary at %s", vd->loc.toChars());
return true;
}
else
{
error("goto skips declaration of variable %s at %s", vd->toPrettyChars(), vd->loc.toChars());
return true;
}
return false;
}
/******************************** LabelStatement ***************************/
LabelStatement::LabelStatement(Loc loc, Identifier *ident, Statement *statement)
: Statement(loc)
{
this->ident = ident;
this->statement = statement;
this->tf = NULL;
this->os = NULL;
this->lastVar = NULL;
this->gotoTarget = NULL;
this->breaks = false;
}
Statement *LabelStatement::syntaxCopy()
{
return new LabelStatement(loc, ident, statement ? statement->syntaxCopy() : NULL);
}
Statement *LabelStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally)
{
//printf("LabelStatement::scopeCode()\n");
if (statement)
statement = statement->scopeCode(sc, sentry, sexit, sfinally);
else
{
*sentry = NULL;
*sexit = NULL;
*sfinally = NULL;
}
return this;
}
Statements *LabelStatement::flatten(Scope *sc)
{
Statements *a = NULL;
if (statement)
{
a = statement->flatten(sc);
if (a)
{
if (!a->length)
{
a->push(new ExpStatement(loc, (Expression *)NULL));
}
// reuse 'this' LabelStatement
this->statement = (*a)[0];
(*a)[0] = this;
}
}
return a;
}
/******************************** LabelDsymbol ***************************/
LabelDsymbol::LabelDsymbol(Identifier *ident)
: Dsymbol(ident)
{
statement = NULL;
}
LabelDsymbol *LabelDsymbol::create(Identifier *ident)
{
return new LabelDsymbol(ident);
}
LabelDsymbol *LabelDsymbol::isLabel() // is this a LabelDsymbol()?
{
return this;
}
/************************ AsmStatement ***************************************/
AsmStatement::AsmStatement(Loc loc, Token *tokens)
: Statement(loc)
{
this->tokens = tokens;
}
Statement *AsmStatement::syntaxCopy()
{
return new AsmStatement(loc, tokens);
}
/************************ InlineAsmStatement **********************************/
InlineAsmStatement::InlineAsmStatement(Loc loc, Token *tokens)
: AsmStatement(loc, tokens)
{
asmcode = NULL;
asmalign = 0;
refparam = false;
naked = false;
regs = 0;
}
Statement *InlineAsmStatement::syntaxCopy()
{
return new InlineAsmStatement(loc, tokens);
}
/************************ GccAsmStatement ***************************************/
GccAsmStatement::GccAsmStatement(Loc loc, Token *tokens)
: AsmStatement(loc, tokens)
{
this->stc = STCundefined;
this->insn = NULL;
this->args = NULL;
this->outputargs = 0;
this->names = NULL;
this->constraints = NULL;
this->clobbers = NULL;
this->labels = NULL;
this->gotos = NULL;
}
Statement *GccAsmStatement::syntaxCopy()
{
return new GccAsmStatement(loc, tokens);
}
/************************ CompoundAsmStatement ***************************************/
CompoundAsmStatement::CompoundAsmStatement(Loc loc, Statements *s, StorageClass stc)
: CompoundStatement(loc, s)
{
this->stc = stc;
}
CompoundAsmStatement *CompoundAsmStatement::syntaxCopy()
{
Statements *a = new Statements();
a->setDim(statements->length);
for (size_t i = 0; i < statements->length; i++)
{
Statement *s = (*statements)[i];
(*a)[i] = s ? s->syntaxCopy() : NULL;
}
return new CompoundAsmStatement(loc, a, stc);
}
Statements *CompoundAsmStatement::flatten(Scope *)
{
return NULL;
}
/************************ ImportStatement ***************************************/
ImportStatement::ImportStatement(Loc loc, Dsymbols *imports)
: Statement(loc)
{
this->imports = imports;
}
Statement *ImportStatement::syntaxCopy()
{
Dsymbols *m = new Dsymbols();
m->setDim(imports->length);
for (size_t i = 0; i < imports->length; i++)
{
Dsymbol *s = (*imports)[i];
(*m)[i] = s->syntaxCopy(NULL);
}
return new ImportStatement(loc, m);
}