D front-end changes: - Explicit package visibility attribute is now always applied to introducing scopes. - Added `__traits(totype, string)' to convert mangled type string to an existing type. - Printf-like and scanf-like functions are now detected by prefixing them with `pragma(printf)' for printf-like functions or `pragma(scanf)' for scanf-like functions. - Added `__c_wchar_t', `__c_complex_float', `__c_complex_double', and `__c_complex_real' types for interfacing with C and C++. - Template alias parameters can now be instantiated with basic types, such as `int` or `void function()`. - Mixins can now be used as types in the form `mixin(string) var'. - Mixin expressions can take an argument list, same as `pragma(msg)'. - Implement DIP1034, add `typeof(*null)' types to represent `noreturn'. - `pragma(msg)' can print expressions of type `void'. - It is now an error to use private variables selectively imported from other modules. Due to a bug, some imported private members were visible from other modules, violating the specification. - Added new syntax to declare an alias to a function type using the `alias' syntax based on the assignment operator. - Function literals can now return a value by reference. Phobos changes: - Synchronize C bindings with the latest port fixes in upstream druntime. - Added alias for a `noreturn' type in object.d - Make use of the new `pragma(printf)' and `pragma(scanf)' pragmas, fix all code that got flagged as being incorrect. - Fixed code that relied on bugs in the D import package system. Reviewed-on: https://github.com/dlang/dmd/pull/12339 https://github.com/dlang/druntime/pull/3422 https://github.com/dlang/phobos/pull/7932 gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd 3b808e838. * Make-lang.in (D_FRONTEND_OBJS): Add d/chkformat.o. * d-codegen.cc (build_struct_literal): Handle special enums. * d-convert.cc (convert_expr): Handle noreturn type. (convert_for_condition): Likewise. * d-target.cc (Target::_init): Set type for wchar_t. (TargetCPP::derivedClassOffset): New method. (Target::libraryObjectMonitors): New method. * decl.cc (get_symbol_decl): Set TREE_THIS_VOLATILE for functions of type noreturn. * toir.cc (IRVisitor::visit (ReturnStatement *)): Handle returning noreturn types. * types.cc (TypeVisitor::visit (TypeNoreturn *)): New method. (TypeVisitor::visit (TypeEnum *)): Handle special enums. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime 483bc129. * libdruntime/Makefile.am (DRUNTIME_DSOURCES_DARWIN): Add core/sys/darwin/fcntl.d. (DRUNTIME_DSOURCES_OPENBSD): Add core/sys/openbsd/unistd.d. (DRUNTIME_DSOURCES_WINDOWS): Add core/sys/windows/stdc/malloc.d. * libdruntime/Makefile.in: Regenerate. * src/MERGE: Merge upstream phobos f89dc217a. * src/Makefile.am (PHOBOS_DSOURCES): Add std/regex/internal/tests2.d. * src/Makefile.in: Regenerate. * testsuite/libphobos.exceptions/chain.d: Fix format arguments. * testsuite/libphobos.exceptions/line_trace.d: Likewise.
3876 lines
142 KiB
C
3876 lines
142 KiB
C
|
|
/* Compiler implementation of the D programming language
|
|
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
|
|
* written by Walter Bright
|
|
* http://www.digitalmars.com
|
|
* Distributed under the Boost Software License, Version 1.0.
|
|
* http://www.boost.org/LICENSE_1_0.txt
|
|
*/
|
|
|
|
#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();
|
|
|
|
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 = expressionSemantic(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 = statementSemantic(s, sc);
|
|
}
|
|
|
|
void visit(CompoundStatement *cs)
|
|
{
|
|
//printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc);
|
|
for (size_t i = 0; i < cs->statements->length; )
|
|
{
|
|
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 = statementSemantic(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 = statementSemantic(sentry, sc);
|
|
cs->statements->insert(i, sentry);
|
|
i++;
|
|
}
|
|
if (sexception)
|
|
sexception = statementSemantic(sexception, sc);
|
|
if (sexception)
|
|
{
|
|
if (i + 1 == cs->statements->length && !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->length; 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 = statementSemantic(s, sc);
|
|
|
|
cs->statements->setDim(i + 1);
|
|
cs->statements->push(s);
|
|
break;
|
|
}
|
|
}
|
|
else if (sfinally)
|
|
{
|
|
if (0 && i + 1 == cs->statements->length)
|
|
{
|
|
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->length; j++)
|
|
{
|
|
a->push((*cs->statements)[j]);
|
|
}
|
|
Statement *body = new CompoundStatement(Loc(), a);
|
|
s = new TryFinallyStatement(Loc(), body, sfinally);
|
|
s = statementSemantic(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->length; ++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->length <= i)
|
|
break;
|
|
goto Lagain;
|
|
}
|
|
}
|
|
if (cs->statements->length == 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->length; i++)
|
|
{
|
|
Statement *s = (*uls->statements)[i];
|
|
if (s)
|
|
{
|
|
//printf("[%d]: %s\n", i, s->toChars());
|
|
s = statementSemantic(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 = statementSemantic(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 = statementSemantic(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 = statementSemantic(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 = statementSemantic(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 = expressionSemantic(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 = statementSemantic(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 = expressionSemantic(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 = expressionSemantic(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);
|
|
type = e->type;
|
|
if (paramtype)
|
|
type = paramtype;
|
|
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->length;
|
|
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 = typeSemantic(p->type, 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 false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fs->error("foreach: key type must be int or uint, not %s", p->type->toChars());
|
|
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 = expressionSemantic(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_ = expressionSemantic(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->length;
|
|
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 = typeSemantic(paramtype, 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->length;
|
|
}
|
|
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->length;
|
|
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 = expressionSemantic(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);
|
|
dsymbolSemantic(vinit, 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())
|
|
{
|
|
ParameterList fparameters = fd->getParameterList();
|
|
|
|
if (fparameters.length() == 1)
|
|
{
|
|
// first param should be the callback function
|
|
Parameter *fparam = fparameters[0];
|
|
if ((fparam->type->ty == Tpointer || fparam->type->ty == Tdelegate) &&
|
|
fparam->type->nextOf()->ty == Tfunction)
|
|
{
|
|
TypeFunction *tf = (TypeFunction *)fparam->type->nextOf();
|
|
foreachParamCount = tf->parameterList.length();
|
|
foundMismatch = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//printf("dim = %d, parameters->length = %d\n", dim, fs->parameters->length);
|
|
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 = statementSemantic(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 = typeSemantic(p->type, 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);
|
|
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->length : 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 != Type::tsize_t->ty)
|
|
{
|
|
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];
|
|
IndexExp *indexExp = new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, fs->key));
|
|
indexExp->indexIsInBounds = true; // disabling bounds checking in foreach statements.
|
|
fs->value->_init = new ExpInitializer(loc, indexExp);
|
|
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 = statementSemantic(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);
|
|
dsymbolSemantic(r, 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);
|
|
dsymbolSemantic(vd, 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->length < dim)
|
|
{
|
|
pos = expandAliasThisTuples(exps, pos);
|
|
if (pos == -1)
|
|
break;
|
|
}
|
|
if (exps->length != dim)
|
|
{
|
|
const char *plural = exps->length > 1 ? "s" : "";
|
|
fs->error("cannot infer argument types, expected %d argument%s, not %d",
|
|
exps->length, 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 = typeSemantic(p->type->addStorageClass(p->storageClass), 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 = statementSemantic(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 *)typeSemantic(fdapply->type, loc, sc2);
|
|
goto Lget;
|
|
}
|
|
else if (tab->ty == Tdelegate)
|
|
{
|
|
tfld = (TypeFunction *)tab->nextOf();
|
|
Lget:
|
|
//printf("tfld = %s\n", tfld->toChars());
|
|
if (tfld->parameterList.parameters->length == 1)
|
|
{
|
|
Parameter *p = tfld->parameterList[0];
|
|
if (p->type && p->type->ty == Tdelegate)
|
|
{
|
|
Type *t = typeSemantic(p->type, 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 = typeSemantic(p->type, loc, sc2);
|
|
p->type = p->type->addStorageClass(p->storageClass);
|
|
if (tfld)
|
|
{
|
|
Parameter *prm = tfld->parameterList[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, NULL));
|
|
}
|
|
// Bugzilla 13840: Throwable nested function inside nothrow function is acceptable.
|
|
StorageClass stc = mergeFuncAttrs(STCsafe | STCpure | STCnogc, fs->func);
|
|
tfld = new TypeFunction(ParameterList(params), Type::tint32, 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 = expressionSemantic(flde, sc2);
|
|
fld->tookAddressOf = 0;
|
|
|
|
// Resolve any forward referenced goto's
|
|
for (size_t i = 0; i < fs->gotos->length; 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->length + 1));
|
|
(*fs->gotos)[i]->statement = s;
|
|
}
|
|
}
|
|
|
|
Expression *e = NULL;
|
|
Expression *ec;
|
|
if (vinit)
|
|
{
|
|
e = new DeclarationExp(loc, vinit);
|
|
e = expressionSemantic(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, NULL));
|
|
params->push(new Parameter(STCin, Type::tsize_t, NULL, NULL, NULL));
|
|
Parameters* dgparams = new Parameters;
|
|
dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL, NULL));
|
|
if (dim == 2)
|
|
dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL, NULL));
|
|
fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams),
|
|
Type::tint32, LINKd));
|
|
params->push(new Parameter(0, fldeTy[i], NULL, 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, NULL));
|
|
Parameters* dgparams = new Parameters;
|
|
dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL, NULL));
|
|
if (dim == 2)
|
|
dgparams->push(new Parameter(0, Type::tvoidptr, NULL, NULL, NULL));
|
|
dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams),
|
|
Type::tint32, LINKd));
|
|
params->push(new Parameter(0, dgty, NULL, 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 = expressionSemantic(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 = expressionSemantic(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->length)
|
|
{
|
|
// 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->length; 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 = statementSemantic(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 = expressionSemantic(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 = expressionSemantic(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 = typeSemantic(fs->prm->type, 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 = expressionSemantic(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 = statementSemantic(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;
|
|
dsymbolSemantic(ifs->match, 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 = expressionSemantic(ifs->condition, scd);
|
|
|
|
if (ifs->match->edtor)
|
|
{
|
|
Statement *sdtor = new DtorExpStatement(ifs->loc, ifs->match->edtor, ifs->match);
|
|
sdtor = new ScopeGuardStatement(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 = expressionSemantic(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))
|
|
{
|
|
DebugCondition *dc = cs->condition->isDebugCondition();
|
|
if (dc)
|
|
{
|
|
sc = sc->push();
|
|
sc->flags |= SCOPEdebug;
|
|
cs->ifbody = statementSemantic(cs->ifbody, sc);
|
|
sc->pop();
|
|
}
|
|
else
|
|
cs->ifbody = statementSemantic(cs->ifbody, sc);
|
|
result = cs->ifbody;
|
|
}
|
|
else
|
|
{
|
|
if (cs->elsebody)
|
|
cs->elsebody = statementSemantic(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->length; i++)
|
|
{
|
|
Expression *e = (*ps->args)[i];
|
|
|
|
sc = sc->startCTFE();
|
|
e = expressionSemantic(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->length != 1)
|
|
ps->error("function name expected for start address");
|
|
else
|
|
{
|
|
Expression *e = (*ps->args)[0];
|
|
|
|
sc = sc->startCTFE();
|
|
e = expressionSemantic(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 = statementSemantic(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->length == 0)
|
|
inlining = PINLINEdefault;
|
|
else if (!ps->args || ps->args->length != 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 = statementSemantic(ps->_body, sc);
|
|
}
|
|
result = ps->_body;
|
|
return;
|
|
|
|
Lerror:
|
|
return setError();
|
|
}
|
|
|
|
void visit(StaticAssertStatement *s)
|
|
{
|
|
semantic2(s->sa, 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 = expressionSemantic(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 = statementSemantic(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.length; 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->length; 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->length;
|
|
for (size_t i = 0; i < dim; i++)
|
|
{
|
|
EnumMember *em = (*ed->members)[i]->isEnumMember();
|
|
if (em)
|
|
{
|
|
for (size_t j = 0; j < ss->cases->length; 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 == CHECKENABLEon))
|
|
{
|
|
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 == CHECKENABLEon &&
|
|
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 = expressionSemantic(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->length; 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.length; )
|
|
{
|
|
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 = statementSemantic(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 = expressionSemantic(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 = expressionSemantic(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)
|
|
statementSemantic(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 = statementSemantic(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 = statementSemantic(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 = expressionSemantic(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->length + 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 = expressionSemantic(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 = expressionSemantic(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
|
|
*/
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 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->length+1;
|
|
rs->exp = new IntegerExp(sc->fes->cases->length + 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->length + 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->length + 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->length + 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 = expressionSemantic(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 = typeSemantic(t, Loc(), sc)->toBasetype();
|
|
assert(t->ty == Tclass);
|
|
|
|
ss->exp = new CastExp(ss->loc, ss->exp, t);
|
|
ss->exp = expressionSemantic(ss->exp, sc);
|
|
}
|
|
|
|
/* Rewrite as:
|
|
* auto tmp = exp;
|
|
* _d_monitorenter(tmp);
|
|
* try { body } finally { _d_monitorexit(tmp); }
|
|
*/
|
|
VarDeclaration *tmp = copyToTemp(0, "__sync", ss->exp);
|
|
dsymbolSemantic(tmp, 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, 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 = statementSemantic(s, sc);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
/* Generate our own critical section, then rewrite as:
|
|
* __gshared void* __critsec;
|
|
* _d_criticalenter2(&__critsec);
|
|
* try { body } finally { _d_criticalexit(__critsec); }
|
|
*/
|
|
Identifier *id = Identifier::generateId("__critsec");
|
|
Type *t = Type::tvoidptr;
|
|
VarDeclaration *tmp = new VarDeclaration(ss->loc, t, id, NULL);
|
|
tmp->storage_class |= STCtemp | STCgshared | STCstatic;
|
|
Expression *tmpExp = new VarExp(ss->loc, tmp);
|
|
|
|
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);
|
|
dsymbolSemantic(v, sc);
|
|
cs->push(new ExpStatement(ss->loc, v));
|
|
|
|
Parameters* args = new Parameters;
|
|
args->push(new Parameter(0, t->pointerTo(), NULL, NULL, NULL));
|
|
|
|
FuncDeclaration *fdenter = FuncDeclaration::genCfunc(args, Type::tvoid, Id::criticalenter, STCnothrow);
|
|
Expression *e = new AddrExp(ss->loc, tmpExp);
|
|
e = expressionSemantic(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 = expressionSemantic(tmpExp, 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 = statementSemantic(s, sc);
|
|
return;
|
|
}
|
|
Lbody:
|
|
if (ss->_body)
|
|
ss->_body = statementSemantic(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 = expressionSemantic(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 = expressionSemantic(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);
|
|
dsymbolSemantic(ws->wthis, 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);
|
|
dsymbolSemantic(tmp, 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 = statementSemantic(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);
|
|
dsymbolSemantic(ws->wthis, 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 = statementSemantic(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->length; i++)
|
|
{
|
|
Catch *c = (*tcs->catches)[i];
|
|
catchSemantic(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->length; 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->length == 0)
|
|
{
|
|
result = tcs->_body->hasCode() ? tcs->_body : NULL;
|
|
return;
|
|
}
|
|
|
|
result = tcs;
|
|
}
|
|
|
|
void visit(TryFinallyStatement *tfs)
|
|
{
|
|
//printf("TryFinallyStatement::semantic()\n");
|
|
tfs->_body = statementSemantic(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(ScopeGuardStatement *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 = expressionSemantic(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 = statementSemantic(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 = statementSemantic(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->length; i++)
|
|
{
|
|
Statement *s = (*cas->statements)[i];
|
|
(*cas->statements)[i] = s ? statementSemantic(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->length; i++)
|
|
{
|
|
Import *s = (*imps->imports)[i]->isImport();
|
|
assert(!s->aliasdecls.length);
|
|
for (size_t j = 0; j < s->names.length; 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);
|
|
}
|
|
|
|
dsymbolSemantic(s, 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.length; j++)
|
|
{
|
|
sc->insert(s->aliasdecls[j]);
|
|
}
|
|
}
|
|
}
|
|
result = imps;
|
|
}
|
|
};
|
|
|
|
Statement *statementSemantic(Statement *s, Scope *sc)
|
|
{
|
|
StatementSemanticVisitor v = StatementSemanticVisitor(sc);
|
|
s->accept(&v);
|
|
return v.result;
|
|
}
|
|
|
|
void catchSemantic(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 = typeSemantic(c->type, 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.cpp.exceptions)
|
|
{
|
|
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);
|
|
dsymbolSemantic(c->var, sc);
|
|
sc->insert(c->var);
|
|
}
|
|
c->handler = statementSemantic(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 = statementSemantic(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);
|
|
}
|