| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2019 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); |
| Expression *semantic(Expression *e, Scope *sc); |
| StringExp *semanticString(Scope *sc, Expression *exp, const char *s); |
| |
| 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.extractString(); |
| 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; |
| } |
| |
| void Statement::print() |
| { |
| fprintf(stderr, "%s\n", toChars()); |
| fflush(stderr); |
| } |
| |
| const char *Statement::toChars() |
| { |
| HdrGenState hgs; |
| |
| OutBuffer buf; |
| ::toCBuffer(this, &buf, &hgs); |
| return buf.extractString(); |
| } |
| |
| |
| 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(OnScopeStatement *) { 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->dim; 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->dim; 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(StaticAssert *) {} |
| void visit(Import *) {} |
| void visit(PragmaDeclaration *) {} |
| |
| void visit(ConditionalDeclaration *d) |
| { |
| result = visitMembers(d->loc, d->include(NULL, NULL)); |
| } |
| |
| void visit(CompileDeclaration *d) |
| { |
| result = visitMembers(d->loc, d->include(NULL, 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 = semantic(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->exp = exp; |
| } |
| |
| Statement *CompileStatement::syntaxCopy() |
| { |
| return new CompileStatement(loc, exp->syntaxCopy()); |
| } |
| |
| static Statements *errorStatements() |
| { |
| Statements *a = new Statements(); |
| a->push(new ErrorStatement()); |
| return a; |
| } |
| |
| Statements *CompileStatement::flatten(Scope *sc) |
| { |
| //printf("CompileStatement::flatten() %s\n", exp->toChars()); |
| StringExp *se = semanticString(sc, exp, "argument to mixin"); |
| if (!se) |
| return errorStatements(); |
| se = se->toUTF8(sc); |
| |
| unsigned errors = global.errors; |
| Parser p(loc, sc->_module, (utf8_t *)se->string, se->len, 0); |
| p.nextToken(); |
| |
| Statements *a = new Statements(); |
| while (p.token.value != TOKeof) |
| { |
| Statement *s = p.parseStatement(PSsemi | PScurlyscope); |
| if (!s || p.errors) |
| { |
| assert(!p.errors || global.errors != errors); // make sure we caught all the cases |
| return errorStatements(); |
| } |
| a->push(s); |
| } |
| return a; |
| } |
| |
| /******************************** 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() |
| { |
| Statements *a = new Statements(); |
| a->setDim(statements->dim); |
| for (size_t i = 0; i < statements->dim; i++) |
| { |
| Statement *s = (*statements)[i]; |
| (*a)[i] = s ? s->syntaxCopy() : NULL; |
| } |
| return new CompoundStatement(loc, a); |
| } |
| |
| Statements *CompoundStatement::flatten(Scope *) |
| { |
| return statements; |
| } |
| |
| ReturnStatement *CompoundStatement::isReturnStatement() |
| { |
| ReturnStatement *rs = NULL; |
| |
| for (size_t i = 0; i < statements->dim; 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->dim; 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->dim); |
| for (size_t i = 0; i < statements->dim; 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->dim); |
| for (size_t i = 0; i < statements->dim; 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; |
| } |
| |
| /******************************** 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->dim; 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, NULL)) |
| { |
| 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; |
| } |
| |
| /******************************** 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->dim; 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->dim); |
| for (size_t i = 0; i < a->dim; 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; |
| } |
| |
| /****************************** OnScopeStatement ***************************/ |
| |
| OnScopeStatement::OnScopeStatement(Loc loc, TOK tok, Statement *statement) |
| : Statement(loc) |
| { |
| this->tok = tok; |
| this->statement = statement; |
| } |
| |
| Statement *OnScopeStatement::syntaxCopy() |
| { |
| return new OnScopeStatement(loc, tok, statement->syntaxCopy()); |
| } |
| |
| Statement *OnScopeStatement::scopeCode(Scope *, Statement **sentry, Statement **sexception, Statement **sfinally) |
| { |
| //printf("OnScopeStatement::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)); |
| *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->dim; 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->dim) |
| { |
| 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->dim); |
| for (size_t i = 0; i < statements->dim; 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->dim); |
| for (size_t i = 0; i < imports->dim; i++) |
| { |
| Dsymbol *s = (*imports)[i]; |
| (*m)[i] = s->syntaxCopy(NULL); |
| } |
| return new ImportStatement(loc, m); |
| } |