/* Compiler implementation of the D programming language * Copyright (c) 1999-2014 by Digital Mars * 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 #include #include #include "rmem.h" #include "target.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" #include "attrib.h" #include "import.h" bool walkPostorder(Statement *s, StoppableVisitor *v); bool isNonAssignmentArrayOp(Expression *e); Identifier *fixupLabelName(Scope *sc, Identifier *ident) { unsigned flags = (sc->flags & SCOPEcontract); if (flags && flags != SCOPEinvariant && !(ident->string[0] == '_' && ident->string[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 = Lexer::idPool(name); } return ident; } LabelStatement *checkLabeledLoop(Scope *sc, Statement *statement) { if (sc->slabel && sc->slabel->statement == statement) { return sc->slabel; } return NULL; } /******************************** 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); } char *Statement::toChars() { HdrGenState hgs; OutBuffer buf; ::toCBuffer(this, &buf, &hgs); return buf.extractString(); } Statement *Statement::semantic(Scope *sc) { return this; } Statement *Statement::semanticNoScope(Scope *sc) { //printf("Statement::semanticNoScope() %s\n", toChars()); Statement *s = this; if (!s->isCompoundStatement() && !s->isScopeStatement()) { s = new CompoundStatement(loc, this); // so scopeCode() gets called } s = s->semantic(sc); return s; } // Same as semanticNoScope(), but do create a new scope Statement *Statement::semanticScope(Scope *sc, Statement *sbreak, Statement *scontinue) { Scope *scd = sc->push(); if (sbreak) scd->sbreak = sbreak; if (scontinue) scd->scontinue = scontinue; Statement *s = semanticNoScope(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 ); } 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 *s) {} void visit(TryCatchStatement *s) { stop = true; } void visit(TryFinallyStatement *s) { stop = true; } void visit(OnScopeStatement *s) { stop = true; } void visit(SynchronizedStatement *s) { 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 *s) {} void visit(CaseStatement *s) { stop = true; } void visit(DefaultStatement *s) { stop = true; } void visit(LabelStatement *s) { stop = true; } void visit(AsmStatement *s) { 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 *s) { stop = true; } void visit(ExpStatement *s) { stop = s->exp != NULL; } void visit(CompoundStatement *s) {} void visit(ScopeStatement *s) {} void visit(ImportStatement *s) {} }; HasCode hc; return walkPostorder(this, &hc); } /* ============================================== */ /* Only valid after semantic analysis * If 'mustNotThrow' is true, generate an error if it throws */ int Statement::blockExit(FuncDeclaration *func, bool mustNotThrow) { class BlockExit : public Visitor { public: FuncDeclaration *func; bool mustNotThrow; int result; BlockExit(FuncDeclaration *func, bool mustNotThrow) : func(func), mustNotThrow(mustNotThrow) { result = BEnone; } void visit(Statement *s) { printf("Statement::blockExit(%p)\n", s); printf("%s\n", s->toChars()); assert(0); result = BEany; } void visit(ErrorStatement *s) { result = BEany; } void visit(ExpStatement *s) { result = BEfallthru; if (s->exp) { if (s->exp->op == TOKhalt) { result = BEhalt; return; } if (s->exp->op == TOKassert) { AssertExp *a = (AssertExp *)s->exp; if (a->e1->isBool(false)) // if it's an assert(0) { result = BEhalt; return; } } if (canThrow(s->exp, func, mustNotThrow)) result |= BEthrow; } } void visit(CompileStatement *s) { assert(global.errors); result = BEfallthru; } void visit(CompoundStatement *cs) { //printf("CompoundStatement::blockExit(%p) %d\n", cs, cs->statements->dim); result = BEfallthru; Statement *slast = NULL; for (size_t i = 0; i < cs->statements->dim; i++) { Statement *s = (*cs->statements)[i]; if (s) { //printf("result = x%x\n", result); //printf("s: %s\n", s->toChars()); if (global.params.warnings && result & BEfallthru && slast) { slast = slast->last(); if (slast && (slast->isCaseStatement() || slast->isDefaultStatement()) && (s->isCaseStatement() || s->isDefaultStatement())) { // Allow if last case/default was empty CaseStatement *sc = slast->isCaseStatement(); DefaultStatement *sd = slast->isDefaultStatement(); if (sc && (!sc->statement->hasCode() || sc->statement->isCaseStatement() || sc->statement->isErrorStatement())) ; else if (sd && (!sd->statement->hasCode() || sd->statement->isCaseStatement() || sd->statement->isErrorStatement())) ; else { const char *gototype = s->isCaseStatement() ? "case" : "default"; s->warning("switch case fallthrough - use 'goto %s;' if intended", gototype); } } } if (!(result & BEfallthru) && !s->comeFrom()) { if (s->blockExit(func, mustNotThrow) != BEhalt && s->hasCode()) s->warning("statement is not reachable"); } else { result &= ~BEfallthru; result |= s->blockExit(func, mustNotThrow); } slast = s; } } } void visit(UnrolledLoopStatement *uls) { result = BEfallthru; for (size_t i = 0; i < uls->statements->dim; i++) { Statement *s = (*uls->statements)[i]; if (s) { int r = s->blockExit(func, mustNotThrow); result |= r & ~(BEbreak | BEcontinue); } } } void visit(ScopeStatement *s) { //printf("ScopeStatement::blockExit(%p)\n", s->statement); result = s->statement ? s->statement->blockExit(func, mustNotThrow) : BEfallthru; } void visit(WhileStatement *s) { assert(global.errors); result = BEfallthru; } void visit(DoStatement *s) { if (s->body) { result = s->body->blockExit(func, mustNotThrow); if (result == BEbreak) { result = BEfallthru; return; } if (result & BEcontinue) result |= BEfallthru; } else result = BEfallthru; if (result & BEfallthru) { if (canThrow(s->condition, func, mustNotThrow)) result |= BEthrow; if (!(result & BEbreak) && s->condition->isBool(true)) result &= ~BEfallthru; } result &= ~(BEbreak | BEcontinue); } void visit(ForStatement *s) { result = BEfallthru; if (s->init) { result = s->init->blockExit(func, mustNotThrow); if (!(result & BEfallthru)) return; } if (s->condition) { if (canThrow(s->condition, func, mustNotThrow)) result |= BEthrow; if (s->condition->isBool(true)) result &= ~BEfallthru; else if (s->condition->isBool(false)) return; } else result &= ~BEfallthru; // the body must do the exiting if (s->body) { int r = s->body->blockExit(func, mustNotThrow); if (r & (BEbreak | BEgoto)) result |= BEfallthru; result |= r & ~(BEfallthru | BEbreak | BEcontinue); } if (s->increment && canThrow(s->increment, func, mustNotThrow)) result |= BEthrow; } void visit(ForeachStatement *s) { result = BEfallthru; if (canThrow(s->aggr, func, mustNotThrow)) result |= BEthrow; if (s->body) result |= s->body->blockExit(func, mustNotThrow) & ~(BEbreak | BEcontinue); } void visit(ForeachRangeStatement *s) { assert(global.errors); result = BEfallthru; } void visit(IfStatement *s) { //printf("IfStatement::blockExit(%p)\n", s); result = BEnone; if (canThrow(s->condition, func, mustNotThrow)) result |= BEthrow; if (s->condition->isBool(true)) { if (s->ifbody) result |= s->ifbody->blockExit(func, mustNotThrow); else result |= BEfallthru; } else if (s->condition->isBool(false)) { if (s->elsebody) result |= s->elsebody->blockExit(func, mustNotThrow); else result |= BEfallthru; } else { if (s->ifbody) result |= s->ifbody->blockExit(func, mustNotThrow); else result |= BEfallthru; if (s->elsebody) result |= s->elsebody->blockExit(func, mustNotThrow); else result |= BEfallthru; } //printf("IfStatement::blockExit(%p) = x%x\n", s, result); } void visit(ConditionalStatement *s) { result = s->ifbody->blockExit(func, mustNotThrow); if (s->elsebody) result |= s->elsebody->blockExit(func, mustNotThrow); } void visit(PragmaStatement *s) { result = BEfallthru; #if 0 // currently, no code is generated for Pragma's, so it's just fallthru if (arrayExpressionCanThrow(s->args, func, mustNotThrow)) result |= BEthrow; if (s->body) result |= s->body->blockExit(func, mustNotThrow); #endif } void visit(StaticAssertStatement *s) { result = BEfallthru; } void visit(SwitchStatement *s) { result = BEnone; if (canThrow(s->condition, func, mustNotThrow)) result |= BEthrow; if (s->body) { result |= s->body->blockExit(func, mustNotThrow); if (result & BEbreak) { result |= BEfallthru; result &= ~BEbreak; } } else result |= BEfallthru; } void visit(CaseStatement *s) { result = s->statement->blockExit(func, mustNotThrow); } void visit(DefaultStatement *s) { result = s->statement->blockExit(func, mustNotThrow); } void visit(GotoDefaultStatement *s) { result = BEgoto; } void visit(GotoCaseStatement *s) { result = BEgoto; } void visit(SwitchErrorStatement *s) { // Switch errors are non-recoverable result = BEhalt; } void visit(ReturnStatement *s) { result = BEreturn; if (s->exp && canThrow(s->exp, func, mustNotThrow)) result |= BEthrow; } void visit(BreakStatement *s) { //printf("BreakStatement::blockExit(%p) = x%x\n", s, s->ident ? BEgoto : BEbreak); result = s->ident ? BEgoto : BEbreak; } void visit(ContinueStatement *s) { result = s->ident ? BEgoto : BEcontinue; } void visit(SynchronizedStatement *s) { result = s->body ? s->body->blockExit(func, mustNotThrow) : BEfallthru; } void visit(WithStatement *s) { result = BEnone; if (canThrow(s->exp, func, mustNotThrow)) result = BEthrow; if (s->body) result |= s->body->blockExit(func, mustNotThrow); else result |= BEfallthru; } void visit(TryCatchStatement *s) { assert(s->body); result = s->body->blockExit(func, false); int catchresult = 0; for (size_t i = 0; i < s->catches->dim; i++) { Catch *c = (*s->catches)[i]; if (c->type == Type::terror) continue; int cresult; if (c->handler) cresult = c->handler->blockExit(func, mustNotThrow); else cresult = BEfallthru; /* If we're catching Object, then there is no throwing */ Identifier *id = c->type->toBasetype()->isClassHandle()->ident; if (c->internalCatch && (cresult & BEfallthru)) { // Bugzilla 11542: leave blockExit flags of the body cresult &= ~BEfallthru; } else if (id == Id::Object || id == Id::Throwable) { result &= ~(BEthrow | BEerrthrow); } else if (id == Id::Exception) { result &= ~BEthrow; } catchresult |= cresult; } if (mustNotThrow && (result & BEthrow)) { // now explain why this is nothrow s->body->blockExit(func, mustNotThrow); } result |= catchresult; } void visit(TryFinallyStatement *s) { result = BEfallthru; if (s->body) result = s->body->blockExit(func, false); // check finally body as well, it may throw (bug #4082) int finalresult = BEfallthru; if (s->finalbody) finalresult = s->finalbody->blockExit(func, false); // If either body or finalbody halts if (result == BEhalt) finalresult = BEnone; if (finalresult == BEhalt) result = BEnone; if (mustNotThrow) { // now explain why this is nothrow if (s->body && (result & BEthrow)) s->body->blockExit(func, mustNotThrow); if (s->finalbody && (finalresult & BEthrow)) s->finalbody->blockExit(func, mustNotThrow); } #if 0 // Bugzilla 13201: Mask to prevent spurious warnings for // destructor call, exit of synchronized statement, etc. if (result == BEhalt && finalresult != BEhalt && s->finalbody && s->finalbody->hasCode()) { s->finalbody->warning("statement is not reachable"); } #endif if (!(finalresult & BEfallthru)) result &= ~BEfallthru; result |= finalresult & ~BEfallthru; } void visit(OnScopeStatement *s) { // At this point, this statement is just an empty placeholder result = BEfallthru; } void visit(ThrowStatement *s) { if (s->internalThrow) { // Bugzilla 8675: Allow throwing 'Throwable' object even if mustNotThrow. result = BEfallthru; return; } Type *t = s->exp->type->toBasetype(); ClassDeclaration *cd = t->isClassHandle(); assert(cd); if (cd == ClassDeclaration::errorException || ClassDeclaration::errorException->isBaseOf(cd, NULL)) { result = BEerrthrow; return; } if (mustNotThrow) s->error("%s is thrown but not caught", s->exp->type->toChars()); result = BEthrow; } void visit(GotoStatement *s) { //printf("GotoStatement::blockExit(%p)\n", s); result = BEgoto; } void visit(LabelStatement *s) { //printf("LabelStatement::blockExit(%p)\n", s); result = s->statement ? s->statement->blockExit(func, mustNotThrow) : BEfallthru; } void visit(AsmStatement *s) { if (mustNotThrow) s->error("asm statements are assumed to throw", s->toChars()); // Assume the worst result = BEfallthru | BEthrow | BEreturn | BEgoto | BEhalt; } void visit(ImportStatement *s) { result = BEfallthru; } }; BlockExit be(func, mustNotThrow); accept(&be); return be.result; } 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 *sc, 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 *sc) { return NULL; } /******************************** ErrorStatement ***************************/ ErrorStatement::ErrorStatement() : Statement(Loc()) { assert(global.gaggedErrors || global.errors); } Statement *ErrorStatement::syntaxCopy() { return this; } Statement *ErrorStatement::semantic(Scope *sc) { return this; } /******************************** PeelStatement ***************************/ PeelStatement::PeelStatement(Statement *s) : Statement(s->loc) { this->s = s; } Statement *PeelStatement::semantic(Scope *sc) { /* "peel" off this wrapper, and don't run semantic() * on the result. */ return 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() { Expression *e = exp ? exp->syntaxCopy() : NULL; ExpStatement *es = new ExpStatement(loc, e); return es; } Statement *ExpStatement::semantic(Scope *sc) { if (exp) { //printf("ExpStatement::semantic() %s\n", exp->toChars()); #if 0 // Doesn't work because of difficulty dealing with things like a.b.c!(args).Foo!(args) // See if this should be rewritten as a TemplateMixin if (exp->op == TOKdeclaration) { DeclarationExp *de = (DeclarationExp *)exp; Dsymbol *s = de->declaration; printf("s: %s %s\n", s->kind(), s->toChars()); VarDeclaration *v = s->isVarDeclaration(); if (v) { printf("%s, %d\n", v->type->toChars(), v->type->ty); } } #endif exp = exp->semantic(sc); exp = exp->addDtorHook(sc); exp = resolveProperties(sc, exp); discardValue(exp); exp = exp->optimize(0); exp = checkGC(sc, exp); if (exp->op == TOKerror) return new ErrorStatement(); } return this; } Statement *ExpStatement::scopeCode(Scope *sc, 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->noscope && !v->isDataseg()) { Expression *e = v->edtor; if (e) { //printf("dtor is: "); e->print(); #if 0 if (v->type->toBasetype()->ty == Tstruct) { /* Need a 'gate' to turn on/off destruction, * in case v gets moved elsewhere. */ Identifier *id = Lexer::uniqueId("__runDtor"); ExpInitializer *ie = new ExpInitializer(loc, new IntegerExp(1)); VarDeclaration *rd = new VarDeclaration(loc, Type::tint32, id, ie); rd->storage_class |= STCtemp; *sentry = new ExpStatement(loc, rd); v->rundtor = rd; /* Rewrite e as: * rundtor && e */ Expression *ve = new VarExp(loc, v->rundtor); e = new AndAndExp(loc, ve, e); e->type = Type::tbool; } #endif *sfinally = new DtorExpStatement(loc, e, v); } v->noscope = 1; // don't add in dtor again } } } return this; } /******************************** DtorExpStatement ***************************/ DtorExpStatement::DtorExpStatement(Loc loc, Expression *exp, VarDeclaration *v) : ExpStatement(loc, exp) { this->var = v; } Statement *DtorExpStatement::syntaxCopy() { Expression *e = exp ? exp->syntaxCopy() : NULL; DtorExpStatement *es = new DtorExpStatement(loc, e, var); return es; } /******************************** 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; } Statements *CompileStatement::flatten(Scope *sc) { //printf("CompileStatement::flatten() %s\n", exp->toChars()); sc = sc->startCTFE(); exp = exp->semantic(sc); exp = resolveProperties(sc, exp); sc = sc->endCTFE(); Statements *a = new Statements(); if (exp->op != TOKerror) { Expression *e = exp->ctfeInterpret(); StringExp *se = e->toStringExp(); if (!se) error("argument to mixin must be a string, not (%s) of type %s", exp->toChars(), exp->type->toChars()); else { se = se->toUTF8(sc); Parser p(loc, sc->module, (utf8_t *)se->string, se->len, 0); p.nextToken(); while (p.token.value != TOKeof) { unsigned errors = global.errors; Statement *s = p.parseStatement(PSsemi | PScurlyscope); if (!s || global.errors != errors) goto Lerror; a->push(s); } return a; } } Lerror: a->push(new ErrorStatement()); 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); } /******************************** 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]; if (s) s = s->syntaxCopy(); (*a)[i] = s; } CompoundStatement *cs = new CompoundStatement(loc, a); return cs; } Statement *CompoundStatement::semantic(Scope *sc) { //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", this, sc); #if 0 for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; if (s) printf("[%d]: %s", i, s->toChars()); } #endif for (size_t i = 0; i < statements->dim; ) { Statement *s = (*statements)[i]; if (s) { Statements *flt = s->flatten(sc); if (flt) { statements->remove(i); statements->insert(i, flt); continue; } s = s->semantic(sc); (*statements)[i] = s; if (s) { Statement *sentry; Statement *sexception; Statement *sfinally; (*statements)[i] = s->scopeCode(sc, &sentry, &sexception, &sfinally); if (sentry) { sentry = sentry->semantic(sc); statements->insert(i, sentry); i++; } if (sexception) sexception = sexception->semantic(sc); if (sexception) { if (i + 1 == statements->dim && !sfinally) { } else { /* Rewrite: * s; s1; s2; * As: * s; * try { s1; s2; } * catch (Throwable __o) * { sexception; throw __o; } */ Statements *a = new Statements(); for (size_t j = i + 1; j < statements->dim; j++) { a->push((*statements)[j]); } Statement *body = new CompoundStatement(Loc(), a); body = new ScopeStatement(Loc(), body); Identifier *id = Lexer::uniqueId("__o"); Statement *handler = new PeelStatement(sexception); if (sexception->blockExit(sc->func, false) & BEfallthru) { ThrowStatement *ts = new ThrowStatement(Loc(), new IdentifierExp(Loc(), id)); ts->internalThrow = true; handler = new CompoundStatement(Loc(), handler, ts); } Catches *catches = new Catches(); Catch *ctch = new Catch(Loc(), NULL, id, handler); ctch->internalCatch = true; catches->push(ctch); s = new TryCatchStatement(Loc(), body, catches); if (sfinally) s = new TryFinallyStatement(Loc(), 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; } */ Statements *a = new Statements(); for (size_t j = i + 1; j < statements->dim; j++) { a->push((*statements)[j]); } Statement *body = new CompoundStatement(Loc(), a); s = new TryFinallyStatement(Loc(), body, sfinally); s = s->semantic(sc); statements->setDim(i + 1); statements->push(s); break; } } } else { /* Remove NULL statements from the list. */ statements->remove(i); continue; } } i++; } for (size_t i = 0; i < statements->dim; ++i) { Lagain: Statement *s = (*statements)[i]; if (!s) continue; Statement *se = s->isErrorStatement(); if (se) return se; /* Bugzilla 11653: 'semantic' may return another CompoundStatement * (eg. CaseRangeStatement), so flatten it here. */ Statements *flt = s->flatten(sc); if (flt) { statements->remove(i); statements->insert(i, flt); if (statements->dim <= i) break; goto Lagain; } } if (statements->dim == 1 #if IN_LLVM && !isAsmBlockStatement() #endif ) { return (*statements)[0]; } return this; } Statements *CompoundStatement::flatten(Scope *sc) { 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]; if (s) s = s->syntaxCopy(); (*a)[i] = s; } CompoundDeclarationStatement *cs = new CompoundDeclarationStatement(loc, a); return cs; } /**************************** 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]; if (s) s = s->syntaxCopy(); (*a)[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); Scope *scd = sc->push(); scd->sbreak = this; scd->scontinue = this; Statement *serror = NULL; for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; if (s) { //printf("[%d]: %s\n", i, s->toChars()); s = s->semantic(scd); (*statements)[i] = s; if (s && !serror) serror = s->isErrorStatement(); } } scd->pop(); return serror ? serror : this; } bool UnrolledLoopStatement::hasBreak() { return true; } bool UnrolledLoopStatement::hasContinue() { return true; } /******************************** 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; } ReturnStatement *ScopeStatement::isReturnStatement() { if (statement) return statement->isReturnStatement(); return NULL; } Statement *ScopeStatement::semantic(Scope *sc) { ScopeDsymbol *sym; //printf("ScopeStatement::semantic(sc = %p)\n", sc); if (statement) { sym = new ScopeDsymbol(); sym->parent = sc->scopesym; sc = sc->push(sym); Statements *a = statement->flatten(sc); if (a) { statement = new CompoundStatement(loc, a); } statement = statement->semantic(sc); if (statement) { if (statement->isErrorStatement()) { sc->pop(); return statement; } Statement *sentry; Statement *sexception; Statement *sfinally; statement = statement->scopeCode(sc, &sentry, &sexception, &sfinally); assert(!sentry); assert(!sexception); if (sfinally) { //printf("adding sfinally\n"); sfinally = sfinally->semantic(sc); statement = new CompoundStatement(loc, statement, sfinally); } } sc->pop(); } return this; } 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) : Statement(loc) { condition = c; body = b; } Statement *WhileStatement::syntaxCopy() { WhileStatement *s = new WhileStatement(loc, condition->syntaxCopy(), body ? body->syntaxCopy() : NULL); return s; } Statement *WhileStatement::semantic(Scope *sc) { /* Rewrite as a for(;condition;) loop */ Statement *s = new ForStatement(loc, NULL, condition, NULL, body); s = s->semantic(sc); return s; } bool WhileStatement::hasBreak() { return true; } bool WhileStatement::hasContinue() { return true; } /******************************** DoStatement ***************************/ DoStatement::DoStatement(Loc loc, Statement *b, Expression *c) : Statement(loc) { body = b; condition = c; } Statement *DoStatement::syntaxCopy() { DoStatement *s = new DoStatement(loc, body ? body->syntaxCopy() : NULL, condition->syntaxCopy()); return s; } Statement *DoStatement::semantic(Scope *sc) { 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 = checkGC(sc, condition); condition = condition->checkToBoolean(sc); if (condition->op == TOKerror) return new ErrorStatement(); if (body && body->isErrorStatement()) return body; return this; } bool DoStatement::hasBreak() { return true; } bool DoStatement::hasContinue() { return true; } /******************************** 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->relatedLabeled = 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) { //printf("ForStatement::semantic %s\n", toChars()); if (init) { /* Rewrite: * for (auto v1 = i1, v2 = i2; condition; increment) { ... } * to: * { auto v1 = i1, v2 = i2; for (; condition; increment) { ... } } * then lowered to: * auto v1 = i1; * try { * auto v2 = i2; * try { * for (; condition; increment) { ... } * } finally { v2.~this(); } * } finally { v1.~this(); } */ Statements *ainit = new Statements(); ainit->push(init), init = NULL; ainit->push(this); Statement *s = new CompoundStatement(loc, ainit); s = new ScopeStatement(loc, s); s = s->semantic(sc); if (!s->isErrorStatement()) { if (LabelStatement *ls = checkLabeledLoop(sc, this)) ls->gotoTarget = this; relatedLabeled = s; } return s; } assert(init == NULL); ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc->scopesym; sc = sc->push(sym); sc->noctor++; if (condition) { condition = condition->semantic(sc); condition = resolveProperties(sc, condition); condition = condition->optimize(WANTvalue); condition = checkGC(sc, condition); condition = condition->checkToBoolean(sc); } if (increment) { increment = increment->semantic(sc); increment = resolveProperties(sc, increment); increment = increment->optimize(0); increment = checkGC(sc, increment); } sc->sbreak = this; sc->scontinue = this; if (body) body = body->semanticNoScope(sc); sc->noctor--; sc->pop(); if (condition && condition->op == TOKerror || increment && increment->op == TOKerror || body && body->isErrorStatement()) return new ErrorStatement(); return this; } 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 *arguments, Expression *aggr, Statement *body) : Statement(loc) { this->op = op; this->arguments = arguments; this->aggr = aggr; this->body = body; this->key = NULL; this->value = NULL; this->func = NULL; this->cases = NULL; this->gotos = NULL; } Statement *ForeachStatement::syntaxCopy() { Parameters *args = Parameter::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; Dsymbol *sapply = NULL; Type *tn = NULL; Type *tnv = NULL; func = sc->func; if (func->fes) func = func->fes->func; if (!inferAggregate(this, sc, sapply)) { error("invalid foreach aggregate %s", aggr->toChars()); Lerror: return new ErrorStatement(); } Dsymbol* sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors /* Check for inference errors */ if (!inferApplyArgTypes(this, sc, sapply)) { /** Try and extract the parameter count of the opApply callback function, e.g.: int opApply(int delegate(int, float)) => 2 args */ bool foundMismatch = false; size_t foreachParamCount = 0; if (sapplyOld) { if (FuncDeclaration *fd = sapplyOld->isFuncDeclaration()) { int fvarargs; // ignored (opApply shouldn't take variadics) Parameters *fparameters = fd->getParameters(&fvarargs); if (Parameter::dim(fparameters) == 1) { // first param should be the callback function Parameter *fparam = Parameter::getNth(fparameters, 0); if ((fparam->type->ty == Tpointer || fparam->type->ty == Tdelegate) && fparam->type->nextOf()->ty == Tfunction) { TypeFunction *tf = (TypeFunction *)fparam->type->nextOf(); foreachParamCount = Parameter::dim(tf->parameters); foundMismatch = true; } } } } //printf("dim = %d, arguments->dim = %d\n", dim, arguments->dim); if (foundMismatch && dim != foreachParamCount) { const char *plural = foreachParamCount > 1 ? "s" : ""; error("cannot infer argument types, expected %d argument%s, not %d", foreachParamCount, plural, dim); } else error("cannot uniquely infer foreach argument types"); goto Lerror; } 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"); goto Lerror; } Type *argtype = (*arguments)[dim-1]->type; if (argtype) { argtype = argtype->semantic(loc, sc); if (argtype->ty == Terror) goto Lerror; } 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 = Parameter::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 = NULL; Type *t = NULL; if (te) e = (*te->exps)[k]; else t = Parameter::getNth(tuple->arguments, k)->type; Parameter *arg = (*arguments)[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()); goto Lerror; } arg->type = arg->type->semantic(loc, sc); TY keyty = arg->type->ty; if (keyty != Tint32 && keyty != Tuns32) { if (global.params.is64bit) { if (keyty != Tint64 && keyty != Tuns64) { error("foreach: key type must be int or uint, long or ulong, not %s", arg->type->toChars()); goto Lerror; } } else { error("foreach: key type must be int or uint, not %s", arg->type->toChars()); goto Lerror; } } Initializer *ie = new ExpInitializer(Loc(), 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 = (*arguments)[1]; // value } // Declare value if (arg->storageClass & (STCout | STClazy) || arg->storageClass & STCref && !te) { error("no storage class for value %s", arg->ident->toChars()); goto Lerror; } Dsymbol *var; if (te) { Type *tb = e->type->toBasetype(); Dsymbol *ds = NULL; if ((tb->ty == Tfunction || tb->ty == Tsarray) && e->op == TOKvar) ds = ((VarExp *)e)->var; else if (e->op == TOKtemplate) ds = ((TemplateExp *)e)->td; else if (e->op == TOKimport) ds = ((ScopeExp *)e)->sds; else if (e->op == TOKfunction) { FuncExp *fe = (FuncExp *)e; ds = fe->td ? (Dsymbol *)fe->td : fe->fd; } if (ds) { var = new AliasDeclaration(loc, arg->ident, ds); if (arg->storageClass & STCref) { error("symbol %s cannot be ref", s->toChars()); goto Lerror; } if (argtype) { error("cannot specify element type for symbol %s", ds->toChars()); goto Lerror; } } else if (e->op == TOKtype) { var = new AliasDeclaration(loc, arg->ident, e->type); if (argtype) { error("cannot specify element type for type %s", e->type->toChars()); goto Lerror; } } else { arg->type = e->type; if (argtype) arg->type = argtype; Initializer *ie = new ExpInitializer(Loc(), e); VarDeclaration *v = new VarDeclaration(loc, arg->type, arg->ident, ie); if (arg->storageClass & STCref) v->storage_class |= STCref | STCforeach; if (e->isConst() || e->op == TOKstring || e->op == TOKstructliteral || e->op == TOKarrayliteral) { if (v->storage_class & STCref) { error("constant value %s cannot be ref", ie->toChars()); goto Lerror; } else v->storage_class |= STCmanifest; } var = v; } } else { var = new AliasDeclaration(loc, arg->ident, t); if (argtype) { error("cannot specify element type for symbol %s", s->toChars()); goto Lerror; } } 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); if (LabelStatement *ls = checkLabeledLoop(sc, this)) ls->gotoTarget = s; if (te && te->e0) s = new CompoundStatement(loc, new ExpStatement(te->e0->loc, te->e0), s); 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"); goto Lerror2; } /* 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) { int i = (dim == 1) ? 0 : 1; // index of value Parameter *arg = (*arguments)[i]; arg->type = arg->type->semantic(loc, sc); arg->type = arg->type->addStorageClass(arg->storageClass); 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"); goto Lerror2; } if (dim == 2) { arg = (*arguments)[0]; if (arg->storageClass & STCref) { error("foreach: key cannot be ref"); goto Lerror2; } } goto Lapply; } } for (size_t i = 0; i < dim; i++) { // Declare args Parameter *arg = (*arguments)[i]; arg->type = arg->type->semantic(loc, sc); arg->type = arg->type->addStorageClass(arg->storageClass); VarDeclaration *var; if (dim == 2 && i == 0) { var = new VarDeclaration(loc, arg->type->mutableOf(), Lexer::uniqueId("__key"), NULL); var->storage_class |= STCtemp | STCforeach; if (var->storage_class & (STCref | STCout)) var->storage_class |= STCnodtor; key = var; if (arg->storageClass & STCref) { if (!var->type->immutableOf()->equals(arg->type->immutableOf()) || !MODimplicitConv(var->type->mod, arg->type->mod)) { error("key type mismatch, %s to ref %s", var->type->toChars(), arg->type->toChars()); goto Lerror2; } } if (tab->ty == Tsarray) { TypeSArray *ta = (TypeSArray *)tab; IntRange dimrange = getIntRange(ta->dim); if (!IntRange::fromType(var->type).contains(dimrange)) { error("index type '%s' cannot cover index range 0..%llu", arg->type->toChars(), ta->dim->toInteger()); } key->range = new IntRange(SignExtendedNumber(0), dimrange.imax); } } else { var = new VarDeclaration(loc, arg->type, arg->ident, NULL); var->storage_class |= STCforeach; var->storage_class |= arg->storageClass & (STCin | STCout | STCref | STC_TYPECTOR); if (var->storage_class & (STCref | STCout)) var->storage_class |= STCnodtor; value = var; if (var->storage_class & STCref) { if (aggr->checkModifiable(sc, 1) == 2) var->storage_class |= STCctorinit; Type *t = tab->nextOf(); if (!t->immutableOf()->equals(arg->type->immutableOf()) || !MODimplicitConv(t->mod, arg->type->mod)) { error("argument type mismatch, %s to ref %s", t->toChars(), arg->type->toChars()); goto Lerror2; } } } } /* Convert to a ForStatement * foreach (key, value; a) body => * for (T[] tmp = a[], size_t key; key < tmp.length; ++key) * { T value = tmp[k]; body } * * foreach_reverse (key, value; a) body => * for (T[] tmp = a[], size_t key = tmp.length; key--; ) * { T value = tmp[k]; body } */ Identifier *id = Lexer::uniqueId("__aggr"); ExpInitializer *ie = new ExpInitializer(loc, new SliceExp(loc, aggr, NULL, NULL)); VarDeclaration *tmp; if (aggr->op == TOKarrayliteral && !((*arguments)[dim - 1]->storageClass & STCref)) { ArrayLiteralExp *ale = (ArrayLiteralExp *)aggr; size_t edim = ale->elements ? ale->elements->dim : 0; aggr->type = tab->nextOf()->sarrayOf(edim); // for (T[edim] tmp = a, ...) tmp = new VarDeclaration(loc, aggr->type, id, ie); } else tmp = new VarDeclaration(loc, tab->nextOf()->arrayOf(), id, ie); tmp->storage_class |= STCtemp; Expression *tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id::length); if (!key) { Identifier *idkey = Lexer::uniqueId("__key"); key = new VarDeclaration(loc, Type::tsize_t, idkey, NULL); key->storage_class |= STCtemp; } if (op == TOKforeach_reverse) key->init = new ExpInitializer(loc, tmp_length); else key->init = new ExpInitializer(loc, new IntegerExp(loc, 0, key->type)); Statements *cs = new Statements(); cs->push(new ExpStatement(loc, tmp)); cs->push(new ExpStatement(loc, key)); Statement *forinit = new CompoundDeclarationStatement(loc, cs); Expression *cond; if (op == TOKforeach_reverse) { // key-- cond = new PostExp(TOKminusminus, loc, new VarExp(loc, key)); } else { // key < tmp.length cond = new CmpExp(TOKlt, loc, new VarExp(loc, key), tmp_length); } Expression *increment = NULL; if (op == TOKforeach) { // key += 1 increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(loc, 1, key->type)); } // T value = tmp[key]; value->init = new ExpInitializer(loc, new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, key))); Statement *ds = new ExpStatement(loc, value); if (dim == 2) { Parameter *arg = (*arguments)[0]; if ((arg->storageClass & STCref) && arg->type->equals(key->type)) { key->range = NULL; AliasDeclaration *v = new AliasDeclaration(loc, arg->ident, key); body = new CompoundStatement(loc, new ExpStatement(loc, v), body); } else { ExpInitializer *ei = new ExpInitializer(loc, new IdentifierExp(loc, key->ident)); VarDeclaration *v = new VarDeclaration(loc, arg->type, arg->ident, ei); v->storage_class |= STCforeach | (arg->storageClass & STCref); body = new CompoundStatement(loc, new ExpStatement(loc, v), body); if (key->range && !arg->type->isMutable()) { /* Limit the range of the key to the specified range */ v->range = new IntRange(key->range->imin, key->range->imax - SignExtendedNumber(1)); } } } body = new CompoundStatement(loc, ds, body); s = new ForStatement(loc, forinit, cond, increment, body); if (LabelStatement *ls = checkLabeledLoop(sc, this)) ls->gotoTarget = s; s = s->semantic(sc); 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"); goto Lerror2; } goto Lapply; case Tclass: case Tstruct: /* Prefer using opApply, if it exists */ if (sapply) goto Lapply; { /* Look for range iteration, i.e. the properties * .empty, .popFront, .popBack, .front and .back * foreach (e; aggr) { ... } * translates to: * for (auto __r = aggr[]; !__r.empty; __r.popFront) { * auto e = __r.front; * ... * } */ AggregateDeclaration *ad = (tab->ty == Tclass) ? (AggregateDeclaration *)((TypeClass *)tab)->sym : (AggregateDeclaration *)((TypeStruct *)tab)->sym; Identifier *idfront; Identifier *idpopFront; if (op == TOKforeach) { idfront = Id::Ffront; idpopFront = Id::FpopFront; } else { idfront = Id::Fback; idpopFront = Id::FpopBack; } Dsymbol *sfront = ad->search(Loc(), idfront); if (!sfront) goto Lapply; /* Generate a temporary __r and initialize it with the aggregate. */ Identifier *rid = Identifier::generateId("__r"); VarDeclaration *r = new VarDeclaration(loc, NULL, rid, new ExpInitializer(loc, aggr)); r->storage_class |= STCtemp; Statement *init = new ExpStatement(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 CallExp(loc, new DotIdExp(loc, e, idpopFront)); /* Declaration statement for e: * auto e = __r.idfront; */ e = new VarExp(loc, r); Expression *einit = new DotIdExp(loc, e, idfront); Statement *makeargs, *forbody; if (dim == 1) { Parameter *arg = (*arguments)[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 | STC_TYPECTOR); DeclarationExp *de = new DeclarationExp(loc, ve); makeargs = new ExpStatement(loc, de); } else { Identifier *id = Lexer::uniqueId("__front"); ExpInitializer *ei = new ExpInitializer(loc, einit); VarDeclaration *vd = new VarDeclaration(loc, NULL, id, ei); vd->storage_class |= STCtemp | STCctfe | STCref | STCforeach; makeargs = new ExpStatement(loc, new DeclarationExp(loc, vd)); Declaration *d = sfront->isDeclaration(); if (FuncDeclaration *f = d->isFuncDeclaration()) { if (!f->functionSemantic()) goto Lrangeerr; } Expression *ve = new VarExp(loc, vd); ve->type = d->type; if (ve->type->toBasetype()->ty == Tfunction) ve->type = ve->type->toBasetype()->nextOf(); if (!ve->type || ve->type->ty == Terror) goto Lrangeerr; // Resolve inout qualifier of front type ve->type = ve->type->substWildTo(tab->mod); Expressions *exps = new Expressions(); exps->push(ve); int pos = 0; while (exps->dim < dim) { pos = expandAliasThisTuples(exps, pos); if (pos == -1) break; } if (exps->dim != dim) { const char *plural = exps->dim > 1 ? "s" : ""; error("cannot infer argument types, expected %d argument%s, not %d", exps->dim, plural, dim); goto Lerror2; } for (size_t i = 0; i < dim; i++) { Parameter *arg = (*arguments)[i]; Expression *exp = (*exps)[i]; #if 0 printf("[%d] arg = %s %s, exp = %s %s\n", i, arg->type ? arg->type->toChars() : "?", arg->ident->toChars(), exp->type->toChars(), exp->toChars()); #endif if (!arg->type) arg->type = exp->type; arg->type = arg->type->addStorageClass(arg->storageClass)->semantic(loc, sc); if (!exp->implicitConvTo(arg->type)) goto Lrangeerr; VarDeclaration *var = new VarDeclaration(loc, arg->type, arg->ident, new ExpInitializer(loc, exp)); var->storage_class |= STCctfe | STCref | STCforeach; DeclarationExp *de = new DeclarationExp(loc, var); makeargs = new CompoundStatement(loc, makeargs, new ExpStatement(loc, de)); } } forbody = new CompoundStatement(loc, makeargs, this->body); s = new ForStatement(loc, init, condition, increment, forbody); if (LabelStatement *ls = checkLabeledLoop(sc, this)) ls->gotoTarget = s; #if 0 printf("init: %s\n", init->toChars()); printf("condition: %s\n", condition->toChars()); printf("increment: %s\n", increment->toChars()); printf("body: %s\n", forbody->toChars()); #endif s = s->semantic(sc); break; Lrangeerr: error("cannot infer argument types"); goto Lerror2; } case Tdelegate: if (op == TOKforeach_reverse) warning("cannot use foreach_reverse with a delegate"); Lapply: { Expression *ec; Expression *e; if (!checkForArgTypes()) { body = body->semanticNoScope(sc); return this; } TypeFunction *tfld = NULL; if (sapply) { FuncDeclaration *fdapply = sapply->isFuncDeclaration(); if (fdapply) { assert(fdapply->type && fdapply->type->ty == Tfunction); tfld = (TypeFunction *)fdapply->type->semantic(loc, sc); goto Lget; } else if (tab->ty == Tdelegate) { tfld = (TypeFunction *)tab->nextOf(); Lget: //printf("tfld = %s\n", tfld->toChars()); if (tfld->parameters->dim == 1) { Parameter *p = Parameter::getNth(tfld->parameters, 0); if (p->type && p->type->ty == Tdelegate) { Type *t = p->type->semantic(loc, sc); assert(t->ty == Tdelegate); tfld = (TypeFunction *)t->nextOf(); } } } } /* Turn body into the function literal: * int delegate(ref T arg) { body } */ Parameters *args = new Parameters(); for (size_t i = 0; i < dim; i++) { Parameter *arg = (*arguments)[i]; StorageClass stc = STCref; Identifier *id; arg->type = arg->type->semantic(loc, sc); arg->type = arg->type->addStorageClass(arg->storageClass); #if IN_LLVM // Type of parameter may be different; see below Type *para_type = arg->type; #endif if (tfld) { Parameter *prm = Parameter::getNth(tfld->parameters, i); //printf("\tprm = %s%s\n", (prm->storageClass&STCref?"ref ":""), prm->ident->toChars()); stc = prm->storageClass & STCref; id = arg->ident; // argument copy is not need. if ((arg->storageClass & STCref) != stc) { if (!stc) { error("foreach: cannot make %s ref", arg->ident->toChars()); goto Lerror2; } goto LcopyArg; } } else if (arg->storageClass & STCref) { // default delegate parameters are marked as ref, then // argument copy is not need. id = arg->ident; } else { // Make a copy of the ref argument so it isn't // a reference. LcopyArg: id = Lexer::uniqueId("__applyArg", (int)i); #if IN_LLVM // In case of a foreach loop on an array the index passed // to the delegate is always of type size_t. The type of // the parameter must be changed to size_t and a cast to // the type used must be inserted. Otherwise the index is // always 0 on a big endian architecture. This fixes // issue #326. Initializer *ie; if (dim == 2 && i == 0 && (tab->ty == Tarray || tab->ty == Tsarray)) { para_type = Type::tsize_t; ie = new ExpInitializer(Loc(), new CastExp(Loc(), new IdentifierExp(Loc(), id), arg->type)); } else ie = new ExpInitializer(Loc(), new IdentifierExp(Loc(), id)); #else Initializer *ie = new ExpInitializer(Loc(), new IdentifierExp(Loc(), id)); #endif VarDeclaration *v = new VarDeclaration(Loc(), arg->type, arg->ident, ie); v->storage_class |= STCtemp; s = new ExpStatement(Loc(), v); body = new CompoundStatement(loc, s, body); } #if IN_LLVM args->push(new Parameter(stc, para_type, id, NULL)); #else args->push(new Parameter(stc, arg->type, id, NULL)); #endif } tfld = new TypeFunction(args, Type::tint32, 0, LINKd); cases = new Statements(); gotos = new ScopeStatements(); FuncLiteralDeclaration *fld = new FuncLiteralDeclaration(loc, Loc(), tfld, TOKdelegate, this); fld->fbody = body; Expression *flde = new FuncExp(loc, fld); flde = flde->semantic(sc); fld->tookAddressOf = 0; // Resolve any forward referenced goto's for (size_t i = 0; i < gotos->dim; i++) { GotoStatement *gs = (GotoStatement *)(*gotos)[i]->statement; if (!gs->label->statement) { // 'Promote' it to this scope, and replace with a return cases->push(gs); s = new ReturnStatement(Loc(), new IntegerExp(cases->dim + 1)); (*gotos)[i]->statement = s; } } if (taa) { // Check types Parameter *arg = (*arguments)[0]; if (dim == 2) { if (arg->storageClass & STCref) { error("foreach: index cannot be ref"); goto Lerror2; } if (!taa->index->implicitConvTo(arg->type)) { error("foreach: index must be type %s, not %s", taa->index->toChars(), arg->type->toChars()); goto Lerror2; } arg = (*arguments)[1]; } if ((!arg->type->equals(taa->nextOf()) && (arg->storageClass & STCref)) || !taa->nextOf()->implicitConvTo(arg->type)) { error("foreach: value must be type %s, not %s", taa->nextOf()->toChars(), arg->type->toChars()); goto Lerror2; } /* Call: * _aaApply(aggr, keysize, flde) */ static const char *name[2] = { "_aaApply", "_aaApply2" }; static FuncDeclaration *fdapply[2] = { NULL, NULL }; static TypeDelegate *fldeTy[2] = { NULL, NULL }; unsigned char i = dim == 2; if (!fdapply[i]) { args = new Parameters; args->push(new Parameter(0, Type::tvoid->pointerTo(), NULL, NULL)); args->push(new Parameter(STCin, Type::tsize_t, NULL, NULL)); Parameters* dgargs = new Parameters; dgargs->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); if (dim == 2) dgargs->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); fldeTy[i] = new TypeDelegate(new TypeFunction(dgargs, Type::tint32, 0, LINKd)); args->push(new Parameter(0, fldeTy[i], NULL, NULL)); fdapply[i] = FuncDeclaration::genCfunc(args, Type::tint32, name[i]); } ec = new VarExp(Loc(), fdapply[i]); Expressions *exps = new Expressions(); exps->push(aggr); size_t keysize = (size_t)taa->index->size(); keysize = (keysize + ((size_t)Target::ptrsize-1)) & ~((size_t)Target::ptrsize-1); // paint delegate argument to the type runtime expects if (!fldeTy[i]->equals(flde->type)) { flde = new CastExp(loc, flde, flde->type); flde->type = fldeTy[i]; } exps->push(new IntegerExp(Loc(), keysize, Type::tsize_t)); exps->push(flde); e = new CallExp(loc, ec, exps); e->type = Type::tint32; // don't run semantic() on e } else if (tab->ty == Tarray || tab->ty == Tsarray) { /* Call: * _aApply(aggr, flde) */ static const char fntab[9][3] = { "cc","cw","cd", "wc","cc","wd", "dc","dw","dd" }; const size_t BUFFER_LEN = 7+1+2+ sizeof(dim)*3 + 1; char fdname[BUFFER_LEN]; 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%llu", r, 2, fntab[flag], (ulonglong)dim); assert(j < BUFFER_LEN); FuncDeclaration *fdapply; TypeDelegate *dgty; args = new Parameters; args->push(new Parameter(STCin, tn->arrayOf(), NULL, NULL)); Parameters* dgargs = new Parameters; dgargs->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); if (dim == 2) dgargs->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); dgty = new TypeDelegate(new TypeFunction(dgargs, Type::tint32, 0, LINKd)); args->push(new Parameter(0, dgty, NULL, NULL)); fdapply = FuncDeclaration::genCfunc(args, Type::tint32, fdname); ec = new VarExp(Loc(), fdapply); Expressions *exps = new Expressions(); if (tab->ty == Tsarray) aggr = aggr->castTo(sc, tn->arrayOf()); exps->push(aggr); // 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::tint32; // don't run semantic() on e } else if (tab->ty == Tdelegate) { /* Call: * aggr(flde) */ Expressions *exps = new Expressions(); exps->push(flde); if (aggr->op == TOKdelegate && ((DelegateExp *)aggr)->func->isNested()) { // See Bugzilla 3560 e = new CallExp(loc, ((DelegateExp *)aggr)->e1, exps); } else e = new CallExp(loc, aggr, exps); e = e->semantic(sc); if (e->op == TOKerror) goto Lerror2; if (e->type != Type::tint32) { error("opApply() function for %s must return an int", tab->toChars()); goto Lerror2; } } else { assert(tab->ty == Tstruct || tab->ty == Tclass); Expressions *exps = new Expressions(); assert(sapply); /* Call: * aggr.apply(flde) */ ec = new DotIdExp(loc, aggr, sapply->ident); exps->push(flde); e = new CallExp(loc, ec, exps); e = e->semantic(sc); if (e->op == TOKerror) goto Lerror2; if (e->type != Type::tint32) { error("opApply() function for %s must return an int", tab->toChars()); goto Lerror2; } } 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(Loc(), NULL); s = new DefaultStatement(Loc(), s); a->push(s); // cases 2... for (size_t i = 0; i < cases->dim; i++) { s = (*cases)[i]; s = new CaseStatement(Loc(), new IntegerExp(i + 2), s); a->push(s); } s = new CompoundStatement(loc, a); s = new SwitchStatement(loc, e, s, false); } s = s->semantic(sc); break; } case Terror: Lerror2: s = new ErrorStatement(); break; default: error("foreach: %s is not an aggregate type", aggr->type->toChars()); goto Lerror2; } sc->noctor--; sc->pop(); return s; } bool ForeachStatement::checkForArgTypes() { bool result = true; for (size_t i = 0; i < arguments->dim; i++) { Parameter *arg = (*arguments)[i]; if (!arg->type) { error("cannot infer type for %s", arg->ident->toChars()); arg->type = Type::terror; result = false; } } return result; } bool ForeachStatement::hasBreak() { return true; } bool ForeachStatement::hasContinue() { return true; } /**************************** ForeachRangeStatement ***************************/ ForeachRangeStatement::ForeachRangeStatement(Loc loc, TOK op, Parameter *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->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); lwr = lwr->semantic(sc); lwr = resolveProperties(sc, lwr); lwr = lwr->optimize(WANTvalue); if (!lwr->type) { error("invalid range lower bound %s", lwr->toChars()); Lerror: return new ErrorStatement(); } upr = upr->semantic(sc); upr = resolveProperties(sc, upr); upr = upr->optimize(WANTvalue); if (!upr->type) { error("invalid range upper bound %s", upr->toChars()); goto Lerror; } if (arg->type) { arg->type = arg->type->semantic(loc, sc); arg->type = arg->type->addStorageClass(arg->storageClass); lwr = lwr->implicitCastTo(sc, arg->type); if (upr->implicitConvTo(arg->type) || (arg->storageClass & STCref)) { upr = upr->implicitCastTo(sc, arg->type); } else { // See if upr-1 fits in arg->type Expression *limit = new MinExp(loc, upr, new IntegerExp(1)); limit = limit->semantic(sc); limit = limit->optimize(WANTvalue); if (!limit->implicitConvTo(arg->type)) { upr = upr->implicitCastTo(sc, arg->type); } } } else { /* Must infer types from lwr and upr */ Type *tlwr = lwr->type->toBasetype(); if (tlwr->ty == Tstruct || tlwr->ty == Tclass) { /* Just picking the first really isn't good enough. */ arg->type = lwr->type; } else if (lwr->type == upr->type) { /* Same logic as CondExp ?lwr:upr */ arg->type = lwr->type; } else { AddExp ea(loc, lwr, upr); if (typeCombine(&ea, sc)) return new ErrorStatement(); arg->type = ea.type; lwr = ea.e1; upr = ea.e2; } arg->type = arg->type->addStorageClass(arg->storageClass); } if (arg->type->ty == Terror || lwr->op == TOKerror || upr->op == TOKerror) return new ErrorStatement(); /* Convert to a for loop: * foreach (key; lwr .. upr) => * for (auto key = lwr, auto tmp = upr; key < tmp; ++key) * * foreach_reverse (key; lwr .. upr) => * for (auto tmp = lwr, auto key = upr; key-- > tmp;) */ ExpInitializer *ie = new ExpInitializer(loc, (op == TOKforeach) ? lwr : upr); key = new VarDeclaration(loc, upr->type->mutableOf(), Lexer::uniqueId("__key"), ie); key->storage_class |= STCtemp; SignExtendedNumber lower = getIntRange(lwr).imin; SignExtendedNumber upper = getIntRange(upr).imax; if (lower <= upper) { key->range = new IntRange(lower, upper); } Identifier *id = Lexer::uniqueId("__limit"); ie = new ExpInitializer(loc, (op == TOKforeach) ? upr : lwr); VarDeclaration *tmp = new VarDeclaration(loc, upr->type, id, ie); tmp->storage_class |= STCtemp; Statements *cs = new Statements(); // Keep order of evaluation as lwr, then upr if (op == TOKforeach) { cs->push(new ExpStatement(loc, key)); cs->push(new ExpStatement(loc, tmp)); } else { cs->push(new ExpStatement(loc, tmp)); cs->push(new ExpStatement(loc, key)); } Statement *forinit = new CompoundDeclarationStatement(loc, cs); Expression *cond; if (op == TOKforeach_reverse) { cond = new PostExp(TOKminusminus, loc, new VarExp(loc, key)); if (arg->type->isscalar()) { // key-- > tmp cond = new CmpExp(TOKgt, loc, cond, new VarExp(loc, tmp)); } else { // key-- != tmp cond = new EqualExp(TOKnotequal, loc, cond, new VarExp(loc, tmp)); } } else { if (arg->type->isscalar()) { // key < tmp cond = new CmpExp(TOKlt, loc, new VarExp(loc, key), new VarExp(loc, tmp)); } else { // key != tmp cond = new EqualExp(TOKnotequal, loc, new VarExp(loc, key), new VarExp(loc, tmp)); } } Expression *increment = NULL; if (op == TOKforeach) // key += 1 //increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(1)); increment = new PreExp(TOKpreplusplus, loc, new VarExp(loc, key)); if ((arg->storageClass & STCref) && arg->type->equals(key->type)) { key->range = NULL; AliasDeclaration *v = new AliasDeclaration(loc, arg->ident, key); body = new CompoundStatement(loc, new ExpStatement(loc, v), body); } else { ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, key), arg->type)); VarDeclaration *v = new VarDeclaration(loc, arg->type, arg->ident, ie); v->storage_class |= STCtemp | STCforeach | (arg->storageClass & STCref); body = new CompoundStatement(loc, new ExpStatement(loc, v), body); if (key->range && !arg->type->isMutable()) { /* Limit the range of the key to the specified range */ v->range = new IntRange(key->range->imin, key->range->imax - SignExtendedNumber(1)); } } if (arg->storageClass & STCref) { if (!key->type->immutableOf()->equals(arg->type->immutableOf()) || !MODimplicitConv(key->type->mod, arg->type->mod)) { error("argument type mismatch, %s to ref %s", key->type->toChars(), arg->type->toChars()); } } ForStatement *s = new ForStatement(loc, forinit, cond, increment, body); if (LabelStatement *ls = checkLabeledLoop(sc, this)) ls->gotoTarget = s; return s->semantic(sc); } bool ForeachRangeStatement::hasBreak() { return true; } bool ForeachRangeStatement::hasContinue() { return true; } /******************************** IfStatement ***************************/ IfStatement::IfStatement(Loc loc, Parameter *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(); Parameter *a = arg ? arg->syntaxCopy() : NULL; IfStatement *s = new IfStatement(loc, a, condition->syntaxCopy(), i, e); return s; } Statement *IfStatement::semantic(Scope *sc) { // Evaluate at runtime unsigned cs0 = sc->callSuper; unsigned cs1; unsigned *fi0 = sc->saveFieldInit(); unsigned *fi1 = NULL; ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc->scopesym; Scope *scd = sc->push(sym); if (arg) { /* Declare arg, which we will set to be the * result of condition. */ match = new VarDeclaration(loc, arg->type, arg->ident, new ExpInitializer(loc, condition)); match->parent = sc->func; match->storage_class |= arg->storageClass; DeclarationExp *de = new DeclarationExp(loc, match); VarExp *ve = new VarExp(Loc(), match); condition = new CommaExp(loc, de, ve); condition = condition->semantic(scd); if (match->edtor) { Statement *sdtor = new ExpStatement(loc, match->edtor); sdtor = new OnScopeStatement(loc, TOKon_scope_exit, sdtor); ifbody = new CompoundStatement(loc, sdtor, ifbody); match->noscope = 1; } } else { condition = condition->semantic(sc); condition = condition->addDtorHook(sc); condition = resolveProperties(sc, condition); } condition = checkGC(sc, condition); // Convert to boolean after declaring arg so this works: // if (S arg = S()) {} // where S is a struct that defines opCast!bool. condition = condition->checkToBoolean(sc); // 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); ifbody = ifbody->semanticNoScope(scd); scd->pop(); cs1 = sc->callSuper; fi1 = sc->fieldinit; sc->callSuper = cs0; sc->fieldinit = fi0; if (elsebody) elsebody = elsebody->semanticScope(sc, NULL, NULL); sc->mergeCallSuper(loc, cs1); sc->mergeFieldInit(loc, fi1); if (condition->op == TOKerror || (ifbody && ifbody->isErrorStatement()) || (elsebody && elsebody->isErrorStatement())) { return new ErrorStatement(); } return this; } /******************************** 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)) { DebugCondition *dc = condition->isDebugCondition(); if (dc) { sc = sc->push(); sc->flags |= SCOPEdebug; ifbody = ifbody->semantic(sc); sc->pop(); } else ifbody = ifbody->semantic(sc); return ifbody; } else { if (elsebody) elsebody = elsebody->semantic(sc); return elsebody; } } 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() { 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 = (*args)[i]; sc = sc->startCTFE(); e = e->semantic(sc); e = resolveProperties(sc, e); sc = sc->endCTFE(); // pragma(msg) is allowed to contain types as well as expressions e = ctfeInterpretForPragmaMsg(e); if (e->op == TOKerror) { errorSupplemental(loc, "while evaluating pragma(msg, %s)", (*args)[i]->toChars()); goto Lerror; } StringExp *se = e->toStringExp(); if (se) { se = se->toUTF8(sc); fprintf(stderr, "%.*s", (int)se->len, (char *)se->string); } else fprintf(stderr, "%s", e->toChars()); } fprintf(stderr, "\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 = (*args)[0]; sc = sc->startCTFE(); e = e->semantic(sc); e = resolveProperties(sc, e); sc = sc->endCTFE(); e = e->ctfeInterpret(); (*args)[0] = e; StringExp *se = e->toStringExp(); if (!se) error("string expected for library name, not '%s'", e->toChars()); else if (global.params.verbose) { char *name = (char *)mem.malloc(se->len + 1); memcpy(name, se->string, se->len); name[se->len] = 0; fprintf(global.stdmsg, "library %s\n", name); mem.free(name); } } #endif } #if IN_LLVM // FIXME Move to pragma.cpp else if (ident == Id::LDC_allow_inline) { sc->func->allowInlining = true; } else if (ident == Id::LDC_never_inline) { sc->func->neverInline = true; } #endif else if (ident == Id::startaddress) { if (!args || args->dim != 1) error("function name expected for start address"); else { Expression *e = (*args)[0]; sc = sc->startCTFE(); e = e->semantic(sc); e = resolveProperties(sc, e); sc = sc->endCTFE(); e = e->ctfeInterpret(); (*args)[0] = e; Dsymbol *sa = getDsymbol(e); if (!sa || !sa->isFuncDeclaration()) error("function name expected for start address, not '%s'", e->toChars()); if (body) { body = body->semantic(sc); } return this; } } else error("unrecognized pragma(%s)", ident->toChars()); Lerror: if (body) { body = body->semantic(sc); } return body; } /******************************** 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; } /******************************** 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; #if IN_LLVM enclosingScopeExit = NULL; #endif cases = NULL; hasNoDefault = 0; hasVars = 0; } Statement *SwitchStatement::syntaxCopy() { //printf("SwitchStatement::syntaxCopy(%p)\n", this); SwitchStatement *s = new SwitchStatement(loc, condition->syntaxCopy(), body->syntaxCopy(), isFinal); return s; } Statement *SwitchStatement::semantic(Scope *sc) { //printf("SwitchStatement::semantic(%p)\n", this); tf = sc->tf; if (cases) return this; // already run condition = condition->semantic(sc); condition = resolveProperties(sc, condition); TypeEnum *te = NULL; // preserve enum type for final switches if (condition->type->ty == Tenum) te = (TypeEnum *)condition->type; 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 = integralPromotions(condition, sc); if (condition->op != TOKerror && !condition->type->isintegral()) error("'%s' must be of integral or string type, it is a %s", condition->toChars(), condition->type->toChars()); } condition = condition->optimize(WANTvalue); condition = checkGC(sc, condition); sc = sc->push(); sc->sbreak = this; sc->sw = this; cases = new CaseStatements(); 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 (size_t i = 0; i < gotoCases.dim; i++) { GotoCaseStatement *gcs = gotoCases[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 (size_t j = 0; j < scx->sw->cases->dim; j++) { CaseStatement *cs = (*scx->sw->cases)[j]; if (cs->exp->equals(gcs->exp)) { gcs->cs = cs; goto Lfoundcase; } } } gcs->error("case %s not found", gcs->exp->toChars()); Lfoundcase: ; } bool needswitcherror = false; if (isFinal) { Type *t = condition->type; while (t && t->ty == Ttypedef) { // Don't use toBasetype() because that will skip past enums t = ((TypeTypedef *)t)->sym->basetype; } Dsymbol *ds; EnumDeclaration *ed = NULL; if (t && ((ds = t->toDsymbol(sc)) != NULL)) ed = ds->isEnumDeclaration(); // typedef'ed enum if (!ed && te && ((ds = te->toDsymbol(sc)) != NULL)) ed = ds->isEnumDeclaration(); if (ed) { size_t dim = ed->members->dim; for (size_t i = 0; i < dim; i++) { EnumMember *em = (*ed->members)[i]->isEnumMember(); if (em) { for (size_t j = 0; j < cases->dim; j++) { CaseStatement *cs = (*cases)[j]; if (cs->exp->equals(em->value) || (!cs->exp->type->isString() && !em->value->type->isString() && cs->exp->toInteger() == em->value->toInteger())) goto L1; } error("enum member %s not represented in final switch", em->toChars()); } L1: ; } } else needswitcherror = true; } if (!sc->sw->sdefault && (!isFinal || needswitcherror || global.params.useAssert)) { hasNoDefault = 1; if (!isFinal && !body->isErrorStatement()) deprecation("switch statement without a default is deprecated; use 'final switch' or add 'default: assert(0);' or add 'default: break;'"); // 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(2); sc->sw->sdefault = new DefaultStatement(loc, s); a->push(body); if (body->blockExit(sc->func, false) & BEfallthru) a->push(new BreakStatement(Loc(), NULL)); a->push(sc->sw->sdefault); cs = new CompoundStatement(loc, a); body = cs; } sc->pop(); return this; } bool SwitchStatement::hasBreak() { return true; } /******************************** CaseStatement ***************************/ CaseStatement::CaseStatement(Loc loc, Expression *exp, Statement *s) : Statement(loc) { this->exp = exp; this->statement = s; index = 0; cblock = NULL; #if IN_LLVM bodyBB = NULL; llvmIdx = NULL; enclosingScopeExit = NULL; #endif } 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()); sc = sc->startCTFE(); exp = exp->semantic(sc); exp = resolveProperties(sc, exp); sc = sc->endCTFE(); if (sw) { exp = exp->implicitCastTo(sc, sw->condition->type); exp = exp->optimize(WANTvalue); /* 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; if (sw->isFinal) error("case variables not allowed in final switch statements"); goto L1; } } else exp = exp->ctfeInterpret(); if (StringExp *se = exp->toStringExp()) exp = se; else if (exp->op != TOKint64 && exp->op != TOKerror) { error("case must be a string or an integral constant, not %s", exp->toChars()); exp = new ErrorExp(); } L1: for (size_t i = 0; i < sw->cases->dim; i++) { CaseStatement *cs = (*sw->cases)[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 (size_t i = 0; i < sw->gotoCases.dim; i++) { GotoCaseStatement *gcs = sw->gotoCases[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(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() { CaseRangeStatement *s = new CaseRangeStatement(loc, first->syntaxCopy(), last->syntaxCopy(), statement->syntaxCopy()); return s; } Statement *CaseRangeStatement::semantic(Scope *sc) { SwitchStatement *sw = sc->sw; if (sw == NULL) { error("case range not in switch statement"); return NULL; } //printf("CaseRangeStatement::semantic() %s\n", toChars()); if (sw->isFinal) error("case ranges not allowed in final switch"); sc = sc->startCTFE(); first = first->semantic(sc); first = resolveProperties(sc, first); sc = sc->endCTFE(); first = first->implicitCastTo(sc, sw->condition->type); first = first->ctfeInterpret(); sc = sc->startCTFE(); last = last->semantic(sc); last = resolveProperties(sc, last); sc = sc->endCTFE(); last = last->implicitCastTo(sc, sw->condition->type); last = last->ctfeInterpret(); if (first->op == TOKerror || last->op == TOKerror) return statement ? statement->semantic(sc) : NULL; uinteger_t fval = first->toInteger(); uinteger_t lval = last->toInteger(); if ( (first->type->isunsigned() && fval > lval) || (!first->type->isunsigned() && (sinteger_t)fval > (sinteger_t)lval)) { error("first case %s is greater than last case %s", first->toChars(), last->toChars()); lval = fval; } if (lval - fval > 256) { error("had %llu cases which is more than 256 cases in case range", lval - fval); lval = fval + 256; } /* This works by replacing the CaseRange with an array of Case's. * * case a: .. case b: s; * => * case a: * [...] * case b: * s; */ Statements *statements = new Statements(); for (uinteger_t i = fval; i != lval + 1; i++) { Statement *s = statement; if (i != lval) // if not last case s = new ExpStatement(loc, (Expression *)NULL); Expression *e = new IntegerExp(loc, i, first->type); Statement *cs = new CaseStatement(loc, e, s); statements->push(cs); } Statement *s = new CompoundStatement(loc, statements); s = s->semantic(sc); return s; } /******************************** DefaultStatement ***************************/ DefaultStatement::DefaultStatement(Loc loc, Statement *s) : Statement(loc) { this->statement = s; #ifdef IN_GCC cblock = NULL; #elif IN_LLVM bodyBB = NULL; enclosingScopeExit = NULL; #endif } 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"); if (sc->sw->isFinal) error("default statement not allowed in final switch statement"); } else error("default not in switch statement"); statement = statement->semantic(sc); return this; } /******************************** GotoDefaultStatement ***************************/ GotoDefaultStatement::GotoDefaultStatement(Loc loc) : Statement(loc) { sw = NULL; } Statement *GotoDefaultStatement::syntaxCopy() { GotoDefaultStatement *s = new GotoDefaultStatement(loc); return s; } Statement *GotoDefaultStatement::semantic(Scope *sc) { sw = sc->sw; if (!sw) error("goto default not in switch statement"); return this; } /******************************** GotoCaseStatement ***************************/ GotoCaseStatement::GotoCaseStatement(Loc loc, Expression *exp) : Statement(loc) { cs = NULL; this->exp = exp; #if IN_LLVM sw = NULL; #endif } Statement *GotoCaseStatement::syntaxCopy() { Expression *e = exp ? exp->syntaxCopy() : NULL; GotoCaseStatement *s = new GotoCaseStatement(loc, e); return s; } Statement *GotoCaseStatement::semantic(Scope *sc) { if (exp) exp = exp->semantic(sc); if (!sc->sw) error("goto case not in switch statement"); else { #if IN_LLVM sw = sc->sw; #endif sc->sw->gotoCases.push(this); if (exp) { exp = exp->implicitCastTo(sc, sc->sw->condition->type); exp = exp->optimize(WANTvalue); } } return this; } /******************************** SwitchErrorStatement ***************************/ SwitchErrorStatement::SwitchErrorStatement(Loc loc) : Statement(loc) { } /******************************** ReturnStatement ***************************/ ReturnStatement::ReturnStatement(Loc loc, Expression *exp) : Statement(loc) { this->exp = exp; this->implicit0 = 0; } 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()); FuncDeclaration *fd = sc->parent->isFuncDeclaration(); Scope *scx = sc; Expression *eorg = NULL; if (fd->fes) fd = fd->fes->func; // fd is now function enclosing foreach TypeFunction *tf = (TypeFunction *)fd->type; assert(tf->ty == Tfunction); Type *tret = tf->next; 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->flags & SCOPEcontract) error("return statements cannot be in contracts"); if (sc->os && sc->os->tok != TOKon_scope_failure) error("return statements cannot be in %s bodies", Token::toChars(sc->os->tok)); if (sc->tf) error("return statements cannot be in finally bodies"); if (fd->isCtorDeclaration()) { // Constructors implicitly do: // return this; if (exp && exp->op != TOKthis) error("cannot return expression from constructor"); exp = new ThisExp(Loc()); exp->type = tret; } if (!exp) fd->nrvo_can = 0; if (exp) { fd->hasReturnExp |= 1; FuncLiteralDeclaration *fld = fd->isFuncLiteralDeclaration(); if (tret) exp = inferType(exp, tbret); else if (fld && fld->treq) exp = inferType(exp, fld->treq->nextOf()->nextOf()); exp = exp->semantic(sc); exp = resolveProperties(sc, exp); if (exp->type && exp->type->ty != Tvoid || exp->op == TOKfunction || exp->op == TOKtype || exp->op == TOKtemplate) { if (!exp->rvalue()) // don't make error for void expression exp = new ErrorExp(); } if (isNonAssignmentArrayOp(exp)) { exp->error("array operation %s without assignment not implemented", exp->toChars()); exp = new ErrorExp(); } if (exp->op == TOKcall) exp = valueNoDtor(exp); // deduce 'auto ref' if (tf->isref && (fd->storage_class & STCauto)) { /* Determine "refness" of function return: * if it's an lvalue, return by ref, else return by value */ if (exp->isLvalue()) { /* Return by ref * (but first ensure it doesn't fail the "check for * escaping reference" test) */ unsigned errors = global.startGagging(); exp->checkEscapeRef(); if (global.endGagging(errors)) tf->isref = false; // return by value } else tf->isref = false; // return by value fd->storage_class &= ~STCauto; } if (!tf->isref) exp = exp->optimize(WANTvalue); // handle NRVO if (fd->nrvo_can && exp->op == TOKvar) { VarExp *ve = (VarExp *)exp; VarDeclaration *v = ve->var->isVarDeclaration(); if (tf->isref) { // Function returns a reference fd->nrvo_can = 0; } else if (!v || v->isOut() || v->isRef()) 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; // infer return type if (fd->inferRetType) { Type *tfret = tf->nextOf(); if (tfret) { if (tfret != Type::terror) { if (!exp->type->equals(tfret)) { int m1 = exp->type->implicitConvTo(tfret); int m2 = tfret->implicitConvTo(exp->type); //printf("exp->type = %s m2<-->m1 tret %s\n", exp->type->toChars(), tfret->toChars()); //printf("m1 = %d, m2 = %d\n", m1, m2); if (m1 && m2) #if IN_LLVM exp = exp->implicitCastTo(sc, tret); #else ; #endif else if (!m1 && m2) tf->next = exp->type; else if (m1 && !m2) ; else if (exp->op != TOKerror) error("mismatched function return type inference of %s and %s", exp->type->toChars(), tfret->toChars()); } } /* The "refness" is determined by the first return statement, * not all of them. This means: * return 3; return x; // ok, x can be a value * return x; return 3; // error, 3 is not an lvalue */ } else tf->next = exp->type; if (!fd->tintro) { tret = tf->next; tbret = tret->toBasetype(); } } if (tbret->ty != Tvoid) { if (!exp->type->implicitConvTo(tret) && fd->parametersIntersect(exp->type)) { if (exp->type->immutableOf()->implicitConvTo(tret)) exp = exp->castTo(sc, exp->type->immutableOf()); else if (exp->type->wildOf()->implicitConvTo(tret)) exp = exp->castTo(sc, exp->type->wildOf()); } if (fd->tintro) exp = exp->implicitCastTo(sc, tf->next); // eorg isn't casted to tret (== fd->tintro->nextOf()) if (fd->returnLabel) eorg = exp->copy(); exp = exp->implicitCastTo(sc, tret); if (!tf->isref) exp = exp->optimize(WANTvalue); } } else if (fd->inferRetType) { if (tf->next) { if (tf->next->ty != Tvoid) error("mismatched function return type inference of void and %s", tf->next->toChars()); } tf->next = Type::tvoid; 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(Loc(), new IntegerExp(sc->fes->cases->dim + 1)); } else if (tf->next->toBasetype() == Type::tvoid) { s = new ReturnStatement(Loc(), NULL); sc->fes->cases->push(s); // Construct: { exp; return cases->dim + 1; } Statement *s1 = new ExpStatement(loc, exp); Statement *s2 = new ReturnStatement(Loc(), new IntegerExp(sc->fes->cases->dim + 1)); s = new CompoundStatement(loc, s1, s2); } else { // Construct: return vresult; if (!fd->vresult) { // Declare vresult Scope *sco = fd->scout ? fd->scout : scx; if (!fd->outId) fd->outId = Id::result; VarDeclaration *v = new VarDeclaration(loc, tret, fd->outId, NULL); if (fd->outId == Id::result) v->storage_class |= STCtemp; v->noscope = 1; v->storage_class |= STCresult; if (tf->isref) v->storage_class |= STCref | STCforeach; v->semantic(sco); if (!sco->insert(v)) assert(0); v->parent = fd; fd->vresult = v; } s = new ReturnStatement(Loc(), new VarExp(Loc(), fd->vresult)); sc->fes->cases->push(s); // Construct: { vresult = exp; return cases->dim + 1; } if (tf->isref) exp = new ConstructExp(loc, new VarExp(Loc(), fd->vresult), exp); else exp = new BlitExp(loc, new VarExp(Loc(), fd->vresult), exp); exp = exp->semantic(sc); Statement *s1 = new ExpStatement(loc, exp); Statement *s2 = new ReturnStatement(Loc(), new IntegerExp(sc->fes->cases->dim + 1)); s = new CompoundStatement(loc, s1, s2); } return s; } if (exp) { exp = checkGC(sc, exp); if (tf->isref && !fd->isCtorDeclaration()) { // Function returns a reference exp = exp->toLvalue(sc, exp); exp->checkEscapeRef(); } else { //exp->print(); exp->checkEscape(); } if (fd->returnLabel && tbret->ty != Tvoid) { fd->buildResultVar(); VarExp *v = new VarExp(Loc(), fd->vresult); assert(eorg); if (tf->isref) exp = new ConstructExp(loc, v, eorg); else exp = new BlitExp(loc, v, eorg); exp = exp->semantic(sc); } } // If any branches have called a ctor, but this branch hasn't, it's an error if (sc->callSuper & CSXany_ctor && !(sc->callSuper & (CSXthis_ctor | CSXsuper_ctor))) error("return without calling constructor"); sc->callSuper |= CSXreturn; if (sc->fieldinit) { AggregateDeclaration *ad = fd->isAggregateMember2(); assert(ad); size_t dim = sc->fieldinit_dim; for (size_t i = 0; i < dim; i++) { VarDeclaration *v = ad->fields[i]; bool mustInit = (v->storage_class & STCnodefaultctor || v->type->needsNested()); if (mustInit && !(sc->fieldinit[i] & CSXthis_ctor)) error("an earlier return statement skips field %s initialization", v->toChars()); sc->fieldinit[i] |= 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(Loc(), exp); return new CompoundStatement(loc, s, gs); } return gs; } if (exp && tbret->ty == Tvoid && !implicit0) { if (exp->type->ty != Tvoid) { error("cannot return non-void from void function"); } /* Replace: * return exp; * with: * cast(void)exp; return; */ Expression *ce = new CastExp(loc, exp, Type::tvoid); Statement *s = new ExpStatement(loc, ce); s = s->semantic(sc); exp = NULL; return new CompoundStatement(loc, s, this); } return this; } /******************************** BreakStatement ***************************/ BreakStatement::BreakStatement(Loc loc, Identifier *ident) : Statement(loc) { this->ident = ident; } Statement *BreakStatement::syntaxCopy() { BreakStatement *s = new BreakStatement(loc, ident); return s; } Statement *BreakStatement::semantic(Scope *sc) { //printf("BreakStatement::semantic()\n"); // If: // break Identifier; if (ident) { ident = fixupLabelName(sc, ident); FuncDeclaration *thisfunc = sc->func; for (Scope *scx = sc; scx; scx = scx->enclosing) { 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. */ sc->fes->cases->push(this); Statement *s = new ReturnStatement(Loc(), new IntegerExp(sc->fes->cases->dim + 1)); return s; } break; // can't break to it } LabelStatement *ls = scx->slabel; if (ls && ls->ident == ident) { Statement *s = ls->statement; if (!s || !s->hasBreak()) error("label '%s' has no break", ident->toChars()); else if (ls->tf != sc->tf) error("cannot break out of finally block"); else #if IN_LLVM { this->target = ls; #endif return this; #if IN_LLVM } #endif return new ErrorStatement(); } } error("enclosing label '%s' for break not found", ident->toChars()); return new ErrorStatement(); } else if (!sc->sbreak) { if (sc->os && sc->os->tok != TOKon_scope_failure) { error("break is not inside %s bodies", Token::toChars(sc->os->tok)); } else if (sc->fes) { // Replace break; with return 1; Statement *s = new ReturnStatement(Loc(), new IntegerExp(1)); return s; } else error("break is not inside a loop or switch"); return new ErrorStatement(); } return this; } /******************************** ContinueStatement ***************************/ ContinueStatement::ContinueStatement(Loc loc, Identifier *ident) : Statement(loc) { this->ident = ident; } Statement *ContinueStatement::syntaxCopy() { ContinueStatement *s = new ContinueStatement(loc, ident); return s; } Statement *ContinueStatement::semantic(Scope *sc) { //printf("ContinueStatement::semantic() %p\n", this); if (ident) { ident = fixupLabelName(sc, 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(Loc(), 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. */ sc->fes->cases->push(this); Statement *s = new ReturnStatement(Loc(), 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 || !s->hasContinue()) error("label '%s' has no continue", ident->toChars()); else if (ls->tf != sc->tf) error("cannot continue out of finally block"); else #if IN_LLVM { this->target = ls; #endif return this; #if IN_LLVM } #endif return new ErrorStatement(); } } error("enclosing label '%s' for continue not found", ident->toChars()); return new ErrorStatement(); } else if (!sc->scontinue) { if (sc->os && sc->os->tok != TOKon_scope_failure) { error("continue is not inside %s bodies", Token::toChars(sc->os->tok)); } else if (sc->fes) { // Replace continue; with return 0; Statement *s = new ReturnStatement(Loc(), new IntegerExp(0)); return s; } else error("continue is not inside a loop"); return new ErrorStatement(); } return this; } /******************************** SynchronizedStatement ***************************/ SynchronizedStatement::SynchronizedStatement(Loc loc, Expression *exp, Statement *body) : Statement(loc) { this->exp = exp; this->body = body; } 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) { exp = exp->semantic(sc); exp = resolveProperties(sc, exp); // exp = exp->optimize(0); //? exp = checkGC(sc, exp); if (exp->op == TOKerror) goto Lbody; ClassDeclaration *cd = exp->type->isClassHandle(); if (!cd) { error("can only synchronize on class objects, not '%s'", exp->type->toChars()); return new ErrorStatement(); } else if (cd->isInterfaceDeclaration()) { /* Cast the interface to an object, as the object has the monitor, * not the interface. */ if (!ClassDeclaration::object) { error("missing or corrupt object.d"); fatal(); } Type *t = ClassDeclaration::object->type; t = t->semantic(Loc(), sc)->toBasetype(); assert(t->ty == Tclass); exp = new CastExp(loc, exp, t); exp = exp->semantic(sc); } #if 1 /* Rewrite as: * auto tmp = exp; * _d_monitorenter(tmp); * try { body } finally { _d_monitorexit(tmp); } */ Identifier *id = Lexer::uniqueId("__sync"); ExpInitializer *ie = new ExpInitializer(loc, exp); VarDeclaration *tmp = new VarDeclaration(loc, exp->type, id, ie); tmp->storage_class |= STCtemp; Statements *cs = new Statements(); cs->push(new ExpStatement(loc, tmp)); Parameters* args = new Parameters; args->push(new Parameter(0, ClassDeclaration::object->type, NULL, NULL)); FuncDeclaration *fdenter = FuncDeclaration::genCfunc(args, Type::tvoid, Id::monitorenter); Expression *e = new CallExp(loc, new VarExp(loc, fdenter), new VarExp(loc, tmp)); e->type = Type::tvoid; // do not run semantic on e cs->push(new ExpStatement(loc, e)); FuncDeclaration *fdexit = FuncDeclaration::genCfunc(args, Type::tvoid, Id::monitorexit); e = new CallExp(loc, new VarExp(loc, fdexit), new VarExp(loc, tmp)); e->type = Type::tvoid; // do not run semantic on e Statement *s = new ExpStatement(loc, e); s = new TryFinallyStatement(loc, body, s); cs->push(s); s = new CompoundStatement(loc, cs); return s->semantic(sc); #endif } else { /* Generate our own critical section, then rewrite as: * __gshared byte[CriticalSection.sizeof] critsec; * _d_criticalenter(critsec.ptr); * try { body } finally { _d_criticalexit(critsec.ptr); } */ Identifier *id = Lexer::uniqueId("__critsec"); Type *t = new TypeSArray(Type::tint8, new IntegerExp(Target::ptrsize + Target::critsecsize())); VarDeclaration *tmp = new VarDeclaration(loc, t, id, NULL); tmp->storage_class |= STCtemp | STCgshared | STCstatic; Statements *cs = new Statements(); cs->push(new ExpStatement(loc, tmp)); /* This is just a dummy variable for "goto skips declaration" error. * Backend optimizer could remove this unused variable. */ VarDeclaration *v = new VarDeclaration(loc, Type::tvoidptr, Lexer::uniqueId("__sync"), NULL); v->semantic(sc); cs->push(new ExpStatement(loc, v)); Parameters* args = new Parameters; args->push(new Parameter(0, t->pointerTo(), NULL, NULL)); FuncDeclaration *fdenter = FuncDeclaration::genCfunc(args, Type::tvoid, Id::criticalenter); Expression *e = new DotIdExp(loc, new VarExp(loc, tmp), Id::ptr); e = e->semantic(sc); e = new CallExp(loc, new VarExp(loc, fdenter), e); e->type = Type::tvoid; // do not run semantic on e cs->push(new ExpStatement(loc, e)); FuncDeclaration *fdexit = FuncDeclaration::genCfunc(args, Type::tvoid, Id::criticalexit); e = new DotIdExp(loc, new VarExp(loc, tmp), Id::ptr); e = e->semantic(sc); e = new CallExp(loc, new VarExp(loc, fdexit), e); e->type = Type::tvoid; // do not run semantic on e Statement *s = new ExpStatement(loc, e); s = new TryFinallyStatement(loc, body, s); cs->push(s); s = new CompoundStatement(loc, cs); return s->semantic(sc); } Lbody: if (body) body = body->semantic(sc); if (body && body->isErrorStatement()) return body; return this; } bool SynchronizedStatement::hasBreak() { return false; //true; } bool SynchronizedStatement::hasContinue() { return false; //true; } /******************************** 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); // exp = exp_>optimize(0); //? exp = checkGC(sc, exp); if (exp->op == TOKerror) return new ErrorStatement(); if (exp->op == TOKimport) { sym = new WithScopeSymbol(this); sym->parent = sc->scopesym; } else if (exp->op == TOKtype) { Dsymbol *s = ((TypeExp *)exp)->type->toDsymbol(sc); if (!s || !s->isScopeDsymbol()) { error("with type %s has no members", exp->toChars()); return new ErrorStatement(); } sym = new WithScopeSymbol(this); sym->parent = sc->scopesym; } else { Type *t = exp->type->toBasetype(); Expression *olde = exp; if (t->ty == Tpointer) { exp = new PtrExp(loc, exp); exp = exp->semantic(sc); t = exp->type->toBasetype(); } 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) { if (!exp->isLvalue()) { init = new ExpInitializer(loc, exp); wthis = new VarDeclaration(loc, exp->type, Lexer::uniqueId("__withtmp"), init); wthis->storage_class |= STCtemp; exp = new CommaExp(loc, new DeclarationExp(loc, wthis), new VarExp(loc, wthis)); exp = exp->semantic(sc); } Expression *e = exp->addressOf(); init = new ExpInitializer(loc, e); wthis = new VarDeclaration(loc, e->type, Id::withSym, init); wthis->semantic(sc); sym = new WithScopeSymbol(this); // Need to set the scope to make use of resolveAliasThis sym->setScope(sc); sym->parent = sc->scopesym; } else { error("with expressions must be aggregate types or pointers to them, not '%s'", olde->type->toChars()); return new ErrorStatement(); } } if (body) { sym->scope = sc; sc = sc->push(sym); sc->insert(sym); body = body->semantic(sc); sc->pop(); if (body && body->isErrorStatement()) return body; } return this; } /******************************** 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]; c = c->syntaxCopy(); (*a)[i] = c; } TryCatchStatement *s = new TryCatchStatement(loc, body->syntaxCopy(), a); return s; } Statement *TryCatchStatement::semantic(Scope *sc) { body = body->semanticScope(sc, NULL, NULL); assert(body); /* Even if body is empty, still do semantic analysis on catches */ bool catchErrors = false; for (size_t i = 0; i < catches->dim; i++) { Catch *c = (*catches)[i]; c->semantic(sc); if (c->type->ty == Terror) { catchErrors = true; continue; } // Determine if current catch 'hides' any previous catches for (size_t j = 0; j < i; j++) { Catch *cj = (*catches)[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); catchErrors = true; } } } if (catchErrors) return new ErrorStatement(); if (body->isErrorStatement()) return body; /* If the try body never throws, we can eliminate any catches * of recoverable exceptions. */ if (!(body->blockExit(sc->func, false) & BEthrow) && ClassDeclaration::exception) { for (size_t i = 0; i < catches->dim; i++) { Catch *c = (*catches)[i]; /* If catch exception type is derived from Exception */ if (c->type->toBasetype()->implicitConvTo(ClassDeclaration::exception->type) && (!c->handler || !c->handler->comeFrom())) { // Remove c from the array of catches catches->remove(i); --i; } } } if (catches->dim == 0) return body->hasCode() ? body : NULL; return this; } 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; internalCatch = false; } Catch *Catch::syntaxCopy() { Catch *c = new Catch(loc, (type ? type->syntaxCopy() : NULL), ident, (handler ? handler->syntaxCopy() : NULL)); c->internalCatch = internalCatch; return c; } void Catch::semantic(Scope *sc) { //printf("Catch::semantic(%s)\n", ident->toChars()); #ifndef IN_GCC if (sc->os && sc->os->tok != TOKon_scope_failure) { // If enclosing is scope(success) or scope(exit), this will be placed in finally block. error(loc, "cannot put catch statement inside %s", Token::toChars(sc->os->tok)); } 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 ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc->scopesym; sc = sc->push(sym); if (!type) { // reference .object.Throwable TypeIdentifier *tid = new TypeIdentifier(Loc(), Id::empty); tid->addIdent(Id::object); tid->addIdent(Id::Throwable); type = tid; } type = type->semantic(loc, sc); ClassDeclaration *cd = type->toBasetype()->isClassHandle(); if (!cd || ((cd != ClassDeclaration::throwable) && !ClassDeclaration::throwable->isBaseOf(cd, NULL))) { if (type != Type::terror) { error(loc, "can only catch class objects derived from Throwable, not '%s'", type->toChars()); type = Type::terror; } } else if (sc->func && !sc->intypeof && !internalCatch && cd != ClassDeclaration::exception && !ClassDeclaration::exception->isBaseOf(cd, NULL) && sc->func->setUnsafe()) { error(loc, "can only catch class objects derived from Exception in @safe code, not '%s'", type->toChars()); type = Type::terror; } else if (ident) { var = new VarDeclaration(loc, type, ident, NULL); var->semantic(sc); sc->insert(var); } handler = handler->semantic(sc); sc->pop(); } /****************************** 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() { TryFinallyStatement *s = new TryFinallyStatement(loc, body->syntaxCopy(), finalbody->syntaxCopy()); return s; } Statement *TryFinallyStatement::semantic(Scope *sc) { //printf("TryFinallyStatement::semantic()\n"); body = body->semantic(sc); sc = sc->push(); sc->tf = this; sc->sbreak = NULL; sc->scontinue = NULL; // no break or continue out of finally block finalbody = finalbody->semanticNoScope(sc); sc->pop(); if (!body) return finalbody; if (!finalbody) return body; if (body->blockExit(sc->func, false) == BEfallthru) { Statement *s = new CompoundStatement(loc, body, finalbody); return s; } return this; } 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() { OnScopeStatement *s = new OnScopeStatement(loc, tok, statement->syntaxCopy()); return s; } Statement *OnScopeStatement::semantic(Scope *sc) { #ifndef IN_GCC if (tok != TOKon_scope_exit) { // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement, // so the generated catch block cannot be placed in finally block. // See also Catch::semantic. if (sc->os && sc->os->tok != TOKon_scope_failure) { // If enclosing is scope(success) or scope(exit), this will be placed in finally block. error("cannot put %s statement inside %s", Token::toChars(tok), Token::toChars(sc->os->tok)); return new ErrorStatement(); } if (sc->tf) { error("cannot put %s statement inside finally block", Token::toChars(tok)); return new ErrorStatement(); } } #endif sc = sc->push(); sc->tf = NULL; sc->os = this; if (tok != TOKon_scope_failure) { // Jump out from scope(failure) block is allowed. sc->sbreak = NULL; sc->scontinue = NULL; } statement = statement->semanticNoScope(sc); sc->pop(); if (!statement || statement->isErrorStatement()) return statement; return this; } Statement *OnScopeStatement::scopeCode(Scope *sc, 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; */ Identifier *id = Lexer::uniqueId("__os"); ExpInitializer *ie = new ExpInitializer(loc, new IntegerExp(Loc(), 0, Type::tbool)); VarDeclaration *v = new VarDeclaration(loc, Type::tbool, id, ie); v->storage_class |= STCtemp; *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); 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; } Statement *ThrowStatement::semantic(Scope *sc) { //printf("ThrowStatement::semantic()\n"); FuncDeclaration *fd = sc->parent->isFuncDeclaration(); fd->hasReturnExp |= 2; exp = exp->semantic(sc); exp = resolveProperties(sc, exp); exp = checkGC(sc, exp); if (exp->op == TOKerror) return new ErrorStatement(); ClassDeclaration *cd = exp->type->toBasetype()->isClassHandle(); if (!cd || ((cd != ClassDeclaration::throwable) && !ClassDeclaration::throwable->isBaseOf(cd, NULL))) { error("can only throw class objects derived from Throwable, not type %s", exp->type->toChars()); return new ErrorStatement(); } return this; } /******************************** DebugStatement **************************/ DebugStatement::DebugStatement(Loc loc, Statement *statement) : Statement(loc) { this->statement = statement; } Statement *DebugStatement::syntaxCopy() { DebugStatement *s = new DebugStatement(loc, statement ? statement->syntaxCopy() : NULL); return s; } Statement *DebugStatement::semantic(Scope *sc) { if (statement) { sc = sc->push(); sc->flags |= SCOPEdebug; statement = statement->semantic(sc); sc->pop(); } return statement; } 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; #if IN_LLVM this->enclosingScopeExit = NULL; #endif this->lastVar = NULL; this->fd = NULL; } Statement *GotoStatement::syntaxCopy() { GotoStatement *s = new GotoStatement(loc, ident); return s; } Statement *GotoStatement::semantic(Scope *sc) { FuncDeclaration *fd = sc->func; //printf("GotoStatement::semantic()\n"); ident = fixupLabelName(sc, ident); this->lastVar = sc->lastVar; this->fd = sc->func; tf = sc->tf; os = sc->os; 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 scope statement * so we can patch it later, and add it to a 'look at this later' * list. */ ScopeStatement *ss = new ScopeStatement(loc, this); sc->fes->gotos->push(ss); // 'look at this later' list return ss; } // Add to fwdref list to check later if (!label->statement) { if (!fd->gotos) fd->gotos = new GotoStatements(); fd->gotos->push(this); } else if (checkLabel()) return new ErrorStatement(); return this; } 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 !IN_LLVM if (label->statement->tf != tf) #else if (label->statement && label->statement->tf != tf) #endif { 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; #if IN_LLVM this->enclosingScopeExit = NULL; #endif this->gotoTarget = NULL; this->lastVar = NULL; this->lblock = NULL; this->fwdrefs = NULL; } Statement *LabelStatement::syntaxCopy() { LabelStatement *s = new LabelStatement(loc, ident, statement ? statement->syntaxCopy() : NULL); return s; } Statement *LabelStatement::semantic(Scope *sc) { FuncDeclaration *fd = sc->parent->isFuncDeclaration(); this->lastVar = sc->lastVar; //printf("LabelStatement::semantic()\n"); ident = fixupLabelName(sc, ident); LabelDsymbol *ls = fd->searchLabel(ident); if (ls->statement) { error("Label '%s' already defined", ls->toChars()); return new ErrorStatement(); } else ls->statement = this; tf = sc->tf; os = sc->os; sc = sc->push(); sc->scopesym = sc->enclosing->scopesym; sc->callSuper |= CSXlabel; if (sc->fieldinit) { size_t dim = sc->fieldinit_dim; for (size_t i = 0; i < dim; i++) sc->fieldinit[i] |= CSXlabel; } sc->slabel = this; if (statement) statement = statement->semantic(sc); sc->pop(); #if IN_LLVM // LDC put in labmap fd->labmap[ident->toChars()] = this; #endif return this; } 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; } #if !IN_LLVM /************************ AsmStatement ***************************************/ AsmStatement::AsmStatement(Loc loc, Token *tokens) : Statement(loc) { this->tokens = tokens; asmcode = NULL; asmalign = 0; refparam = false; naked = false; regs = 0; } Statement *AsmStatement::syntaxCopy() { return new AsmStatement(loc, tokens); } #endif /************************ 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); } Statement *ImportStatement::semantic(Scope *sc) { for (size_t i = 0; i < imports->dim; i++) { Import *s = (*imports)[i]->isImport(); assert(!s->aliasdecls.dim); for (size_t j = 0; j < s->names.dim; j++) { Identifier *name = s->names[j]; Identifier *alias = s->aliases[j]; if (!alias) alias = name; TypeIdentifier *tname = new TypeIdentifier(s->loc, name); AliasDeclaration *ad = new AliasDeclaration(s->loc, alias, tname); ad->import = s; s->aliasdecls.push(ad); } s->semantic(sc); s->semantic2(sc); sc->insert(s); for (size_t j = 0; j < s->aliasdecls.dim; j++) { sc->insert(s->aliasdecls[j]); } } return this; }