/* Compiler implementation of the D programming language * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved * written by Walter Bright * http://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * http://www.boost.org/LICENSE_1_0.txt */ #include "root/dsystem.h" #include "root/rmem.h" #include "root/checkedint.h" #include "errors.h" #include "statement.h" #include "attrib.h" #include "expression.h" #include "cond.h" #include "init.h" #include "staticassert.h" #include "module.h" #include "scope.h" #include "declaration.h" #include "aggregate.h" #include "id.h" #include "enum.h" #include "template.h" #include "import.h" #include "target.h" #include "visitor.h" StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration *f); bool checkReturnEscapeRef(Scope *sc, Expression *e, bool gag); bool checkThrowEscape(Scope *sc, Expression *e, bool gag); LabelStatement *checkLabeledLoop(Scope *sc, Statement *statement); Identifier *fixupLabelName(Scope *sc, Identifier *ident); FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL); VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e); Expression *checkAssignmentAsCondition(Expression *e); TypeIdentifier *getThrowable(); Expression *semantic(Expression *e, Scope *sc); Statement *semantic(Statement *s, Scope *sc); void semantic(Catch *c, Scope *sc); Statement *semanticNoScope(Statement *s, Scope *sc); Statement *semanticScope(Statement *s, Scope *sc, Statement *sbreak, Statement *scontinue); int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow); class StatementSemanticVisitor : public Visitor { public: Statement *result; Scope *sc; StatementSemanticVisitor(Scope *sc) { this->result = NULL; this->sc = sc; } private: void setError() { result = new ErrorStatement(); } public: void visit(Statement *s) { result = s; } void visit(ErrorStatement *s) { result = s; } void visit(PeelStatement *s) { /* "peel" off this wrapper, and don't run semantic() * on the result. */ result = s->s; } void visit(ExpStatement *s) { if (s->exp) { //printf("ExpStatement::semantic() %s\n", s->exp->toChars()); // Allow CommaExp in ExpStatement because return isn't used if (s->exp->op == TOKcomma) ((CommaExp *)s->exp)->allowCommaExp = true; s->exp = semantic(s->exp, sc); s->exp = resolveProperties(sc, s->exp); s->exp = s->exp->addDtorHook(sc); if (checkNonAssignmentArrayOp(s->exp)) s->exp = new ErrorExp(); if (FuncDeclaration *f = isFuncAddress(s->exp)) { if (f->checkForwardRef(s->exp->loc)) s->exp = new ErrorExp(); } if (discardValue(s->exp)) s->exp = new ErrorExp(); s->exp = s->exp->optimize(WANTvalue); s->exp = checkGC(sc, s->exp); if (s->exp->op == TOKerror) return setError(); } result = s; } void visit(CompileStatement *cs) { //printf("CompileStatement::semantic() %s\n", cs->exp->toChars()); Statements *a = cs->flatten(sc); if (!a) return; Statement *s = new CompoundStatement(cs->loc, a); result = semantic(s, sc); } void visit(CompoundStatement *cs) { //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc); for (size_t i = 0; i < cs->statements->dim; ) { Statement *s = (*cs->statements)[i]; if (s) { Statements *flt = s->flatten(sc); if (flt) { cs->statements->remove(i); cs->statements->insert(i, flt); continue; } s = semantic(s, sc); (*cs->statements)[i] = s; if (s) { Statement *sentry; Statement *sexception; Statement *sfinally; (*cs->statements)[i] = s->scopeCode(sc, &sentry, &sexception, &sfinally); if (sentry) { sentry = semantic(sentry, sc); cs->statements->insert(i, sentry); i++; } if (sexception) sexception = semantic(sexception, sc); if (sexception) { if (i + 1 == cs->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 < cs->statements->dim; j++) { a->push((*cs->statements)[j]); } Statement *body = new CompoundStatement(Loc(), a); body = new ScopeStatement(Loc(), body, Loc()); Identifier *id = Identifier::generateId("__o"); Statement *handler = new PeelStatement(sexception); if (blockExit(sexception, 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(), getThrowable(), id, handler); ctch->internalCatch = true; catches->push(ctch); s = new TryCatchStatement(Loc(), body, catches); if (sfinally) s = new TryFinallyStatement(Loc(), s, sfinally); s = semantic(s, sc); cs->statements->setDim(i + 1); cs->statements->push(s); break; } } else if (sfinally) { if (0 && i + 1 == cs->statements->dim) { cs->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 < cs->statements->dim; j++) { a->push((*cs->statements)[j]); } Statement *body = new CompoundStatement(Loc(), a); s = new TryFinallyStatement(Loc(), body, sfinally); s = semantic(s, sc); cs->statements->setDim(i + 1); cs->statements->push(s); break; } } } else { /* Remove NULL statements from the list. */ cs->statements->remove(i); continue; } } i++; } for (size_t i = 0; i < cs->statements->dim; ++i) { Lagain: Statement *s = (*cs->statements)[i]; if (!s) continue; Statement *se = s->isErrorStatement(); if (se) { result = se; return; } /* Bugzilla 11653: 'semantic' may return another CompoundStatement * (eg. CaseRangeStatement), so flatten it here. */ Statements *flt = s->flatten(sc); if (flt) { cs->statements->remove(i); cs->statements->insert(i, flt); if (cs->statements->dim <= i) break; goto Lagain; } } if (cs->statements->dim == 1) { result = (*cs->statements)[0]; return; } result = cs; } void visit(UnrolledLoopStatement *uls) { //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc); Scope *scd = sc->push(); scd->sbreak = uls; scd->scontinue = uls; Statement *serror = NULL; for (size_t i = 0; i < uls->statements->dim; i++) { Statement *s = (*uls->statements)[i]; if (s) { //printf("[%d]: %s\n", i, s->toChars()); s = semantic(s, scd); (*uls->statements)[i] = s; if (s && !serror) serror = s->isErrorStatement(); } } scd->pop(); result = serror ? serror : uls; } void visit(ScopeStatement *ss) { //printf("ScopeStatement::semantic(sc = %p)\n", sc); if (ss->statement) { ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc->scopesym; sym->endlinnum = ss->endloc.linnum; sc = sc->push(sym); Statements *a = ss->statement->flatten(sc); if (a) { ss->statement = new CompoundStatement(ss->loc, a); } ss->statement = semantic(ss->statement, sc); if (ss->statement) { if (ss->statement->isErrorStatement()) { sc->pop(); result = ss->statement; return; } Statement *sentry; Statement *sexception; Statement *sfinally; ss->statement = ss->statement->scopeCode(sc, &sentry, &sexception, &sfinally); assert(!sentry); assert(!sexception); if (sfinally) { //printf("adding sfinally\n"); sfinally = semantic(sfinally, sc); ss->statement = new CompoundStatement(ss->loc, ss->statement, sfinally); } } sc->pop(); } result = ss; } void visit(ForwardingStatement *ss) { assert(ss->sym); for (Scope *csc = sc; !ss->sym->forward; csc = csc->enclosing) { assert(csc); ss->sym->forward = csc->scopesym; } sc = sc->push(ss->sym); sc->sbreak = ss; sc->scontinue = ss; ss->statement = semantic(ss->statement, sc); sc = sc->pop(); result = ss->statement; } void visit(WhileStatement *ws) { /* Rewrite as a for(;condition;) loop */ Statement *s = new ForStatement(ws->loc, NULL, ws->condition, NULL, ws->_body, ws->endloc); s = semantic(s, sc); result = s; } void visit(DoStatement *ds) { sc->noctor++; if (ds->_body) ds->_body = semanticScope(ds->_body, sc, ds, ds); sc->noctor--; if (ds->condition->op == TOKdotid) ((DotIdExp *)ds->condition)->noderef = true; // check in syntax level ds->condition = checkAssignmentAsCondition(ds->condition); ds->condition = semantic(ds->condition, sc); ds->condition = resolveProperties(sc, ds->condition); if (checkNonAssignmentArrayOp(ds->condition)) ds->condition = new ErrorExp(); ds->condition = ds->condition->optimize(WANTvalue); ds->condition = checkGC(sc, ds->condition); ds->condition = ds->condition->toBoolean(sc); if (ds->condition->op == TOKerror) return setError(); if (ds->_body && ds->_body->isErrorStatement()) { result = ds->_body; return; } result = ds; } void visit(ForStatement *fs) { //printf("ForStatement::semantic %s\n", toChars()); if (fs->_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(fs->_init); fs->_init = NULL; ainit->push(fs); Statement *s = new CompoundStatement(fs->loc, ainit); s = new ScopeStatement(fs->loc, s, fs->endloc); s = semantic(s, sc); if (!s->isErrorStatement()) { if (LabelStatement *ls = checkLabeledLoop(sc, fs)) ls->gotoTarget = fs; fs->relatedLabeled = s; } result = s; return; } assert(fs->_init == NULL); ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc->scopesym; sym->endlinnum = fs->endloc.linnum; sc = sc->push(sym); sc->noctor++; if (fs->condition) { if (fs->condition->op == TOKdotid) ((DotIdExp *)fs->condition)->noderef = true; // check in syntax level fs->condition = checkAssignmentAsCondition(fs->condition); fs->condition = semantic(fs->condition, sc); fs->condition = resolveProperties(sc, fs->condition); if (checkNonAssignmentArrayOp(fs->condition)) fs->condition = new ErrorExp(); fs->condition = fs->condition->optimize(WANTvalue); fs->condition = checkGC(sc, fs->condition); fs->condition = fs->condition->toBoolean(sc); } if (fs->increment) { if (fs->increment->op == TOKcomma) ((CommaExp *)fs->increment)->allowCommaExp = true; fs->increment = semantic(fs->increment, sc); fs->increment = resolveProperties(sc, fs->increment); if (checkNonAssignmentArrayOp(fs->increment)) fs->increment = new ErrorExp(); fs->increment = fs->increment->optimize(WANTvalue); fs->increment = checkGC(sc, fs->increment); } sc->sbreak = fs; sc->scontinue = fs; if (fs->_body) fs->_body = semanticNoScope(fs->_body, sc); sc->noctor--; sc->pop(); if ((fs->condition && fs->condition->op == TOKerror) || (fs->increment && fs->increment->op == TOKerror) || (fs->_body && fs->_body->isErrorStatement())) return setError(); result = fs; } /*********************** * Declares a unrolled `foreach` loop variable or a `static foreach` variable. * * Params: * storageClass = The storage class of the variable. * type = The declared type of the variable. * ident = The name of the variable. * e = The initializer of the variable (i.e. the current element of the looped over aggregate). * t = The type of the initializer. * Returns: * `true` iff the declaration was successful. */ bool declareVariable(ForeachStatement *fs, Type *paramtype, TupleExp *te, bool needExpansion, bool isStatic, Statements *statements, Dsymbols *declarations, StorageClass storageClass, Type *type, Identifier *ident, Expression *e, Type *t) { Loc loc = fs->loc; if (storageClass & (STCout | STClazy) || (storageClass & STCref && !te)) { fs->error("no storage class for value %s", ident->toChars()); return false; } Declaration *var; if (e) { Type *tb = e->type->toBasetype(); Dsymbol *ds = NULL; if (!(storageClass & STCmanifest)) { if ((isStatic || tb->ty == Tfunction || tb->ty == Tsarray || storageClass & STCalias) && e->op == TOKvar) ds = ((VarExp *)e)->var; else if (e->op == TOKtemplate) ds = ((TemplateExp *)e)->td; else if (e->op == TOKscope) ds = ((ScopeExp *)e)->sds; else if (e->op == TOKfunction) { FuncExp *fe = (FuncExp *)e; ds = fe->td ? (Dsymbol *)fe->td : fe->fd; } } else if (storageClass & STCalias) { fs->error("foreach loop variable cannot be both enum and alias"); return false; } if (ds) { var = new AliasDeclaration(loc, ident, ds); if (storageClass & STCref) { fs->error("symbol %s cannot be ref", ds->toChars()); return false; } if (paramtype) { fs->error("cannot specify element type for symbol %s", ds->toChars()); return false; } } else if (e->op == TOKtype) { var = new AliasDeclaration(loc, ident, e->type); if (paramtype) { fs->error("cannot specify element type for type %s", e->type->toChars()); return false; } } else { e = resolveProperties(sc, e); Initializer *ie = new ExpInitializer(Loc(), e); VarDeclaration *v = new VarDeclaration(loc, type, ident, ie); if (storageClass & STCref) v->storage_class |= STCref | STCforeach; if (isStatic || storageClass & STCmanifest || e->isConst() || e->op == TOKstring || e->op == TOKstructliteral || e->op == TOKarrayliteral) { if (v->storage_class & STCref) { if (!isStatic || !needExpansion) { fs->error("constant value %s cannot be ref", ie->toChars()); } else { fs->error("constant value %s cannot be ref", ident->toChars()); } return false; } else v->storage_class |= STCmanifest; } var = v; } } else { var = new AliasDeclaration(loc, ident, t); if (paramtype) { fs->error("cannot specify element type for symbol %s", fs->toChars()); return false; } } if (isStatic) var->storage_class |= STClocal; if (statements) statements->push(new ExpStatement(loc, var)); else if (declarations) declarations->push(var); else assert(0); return true; } bool makeTupleForeachBody(ForeachStatement *fs, size_t k, Type *paramtype, TupleExp *te, TypeTuple *tuple, bool needExpansion, bool isStatic, bool isDecl, Statements *statements, Dsymbols *declarations, Dsymbols *dbody) { Loc loc = fs->loc; Expression *e = NULL; Type *t = NULL; if (te) e = (*te->exps)[k]; else t = Parameter::getNth(tuple->arguments, k)->type; Parameter *p = (*fs->parameters)[0]; Statements *stmts = (isDecl) ? NULL : new Statements(); Dsymbols *decls = (isDecl) ? new Dsymbols() : NULL; size_t dim = fs->parameters->dim; if (!needExpansion && dim == 2) { // Declare key if (p->storageClass & (STCout | STCref | STClazy)) { fs->error("no storage class for key %s", p->ident->toChars()); return false; } if (isStatic) { if (!p->type) { p->type = Type::tsize_t; } } p->type = p->type->semantic(loc, sc); if (!p->type->isintegral()) { fs->error("foreach: key cannot be of non-integral type `%s`", p->type->toChars()); return false; } unsigned length = te ? te->exps->dim : tuple->arguments->dim; IntRange dimrange = IntRange(SignExtendedNumber(length)).cast(Type::tsize_t); // https://issues.dlang.org/show_bug.cgi?id=12504 dimrange.imax = SignExtendedNumber(dimrange.imax.value-1); if (!IntRange::fromType(p->type).contains(dimrange)) { fs->error("index type `%s` cannot cover index range 0..%llu", p->type->toChars(), (ulonglong)length); return false; } Initializer *ie = new ExpInitializer(Loc(), new IntegerExp(k)); VarDeclaration *var = new VarDeclaration(loc, p->type, p->ident, ie); var->storage_class |= STCmanifest; if (isStatic) var->storage_class |= STClocal; if (!isDecl) stmts->push(new ExpStatement(loc, var)); else decls->push(var); p = (*fs->parameters)[1]; // value } if (!isStatic || !needExpansion) { // Declare value if (!declareVariable(fs, paramtype, te, needExpansion, isStatic, stmts, decls, p->storageClass, p->type, p->ident, e, t)) { return false; } } else { // expand tuples into multiple `static foreach` variables. assert(e && !t); Identifier *ident = Identifier::generateId("__value"); declareVariable(fs, paramtype, te, needExpansion, isStatic, stmts, decls, 0, e->type, ident, e, NULL); Identifier *field = Identifier::idPool("tuple"); Expression *access = new DotIdExp(loc, e, field); access = semantic(access, sc); if (!tuple) return false; //printf("%s\n", tuple->toChars()); for (size_t l = 0; l < dim; l++) { Parameter *cp = (*fs->parameters)[l]; Expression *init_ = new IndexExp(loc, access, new IntegerExp(loc, l, Type::tsize_t)); init_ = semantic(init_, sc); assert(init_->type); declareVariable(fs, paramtype, te, needExpansion, isStatic, stmts, decls, p->storageClass, init_->type, cp->ident, init_, NULL); } } Statement *fwdstmt = NULL; Dsymbol *fwddecl = NULL; if (!isDecl) { if (fs->_body) stmts->push(fs->_body->syntaxCopy()); fwdstmt = new CompoundStatement(loc, stmts); } else { decls->append(Dsymbol::arraySyntaxCopy(dbody)); } if (!isStatic) { fwdstmt = new ScopeStatement(loc, fwdstmt, fs->endloc); } else if (!isDecl) { fwdstmt = new ForwardingStatement(loc, fwdstmt); } else { fwddecl = new ForwardingAttribDeclaration(decls); } if (statements) statements->push(fwdstmt); else if (declarations) declarations->push(fwddecl); else assert(0); return true; } /******************* * Type check and unroll `foreach` over an expression tuple as well * as `static foreach` statements and `static foreach` * declarations. For `static foreach` statements and `static * foreach` declarations, the visitor interface is used (and the * result is written into the `result` field.) For `static * foreach` declarations, the resulting Dsymbols* are returned * directly. * * The unrolled body is wrapped into a * - UnrolledLoopStatement, for `foreach` over an expression tuple. * - ForwardingStatement, for `static foreach` statements. * - ForwardingAttribDeclaration, for `static foreach` declarations. * * `static foreach` variables are declared as `STClocal`, such * that they are inserted into the local symbol tables of the * forwarding constructs instead of forwarded. For `static * foreach` with multiple foreach loop variables whose aggregate * has been lowered into a sequence of tuples, this function * expands the tuples into multiple `STClocal` `static foreach` * variables. */ bool makeTupleForeach(ForeachStatement *fs, bool needExpansion, bool isStatic, bool isDecl, Statements *statements, Dsymbols *declarations, Dsymbols *dbody) { Loc loc = fs->loc; size_t dim = fs->parameters->dim; if (!needExpansion && (dim < 1 || dim > 2)) { fs->error("only one (value) or two (key,value) arguments for tuple foreach"); return false; } Type *paramtype = (*fs->parameters)[dim-1]->type; if (paramtype) { paramtype = paramtype->semantic(loc, sc); if (paramtype->ty == Terror) return false; } Type *tab = fs->aggr->type->toBasetype(); TypeTuple *tuple = (TypeTuple *)tab; //printf("aggr: op = %d, %s\n", fs->aggr->op, fs->aggr->toChars()); size_t n; TupleExp *te = NULL; if (fs->aggr->op == TOKtuple) // expression tuple { te = (TupleExp *)fs->aggr; n = te->exps->dim; } else if (fs->aggr->op == TOKtype) // type tuple { n = Parameter::dim(tuple->arguments); } else assert(0); for (size_t j = 0; j < n; j++) { size_t k = (fs->op == TOKforeach) ? j : n - 1 - j; if (!makeTupleForeachBody(fs, k, paramtype, te, tuple, needExpansion, isStatic, isDecl, statements, declarations, dbody)) return false; } return true; } Dsymbols *makeTupleForeachStaticDecl(ForeachStatement *fs, Dsymbols *dbody, bool needExpansion) { assert(sc); Dsymbols *declarations = new Dsymbols(); if (!makeTupleForeach(fs, needExpansion, true, true, NULL, declarations, dbody)) return NULL; return declarations; } void makeTupleForeachStatic(ForeachStatement *fs, bool needExpansion) { Loc loc = fs->loc; assert(sc); Statements *statements = new Statements(); if (!makeTupleForeach(fs, needExpansion, true, false, statements, NULL, NULL)) return setError(); result = new CompoundStatement(loc, statements); } void visit(ForeachStatement *fs) { //printf("ForeachStatement::semantic() %p\n", fs); ScopeDsymbol *sym; Statement *s = fs; Loc loc = fs->loc; size_t dim = fs->parameters->dim; TypeAArray *taa = NULL; Dsymbol *sapply = NULL; Type *tn = NULL; Type *tnv = NULL; fs->func = sc->func; if (fs->func->fes) fs->func = fs->func->fes->func; VarDeclaration *vinit = NULL; fs->aggr = semantic(fs->aggr, sc); fs->aggr = resolveProperties(sc, fs->aggr); fs->aggr = fs->aggr->optimize(WANTvalue); if (fs->aggr->op == TOKerror) return setError(); Expression *oaggr = fs->aggr; if (fs->aggr->type && fs->aggr->type->toBasetype()->ty == Tstruct && ((TypeStruct *)(fs->aggr->type->toBasetype()))->sym->dtor && fs->aggr->op != TOKtype && !fs->aggr->isLvalue()) { // Bugzilla 14653: Extend the life of rvalue aggregate till the end of foreach. vinit = copyToTemp(STCrvalue, "__aggr", fs->aggr); vinit->semantic(sc); fs->aggr = new VarExp(fs->aggr->loc, vinit); } if (!inferAggregate(fs, sc, sapply)) { const char *msg = ""; if (fs->aggr->type && isAggregate(fs->aggr->type)) { msg = ", define opApply(), range primitives, or use .tupleof"; } fs->error("invalid foreach aggregate %s%s", oaggr->toChars(), msg); return setError(); } Dsymbol* sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors /* Check for inference errors */ if (!inferApplyArgTypes(fs, 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, parameters->dim = %d\n", dim, fs->parameters->dim); if (foundMismatch && dim != foreachParamCount) { const char *plural = foreachParamCount > 1 ? "s" : ""; fs->error("cannot infer argument types, expected %d argument%s, not %d", foreachParamCount, plural, dim); } else fs->error("cannot uniquely infer foreach argument types"); return setError(); } Type *tab = fs->aggr->type->toBasetype(); if (tab->ty == Ttuple) // don't generate new scope for tuple loops { Statements *statements = new Statements(); if (!makeTupleForeach(fs, false, false, false, statements, NULL, NULL)) return setError(); result = new UnrolledLoopStatement(loc, statements); if (LabelStatement *ls = checkLabeledLoop(sc, fs)) ls->gotoTarget = result; if (fs->aggr->op == TOKtuple) { TupleExp *te = (TupleExp *)fs->aggr; if (te->e0) result = new CompoundStatement(loc, new ExpStatement(te->e0->loc, te->e0), result); } if (vinit) result = new CompoundStatement(loc, new ExpStatement(loc, vinit), result); result = semantic(result, sc); return; } sym = new ScopeDsymbol(); sym->parent = sc->scopesym; sym->endlinnum = fs->endloc.linnum; Scope *sc2 = sc->push(sym); sc2->noctor++; for (size_t i = 0; i < dim; i++) { Parameter *p = (*fs->parameters)[i]; if (p->storageClass & STCmanifest) { fs->error("cannot declare enum loop variables for non-unrolled foreach"); } if (p->storageClass & STCalias) { fs->error("cannot declare alias loop variables for non-unrolled foreach"); } } switch (tab->ty) { case Tarray: case Tsarray: { if (fs->checkForArgTypes()) { result = fs; return; } if (dim < 1 || dim > 2) { fs->error("only one or two arguments for array foreach"); goto Lerror2; } // Finish semantic on all foreach parameter types. for (size_t i = 0; i < dim; i++) { Parameter *p = (*fs->parameters)[i]; p->type = p->type->semantic(loc, sc2); p->type = p->type->addStorageClass(p->storageClass); } tn = tab->nextOf()->toBasetype(); if (dim == 2) { Type *tindex = (*fs->parameters)[0]->type; if (!tindex->isintegral()) { fs->error("foreach: key cannot be of non-integral type `%s`", tindex->toChars()); goto Lerror2; } /* What cases to deprecate implicit conversions for: * 1. foreach aggregate is a dynamic array * 2. foreach body is lowered to _aApply (see special case below). */ Type *tv = (*fs->parameters)[1]->type->toBasetype(); if ((tab->ty == Tarray || (tn->ty != tv->ty && (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar) && (tv->ty == Tchar || tv->ty == Twchar || tv->ty == Tdchar))) && !Type::tsize_t->implicitConvTo(tindex)) { fs->deprecation("foreach: loop index implicitly converted from `size_t` to `%s`", tindex->toChars()); } } /* Look for special case of parsing char types out of char type * array. */ if (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar) { int i = (dim == 1) ? 0 : 1; // index of value Parameter *p = (*fs->parameters)[i]; tnv = p->type->toBasetype(); if (tnv->ty != tn->ty && (tnv->ty == Tchar || tnv->ty == Twchar || tnv->ty == Tdchar)) { if (p->storageClass & STCref) { fs->error("foreach: value of UTF conversion cannot be ref"); goto Lerror2; } if (dim == 2) { p = (*fs->parameters)[0]; if (p->storageClass & STCref) { fs->error("foreach: key cannot be ref"); goto Lerror2; } } goto Lapply; } } for (size_t i = 0; i < dim; i++) { // Declare parameterss Parameter *p = (*fs->parameters)[i]; VarDeclaration *var; if (dim == 2 && i == 0) { var = new VarDeclaration(loc, p->type->mutableOf(), Identifier::generateId("__key"), NULL); var->storage_class |= STCtemp | STCforeach; if (var->storage_class & (STCref | STCout)) var->storage_class |= STCnodtor; fs->key = var; if (p->storageClass & STCref) { if (var->type->constConv(p->type) <= MATCHnomatch) { fs->error("key type mismatch, %s to ref %s", var->type->toChars(), p->type->toChars()); goto Lerror2; } } if (tab->ty == Tsarray) { TypeSArray *ta = (TypeSArray *)tab; IntRange dimrange = getIntRange(ta->dim); // https://issues.dlang.org/show_bug.cgi?id=12504 dimrange.imax = SignExtendedNumber(dimrange.imax.value-1); if (!IntRange::fromType(var->type).contains(dimrange)) { fs->error("index type '%s' cannot cover index range 0..%llu", p->type->toChars(), ta->dim->toInteger()); goto Lerror2; } fs->key->range = new IntRange(SignExtendedNumber(0), dimrange.imax); } } else { var = new VarDeclaration(loc, p->type, p->ident, NULL); var->storage_class |= STCforeach; var->storage_class |= p->storageClass & (STCin | STCout | STCref | STC_TYPECTOR); if (var->storage_class & (STCref | STCout)) var->storage_class |= STCnodtor; fs->value = var; if (var->storage_class & STCref) { if (fs->aggr->checkModifiable(sc2, 1) == 2) var->storage_class |= STCctorinit; Type *t = tab->nextOf(); if (t->constConv(p->type) <= MATCHnomatch) { fs->error("argument type mismatch, %s to ref %s", t->toChars(), p->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 = Identifier::generateId("__r"); ExpInitializer *ie = new ExpInitializer(loc, new SliceExp(loc, fs->aggr, NULL, NULL)); VarDeclaration *tmp; if (fs->aggr->op == TOKarrayliteral && !((*fs->parameters)[dim - 1]->storageClass & STCref)) { ArrayLiteralExp *ale = (ArrayLiteralExp *)fs->aggr; size_t edim = ale->elements ? ale->elements->dim : 0; Type *telem = (*fs->parameters)[dim - 1]->type; // Bugzilla 12936: if telem has been specified explicitly, // converting array literal elements to telem might make it @nogc. fs->aggr = fs->aggr->implicitCastTo(sc, telem->sarrayOf(edim)); if (fs->aggr->op == TOKerror) goto Lerror2; // for (T[edim] tmp = a, ...) tmp = new VarDeclaration(loc, fs->aggr->type, id, ie); } else tmp = new VarDeclaration(loc, tab->nextOf()->arrayOf(), id, ie); tmp->storage_class |= STCtemp; tmp->endlinnum = fs->endloc.linnum; Expression *tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id::length); if (!fs->key) { Identifier *idkey = Identifier::generateId("__key"); fs->key = new VarDeclaration(loc, Type::tsize_t, idkey, NULL); fs->key->storage_class |= STCtemp; } else if (fs->key->type->ty != Tsize_t) { tmp_length = new CastExp(loc, tmp_length, fs->key->type); } if (fs->op == TOKforeach_reverse) fs->key->_init = new ExpInitializer(loc, tmp_length); else fs->key->_init = new ExpInitializer(loc, new IntegerExp(loc, 0, fs->key->type)); Statements *cs = new Statements(); if (vinit) cs->push(new ExpStatement(loc, vinit)); cs->push(new ExpStatement(loc, tmp)); cs->push(new ExpStatement(loc, fs->key)); Statement *forinit = new CompoundDeclarationStatement(loc, cs); Expression *cond; if (fs->op == TOKforeach_reverse) { // key-- cond = new PostExp(TOKminusminus, loc, new VarExp(loc, fs->key)); } else { // key < tmp.length cond = new CmpExp(TOKlt, loc, new VarExp(loc, fs->key), tmp_length); } Expression *increment = NULL; if (fs->op == TOKforeach) { // key += 1 increment = new AddAssignExp(loc, new VarExp(loc, fs->key), new IntegerExp(loc, 1, fs->key->type)); } // T value = tmp[key]; fs->value->_init = new ExpInitializer(loc, new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, fs->key))); Statement *ds = new ExpStatement(loc, fs->value); if (dim == 2) { Parameter *p = (*fs->parameters)[0]; if ((p->storageClass & STCref) && p->type->equals(fs->key->type)) { fs->key->range = NULL; AliasDeclaration *v = new AliasDeclaration(loc, p->ident, fs->key); fs->_body = new CompoundStatement(loc, new ExpStatement(loc, v), fs->_body); } else { ExpInitializer *ei = new ExpInitializer(loc, new IdentifierExp(loc, fs->key->ident)); VarDeclaration *v = new VarDeclaration(loc, p->type, p->ident, ei); v->storage_class |= STCforeach | (p->storageClass & STCref); fs->_body = new CompoundStatement(loc, new ExpStatement(loc, v), fs->_body); if (fs->key->range && !p->type->isMutable()) { /* Limit the range of the key to the specified range */ v->range = new IntRange(fs->key->range->imin, fs->key->range->imax - SignExtendedNumber(1)); } } } fs->_body = new CompoundStatement(loc, ds, fs->_body); s = new ForStatement(loc, forinit, cond, increment, fs->_body, fs->endloc); if (LabelStatement *ls = checkLabeledLoop(sc, fs)) // Bugzilla 15450: don't use sc2 ls->gotoTarget = s; s = semantic(s, sc2); break; } case Taarray: if (fs->op == TOKforeach_reverse) fs->warning("cannot use foreach_reverse with an associative array"); if (fs->checkForArgTypes()) { result = fs; return; } taa = (TypeAArray *)tab; if (dim < 1 || dim > 2) { fs->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 (fs->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. */ VarDeclaration *r; Statement *init; if (vinit && fs->aggr->op == TOKvar && ((VarExp *)fs->aggr)->var == vinit) { r = vinit; init = new ExpStatement(loc, vinit); } else { r = copyToTemp(0, "__r", fs->aggr); r->semantic(sc); init = new ExpStatement(loc, r); if (vinit) init = new CompoundStatement(loc, new ExpStatement(loc, vinit), init); } // !__r.empty Expression *e = new VarExp(loc, r); e = new DotIdExp(loc, e, Id::Fempty); Expression *condition = new NotExp(loc, e); // __r.idpopFront() 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 *p = (*fs->parameters)[0]; VarDeclaration *ve = new VarDeclaration(loc, p->type, p->ident, new ExpInitializer(loc, einit)); ve->storage_class |= STCforeach; ve->storage_class |= p->storageClass & (STCin | STCout | STCref | STC_TYPECTOR); makeargs = new ExpStatement(loc, ve); } else { VarDeclaration *vd = copyToTemp(STCref, "__front", einit); vd->semantic(sc); makeargs = new ExpStatement(loc, vd); Type *tfront = NULL; if (FuncDeclaration *fd = sfront->isFuncDeclaration()) { if (!fd->functionSemantic()) goto Lrangeerr; tfront = fd->type; } else if (TemplateDeclaration *td = sfront->isTemplateDeclaration()) { Expressions a; if (FuncDeclaration *f = resolveFuncCall(loc, sc, td, NULL, tab, &a, 1)) tfront = f->type; } else if (Declaration *d = sfront->isDeclaration()) { tfront = d->type; } if (!tfront || tfront->ty == Terror) goto Lrangeerr; if (tfront->toBasetype()->ty == Tfunction) tfront = tfront->toBasetype()->nextOf(); if (tfront->ty == Tvoid) { fs->error("%s.front is void and has no value", oaggr->toChars()); goto Lerror2; } // Resolve inout qualifier of front type tfront = tfront->substWildTo(tab->mod); Expression *ve = new VarExp(loc, vd); ve->type = tfront; 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" : ""; fs->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 *p = (*fs->parameters)[i]; Expression *exp = (*exps)[i]; if (!p->type) p->type = exp->type; p->type = p->type->addStorageClass(p->storageClass)->semantic(loc, sc2); if (!exp->implicitConvTo(p->type)) goto Lrangeerr; VarDeclaration *var = new VarDeclaration(loc, p->type, p->ident, new ExpInitializer(loc, exp)); var->storage_class |= STCctfe | STCref | STCforeach; makeargs = new CompoundStatement(loc, makeargs, new ExpStatement(loc, var)); } } forbody = new CompoundStatement(loc, makeargs, fs->_body); s = new ForStatement(loc, init, condition, increment, forbody, fs->endloc); if (LabelStatement *ls = checkLabeledLoop(sc, fs)) ls->gotoTarget = s; s = semantic(s, sc2); break; Lrangeerr: fs->error("cannot infer argument types"); goto Lerror2; } case Tdelegate: if (fs->op == TOKforeach_reverse) fs->deprecation("cannot use foreach_reverse with a delegate"); Lapply: { if (fs->checkForArgTypes()) { fs->_body = semanticNoScope(fs->_body, sc2); result = fs; return; } TypeFunction *tfld = NULL; if (sapply) { FuncDeclaration *fdapply = sapply->isFuncDeclaration(); if (fdapply) { assert(fdapply->type && fdapply->type->ty == Tfunction); tfld = (TypeFunction *)fdapply->type->semantic(loc, sc2); 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, sc2); assert(t->ty == Tdelegate); tfld = (TypeFunction *)t->nextOf(); } } } } /* Turn body into the function literal: * int delegate(ref T param) { body } */ Parameters *params = new Parameters(); for (size_t i = 0; i < dim; i++) { Parameter *p = (*fs->parameters)[i]; StorageClass stc = STCref; Identifier *id; p->type = p->type->semantic(loc, sc2); p->type = p->type->addStorageClass(p->storageClass); 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 = p->ident; // argument copy is not need. if ((p->storageClass & STCref) != stc) { if (!stc) { fs->error("foreach: cannot make %s ref", p->ident->toChars()); goto Lerror2; } goto LcopyArg; } } else if (p->storageClass & STCref) { // default delegate parameters are marked as ref, then // argument copy is not need. id = p->ident; } else { // Make a copy of the ref argument so it isn't // a reference. LcopyArg: id = Identifier::generateId("__applyArg", (int)i); Initializer *ie = new ExpInitializer(Loc(), new IdentifierExp(Loc(), id)); VarDeclaration *v = new VarDeclaration(Loc(), p->type, p->ident, ie); v->storage_class |= STCtemp; s = new ExpStatement(Loc(), v); fs->_body = new CompoundStatement(loc, s, fs->_body); } params->push(new Parameter(stc, p->type, id, NULL)); } // Bugzilla 13840: Throwable nested function inside nothrow function is acceptable. StorageClass stc = mergeFuncAttrs(STCsafe | STCpure | STCnogc, fs->func); tfld = new TypeFunction(params, Type::tint32, 0, LINKd, stc); fs->cases = new Statements(); fs->gotos = new ScopeStatements(); FuncLiteralDeclaration *fld = new FuncLiteralDeclaration(loc, Loc(), tfld, TOKdelegate, fs); fld->fbody = fs->_body; Expression *flde = new FuncExp(loc, fld); flde = semantic(flde, sc2); fld->tookAddressOf = 0; // Resolve any forward referenced goto's for (size_t i = 0; i < fs->gotos->dim; i++) { GotoStatement *gs = (GotoStatement *)(*fs->gotos)[i]->statement; if (!gs->label->statement) { // 'Promote' it to this scope, and replace with a return fs->cases->push(gs); s = new ReturnStatement(Loc(), new IntegerExp(fs->cases->dim + 1)); (*fs->gotos)[i]->statement = s; } } Expression *e = NULL; Expression *ec; if (vinit) { e = new DeclarationExp(loc, vinit); e = semantic(e, sc2); if (e->op == TOKerror) goto Lerror2; } if (taa) { // Check types Parameter *p = (*fs->parameters)[0]; bool isRef = (p->storageClass & STCref) != 0; Type *ta = p->type; if (dim == 2) { Type *ti = (isRef ? taa->index->addMod(MODconst) : taa->index); if (isRef ? !ti->constConv(ta) : !ti->implicitConvTo(ta)) { fs->error("foreach: index must be type %s, not %s", ti->toChars(), ta->toChars()); goto Lerror2; } p = (*fs->parameters)[1]; isRef = (p->storageClass & STCref) != 0; ta = p->type; } Type *taav = taa->nextOf(); if (isRef ? !taav->constConv(ta) : !taav->implicitConvTo(ta)) { fs->error("foreach: value must be type %s, not %s", taav->toChars(), ta->toChars()); goto Lerror2; } /* Call: * extern(C) int _aaApply(void*, in size_t, int delegate(void*)) * _aaApply(aggr, keysize, flde) * * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*)) * _aaApply2(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 ? 1 : 0); if (!fdapply[i]) { params = new Parameters(); params->push(new Parameter(0, Type::tvoid->pointerTo(), NULL, NULL)); params->push(new Parameter(STCin, Type::tsize_t, NULL, NULL)); Parameters* dgparams = new Parameters; dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); if (dim == 2) dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); fldeTy[i] = new TypeDelegate(new TypeFunction(dgparams, Type::tint32, 0, LINKd)); params->push(new Parameter(0, fldeTy[i], NULL, NULL)); fdapply[i] = FuncDeclaration::genCfunc(params, Type::tint32, name[i]); } Expressions *exps = new Expressions(); exps->push(fs->aggr); d_uns64 keysize = taa->index->size(); if (keysize == SIZE_INVALID) goto Lerror2; assert(keysize < UINT64_MAX - Target::ptrsize); keysize = (keysize + (Target::ptrsize- 1)) & ~(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); ec = new VarExp(Loc(), fdapply[i], false); ec = new CallExp(loc, ec, exps); ec->type = Type::tint32; // don't run semantic() on ec } 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 int 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 = (fs->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; params = new Parameters(); params->push(new Parameter(STCin, tn->arrayOf(), NULL, NULL)); Parameters* dgparams = new Parameters; dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); if (dim == 2) dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); dgty = new TypeDelegate(new TypeFunction(dgparams, Type::tint32, 0, LINKd)); params->push(new Parameter(0, dgty, NULL, NULL)); fdapply = FuncDeclaration::genCfunc(params, Type::tint32, fdname); if (tab->ty == Tsarray) fs->aggr = fs->aggr->castTo(sc2, tn->arrayOf()); // paint delegate argument to the type runtime expects if (!dgty->equals(flde->type)) { flde = new CastExp(loc, flde, flde->type); flde->type = dgty; } ec = new VarExp(Loc(), fdapply, false); ec = new CallExp(loc, ec, fs->aggr, flde); ec->type = Type::tint32; // don't run semantic() on ec } else if (tab->ty == Tdelegate) { /* Call: * aggr(flde) */ if (fs->aggr->op == TOKdelegate && ((DelegateExp *)fs->aggr)->func->isNested()) { // See Bugzilla 3560 fs->aggr = ((DelegateExp *)fs->aggr)->e1; } ec = new CallExp(loc, fs->aggr, flde); ec = semantic(ec, sc2); if (ec->op == TOKerror) goto Lerror2; if (ec->type != Type::tint32) { fs->error("opApply() function for %s must return an int", tab->toChars()); goto Lerror2; } } else { if (global.params.vsafe) fld->tookAddressOf = 1; // allocate a closure unless the opApply() uses 'scope' assert(tab->ty == Tstruct || tab->ty == Tclass); assert(sapply); /* Call: * aggr.apply(flde) */ ec = new DotIdExp(loc, fs->aggr, sapply->ident); ec = new CallExp(loc, ec, flde); ec = semantic(ec, sc2); if (ec->op == TOKerror) goto Lerror2; if (ec->type != Type::tint32) { fs->error("opApply() function for %s must return an int", tab->toChars()); goto Lerror2; } } e = Expression::combine(e, ec); if (!fs->cases->dim) { // Easy case, a clean exit from the loop e = new CastExp(loc, e, Type::tvoid); // Bugzilla 13899 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 < fs->cases->dim; i++) { s = (*fs->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 = semantic(s, sc2); break; } case Terror: Lerror2: s = new ErrorStatement(); break; default: fs->error("foreach: %s is not an aggregate type", fs->aggr->type->toChars()); goto Lerror2; } sc2->noctor--; sc2->pop(); result = s; } void visit(ForeachRangeStatement *fs) { //printf("ForeachRangeStatement::semantic() %p\n", fs); Loc loc = fs->loc; fs->lwr = semantic(fs->lwr, sc); fs->lwr = resolveProperties(sc, fs->lwr); fs->lwr = fs->lwr->optimize(WANTvalue); if (!fs->lwr->type) { fs->error("invalid range lower bound %s", fs->lwr->toChars()); Lerror: return setError(); } fs->upr = semantic(fs->upr, sc); fs->upr = resolveProperties(sc, fs->upr); fs->upr = fs->upr->optimize(WANTvalue); if (!fs->upr->type) { fs->error("invalid range upper bound %s", fs->upr->toChars()); goto Lerror; } if (fs->prm->type) { fs->prm->type = fs->prm->type->semantic(loc, sc); fs->prm->type = fs->prm->type->addStorageClass(fs->prm->storageClass); fs->lwr = fs->lwr->implicitCastTo(sc, fs->prm->type); if (fs->upr->implicitConvTo(fs->prm->type) || (fs->prm->storageClass & STCref)) { fs->upr = fs->upr->implicitCastTo(sc, fs->prm->type); } else { // See if upr-1 fits in prm->type Expression *limit = new MinExp(loc, fs->upr, new IntegerExp(1)); limit = semantic(limit, sc); limit = limit->optimize(WANTvalue); if (!limit->implicitConvTo(fs->prm->type)) { fs->upr = fs->upr->implicitCastTo(sc, fs->prm->type); } } } else { /* Must infer types from lwr and upr */ Type *tlwr = fs->lwr->type->toBasetype(); if (tlwr->ty == Tstruct || tlwr->ty == Tclass) { /* Just picking the first really isn't good enough. */ fs->prm->type = fs->lwr->type; } else if (fs->lwr->type == fs->upr->type) { /* Same logic as CondExp ?lwr:upr */ fs->prm->type = fs->lwr->type; } else { AddExp ea(loc, fs->lwr, fs->upr); if (typeCombine(&ea, sc)) return setError(); fs->prm->type = ea.type; fs->lwr = ea.e1; fs->upr = ea.e2; } fs->prm->type = fs->prm->type->addStorageClass(fs->prm->storageClass); } if (fs->prm->type->ty == Terror || fs->lwr->op == TOKerror || fs->upr->op == TOKerror) { return setError(); } /* 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, (fs->op == TOKforeach) ? fs->lwr : fs->upr); fs->key = new VarDeclaration(loc, fs->upr->type->mutableOf(), Identifier::generateId("__key"), ie); fs->key->storage_class |= STCtemp; SignExtendedNumber lower = getIntRange(fs->lwr).imin; SignExtendedNumber upper = getIntRange(fs->upr).imax; if (lower <= upper) { fs->key->range = new IntRange(lower, upper); } Identifier *id = Identifier::generateId("__limit"); ie = new ExpInitializer(loc, (fs->op == TOKforeach) ? fs->upr : fs->lwr); VarDeclaration *tmp = new VarDeclaration(loc, fs->upr->type, id, ie); tmp->storage_class |= STCtemp; Statements *cs = new Statements(); // Keep order of evaluation as lwr, then upr if (fs->op == TOKforeach) { cs->push(new ExpStatement(loc, fs->key)); cs->push(new ExpStatement(loc, tmp)); } else { cs->push(new ExpStatement(loc, tmp)); cs->push(new ExpStatement(loc, fs->key)); } Statement *forinit = new CompoundDeclarationStatement(loc, cs); Expression *cond; if (fs->op == TOKforeach_reverse) { cond = new PostExp(TOKminusminus, loc, new VarExp(loc, fs->key)); if (fs->prm->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 (fs->prm->type->isscalar()) { // key < tmp cond = new CmpExp(TOKlt, loc, new VarExp(loc, fs->key), new VarExp(loc, tmp)); } else { // key != tmp cond = new EqualExp(TOKnotequal, loc, new VarExp(loc, fs->key), new VarExp(loc, tmp)); } } Expression *increment = NULL; if (fs->op == TOKforeach) { // key += 1 //increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(1)); increment = new PreExp(TOKpreplusplus, loc, new VarExp(loc, fs->key)); } if ((fs->prm->storageClass & STCref) && fs->prm->type->equals(fs->key->type)) { fs->key->range = NULL; AliasDeclaration *v = new AliasDeclaration(loc, fs->prm->ident, fs->key); fs->_body = new CompoundStatement(loc, new ExpStatement(loc, v), fs->_body); } else { ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs->key), fs->prm->type)); VarDeclaration *v = new VarDeclaration(loc, fs->prm->type, fs->prm->ident, ie); v->storage_class |= STCtemp | STCforeach | (fs->prm->storageClass & STCref); fs->_body = new CompoundStatement(loc, new ExpStatement(loc, v), fs->_body); if (fs->key->range && !fs->prm->type->isMutable()) { /* Limit the range of the key to the specified range */ v->range = new IntRange(fs->key->range->imin, fs->key->range->imax - SignExtendedNumber(1)); } } if (fs->prm->storageClass & STCref) { if (fs->key->type->constConv(fs->prm->type) <= MATCHnomatch) { fs->error("prmument type mismatch, %s to ref %s", fs->key->type->toChars(), fs->prm->type->toChars()); goto Lerror; } } ForStatement *s = new ForStatement(loc, forinit, cond, increment, fs->_body, fs->endloc); if (LabelStatement *ls = checkLabeledLoop(sc, fs)) ls->gotoTarget = s; result = semantic(s, sc); } void visit(IfStatement *ifs) { // Evaluate at runtime unsigned cs0 = sc->callSuper; unsigned cs1; unsigned *fi0 = sc->saveFieldInit(); unsigned *fi1 = NULL; // check in syntax level ifs->condition = checkAssignmentAsCondition(ifs->condition); ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc->scopesym; sym->endlinnum = ifs->endloc.linnum; Scope *scd = sc->push(sym); if (ifs->prm) { /* Declare prm, which we will set to be the * result of condition. */ ExpInitializer *ei = new ExpInitializer(ifs->loc, ifs->condition); ifs->match = new VarDeclaration(ifs->loc, ifs->prm->type, ifs->prm->ident, ei); ifs->match->parent = sc->func; ifs->match->storage_class |= ifs->prm->storageClass; ifs->match->semantic(scd); DeclarationExp *de = new DeclarationExp(ifs->loc, ifs->match); VarExp *ve = new VarExp(ifs->loc, ifs->match); ifs->condition = new CommaExp(ifs->loc, de, ve); ifs->condition = semantic(ifs->condition, scd); if (ifs->match->edtor) { Statement *sdtor = new DtorExpStatement(ifs->loc, ifs->match->edtor, ifs->match); sdtor = new OnScopeStatement(ifs->loc, TOKon_scope_exit, sdtor); ifs->ifbody = new CompoundStatement(ifs->loc, sdtor, ifs->ifbody); ifs->match->storage_class |= STCnodtor; } } else { if (ifs->condition->op == TOKdotid) ((DotIdExp *)ifs->condition)->noderef = true; ifs->condition = semantic(ifs->condition, sc); ifs->condition = resolveProperties(sc, ifs->condition); ifs->condition = ifs->condition->addDtorHook(sc); } if (checkNonAssignmentArrayOp(ifs->condition)) ifs->condition = new ErrorExp(); ifs->condition = checkGC(sc, ifs->condition); // Convert to boolean after declaring prm so this works: // if (S prm = S()) {} // where S is a struct that defines opCast!bool. ifs->condition = ifs->condition->toBoolean(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. ifs->condition = ifs->condition->optimize(WANTvalue); ifs->ifbody = semanticNoScope(ifs->ifbody, scd); scd->pop(); cs1 = sc->callSuper; fi1 = sc->fieldinit; sc->callSuper = cs0; sc->fieldinit = fi0; if (ifs->elsebody) ifs->elsebody = semanticScope(ifs->elsebody, sc, NULL, NULL); sc->mergeCallSuper(ifs->loc, cs1); sc->mergeFieldInit(ifs->loc, fi1); if (ifs->condition->op == TOKerror || (ifs->ifbody && ifs->ifbody->isErrorStatement()) || (ifs->elsebody && ifs->elsebody->isErrorStatement())) { return setError(); } result = ifs; } void visit(ConditionalStatement *cs) { //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 (cs->condition->include(sc, NULL)) { DebugCondition *dc = cs->condition->isDebugCondition(); if (dc) { sc = sc->push(); sc->flags |= SCOPEdebug; cs->ifbody = semantic(cs->ifbody, sc); sc->pop(); } else cs->ifbody = semantic(cs->ifbody, sc); result = cs->ifbody; } else { if (cs->elsebody) cs->elsebody = semantic(cs->elsebody, sc); result = cs->elsebody; } } void visit(PragmaStatement *ps) { // Should be merged with PragmaDeclaration //printf("PragmaStatement::semantic() %s\n", ps->toChars()); //printf("body = %p\n", ps->_body); if (ps->ident == Id::msg) { if (ps->args) { for (size_t i = 0; i < ps->args->dim; i++) { Expression *e = (*ps->args)[i]; sc = sc->startCTFE(); e = semantic(e, 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(ps->loc, "while evaluating pragma(msg, %s)", (*ps->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 (ps->ident == Id::lib) { /* Should this be allowed? */ ps->error("pragma(lib) not allowed as statement"); goto Lerror; } else if (ps->ident == Id::startaddress) { if (!ps->args || ps->args->dim != 1) ps->error("function name expected for start address"); else { Expression *e = (*ps->args)[0]; sc = sc->startCTFE(); e = semantic(e, sc); e = resolveProperties(sc, e); sc = sc->endCTFE(); e = e->ctfeInterpret(); (*ps->args)[0] = e; Dsymbol *sa = getDsymbol(e); if (!sa || !sa->isFuncDeclaration()) { ps->error("function name expected for start address, not '%s'", e->toChars()); goto Lerror; } if (ps->_body) { ps->_body = semantic(ps->_body, sc); if (ps->_body->isErrorStatement()) { result = ps->_body; return; } } result = ps; return; } } else if (ps->ident == Id::Pinline) { PINLINE inlining = PINLINEdefault; if (!ps->args || ps->args->dim == 0) inlining = PINLINEdefault; else if (!ps->args || ps->args->dim != 1) { ps->error("boolean expression expected for pragma(inline)"); goto Lerror; } else { Expression *e = (*ps->args)[0]; if (e->op != TOKint64 || !e->type->equals(Type::tbool)) { ps->error("pragma(inline, true or false) expected, not %s", e->toChars()); goto Lerror; } if (e->isBool(true)) inlining = PINLINEalways; else if (e->isBool(false)) inlining = PINLINEnever; FuncDeclaration *fd = sc->func; if (!fd) { ps->error("pragma(inline) is not inside a function"); goto Lerror; } fd->inlining = inlining; } } else { ps->error("unrecognized pragma(%s)", ps->ident->toChars()); goto Lerror; } if (ps->_body) { if (ps->ident == Id::msg || ps->ident == Id::startaddress) { ps->error("`pragma(%s)` is missing a terminating `;`", ps->ident->toChars()); return setError(); } ps->_body = semantic(ps->_body, sc); } result = ps->_body; return; Lerror: return setError(); } void visit(StaticAssertStatement *s) { s->sa->semantic2(sc); } void visit(SwitchStatement *ss) { //printf("SwitchStatement::semantic(%p)\n", ss); ss->tf = sc->tf; if (ss->cases) { result = ss; // already run return; } bool conditionError = false; ss->condition = semantic(ss->condition, sc); ss->condition = resolveProperties(sc, ss->condition); Type *att = NULL; TypeEnum *te = NULL; while (ss->condition->op != TOKerror) { // preserve enum type for final switches if (ss->condition->type->ty == Tenum) te = (TypeEnum *)ss->condition->type; if (ss->condition->type->isString()) { // If it's not an array, cast it to one if (ss->condition->type->ty != Tarray) { ss->condition = ss->condition->implicitCastTo(sc, ss->condition->type->nextOf()->arrayOf()); } ss->condition->type = ss->condition->type->constOf(); break; } ss->condition = integralPromotions(ss->condition, sc); if (ss->condition->op != TOKerror && ss->condition->type->isintegral()) break; AggregateDeclaration *ad = isAggregate(ss->condition->type); if (ad && ad->aliasthis && ss->condition->type != att) { if (!att && ss->condition->type->checkAliasThisRec()) att = ss->condition->type; if (Expression *e = resolveAliasThis(sc, ss->condition, true)) { ss->condition = e; continue; } } if (ss->condition->op != TOKerror) { ss->error("'%s' must be of integral or string type, it is a %s", ss->condition->toChars(), ss->condition->type->toChars()); conditionError = true; break; } } if (checkNonAssignmentArrayOp(ss->condition)) ss->condition = new ErrorExp(); ss->condition = ss->condition->optimize(WANTvalue); ss->condition = checkGC(sc, ss->condition); if (ss->condition->op == TOKerror) conditionError = true; bool needswitcherror = false; ss->lastVar = sc->lastVar; sc = sc->push(); sc->sbreak = ss; sc->sw = ss; ss->cases = new CaseStatements(); sc->noctor++; // BUG: should use Scope::mergeCallSuper() for each case instead ss->_body = semantic(ss->_body, sc); sc->noctor--; if (conditionError || (ss->_body && ss->_body->isErrorStatement())) goto Lerror; // Resolve any goto case's with exp for (size_t i = 0; i < ss->gotoCases.dim; i++) { GotoCaseStatement *gcs = ss->gotoCases[i]; if (!gcs->exp) { gcs->error("no case statement following goto case;"); goto Lerror; } 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()); goto Lerror; Lfoundcase: ; } if (ss->isFinal) { Type *t = ss->condition->type; 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 < ss->cases->dim; j++) { CaseStatement *cs = (*ss->cases)[j]; if (cs->exp->equals(em->value()) || (!cs->exp->type->isString() && !em->value()->type->isString() && cs->exp->toInteger() == em->value()->toInteger())) goto L1; } ss->error("enum member %s not represented in final switch", em->toChars()); goto Lerror; } L1: ; } } else needswitcherror = true; } if (!sc->sw->sdefault && (!ss->isFinal || needswitcherror || global.params.useAssert)) { ss->hasNoDefault = 1; if (!ss->isFinal && (!ss->_body || !ss->_body->isErrorStatement())) ss->error("switch statement without a default; 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 && global.params.checkAction != CHECKACTION_halt) { if (global.params.checkAction == CHECKACTION_C) { /* Rewrite as an assert(0) and let e2ir generate * the call to the C assert failure function */ s = new ExpStatement(ss->loc, new AssertExp(ss->loc, new IntegerExp(ss->loc, 0, Type::tint32))); } else s = new SwitchErrorStatement(ss->loc); } else s = new ExpStatement(ss->loc, new HaltExp(ss->loc)); a->reserve(2); sc->sw->sdefault = new DefaultStatement(ss->loc, s); a->push(ss->_body); if (blockExit(ss->_body, sc->func, false) & BEfallthru) a->push(new BreakStatement(Loc(), NULL)); a->push(sc->sw->sdefault); cs = new CompoundStatement(ss->loc, a); ss->_body = cs; } if (ss->checkLabel()) goto Lerror; sc->pop(); result = ss; return; Lerror: sc->pop(); result = new ErrorStatement(); } void visit(CaseStatement *cs) { SwitchStatement *sw = sc->sw; bool errors = false; //printf("CaseStatement::semantic() %s\n", cs->toChars()); sc = sc->startCTFE(); cs->exp = semantic(cs->exp, sc); cs->exp = resolveProperties(sc, cs->exp); sc = sc->endCTFE(); if (sw) { cs->exp = cs->exp->implicitCastTo(sc, sw->condition->type); cs->exp = cs->exp->optimize(WANTvalue | WANTexpand); Expression *e = cs->exp; // Remove all the casts the user and/or implicitCastTo may introduce // otherwise we'd sometimes fail the check below. while (e->op == TOKcast) e = ((CastExp *)e)->e1; /* This is where variables are allowed as case expressions. */ if (e->op == TOKvar) { VarExp *ve = (VarExp *)e; VarDeclaration *v = ve->var->isVarDeclaration(); Type *t = cs->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; /* TODO check if v can be uninitialized at that point. */ if (!v->isConst() && !v->isImmutable()) { cs->deprecation("case variables have to be const or immutable"); } if (sw->isFinal) { cs->error("case variables not allowed in final switch statements"); errors = true; } /* Also check if the VarExp is declared in a scope outside of this one. * 'scx' is set to the scope of the switch statement. */ for (Scope *scx = sc; scx; scx = scx->enclosing) { if (scx->enclosing && scx->enclosing->sw == sw) continue; assert(scx->sw == sw); if (!scx->search(cs->exp->loc, v->ident, NULL)) { cs->error("case variable `%s` declared at %s cannot be declared in switch body", v->toChars(), v->loc.toChars()); errors = true; } break; } goto L1; } } else cs->exp = cs->exp->ctfeInterpret(); if (StringExp *se = cs->exp->toStringExp()) cs->exp = se; else if (cs->exp->op != TOKint64 && cs->exp->op != TOKerror) { cs->error("case must be a string or an integral constant, not %s", cs->exp->toChars()); errors = true; } L1: for (size_t i = 0; i < sw->cases->dim; i++) { CaseStatement *cs2 = (*sw->cases)[i]; //printf("comparing '%s' with '%s'\n", cs->exp->toChars(), cs2->exp->toChars()); if (cs2->exp->equals(cs->exp)) { cs->error("duplicate case %s in switch statement", cs->exp->toChars()); errors = true; break; } } sw->cases->push(cs); // Resolve any goto case's with no exp to this case statement for (size_t i = 0; i < sw->gotoCases.dim; ) { GotoCaseStatement *gcs = sw->gotoCases[i]; if (!gcs->exp) { gcs->cs = cs; sw->gotoCases.remove(i); // remove from array continue; } i++; } if (sc->sw->tf != sc->tf) { cs->error("switch and case are in different finally blocks"); errors = true; } } else { cs->error("case not in switch statement"); errors = true; } cs->statement = semantic(cs->statement, sc); if (cs->statement->isErrorStatement()) { result = cs->statement; return; } if (errors || cs->exp->op == TOKerror) return setError(); cs->lastVar = sc->lastVar; result = cs; } void visit(CaseRangeStatement *crs) { SwitchStatement *sw = sc->sw; if (sw == NULL) { crs->error("case range not in switch statement"); return setError(); } //printf("CaseRangeStatement::semantic() %s\n", toChars()); bool errors = false; if (sw->isFinal) { crs->error("case ranges not allowed in final switch"); errors = true; } sc = sc->startCTFE(); crs->first = semantic(crs->first, sc); crs->first = resolveProperties(sc, crs->first); sc = sc->endCTFE(); crs->first = crs->first->implicitCastTo(sc, sw->condition->type); crs->first = crs->first->ctfeInterpret(); sc = sc->startCTFE(); crs->last = semantic(crs->last, sc); crs->last = resolveProperties(sc, crs->last); sc = sc->endCTFE(); crs->last = crs->last->implicitCastTo(sc, sw->condition->type); crs->last = crs->last->ctfeInterpret(); if (crs->first->op == TOKerror || crs->last->op == TOKerror || errors) { if (crs->statement) semantic(crs->statement, sc); return setError(); } uinteger_t fval = crs->first->toInteger(); uinteger_t lval = crs->last->toInteger(); if ( (crs->first->type->isunsigned() && fval > lval) || (!crs->first->type->isunsigned() && (sinteger_t)fval > (sinteger_t)lval)) { crs->error("first case %s is greater than last case %s", crs->first->toChars(), crs->last->toChars()); errors = true; lval = fval; } if (lval - fval > 256) { crs->error("had %llu cases which is more than 256 cases in case range", lval - fval); errors = true; lval = fval + 256; } if (errors) return setError(); /* 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 = crs->statement; if (i != lval) // if not last case s = new ExpStatement(crs->loc, (Expression *)NULL); Expression *e = new IntegerExp(crs->loc, i, crs->first->type); Statement *cs = new CaseStatement(crs->loc, e, s); statements->push(cs); } Statement *s = new CompoundStatement(crs->loc, statements); s = semantic(s, sc); result = s; } void visit(DefaultStatement *ds) { //printf("DefaultStatement::semantic()\n"); bool errors = false; if (sc->sw) { if (sc->sw->sdefault) { ds->error("switch statement already has a default"); errors = true; } sc->sw->sdefault = ds; if (sc->sw->tf != sc->tf) { ds->error("switch and default are in different finally blocks"); errors = true; } if (sc->sw->isFinal) { ds->error("default statement not allowed in final switch statement"); errors = true; } } else { ds->error("default not in switch statement"); errors = true; } ds->statement = semantic(ds->statement, sc); if (errors || ds->statement->isErrorStatement()) return setError(); ds->lastVar = sc->lastVar; result = ds; } void visit(GotoDefaultStatement *gds) { gds->sw = sc->sw; if (!gds->sw) { gds->error("goto default not in switch statement"); return setError(); } if (gds->sw->isFinal) { gds->error("goto default not allowed in final switch statement"); return setError(); } result = gds; } void visit(GotoCaseStatement *gcs) { if (!sc->sw) { gcs->error("goto case not in switch statement"); return setError(); } if (gcs->exp) { gcs->exp = semantic(gcs->exp, sc); gcs->exp = gcs->exp->implicitCastTo(sc, sc->sw->condition->type); gcs->exp = gcs->exp->optimize(WANTvalue); if (gcs->exp->op == TOKerror) return setError(); } sc->sw->gotoCases.push(gcs); result = gcs; } void visit(ReturnStatement *rs) { //printf("ReturnStatement::semantic() %s\n", toChars()); FuncDeclaration *fd = sc->parent->isFuncDeclaration(); if (fd->fes) fd = fd->fes->func; // fd is now function enclosing foreach TypeFunction *tf = (TypeFunction *)fd->type; assert(tf->ty == Tfunction); if (rs->exp && rs->exp->op == TOKvar && ((VarExp *)rs->exp)->var == fd->vresult) { // return vresult; if (sc->fes) { assert(rs->caseDim == 0); sc->fes->cases->push(rs); result = new ReturnStatement(Loc(), new IntegerExp(sc->fes->cases->dim + 1)); return; } if (fd->returnLabel) { GotoStatement *gs = new GotoStatement(rs->loc, Id::returnLabel); gs->label = fd->returnLabel; result = gs; return; } if (!fd->returns) fd->returns = new ReturnStatements(); fd->returns->push(rs); result = rs; return; } Type *tret = tf->next; Type *tbret = tret ? tret->toBasetype() : NULL; bool inferRef = (tf->isref && (fd->storage_class & STCauto)); Expression *e0 = NULL; bool errors = false; if (sc->flags & SCOPEcontract) { rs->error("return statements cannot be in contracts"); errors = true; } if (sc->os && sc->os->tok != TOKon_scope_failure) { rs->error("return statements cannot be in %s bodies", Token::toChars(sc->os->tok)); errors = true; } if (sc->tf) { rs->error("return statements cannot be in finally bodies"); errors = true; } if (fd->isCtorDeclaration()) { if (rs->exp) { rs->error("cannot return expression from constructor"); errors = true; } // Constructors implicitly do: // return this; rs->exp = new ThisExp(Loc()); rs->exp->type = tret; } else if (rs->exp) { fd->hasReturnExp |= (fd->hasReturnExp & 1 ? 16 : 1); FuncLiteralDeclaration *fld = fd->isFuncLiteralDeclaration(); if (tret) rs->exp = inferType(rs->exp, tret); else if (fld && fld->treq) rs->exp = inferType(rs->exp, fld->treq->nextOf()->nextOf()); rs->exp = semantic(rs->exp, sc); // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684 if (rs->exp->op == TOKtype) rs->exp = resolveAliasThis(sc, rs->exp); rs->exp = resolveProperties(sc, rs->exp); if (rs->exp->checkType()) rs->exp = new ErrorExp(); if (FuncDeclaration *f = isFuncAddress(rs->exp)) { if (fd->inferRetType && f->checkForwardRef(rs->exp->loc)) rs->exp = new ErrorExp(); } if (checkNonAssignmentArrayOp(rs->exp)) rs->exp = new ErrorExp(); // Extract side-effect part rs->exp = Expression::extractLast(rs->exp, &e0); if (rs->exp->op == TOKcall) rs->exp = valueNoDtor(rs->exp); if (e0) e0 = e0->optimize(WANTvalue); /* Void-return function can have void typed expression * on return statement. */ if ((tbret && tbret->ty == Tvoid) || rs->exp->type->ty == Tvoid) { if (rs->exp->type->ty != Tvoid) { rs->error("cannot return non-void from void function"); errors = true; rs->exp = new CastExp(rs->loc, rs->exp, Type::tvoid); rs->exp = semantic(rs->exp, sc); } /* Replace: * return exp; * with: * exp; return; */ e0 = Expression::combine(e0, rs->exp); rs->exp = NULL; } if (e0) e0 = checkGC(sc, e0); } if (rs->exp) { if (fd->inferRetType) // infer return type { if (!tret) { tf->next = rs->exp->type; } else if (tret->ty != Terror && !rs->exp->type->equals(tret)) { int m1 = rs->exp->type->implicitConvTo(tret); int m2 = tret->implicitConvTo(rs->exp->type); //printf("exp->type = %s m2<-->m1 tret %s\n", rs->exp->type->toChars(), tret->toChars()); //printf("m1 = %d, m2 = %d\n", m1, m2); if (m1 && m2) ; else if (!m1 && m2) tf->next = rs->exp->type; else if (m1 && !m2) ; else if (rs->exp->op != TOKerror) { rs->error("mismatched function return type inference of %s and %s", rs->exp->type->toChars(), tret->toChars()); errors = true; tf->next = Type::terror; } } tret = tf->next; tbret = tret->toBasetype(); } if (inferRef) // deduce 'auto ref' { /* Determine "refness" of function return: * if it's an lvalue, return by ref, else return by value */ if (rs->exp->isLvalue()) { /* May return by ref */ if (checkReturnEscapeRef(sc, rs->exp, true)) tf->isref = false; // return by value } else tf->isref = false; // return by value /* The "refness" is determined by all of return statements. * This means: * return 3; return x; // ok, x can be a value * return x; return 3; // ok, x can be a value */ } // handle NRVO if (fd->nrvo_can && rs->exp->op == TOKvar) { VarExp *ve = (VarExp *)rs->exp; VarDeclaration *v = ve->var->isVarDeclaration(); if (tf->isref) { // Function returns a reference if (!inferRef) 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 //if (!exp->isLvalue()) // keep NRVO-ability fd->nrvo_can = 0; } else { // handle NRVO fd->nrvo_can = 0; // infer return type if (fd->inferRetType) { if (tf->next && tf->next->ty != Tvoid) { if (tf->next->ty != Terror) { rs->error("mismatched function return type inference of void and %s", tf->next->toChars()); } errors = true; tf->next = Type::terror; } else tf->next = Type::tvoid; tret = tf->next; tbret = tret->toBasetype(); } if (inferRef) // deduce 'auto ref' tf->isref = false; if (tbret->ty != Tvoid) // if non-void return { if (tbret->ty != Terror) rs->error("return expression expected"); errors = true; } else if (fd->isMain()) { // main() returns 0, even if it returns void rs->exp = new IntegerExp(0); } } // 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))) { rs->error("return without calling constructor"); errors = true; } sc->callSuper |= CSXreturn; if (sc->fieldinit) { AggregateDeclaration *ad = fd->isMember2(); 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)) { rs->error("an earlier return statement skips field %s initialization", v->toChars()); errors = true; } sc->fieldinit[i] |= CSXreturn; } } if (errors) return setError(); if (sc->fes) { if (!rs->exp) { // Send out "case receiver" statement to the foreach. // return exp; Statement *s = new ReturnStatement(Loc(), rs->exp); sc->fes->cases->push(s); // Immediately rewrite "this" return statement as: // return cases->dim+1; rs->exp = new IntegerExp(sc->fes->cases->dim + 1); if (e0) { result = new CompoundStatement(rs->loc, new ExpStatement(rs->loc, e0), rs); return; } result = rs; return; } else { fd->buildResultVar(NULL, rs->exp->type); bool r = fd->vresult->checkNestedReference(sc, Loc()); assert(!r); // vresult should be always accessible // Send out "case receiver" statement to the foreach. // return vresult; Statement *s = new ReturnStatement(Loc(), new VarExp(Loc(), fd->vresult)); sc->fes->cases->push(s); // Save receiver index for the later rewriting from: // return exp; // to: // vresult = exp; retrun caseDim; rs->caseDim = sc->fes->cases->dim + 1; } } if (rs->exp) { if (!fd->returns) fd->returns = new ReturnStatements(); fd->returns->push(rs); } if (e0) { result = new CompoundStatement(rs->loc, new ExpStatement(rs->loc, e0), rs); return; } result = rs; } void visit(BreakStatement *bs) { //printf("BreakStatement::semantic()\n"); // If: // break Identifier; if (bs->ident) { bs->ident = fixupLabelName(sc, bs->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(bs); result = new ReturnStatement(Loc(), new IntegerExp(sc->fes->cases->dim + 1)); return; } break; // can't break to it } LabelStatement *ls = scx->slabel; if (ls && ls->ident == bs->ident) { Statement *s = ls->statement; if (!s || !s->hasBreak()) bs->error("label '%s' has no break", bs->ident->toChars()); else if (ls->tf != sc->tf) bs->error("cannot break out of finally block"); else { ls->breaks = true; result = bs; return; } return setError(); } } bs->error("enclosing label '%s' for break not found", bs->ident->toChars()); return setError(); } else if (!sc->sbreak) { if (sc->os && sc->os->tok != TOKon_scope_failure) { bs->error("break is not inside %s bodies", Token::toChars(sc->os->tok)); } else if (sc->fes) { // Replace break; with return 1; result = new ReturnStatement(Loc(), new IntegerExp(1)); return; } else bs->error("break is not inside a loop or switch"); return setError(); } else if (sc->sbreak->isForwardingStatement()) { bs->error("must use labeled `break` within `static foreach`"); } result = bs; } void visit(ContinueStatement *cs) { //printf("ContinueStatement::semantic() %p\n", cs); if (cs->ident) { cs->ident = fixupLabelName(sc, cs->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 == cs->ident && ls->statement == sc->fes) { // Replace continue ident; with return 0; result = new ReturnStatement(Loc(), new IntegerExp(0)); return; } } /* 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(cs); result = new ReturnStatement(Loc(), new IntegerExp(sc->fes->cases->dim + 1)); return; } break; // can't continue to it } ls = scx->slabel; if (ls && ls->ident == cs->ident) { Statement *s = ls->statement; if (!s || !s->hasContinue()) cs->error("label '%s' has no continue", cs->ident->toChars()); else if (ls->tf != sc->tf) cs->error("cannot continue out of finally block"); else { result = cs; return; } return setError(); } } cs->error("enclosing label '%s' for continue not found", cs->ident->toChars()); return setError(); } else if (!sc->scontinue) { if (sc->os && sc->os->tok != TOKon_scope_failure) { cs->error("continue is not inside %s bodies", Token::toChars(sc->os->tok)); } else if (sc->fes) { // Replace continue; with return 0; result = new ReturnStatement(Loc(), new IntegerExp(0)); return; } else cs->error("continue is not inside a loop"); return setError(); } else if (sc->scontinue->isForwardingStatement()) { cs->error("must use labeled `continue` within `static foreach`"); } result = cs; } void visit(SynchronizedStatement *ss) { if (ss->exp) { ss->exp = semantic(ss->exp, sc); ss->exp = resolveProperties(sc, ss->exp); ss->exp = ss->exp->optimize(WANTvalue); ss->exp = checkGC(sc, ss->exp); if (ss->exp->op == TOKerror) goto Lbody; ClassDeclaration *cd = ss->exp->type->isClassHandle(); if (!cd) { ss->error("can only synchronize on class objects, not '%s'", ss->exp->type->toChars()); return setError(); } else if (cd->isInterfaceDeclaration()) { /* Cast the interface to an object, as the object has the monitor, * not the interface. */ if (!ClassDeclaration::object) { ss->error("missing or corrupt object.d"); fatal(); } Type *t = ClassDeclaration::object->type; t = t->semantic(Loc(), sc)->toBasetype(); assert(t->ty == Tclass); ss->exp = new CastExp(ss->loc, ss->exp, t); ss->exp = semantic(ss->exp, sc); } /* Rewrite as: * auto tmp = exp; * _d_monitorenter(tmp); * try { body } finally { _d_monitorexit(tmp); } */ VarDeclaration *tmp = copyToTemp(0, "__sync", ss->exp); tmp->semantic(sc); Statements *cs = new Statements(); cs->push(new ExpStatement(ss->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(ss->loc, new VarExp(ss->loc, fdenter, false), new VarExp(ss->loc, tmp)); e->type = Type::tvoid; // do not run semantic on e cs->push(new ExpStatement(ss->loc, e)); FuncDeclaration *fdexit = FuncDeclaration::genCfunc(args, Type::tvoid, Id::monitorexit); e = new CallExp(ss->loc, new VarExp(ss->loc, fdexit, false), new VarExp(ss->loc, tmp)); e->type = Type::tvoid; // do not run semantic on e Statement *s = new ExpStatement(ss->loc, e); s = new TryFinallyStatement(ss->loc, ss->_body, s); cs->push(s); s = new CompoundStatement(ss->loc, cs); result = semantic(s, sc); return; } 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 = Identifier::generateId("__critsec"); Type *t = Type::tint8->sarrayOf(Target::ptrsize + Target::critsecsize()); VarDeclaration *tmp = new VarDeclaration(ss->loc, t, id, NULL); tmp->storage_class |= STCtemp | STCgshared | STCstatic; Statements *cs = new Statements(); cs->push(new ExpStatement(ss->loc, tmp)); /* This is just a dummy variable for "goto skips declaration" error. * Backend optimizer could remove this unused variable. */ VarDeclaration *v = new VarDeclaration(ss->loc, Type::tvoidptr, Identifier::generateId("__sync"), NULL); v->semantic(sc); cs->push(new ExpStatement(ss->loc, v)); Parameters* args = new Parameters; args->push(new Parameter(0, t->pointerTo(), NULL, NULL)); FuncDeclaration *fdenter = FuncDeclaration::genCfunc(args, Type::tvoid, Id::criticalenter, STCnothrow); Expression *e = new DotIdExp(ss->loc, new VarExp(ss->loc, tmp), Id::ptr); e = semantic(e, sc); e = new CallExp(ss->loc, new VarExp(ss->loc, fdenter, false), e); e->type = Type::tvoid; // do not run semantic on e cs->push(new ExpStatement(ss->loc, e)); FuncDeclaration *fdexit = FuncDeclaration::genCfunc(args, Type::tvoid, Id::criticalexit, STCnothrow); e = new DotIdExp(ss->loc, new VarExp(ss->loc, tmp), Id::ptr); e = semantic(e, sc); e = new CallExp(ss->loc, new VarExp(ss->loc, fdexit, false), e); e->type = Type::tvoid; // do not run semantic on e Statement *s = new ExpStatement(ss->loc, e); s = new TryFinallyStatement(ss->loc, ss->_body, s); cs->push(s); s = new CompoundStatement(ss->loc, cs); result = semantic(s, sc); return; } Lbody: if (ss->_body) ss->_body = semantic(ss->_body, sc); if (ss->_body && ss->_body->isErrorStatement()) { result = ss->_body; return; } result = ss; } void visit(WithStatement *ws) { ScopeDsymbol *sym; Initializer *init; //printf("WithStatement::semantic()\n"); ws->exp = semantic(ws->exp, sc); ws->exp = resolveProperties(sc, ws->exp); ws->exp = ws->exp->optimize(WANTvalue); ws->exp = checkGC(sc, ws->exp); if (ws->exp->op == TOKerror) return setError(); if (ws->exp->op == TOKscope) { sym = new WithScopeSymbol(ws); sym->parent = sc->scopesym; sym->endlinnum = ws->endloc.linnum; } else if (ws->exp->op == TOKtype) { Dsymbol *s = ((TypeExp *)ws->exp)->type->toDsymbol(sc); if (!s || !s->isScopeDsymbol()) { ws->error("with type %s has no members", ws->exp->toChars()); return setError(); } sym = new WithScopeSymbol(ws); sym->parent = sc->scopesym; sym->endlinnum = ws->endloc.linnum; } else { Type *t = ws->exp->type->toBasetype(); Expression *olde = ws->exp; if (t->ty == Tpointer) { ws->exp = new PtrExp(ws->loc, ws->exp); ws->exp = semantic(ws->exp, sc); t = ws->exp->type->toBasetype(); } assert(t); t = t->toBasetype(); if (t->isClassHandle()) { init = new ExpInitializer(ws->loc, ws->exp); ws->wthis = new VarDeclaration(ws->loc, ws->exp->type, Id::withSym, init); ws->wthis->semantic(sc); sym = new WithScopeSymbol(ws); sym->parent = sc->scopesym; sym->endlinnum = ws->endloc.linnum; } else if (t->ty == Tstruct) { if (!ws->exp->isLvalue()) { /* Re-write to * { * auto __withtmp = exp * with(__withtmp) * { * ... * } * } */ VarDeclaration *tmp = copyToTemp(0, "__withtmp", ws->exp); tmp->semantic(sc); ExpStatement *es = new ExpStatement(ws->loc, tmp); ws->exp = new VarExp(ws->loc, tmp); Statement *ss = new ScopeStatement(ws->loc, new CompoundStatement(ws->loc, es, ws), ws->endloc); result = semantic(ss, sc); return; } Expression *e = ws->exp->addressOf(); init = new ExpInitializer(ws->loc, e); ws->wthis = new VarDeclaration(ws->loc, e->type, Id::withSym, init); ws->wthis->semantic(sc); sym = new WithScopeSymbol(ws); // Need to set the scope to make use of resolveAliasThis sym->setScope(sc); sym->parent = sc->scopesym; sym->endlinnum = ws->endloc.linnum; } else { ws->error("with expressions must be aggregate types or pointers to them, not '%s'", olde->type->toChars()); return setError(); } } if (ws->_body) { sym->_scope = sc; sc = sc->push(sym); sc->insert(sym); ws->_body = semantic(ws->_body, sc); sc->pop(); if (ws->_body && ws->_body->isErrorStatement()) { result = ws->_body; return; } } result = ws; } void visit(TryCatchStatement *tcs) { if (!global.params.useExceptions) { tcs->error("Cannot use try-catch statements with -betterC"); return setError(); } if (!ClassDeclaration::throwable) { tcs->error("Cannot use try-catch statements because `object.Throwable` was not declared"); return setError(); } unsigned flags = 0; const unsigned FLAGcpp = 1; const unsigned FLAGd = 2; tcs->_body = semanticScope(tcs->_body, sc, NULL, NULL); assert(tcs->_body); /* Even if body is empty, still do semantic analysis on catches */ bool catchErrors = false; for (size_t i = 0; i < tcs->catches->dim; i++) { Catch *c = (*tcs->catches)[i]; semantic(c, sc); if (c->errors) { catchErrors = true; continue; } ClassDeclaration *cd = c->type->toBasetype()->isClassHandle(); flags |= cd->isCPPclass() ? FLAGcpp : FLAGd; // Determine if current catch 'hides' any previous catches for (size_t j = 0; j < i; j++) { Catch *cj = (*tcs->catches)[j]; const char *si = c->loc.toChars(); const char *sj = cj->loc.toChars(); if (c->type->toBasetype()->implicitConvTo(cj->type->toBasetype())) { tcs->error("catch at %s hides catch at %s", sj, si); catchErrors = true; } } } if (sc->func) { if (flags == (FLAGcpp | FLAGd)) { tcs->error("cannot mix catching D and C++ exceptions in the same try-catch"); catchErrors = true; } } if (catchErrors) return setError(); if (tcs->_body->isErrorStatement()) { result = tcs->_body; return; } /* If the try body never throws, we can eliminate any catches * of recoverable exceptions. */ if (!(blockExit(tcs->_body, sc->func, false) & BEthrow) && ClassDeclaration::exception) { for (size_t i = 0; i < tcs->catches->dim; i++) { Catch *c = (*tcs->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 tcs->catches->remove(i); --i; } } } if (tcs->catches->dim == 0) { result = tcs->_body->hasCode() ? tcs->_body : NULL; return; } result = tcs; } void visit(TryFinallyStatement *tfs) { //printf("TryFinallyStatement::semantic()\n"); tfs->_body = semantic(tfs->_body, sc); sc = sc->push(); sc->tf = tfs; sc->sbreak = NULL; sc->scontinue = NULL; // no break or continue out of finally block tfs->finalbody = semanticNoScope(tfs->finalbody, sc); sc->pop(); if (!tfs->_body) { result = tfs->finalbody; return; } if (!tfs->finalbody) { result = tfs->_body; return; } int blockexit = blockExit(tfs->_body, sc->func, false); // if not worrying about exceptions if (!(global.params.useExceptions && ClassDeclaration::throwable)) blockexit &= ~BEthrow; // don't worry about paths that otherwise may throw // Don't care about paths that halt, either if ((blockexit & ~BEhalt) == BEfallthru) { result = new CompoundStatement(tfs->loc, tfs->_body, tfs->finalbody); return; } result = tfs; } void visit(OnScopeStatement *oss) { if (oss->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. oss->error("cannot put %s statement inside %s", Token::toChars(oss->tok), Token::toChars(sc->os->tok)); return setError(); } if (sc->tf) { oss->error("cannot put %s statement inside finally block", Token::toChars(oss->tok)); return setError(); } } sc = sc->push(); sc->tf = NULL; sc->os = oss; if (oss->tok != TOKon_scope_failure) { // Jump out from scope(failure) block is allowed. sc->sbreak = NULL; sc->scontinue = NULL; } oss->statement = semanticNoScope(oss->statement, sc); sc->pop(); if (!oss->statement || oss->statement->isErrorStatement()) { result = oss->statement; return; } result = oss; } void visit(ThrowStatement *ts) { //printf("ThrowStatement::semantic()\n"); if (!global.params.useExceptions) { ts->error("Cannot use `throw` statements with -betterC"); return setError(); } if (!ClassDeclaration::throwable) { ts->error("Cannot use `throw` statements because `object.Throwable` was not declared"); return setError(); } FuncDeclaration *fd = sc->parent->isFuncDeclaration(); fd->hasReturnExp |= 2; ts->exp = semantic(ts->exp, sc); ts->exp = resolveProperties(sc, ts->exp); ts->exp = checkGC(sc, ts->exp); if (ts->exp->op == TOKerror) return setError(); checkThrowEscape(sc, ts->exp, false); ClassDeclaration *cd = ts->exp->type->toBasetype()->isClassHandle(); if (!cd || ((cd != ClassDeclaration::throwable) && !ClassDeclaration::throwable->isBaseOf(cd, NULL))) { ts->error("can only throw class objects derived from Throwable, not type %s", ts->exp->type->toChars()); return setError(); } result = ts; } void visit(DebugStatement *ds) { if (ds->statement) { sc = sc->push(); sc->flags |= SCOPEdebug; ds->statement = semantic(ds->statement, sc); sc->pop(); } result = ds->statement; } void visit(GotoStatement *gs) { //printf("GotoStatement::semantic()\n"); FuncDeclaration *fd = sc->func; gs->ident = fixupLabelName(sc, gs->ident); gs->label = fd->searchLabel(gs->ident); gs->tf = sc->tf; gs->os = sc->os; gs->lastVar = sc->lastVar; if (!gs->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(gs->loc, gs, gs->loc); sc->fes->gotos->push(ss); // 'look at this later' list result = ss; return; } // Add to fwdref list to check later if (!gs->label->statement) { if (!fd->gotos) fd->gotos = new GotoStatements(); fd->gotos->push(gs); } else if (gs->checkLabel()) return setError(); result = gs; } void visit(LabelStatement *ls) { //printf("LabelStatement::semantic()\n"); FuncDeclaration *fd = sc->parent->isFuncDeclaration(); ls->ident = fixupLabelName(sc, ls->ident); ls->tf = sc->tf; ls->os = sc->os; ls->lastVar = sc->lastVar; LabelDsymbol *ls2 = fd->searchLabel(ls->ident); if (ls2->statement) { ls->error("label '%s' already defined", ls2->toChars()); return setError(); } else ls2->statement = ls; 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 = ls; if (ls->statement) ls->statement = semantic(ls->statement, sc); sc->pop(); result = ls; } void visit(AsmStatement *s) { result = asmSemantic(s, sc); } void visit(CompoundAsmStatement *cas) { // Apply postfix attributes of the asm block to each statement. sc = sc->push(); sc->stc |= cas->stc; for (size_t i = 0; i < cas->statements->dim; i++) { Statement *s = (*cas->statements)[i]; (*cas->statements)[i] = s ? semantic(s, sc) : NULL; } assert(sc->func); // use setImpure/setGC when the deprecation cycle is over PURE purity; if (!(cas->stc & STCpure) && (purity = sc->func->isPureBypassingInference()) != PUREimpure && purity != PUREfwdref) cas->deprecation("asm statement is assumed to be impure - mark it with 'pure' if it is not"); if (!(cas->stc & STCnogc) && sc->func->isNogcBypassingInference()) cas->deprecation("asm statement is assumed to use the GC - mark it with '@nogc' if it does not"); if (!(cas->stc & (STCtrusted|STCsafe)) && sc->func->setUnsafe()) cas->error("asm statement is assumed to be @system - mark it with '@trusted' if it is not"); sc->pop(); result = cas; } void visit(ImportStatement *imps) { for (size_t i = 0; i < imps->imports->dim; i++) { Import *s = (*imps->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); // https://issues.dlang.org/show_bug.cgi?id=19942 // If the module that's being imported doesn't exist, don't add it to the symbol table // for the current scope. if (s->mod != NULL) { Module::addDeferredSemantic2(s); // Bugzilla 14666 sc->insert(s); for (size_t j = 0; j < s->aliasdecls.dim; j++) { sc->insert(s->aliasdecls[j]); } } } result = imps; } }; Statement *semantic(Statement *s, Scope *sc) { StatementSemanticVisitor v = StatementSemanticVisitor(sc); s->accept(&v); return v.result; } void semantic(Catch *c, Scope *sc) { //printf("Catch::semantic(%s)\n", ident->toChars()); 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(c->loc, "cannot put catch statement inside %s", Token::toChars(sc->os->tok)); c->errors = true; } 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(c->loc, "cannot put catch statement inside finally block"); c->errors = true; } ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc->scopesym; sc = sc->push(sym); if (!c->type) { deprecation(c->loc, "catch statement without an exception specification is deprecated; use catch(Throwable) for old behavior"); // reference .object.Throwable c->type = getThrowable(); } c->type = c->type->semantic(c->loc, sc); if (c->type == Type::terror) c->errors = true; else { ClassDeclaration *cd = c->type->toBasetype()->isClassHandle(); if (!cd) { error(c->loc, "can only catch class objects, not '%s'", c->type->toChars()); c->errors = true; } else if (cd->isCPPclass()) { if (!Target::cppExceptions) { error(c->loc, "catching C++ class objects not supported for this target"); c->errors = true; } if (sc->func && !sc->intypeof && !c->internalCatch && sc->func->setUnsafe()) { error(c->loc, "cannot catch C++ class objects in @safe code"); c->errors = true; } } else if (cd != ClassDeclaration::throwable && !ClassDeclaration::throwable->isBaseOf(cd, NULL)) { error(c->loc, "can only catch class objects derived from Throwable, not '%s'", c->type->toChars()); c->errors = true; } else if (sc->func && !sc->intypeof && !c->internalCatch && cd != ClassDeclaration::exception && !ClassDeclaration::exception->isBaseOf(cd, NULL) && sc->func->setUnsafe()) { error(c->loc, "can only catch class objects derived from Exception in @safe code, not '%s'", c->type->toChars()); c->errors = true; } if (c->ident) { c->var = new VarDeclaration(c->loc, c->type, c->ident, NULL); c->var->semantic(sc); sc->insert(c->var); } c->handler = semantic(c->handler, sc); if (c->handler && c->handler->isErrorStatement()) c->errors = true; } sc->pop(); } Statement *semanticNoScope(Statement *s, Scope *sc) { //printf("Statement::semanticNoScope() %s\n", toChars()); if (!s->isCompoundStatement() && !s->isScopeStatement()) { s = new CompoundStatement(s->loc, s); // so scopeCode() gets called } s = semantic(s, sc); return s; } // Same as semanticNoScope(), but do create a new scope Statement *semanticScope(Statement *s, Scope *sc, Statement *sbreak, Statement *scontinue) { ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc->scopesym; Scope *scd = sc->push(sym); if (sbreak) scd->sbreak = sbreak; if (scontinue) scd->scontinue = scontinue; s = semanticNoScope(s, scd); scd->pop(); return s; } /******************* * See StatementSemanticVisitor.makeTupleForeach. This is a simple * wrapper that returns the generated statements/declarations. */ Statement *makeTupleForeachStatic(Scope *sc, ForeachStatement *fs, bool needExpansion) { StatementSemanticVisitor v = StatementSemanticVisitor(sc); v.makeTupleForeachStatic(fs, needExpansion); return v.result; } Dsymbols *makeTupleForeachStaticDecl(Scope *sc, ForeachStatement *fs, Dsymbols *dbody, bool needExpansion) { StatementSemanticVisitor v = StatementSemanticVisitor(sc); return v.makeTupleForeachStaticDecl(fs, dbody, needExpansion); }