| |
| /* 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 "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 (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) |
| { |
| ScopeDsymbol *sym; |
| //printf("ScopeStatement::semantic(sc = %p)\n", sc); |
| if (ss->statement) |
| { |
| 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(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); |
| 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); |
| 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); |
| 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; |
| } |
| |
| 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 |
| { |
| if (dim < 1 || dim > 2) |
| { |
| fs->error("only one (value) or two (key,value) arguments for tuple foreach"); |
| return setError(); |
| } |
| |
| Type *paramtype = (*fs->parameters)[dim-1]->type; |
| if (paramtype) |
| { |
| paramtype = paramtype->semantic(loc, sc); |
| if (paramtype->ty == Terror) |
| return setError(); |
| } |
| |
| TypeTuple *tuple = (TypeTuple *)tab; |
| Statements *statements = new Statements(); |
| //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; |
| 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 *st = new Statements(); |
| |
| if (dim == 2) |
| { |
| // Declare key |
| if (p->storageClass & (STCout | STCref | STClazy)) |
| { |
| fs->error("no storage class for key %s", p->ident->toChars()); |
| return setError(); |
| } |
| p->type = p->type->semantic(loc, sc); |
| TY keyty = p->type->ty; |
| if (keyty != Tint32 && keyty != Tuns32) |
| { |
| if (global.params.isLP64) |
| { |
| if (keyty != Tint64 && keyty != Tuns64) |
| { |
| fs->error("foreach: key type must be int or uint, long or ulong, not %s", p->type->toChars()); |
| return setError(); |
| } |
| } |
| else |
| { |
| fs->error("foreach: key type must be int or uint, not %s", p->type->toChars()); |
| return setError(); |
| } |
| } |
| Initializer *ie = new ExpInitializer(Loc(), new IntegerExp(k)); |
| VarDeclaration *var = new VarDeclaration(loc, p->type, p->ident, ie); |
| var->storage_class |= STCmanifest; |
| st->push(new ExpStatement(loc, var)); |
| p = (*fs->parameters)[1]; // value |
| } |
| // Declare value |
| if (p->storageClass & (STCout | STClazy) || |
| (p->storageClass & STCref && !te)) |
| { |
| fs->error("no storage class for value %s", p->ident->toChars()); |
| return setError(); |
| } |
| Dsymbol *var; |
| if (te) |
| { |
| Type *tb = e->type->toBasetype(); |
| Dsymbol *ds = NULL; |
| if ((tb->ty == Tfunction || tb->ty == Tsarray) && e->op == TOKvar) |
| ds = ((VarExp *)e)->var; |
| else if (e->op == TOKtemplate) |
| ds = ((TemplateExp *)e)->td; |
| else if (e->op == TOKscope) |
| ds = ((ScopeExp *)e)->sds; |
| else if (e->op == TOKfunction) |
| { |
| FuncExp *fe = (FuncExp *)e; |
| ds = fe->td ? (Dsymbol *)fe->td : fe->fd; |
| } |
| |
| if (ds) |
| { |
| var = new AliasDeclaration(loc, p->ident, ds); |
| if (p->storageClass & STCref) |
| { |
| fs->error("symbol %s cannot be ref", s->toChars()); |
| return setError(); |
| } |
| if (paramtype) |
| { |
| fs->error("cannot specify element type for symbol %s", ds->toChars()); |
| return setError(); |
| } |
| } |
| else if (e->op == TOKtype) |
| { |
| var = new AliasDeclaration(loc, p->ident, e->type); |
| if (paramtype) |
| { |
| fs->error("cannot specify element type for type %s", e->type->toChars()); |
| return setError(); |
| } |
| } |
| else |
| { |
| p->type = e->type; |
| if (paramtype) |
| p->type = paramtype; |
| Initializer *ie = new ExpInitializer(Loc(), e); |
| VarDeclaration *v = new VarDeclaration(loc, p->type, p->ident, ie); |
| if (p->storageClass & STCref) |
| v->storage_class |= STCref | STCforeach; |
| if (e->isConst() || e->op == TOKstring || |
| e->op == TOKstructliteral || e->op == TOKarrayliteral) |
| { |
| if (v->storage_class & STCref) |
| { |
| fs->error("constant value %s cannot be ref", ie->toChars()); |
| return setError(); |
| } |
| else |
| v->storage_class |= STCmanifest; |
| } |
| var = v; |
| } |
| } |
| else |
| { |
| var = new AliasDeclaration(loc, p->ident, t); |
| if (paramtype) |
| { |
| fs->error("cannot specify element type for symbol %s", s->toChars()); |
| return setError(); |
| } |
| } |
| st->push(new ExpStatement(loc, var)); |
| |
| if (fs->_body) |
| st->push(fs->_body->syntaxCopy()); |
| s = new CompoundStatement(loc, st); |
| s = new ScopeStatement(loc, s, fs->endloc); |
| statements->push(s); |
| } |
| |
| s = new UnrolledLoopStatement(loc, statements); |
| if (LabelStatement *ls = checkLabeledLoop(sc, fs)) |
| ls->gotoTarget = s; |
| if (te && te->e0) |
| s = new CompoundStatement(loc, new ExpStatement(te->e0->loc, te->e0), s); |
| if (vinit) |
| s = new CompoundStatement(loc, new ExpStatement(loc, vinit), s); |
| s = semantic(s, sc); |
| result = s; |
| return; |
| } |
| |
| sym = new ScopeDsymbol(); |
| sym->parent = sc->scopesym; |
| sym->endlinnum = fs->endloc.linnum; |
| Scope *sc2 = sc->push(sym); |
| |
| sc2->noctor++; |
| |
| 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; |
| } |
| |
| /* Look for special case of parsing char types out of char type |
| * array. |
| */ |
| tn = tab->nextOf()->toBasetype(); |
| if (tn->ty == Tchar || tn->ty == Twchar || tn->ty == Tdchar) |
| { |
| int i = (dim == 1) ? 0 : 1; // index of value |
| Parameter *p = (*fs->parameters)[i]; |
| p->type = p->type->semantic(loc, sc2); |
| p->type = p->type->addStorageClass(p->storageClass); |
| 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]; |
| p->type = p->type->semantic(loc, sc2); |
| p->type = p->type->addStorageClass(p->storageClass); |
| 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); |
| 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; |
| } |
| 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); |
| 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); |
| 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); |
| } |
| 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) |
| { |
| 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; |
| } |
| } |
| 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->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->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) |
|