mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-08 03:46:02 +03:00

Add the commandline options -fprofile-instr-generate[=filename] and -profile-instr-use=filename -fprofile-instr-generate -- Add instrumentation on branches, switches, and function entry; uses LLVM's InstrProf pass. -- Link to profile runtime that writes instrumentation counters to a file. -fprofile-instr-use -- Read profile data from a file and apply branch weights to branches and switches, and annotate functions with entrycount in LLVM IR. -- Functions with low or high entrycount are marked with 'cold' or 'inlinehint'. The only statement type without PGO yet is "try-finally". A new pragma, `pragma(LDC_profile_instr, [ true | false ])`, is added to selectively disable/enable instrumentation of functions (granularity = whole functions). The runtime library ldc-profile-rt is a copy of LLVM compiler-rt lib/profile. It has to be exactly in-sync with the LLVM version, and thus we need a copy for each PGO-supported LLVM (>=3.7). import ldc.profile for a D interface to ldc-profile-rt (for example to reset execution counts after a program startup phase). The instrumentation data is mainly passed on to LLVM: function-entry counts and branch counts/probabilities. LDC marks functions as hot when "execution count is 30% of the maximum function execution count", and marks functions as cold if their count is 1% of maximum function execution count. The source of LLVM's llvm-profdata tool is hereby included in LDCs repository (different source for each LLVM version), and the binary is included in the install bin folder. The executable is named "ldc-profdata" to avoid clashing with llvm-profdata on the same machine. This is needed because profdata executable has to be in-sync with the LLVM version used to build LDC. Maintenance burden: for trunk LLVM, we have to keep ldc-profile-rt and llvm-profdata in sync. There is no diff with upstream; but because of active development there are the occasional API changes.
6171 lines
191 KiB
D
6171 lines
191 KiB
D
// Compiler implementation of the D programming language
|
|
// Copyright (c) 1999-2015 by Digital Mars
|
|
// All Rights Reserved
|
|
// written by Walter Bright
|
|
// http://www.digitalmars.com
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// http://www.boost.org/LICENSE_1_0.txt
|
|
|
|
module ddmd.statement;
|
|
|
|
import core.stdc.stdarg;
|
|
import core.stdc.stdio;
|
|
|
|
import ddmd.aggregate;
|
|
import ddmd.aliasthis;
|
|
import ddmd.arrayop;
|
|
import ddmd.arraytypes;
|
|
import ddmd.attrib;
|
|
import ddmd.gluelayer;
|
|
import ddmd.canthrow;
|
|
import ddmd.clone;
|
|
import ddmd.cond;
|
|
import ddmd.ctfeexpr;
|
|
import ddmd.dcast;
|
|
import ddmd.dclass;
|
|
import ddmd.declaration;
|
|
import ddmd.denum;
|
|
import ddmd.dimport;
|
|
import ddmd.dinterpret;
|
|
import ddmd.dscope;
|
|
import ddmd.dsymbol;
|
|
import ddmd.dtemplate;
|
|
import ddmd.errors;
|
|
import ddmd.escape;
|
|
import ddmd.expression;
|
|
import ddmd.func;
|
|
import ddmd.globals;
|
|
import ddmd.hdrgen;
|
|
import ddmd.id;
|
|
import ddmd.identifier;
|
|
import ddmd.init;
|
|
import ddmd.inline;
|
|
import ddmd.intrange;
|
|
import ddmd.mtype;
|
|
import ddmd.mtype;
|
|
import ddmd.nogc;
|
|
import ddmd.opover;
|
|
import ddmd.parse;
|
|
import ddmd.root.outbuffer;
|
|
import ddmd.root.rootobject;
|
|
import ddmd.sapply;
|
|
import ddmd.sideeffect;
|
|
import ddmd.staticassert;
|
|
import ddmd.target;
|
|
import ddmd.tokens;
|
|
import ddmd.visitor;
|
|
version(IN_LLVM)
|
|
{
|
|
import gen.dpragma;
|
|
}
|
|
|
|
extern (C++) Identifier fixupLabelName(Scope* sc, Identifier ident)
|
|
{
|
|
uint flags = (sc.flags & SCOPEcontract);
|
|
if (flags && flags != SCOPEinvariant && !(ident.string[0] == '_' && ident.string[1] == '_'))
|
|
{
|
|
/* CTFE requires FuncDeclaration::labtab for the interpretation.
|
|
* So fixing the label name inside in/out contracts is necessary
|
|
* for the uniqueness in labtab.
|
|
*/
|
|
const(char)* prefix = flags == SCOPErequire ? "__in_" : "__out_";
|
|
OutBuffer buf;
|
|
buf.printf("%s%s", prefix, ident.toChars());
|
|
ident = Identifier.idPool(buf.peekSlice());
|
|
}
|
|
return ident;
|
|
}
|
|
|
|
extern (C++) LabelStatement checkLabeledLoop(Scope* sc, Statement statement)
|
|
{
|
|
if (sc.slabel && sc.slabel.statement == statement)
|
|
{
|
|
return sc.slabel;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/***********************************************************
|
|
* Check an assignment is used as a condition.
|
|
* Intended to be use before the `semantic` call on `e`.
|
|
* Params:
|
|
* e = condition expression which is not yet run semantic analysis.
|
|
* Returns:
|
|
* `e` or ErrorExp.
|
|
*/
|
|
Expression checkAssignmentAsCondition(Expression e)
|
|
{
|
|
auto ec = e;
|
|
while (ec.op == TOKcomma)
|
|
ec = (cast(CommaExp)ec).e2;
|
|
if (ec.op == TOKassign)
|
|
{
|
|
ec.error("assignment cannot be used as a condition, perhaps == was meant?");
|
|
return new ErrorExp();
|
|
}
|
|
return e;
|
|
}
|
|
|
|
enum BE : int
|
|
{
|
|
BEnone = 0,
|
|
BEfallthru = 1,
|
|
BEthrow = 2,
|
|
BEreturn = 4,
|
|
BEgoto = 8,
|
|
BEhalt = 0x10,
|
|
BEbreak = 0x20,
|
|
BEcontinue = 0x40,
|
|
BEerrthrow = 0x80,
|
|
BEany = (BEfallthru | BEthrow | BEreturn | BEgoto | BEhalt),
|
|
}
|
|
|
|
alias BEnone = BE.BEnone;
|
|
alias BEfallthru = BE.BEfallthru;
|
|
alias BEthrow = BE.BEthrow;
|
|
alias BEreturn = BE.BEreturn;
|
|
alias BEgoto = BE.BEgoto;
|
|
alias BEhalt = BE.BEhalt;
|
|
alias BEbreak = BE.BEbreak;
|
|
alias BEcontinue = BE.BEcontinue;
|
|
alias BEerrthrow = BE.BEerrthrow;
|
|
alias BEany = BE.BEany;
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class Statement : RootObject
|
|
{
|
|
public:
|
|
Loc loc;
|
|
|
|
final extern (D) this(Loc loc)
|
|
{
|
|
this.loc = loc;
|
|
// If this is an in{} contract scope statement (skip for determining
|
|
// inlineStatus of a function body for header content)
|
|
}
|
|
|
|
Statement syntaxCopy()
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
override final void print()
|
|
{
|
|
fprintf(stderr, "%s\n", toChars());
|
|
fflush(stderr);
|
|
}
|
|
|
|
override final const(char)* toChars()
|
|
{
|
|
HdrGenState hgs;
|
|
OutBuffer buf;
|
|
.toCBuffer(this, &buf, &hgs);
|
|
return buf.extractString();
|
|
}
|
|
|
|
final void error(const(char)* format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
.verror(loc, format, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
final void warning(const(char)* format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
.vwarning(loc, format, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
final void deprecation(const(char)* format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
.vdeprecation(loc, format, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
Statement semantic(Scope* sc)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
// Same as semanticNoScope(), but do create a new scope
|
|
final Statement semanticScope(Scope* sc, Statement sbreak, Statement scontinue)
|
|
{
|
|
Scope* scd = sc.push();
|
|
if (sbreak)
|
|
scd.sbreak = sbreak;
|
|
if (scontinue)
|
|
scd.scontinue = scontinue;
|
|
Statement s = semanticNoScope(scd);
|
|
scd.pop();
|
|
return s;
|
|
}
|
|
|
|
final Statement semanticNoScope(Scope* sc)
|
|
{
|
|
//printf("Statement::semanticNoScope() %s\n", toChars());
|
|
Statement s = this;
|
|
if (!s.isCompoundStatement() && !s.isScopeStatement())
|
|
{
|
|
s = new CompoundStatement(loc, this); // so scopeCode() gets called
|
|
}
|
|
s = s.semantic(sc);
|
|
return s;
|
|
}
|
|
|
|
Statement getRelatedLabeled()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
bool hasBreak()
|
|
{
|
|
//printf("Statement::hasBreak()\n");
|
|
return false;
|
|
}
|
|
|
|
bool hasContinue()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* ============================================== */
|
|
// true if statement uses exception handling
|
|
final bool usesEH()
|
|
{
|
|
extern (C++) final class UsesEH : StoppableVisitor
|
|
{
|
|
alias visit = super.visit;
|
|
public:
|
|
override void visit(Statement s)
|
|
{
|
|
}
|
|
|
|
override void visit(TryCatchStatement s)
|
|
{
|
|
stop = true;
|
|
}
|
|
|
|
override void visit(TryFinallyStatement s)
|
|
{
|
|
stop = true;
|
|
}
|
|
|
|
override void visit(OnScopeStatement s)
|
|
{
|
|
stop = true;
|
|
}
|
|
|
|
override void visit(SynchronizedStatement s)
|
|
{
|
|
stop = true;
|
|
}
|
|
}
|
|
|
|
scope UsesEH ueh = new UsesEH();
|
|
return walkPostorder(this, ueh);
|
|
}
|
|
|
|
/* ============================================== */
|
|
/* Only valid after semantic analysis
|
|
* If 'mustNotThrow' is true, generate an error if it throws
|
|
*/
|
|
final int blockExit(FuncDeclaration func, bool mustNotThrow)
|
|
{
|
|
extern (C++) final class BlockExit : Visitor
|
|
{
|
|
alias visit = super.visit;
|
|
public:
|
|
FuncDeclaration func;
|
|
bool mustNotThrow;
|
|
int result;
|
|
|
|
extern (D) this(FuncDeclaration func, bool mustNotThrow)
|
|
{
|
|
this.func = func;
|
|
this.mustNotThrow = mustNotThrow;
|
|
result = BEnone;
|
|
}
|
|
|
|
override void visit(Statement s)
|
|
{
|
|
printf("Statement::blockExit(%p)\n", s);
|
|
printf("%s\n", s.toChars());
|
|
assert(0);
|
|
}
|
|
|
|
override void visit(ErrorStatement s)
|
|
{
|
|
result = BEany;
|
|
}
|
|
|
|
override void visit(ExpStatement s)
|
|
{
|
|
result = BEfallthru;
|
|
if (s.exp)
|
|
{
|
|
if (s.exp.op == TOKhalt)
|
|
{
|
|
result = BEhalt;
|
|
return;
|
|
}
|
|
if (s.exp.op == TOKassert)
|
|
{
|
|
AssertExp a = cast(AssertExp)s.exp;
|
|
if (a.e1.isBool(false)) // if it's an assert(0)
|
|
{
|
|
result = BEhalt;
|
|
return;
|
|
}
|
|
}
|
|
if (canThrow(s.exp, func, mustNotThrow))
|
|
result |= BEthrow;
|
|
}
|
|
}
|
|
|
|
override void visit(CompileStatement s)
|
|
{
|
|
assert(global.errors);
|
|
result = BEfallthru;
|
|
}
|
|
|
|
override void visit(CompoundStatement cs)
|
|
{
|
|
//printf("CompoundStatement::blockExit(%p) %d\n", cs, cs->statements->dim);
|
|
result = BEfallthru;
|
|
Statement slast = null;
|
|
foreach (s; *cs.statements)
|
|
{
|
|
if (s)
|
|
{
|
|
//printf("result = x%x\n", result);
|
|
//printf("s: %s\n", s->toChars());
|
|
if (global.params.warnings && result & BEfallthru && slast)
|
|
{
|
|
slast = slast.last();
|
|
if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement()))
|
|
{
|
|
// Allow if last case/default was empty
|
|
CaseStatement sc = slast.isCaseStatement();
|
|
DefaultStatement sd = slast.isDefaultStatement();
|
|
if (sc && (!sc.statement.hasCode() || sc.statement.isCaseStatement() || sc.statement.isErrorStatement()))
|
|
{
|
|
}
|
|
else if (sd && (!sd.statement.hasCode() || sd.statement.isCaseStatement() || sd.statement.isErrorStatement()))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
const(char)* gototype = s.isCaseStatement() ? "case" : "default";
|
|
s.warning("switch case fallthrough - use 'goto %s;' if intended", gototype);
|
|
}
|
|
}
|
|
}
|
|
if (!(result & BEfallthru) && !s.comeFrom())
|
|
{
|
|
if (s.blockExit(func, mustNotThrow) != BEhalt && s.hasCode())
|
|
s.warning("statement is not reachable");
|
|
}
|
|
else
|
|
{
|
|
result &= ~BEfallthru;
|
|
result |= s.blockExit(func, mustNotThrow);
|
|
}
|
|
slast = s;
|
|
}
|
|
}
|
|
}
|
|
|
|
override void visit(UnrolledLoopStatement uls)
|
|
{
|
|
result = BEfallthru;
|
|
foreach (s; *uls.statements)
|
|
{
|
|
if (s)
|
|
{
|
|
int r = s.blockExit(func, mustNotThrow);
|
|
result |= r & ~(BEbreak | BEcontinue | BEfallthru);
|
|
if ((r & (BEfallthru | BEcontinue | BEbreak)) == 0)
|
|
result &= ~BEfallthru;
|
|
}
|
|
}
|
|
}
|
|
|
|
override void visit(ScopeStatement s)
|
|
{
|
|
//printf("ScopeStatement::blockExit(%p)\n", s->statement);
|
|
result = s.statement ? s.statement.blockExit(func, mustNotThrow) : BEfallthru;
|
|
}
|
|
|
|
override void visit(WhileStatement s)
|
|
{
|
|
assert(global.errors);
|
|
result = BEfallthru;
|
|
}
|
|
|
|
override void visit(DoStatement s)
|
|
{
|
|
if (s._body)
|
|
{
|
|
result = s._body.blockExit(func, mustNotThrow);
|
|
if (result == BEbreak)
|
|
{
|
|
result = BEfallthru;
|
|
return;
|
|
}
|
|
if (result & BEcontinue)
|
|
result |= BEfallthru;
|
|
}
|
|
else
|
|
result = BEfallthru;
|
|
if (result & BEfallthru)
|
|
{
|
|
if (canThrow(s.condition, func, mustNotThrow))
|
|
result |= BEthrow;
|
|
if (!(result & BEbreak) && s.condition.isBool(true))
|
|
result &= ~BEfallthru;
|
|
}
|
|
result &= ~(BEbreak | BEcontinue);
|
|
}
|
|
|
|
override void visit(ForStatement s)
|
|
{
|
|
result = BEfallthru;
|
|
if (s._init)
|
|
{
|
|
result = s._init.blockExit(func, mustNotThrow);
|
|
if (!(result & BEfallthru))
|
|
return;
|
|
}
|
|
if (s.condition)
|
|
{
|
|
if (canThrow(s.condition, func, mustNotThrow))
|
|
result |= BEthrow;
|
|
if (s.condition.isBool(true))
|
|
result &= ~BEfallthru;
|
|
else if (s.condition.isBool(false))
|
|
return;
|
|
}
|
|
else
|
|
result &= ~BEfallthru; // the body must do the exiting
|
|
if (s._body)
|
|
{
|
|
int r = s._body.blockExit(func, mustNotThrow);
|
|
if (r & (BEbreak | BEgoto))
|
|
result |= BEfallthru;
|
|
result |= r & ~(BEfallthru | BEbreak | BEcontinue);
|
|
}
|
|
if (s.increment && canThrow(s.increment, func, mustNotThrow))
|
|
result |= BEthrow;
|
|
}
|
|
|
|
override void visit(ForeachStatement s)
|
|
{
|
|
result = BEfallthru;
|
|
if (canThrow(s.aggr, func, mustNotThrow))
|
|
result |= BEthrow;
|
|
if (s._body)
|
|
result |= s._body.blockExit(func, mustNotThrow) & ~(BEbreak | BEcontinue);
|
|
}
|
|
|
|
override void visit(ForeachRangeStatement s)
|
|
{
|
|
assert(global.errors);
|
|
result = BEfallthru;
|
|
}
|
|
|
|
override void visit(IfStatement s)
|
|
{
|
|
//printf("IfStatement::blockExit(%p)\n", s);
|
|
result = BEnone;
|
|
if (canThrow(s.condition, func, mustNotThrow))
|
|
result |= BEthrow;
|
|
if (s.condition.isBool(true))
|
|
{
|
|
if (s.ifbody)
|
|
result |= s.ifbody.blockExit(func, mustNotThrow);
|
|
else
|
|
result |= BEfallthru;
|
|
}
|
|
else if (s.condition.isBool(false))
|
|
{
|
|
if (s.elsebody)
|
|
result |= s.elsebody.blockExit(func, mustNotThrow);
|
|
else
|
|
result |= BEfallthru;
|
|
}
|
|
else
|
|
{
|
|
if (s.ifbody)
|
|
result |= s.ifbody.blockExit(func, mustNotThrow);
|
|
else
|
|
result |= BEfallthru;
|
|
if (s.elsebody)
|
|
result |= s.elsebody.blockExit(func, mustNotThrow);
|
|
else
|
|
result |= BEfallthru;
|
|
}
|
|
//printf("IfStatement::blockExit(%p) = x%x\n", s, result);
|
|
}
|
|
|
|
override void visit(ConditionalStatement s)
|
|
{
|
|
result = s.ifbody.blockExit(func, mustNotThrow);
|
|
if (s.elsebody)
|
|
result |= s.elsebody.blockExit(func, mustNotThrow);
|
|
}
|
|
|
|
override void visit(PragmaStatement s)
|
|
{
|
|
result = BEfallthru;
|
|
}
|
|
|
|
override void visit(StaticAssertStatement s)
|
|
{
|
|
result = BEfallthru;
|
|
}
|
|
|
|
override void visit(SwitchStatement s)
|
|
{
|
|
result = BEnone;
|
|
if (canThrow(s.condition, func, mustNotThrow))
|
|
result |= BEthrow;
|
|
if (s._body)
|
|
{
|
|
result |= s._body.blockExit(func, mustNotThrow);
|
|
if (result & BEbreak)
|
|
{
|
|
result |= BEfallthru;
|
|
result &= ~BEbreak;
|
|
}
|
|
}
|
|
else
|
|
result |= BEfallthru;
|
|
}
|
|
|
|
override void visit(CaseStatement s)
|
|
{
|
|
result = s.statement.blockExit(func, mustNotThrow);
|
|
}
|
|
|
|
override void visit(DefaultStatement s)
|
|
{
|
|
result = s.statement.blockExit(func, mustNotThrow);
|
|
}
|
|
|
|
override void visit(GotoDefaultStatement s)
|
|
{
|
|
result = BEgoto;
|
|
}
|
|
|
|
override void visit(GotoCaseStatement s)
|
|
{
|
|
result = BEgoto;
|
|
}
|
|
|
|
override void visit(SwitchErrorStatement s)
|
|
{
|
|
// Switch errors are non-recoverable
|
|
result = BEhalt;
|
|
}
|
|
|
|
override void visit(ReturnStatement s)
|
|
{
|
|
result = BEreturn;
|
|
if (s.exp && canThrow(s.exp, func, mustNotThrow))
|
|
result |= BEthrow;
|
|
}
|
|
|
|
override void visit(BreakStatement s)
|
|
{
|
|
//printf("BreakStatement::blockExit(%p) = x%x\n", s, s->ident ? BEgoto : BEbreak);
|
|
result = s.ident ? BEgoto : BEbreak;
|
|
}
|
|
|
|
override void visit(ContinueStatement s)
|
|
{
|
|
result = s.ident ? BEgoto : BEcontinue;
|
|
}
|
|
|
|
override void visit(SynchronizedStatement s)
|
|
{
|
|
result = s._body ? s._body.blockExit(func, mustNotThrow) : BEfallthru;
|
|
}
|
|
|
|
override void visit(WithStatement s)
|
|
{
|
|
result = BEnone;
|
|
if (canThrow(s.exp, func, mustNotThrow))
|
|
result = BEthrow;
|
|
if (s._body)
|
|
result |= s._body.blockExit(func, mustNotThrow);
|
|
else
|
|
result |= BEfallthru;
|
|
}
|
|
|
|
override void visit(TryCatchStatement s)
|
|
{
|
|
assert(s._body);
|
|
result = s._body.blockExit(func, false);
|
|
int catchresult = 0;
|
|
foreach (c; *s.catches)
|
|
{
|
|
if (c.type == Type.terror)
|
|
continue;
|
|
int cresult;
|
|
if (c.handler)
|
|
cresult = c.handler.blockExit(func, mustNotThrow);
|
|
else
|
|
cresult = BEfallthru;
|
|
/* If we're catching Object, then there is no throwing
|
|
*/
|
|
Identifier id = c.type.toBasetype().isClassHandle().ident;
|
|
if (c.internalCatch && (cresult & BEfallthru))
|
|
{
|
|
// Bugzilla 11542: leave blockExit flags of the body
|
|
cresult &= ~BEfallthru;
|
|
}
|
|
else if (id == Id.Object || id == Id.Throwable)
|
|
{
|
|
result &= ~(BEthrow | BEerrthrow);
|
|
}
|
|
else if (id == Id.Exception)
|
|
{
|
|
result &= ~BEthrow;
|
|
}
|
|
catchresult |= cresult;
|
|
}
|
|
if (mustNotThrow && (result & BEthrow))
|
|
{
|
|
// now explain why this is nothrow
|
|
s._body.blockExit(func, mustNotThrow);
|
|
}
|
|
result |= catchresult;
|
|
}
|
|
|
|
override void visit(TryFinallyStatement s)
|
|
{
|
|
result = BEfallthru;
|
|
if (s._body)
|
|
result = s._body.blockExit(func, false);
|
|
// check finally body as well, it may throw (bug #4082)
|
|
int finalresult = BEfallthru;
|
|
if (s.finalbody)
|
|
finalresult = s.finalbody.blockExit(func, false);
|
|
// If either body or finalbody halts
|
|
if (result == BEhalt)
|
|
finalresult = BEnone;
|
|
if (finalresult == BEhalt)
|
|
result = BEnone;
|
|
if (mustNotThrow)
|
|
{
|
|
// now explain why this is nothrow
|
|
if (s._body && (result & BEthrow))
|
|
s._body.blockExit(func, mustNotThrow);
|
|
if (s.finalbody && (finalresult & BEthrow))
|
|
s.finalbody.blockExit(func, mustNotThrow);
|
|
}
|
|
version (none)
|
|
{
|
|
// Bugzilla 13201: Mask to prevent spurious warnings for
|
|
// destructor call, exit of synchronized statement, etc.
|
|
if (result == BEhalt && finalresult != BEhalt && s.finalbody && s.finalbody.hasCode())
|
|
{
|
|
s.finalbody.warning("statement is not reachable");
|
|
}
|
|
}
|
|
if (!(finalresult & BEfallthru))
|
|
result &= ~BEfallthru;
|
|
result |= finalresult & ~BEfallthru;
|
|
}
|
|
|
|
override void visit(OnScopeStatement s)
|
|
{
|
|
// At this point, this statement is just an empty placeholder
|
|
result = BEfallthru;
|
|
}
|
|
|
|
override void visit(ThrowStatement s)
|
|
{
|
|
if (s.internalThrow)
|
|
{
|
|
// Bugzilla 8675: Allow throwing 'Throwable' object even if mustNotThrow.
|
|
result = BEfallthru;
|
|
return;
|
|
}
|
|
Type t = s.exp.type.toBasetype();
|
|
ClassDeclaration cd = t.isClassHandle();
|
|
assert(cd);
|
|
if (cd == ClassDeclaration.errorException || ClassDeclaration.errorException.isBaseOf(cd, null))
|
|
{
|
|
result = BEerrthrow;
|
|
return;
|
|
}
|
|
if (mustNotThrow)
|
|
s.error("%s is thrown but not caught", s.exp.type.toChars());
|
|
result = BEthrow;
|
|
}
|
|
|
|
override void visit(GotoStatement s)
|
|
{
|
|
//printf("GotoStatement::blockExit(%p)\n", s);
|
|
result = BEgoto;
|
|
}
|
|
|
|
override void visit(LabelStatement s)
|
|
{
|
|
//printf("LabelStatement::blockExit(%p)\n", s);
|
|
result = s.statement ? s.statement.blockExit(func, mustNotThrow) : BEfallthru;
|
|
if (s.breaks)
|
|
result |= BEfallthru;
|
|
}
|
|
|
|
override void visit(CompoundAsmStatement s)
|
|
{
|
|
if (mustNotThrow && !(s.stc & STCnothrow))
|
|
s.deprecation("asm statement is assumed to throw - mark it with 'nothrow' if it does not");
|
|
// Assume the worst
|
|
result = BEfallthru | BEreturn | BEgoto | BEhalt;
|
|
if (!(s.stc & STCnothrow))
|
|
result |= BEthrow;
|
|
}
|
|
|
|
override void visit(ImportStatement s)
|
|
{
|
|
result = BEfallthru;
|
|
}
|
|
}
|
|
|
|
scope BlockExit be = new BlockExit(func, mustNotThrow);
|
|
accept(be);
|
|
return be.result;
|
|
}
|
|
|
|
/* ============================================== */
|
|
// true if statement 'comes from' somewhere else, like a goto
|
|
final bool comeFrom()
|
|
{
|
|
extern (C++) final class ComeFrom : StoppableVisitor
|
|
{
|
|
alias visit = super.visit;
|
|
public:
|
|
override void visit(Statement s)
|
|
{
|
|
}
|
|
|
|
override void visit(CaseStatement s)
|
|
{
|
|
stop = true;
|
|
}
|
|
|
|
override void visit(DefaultStatement s)
|
|
{
|
|
stop = true;
|
|
}
|
|
|
|
override void visit(LabelStatement s)
|
|
{
|
|
stop = true;
|
|
}
|
|
|
|
override void visit(AsmStatement s)
|
|
{
|
|
stop = true;
|
|
}
|
|
}
|
|
|
|
scope ComeFrom cf = new ComeFrom();
|
|
return walkPostorder(this, cf);
|
|
}
|
|
|
|
/* ============================================== */
|
|
// Return true if statement has executable code.
|
|
final bool hasCode()
|
|
{
|
|
extern (C++) final class HasCode : StoppableVisitor
|
|
{
|
|
alias visit = super.visit;
|
|
public:
|
|
override void visit(Statement s)
|
|
{
|
|
stop = true;
|
|
}
|
|
|
|
override void visit(ExpStatement s)
|
|
{
|
|
stop = s.exp !is null;
|
|
}
|
|
|
|
override void visit(CompoundStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(ScopeStatement s)
|
|
{
|
|
}
|
|
|
|
override void visit(ImportStatement s)
|
|
{
|
|
}
|
|
}
|
|
|
|
scope HasCode hc = new HasCode();
|
|
return walkPostorder(this, hc);
|
|
}
|
|
|
|
/****************************************
|
|
* If this statement has code that needs to run in a finally clause
|
|
* at the end of the current scope, return that code in the form of
|
|
* a Statement.
|
|
* Output:
|
|
* *sentry code executed upon entry to the scope
|
|
* *sexception code executed upon exit from the scope via exception
|
|
* *sfinally code executed in finally block
|
|
*/
|
|
Statement scopeCode(Scope* sc, Statement* sentry, Statement* sexception, Statement* sfinally)
|
|
{
|
|
//printf("Statement::scopeCode()\n");
|
|
//print();
|
|
*sentry = null;
|
|
*sexception = null;
|
|
*sfinally = null;
|
|
return this;
|
|
}
|
|
|
|
/*********************************
|
|
* Flatten out the scope by presenting the statement
|
|
* as an array of statements.
|
|
* Returns NULL if no flattening necessary.
|
|
*/
|
|
Statements* flatten(Scope* sc)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Statement last()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
// Avoid dynamic_cast
|
|
ErrorStatement isErrorStatement()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
ScopeStatement isScopeStatement()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
ExpStatement isExpStatement()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
CompoundStatement isCompoundStatement()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
ReturnStatement isReturnStatement()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
IfStatement isIfStatement()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
CaseStatement isCaseStatement()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
DefaultStatement isDefaultStatement()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
LabelStatement isLabelStatement()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
DtorExpStatement isDtorExpStatement()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
CompoundAsmStatement isCompoundAsmBlockStatement()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
CompoundAsmStatement endsWithAsm()
|
|
{
|
|
// does not end with inline asm
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* Any Statement that fails semantic() or has a component that is an ErrorExp or
|
|
* a TypeError should return an ErrorStatement from semantic().
|
|
*/
|
|
extern (C++) final class ErrorStatement : Statement
|
|
{
|
|
public:
|
|
extern (D) this()
|
|
{
|
|
super(Loc());
|
|
assert(global.gaggedErrors || global.errors);
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override ErrorStatement isErrorStatement()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class PeelStatement : Statement
|
|
{
|
|
public:
|
|
Statement s;
|
|
|
|
extern (D) this(Statement s)
|
|
{
|
|
super(s.loc);
|
|
this.s = s;
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
/* "peel" off this wrapper, and don't run semantic()
|
|
* on the result.
|
|
*/
|
|
return s;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* Convert TemplateMixin members (== Dsymbols) to Statements.
|
|
*/
|
|
extern (C++) Statement toStatement(Dsymbol s)
|
|
{
|
|
extern (C++) final class ToStmt : Visitor
|
|
{
|
|
alias visit = super.visit;
|
|
public:
|
|
Statement result;
|
|
|
|
Statement visitMembers(Loc loc, Dsymbols* a)
|
|
{
|
|
if (!a)
|
|
return null;
|
|
auto statements = new Statements();
|
|
foreach (s; *a)
|
|
{
|
|
statements.push(toStatement(s));
|
|
}
|
|
return new CompoundStatement(loc, statements);
|
|
}
|
|
|
|
override void visit(Dsymbol s)
|
|
{
|
|
.error(Loc(), "Internal Compiler Error: cannot mixin %s %s\n", s.kind(), s.toChars());
|
|
result = new ErrorStatement();
|
|
}
|
|
|
|
override void visit(TemplateMixin tm)
|
|
{
|
|
auto a = new Statements();
|
|
foreach (m; *tm.members)
|
|
{
|
|
Statement s = toStatement(m);
|
|
if (s)
|
|
a.push(s);
|
|
}
|
|
result = new CompoundStatement(tm.loc, a);
|
|
}
|
|
|
|
/* An actual declaration symbol will be converted to DeclarationExp
|
|
* with ExpStatement.
|
|
*/
|
|
Statement declStmt(Dsymbol s)
|
|
{
|
|
auto de = new DeclarationExp(s.loc, s);
|
|
de.type = Type.tvoid; // avoid repeated semantic
|
|
return new ExpStatement(s.loc, de);
|
|
}
|
|
|
|
override void visit(VarDeclaration d)
|
|
{
|
|
result = declStmt(d);
|
|
}
|
|
|
|
override void visit(AggregateDeclaration d)
|
|
{
|
|
result = declStmt(d);
|
|
}
|
|
|
|
override void visit(FuncDeclaration d)
|
|
{
|
|
result = declStmt(d);
|
|
}
|
|
|
|
override void visit(EnumDeclaration d)
|
|
{
|
|
result = declStmt(d);
|
|
}
|
|
|
|
override void visit(AliasDeclaration d)
|
|
{
|
|
result = declStmt(d);
|
|
}
|
|
|
|
override void visit(TemplateDeclaration d)
|
|
{
|
|
result = declStmt(d);
|
|
}
|
|
|
|
/* All attributes have been already picked by the semantic analysis of
|
|
* 'bottom' declarations (function, struct, class, etc).
|
|
* So we don't have to copy them.
|
|
*/
|
|
override void visit(StorageClassDeclaration d)
|
|
{
|
|
result = visitMembers(d.loc, d.decl);
|
|
}
|
|
|
|
override void visit(DeprecatedDeclaration d)
|
|
{
|
|
result = visitMembers(d.loc, d.decl);
|
|
}
|
|
|
|
override void visit(LinkDeclaration d)
|
|
{
|
|
result = visitMembers(d.loc, d.decl);
|
|
}
|
|
|
|
override void visit(ProtDeclaration d)
|
|
{
|
|
result = visitMembers(d.loc, d.decl);
|
|
}
|
|
|
|
override void visit(AlignDeclaration d)
|
|
{
|
|
result = visitMembers(d.loc, d.decl);
|
|
}
|
|
|
|
override void visit(UserAttributeDeclaration d)
|
|
{
|
|
result = visitMembers(d.loc, d.decl);
|
|
}
|
|
|
|
override void visit(StaticAssert s)
|
|
{
|
|
}
|
|
|
|
override void visit(Import s)
|
|
{
|
|
}
|
|
|
|
override void visit(PragmaDeclaration d)
|
|
{
|
|
}
|
|
|
|
override void visit(ConditionalDeclaration d)
|
|
{
|
|
result = visitMembers(d.loc, d.include(null, null));
|
|
}
|
|
|
|
override void visit(CompileDeclaration d)
|
|
{
|
|
result = visitMembers(d.loc, d.include(null, null));
|
|
}
|
|
}
|
|
|
|
if (!s)
|
|
return null;
|
|
scope ToStmt v = new ToStmt();
|
|
s.accept(v);
|
|
return v.result;
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class ExpStatement : Statement
|
|
{
|
|
public:
|
|
Expression exp;
|
|
|
|
final extern (D) this(Loc loc, Expression exp)
|
|
{
|
|
super(loc);
|
|
this.exp = exp;
|
|
}
|
|
|
|
final extern (D) this(Loc loc, Dsymbol declaration)
|
|
{
|
|
super(loc);
|
|
this.exp = new DeclarationExp(loc, declaration);
|
|
}
|
|
|
|
final static ExpStatement create(Loc loc, Expression exp)
|
|
{
|
|
return new ExpStatement(loc, exp);
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new ExpStatement(loc, exp ? exp.syntaxCopy() : null);
|
|
}
|
|
|
|
override final Statement semantic(Scope* sc)
|
|
{
|
|
if (exp)
|
|
{
|
|
//printf("ExpStatement::semantic() %s\n", exp.toChars());
|
|
exp = exp.semantic(sc);
|
|
exp = resolveProperties(sc, exp);
|
|
exp = exp.addDtorHook(sc);
|
|
if (checkNonAssignmentArrayOp(exp))
|
|
exp = new ErrorExp();
|
|
if (auto f = isFuncAddress(exp))
|
|
{
|
|
if (f.checkForwardRef(exp.loc))
|
|
exp = new ErrorExp();
|
|
}
|
|
discardValue(exp);
|
|
|
|
exp = exp.optimize(WANTvalue);
|
|
exp = checkGC(sc, exp);
|
|
if (exp.op == TOKerror)
|
|
return new ErrorStatement();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override final Statement scopeCode(Scope* sc, Statement* sentry, Statement* sexception, Statement* sfinally)
|
|
{
|
|
//printf("ExpStatement::scopeCode()\n");
|
|
//print();
|
|
*sentry = null;
|
|
*sexception = null;
|
|
*sfinally = null;
|
|
if (exp && exp.op == TOKdeclaration)
|
|
{
|
|
auto de = cast(DeclarationExp)exp;
|
|
auto v = de.declaration.isVarDeclaration();
|
|
if (v && !v.isDataseg())
|
|
{
|
|
if (v.needsScopeDtor())
|
|
{
|
|
//printf("dtor is: "); v.edtor.print();
|
|
*sfinally = new DtorExpStatement(loc, v.edtor, v);
|
|
v.noscope = true; // don't add in dtor again
|
|
}
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override final Statements* flatten(Scope* sc)
|
|
{
|
|
/* Bugzilla 14243: expand template mixin in statement scope
|
|
* to handle variable destructors.
|
|
*/
|
|
if (exp && exp.op == TOKdeclaration)
|
|
{
|
|
Dsymbol d = (cast(DeclarationExp)exp).declaration;
|
|
if (TemplateMixin tm = d.isTemplateMixin())
|
|
{
|
|
Expression e = exp.semantic(sc);
|
|
if (e.op == TOKerror || tm.errors)
|
|
{
|
|
auto a = new Statements();
|
|
a.push(new ErrorStatement());
|
|
return a;
|
|
}
|
|
assert(tm.members);
|
|
Statement s = toStatement(tm);
|
|
version (none)
|
|
{
|
|
OutBuffer buf;
|
|
buf.doindent = 1;
|
|
HdrGenState hgs;
|
|
hgs.hdrgen = true;
|
|
toCBuffer(s, &buf, &hgs);
|
|
printf("tm ==> s = %s\n", buf.peekString());
|
|
}
|
|
auto a = new Statements();
|
|
a.push(s);
|
|
return a;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
override final ExpStatement isExpStatement()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class DtorExpStatement : ExpStatement
|
|
{
|
|
public:
|
|
// Wraps an expression that is the destruction of 'var'
|
|
VarDeclaration var;
|
|
|
|
extern (D) this(Loc loc, Expression exp, VarDeclaration v)
|
|
{
|
|
super(loc, exp);
|
|
this.var = v;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new DtorExpStatement(loc, exp ? exp.syntaxCopy() : null, var);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
|
|
override DtorExpStatement isDtorExpStatement()
|
|
{
|
|
return this;
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class CompileStatement : Statement
|
|
{
|
|
public:
|
|
Expression exp;
|
|
|
|
extern (D) this(Loc loc, Expression exp)
|
|
{
|
|
super(loc);
|
|
this.exp = exp;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new CompileStatement(loc, exp.syntaxCopy());
|
|
}
|
|
|
|
override Statements* flatten(Scope* sc)
|
|
{
|
|
//printf("CompileStatement::flatten() %s\n", exp->toChars());
|
|
sc = sc.startCTFE();
|
|
exp = exp.semantic(sc);
|
|
exp = resolveProperties(sc, exp);
|
|
sc = sc.endCTFE();
|
|
auto a = new Statements();
|
|
if (exp.op != TOKerror)
|
|
{
|
|
Expression e = exp.ctfeInterpret();
|
|
StringExp se = e.toStringExp();
|
|
if (!se)
|
|
error("argument to mixin must be a string, not (%s) of type %s", exp.toChars(), exp.type.toChars());
|
|
else
|
|
{
|
|
se = se.toUTF8(sc);
|
|
uint errors = global.errors;
|
|
scope Parser p = new Parser(loc, sc._module, se.toStringz(), se.len, 0);
|
|
p.nextToken();
|
|
while (p.token.value != TOKeof)
|
|
{
|
|
Statement s = p.parseStatement(PSsemi | PScurlyscope);
|
|
if (!s || p.errors)
|
|
{
|
|
assert(!p.errors || global.errors != errors); // make sure we caught all the cases
|
|
goto Lerror;
|
|
}
|
|
a.push(s);
|
|
}
|
|
return a;
|
|
}
|
|
}
|
|
Lerror:
|
|
a.push(new ErrorStatement());
|
|
return a;
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("CompileStatement::semantic() %s\n", exp->toChars());
|
|
Statements* a = flatten(sc);
|
|
if (!a)
|
|
return null;
|
|
Statement s = new CompoundStatement(loc, a);
|
|
return s.semantic(sc);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) class CompoundStatement : Statement
|
|
{
|
|
public:
|
|
Statements* statements;
|
|
|
|
final extern (D) this(Loc loc, Statements* s)
|
|
{
|
|
super(loc);
|
|
statements = s;
|
|
}
|
|
|
|
final extern (D) this(Loc loc, Statement s1)
|
|
{
|
|
super(loc);
|
|
statements = new Statements();
|
|
statements.push(s1);
|
|
}
|
|
|
|
final extern (D) this(Loc loc, Statement s1, Statement s2)
|
|
{
|
|
super(loc);
|
|
statements = new Statements();
|
|
statements.reserve(2);
|
|
statements.push(s1);
|
|
statements.push(s2);
|
|
}
|
|
|
|
final static CompoundStatement create(Loc loc, Statement s1, Statement s2)
|
|
{
|
|
return new CompoundStatement(loc, s1, s2);
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
auto a = new Statements();
|
|
a.setDim(statements.dim);
|
|
foreach (i, s; *statements)
|
|
{
|
|
(*a)[i] = s ? s.syntaxCopy() : null;
|
|
}
|
|
return new CompoundStatement(loc, a);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("CompoundStatement::semantic(this = %p, sc = %p)\n", this, sc);
|
|
version (none)
|
|
{
|
|
foreach (i, s; statements)
|
|
{
|
|
if (s)
|
|
printf("[%d]: %s", i, s.toChars());
|
|
}
|
|
}
|
|
for (size_t i = 0; i < statements.dim;)
|
|
{
|
|
Statement s = (*statements)[i];
|
|
if (s)
|
|
{
|
|
Statements* flt = s.flatten(sc);
|
|
if (flt)
|
|
{
|
|
statements.remove(i);
|
|
statements.insert(i, flt);
|
|
continue;
|
|
}
|
|
s = s.semantic(sc);
|
|
(*statements)[i] = s;
|
|
if (s)
|
|
{
|
|
Statement sentry;
|
|
Statement sexception;
|
|
Statement sfinally;
|
|
(*statements)[i] = s.scopeCode(sc, &sentry, &sexception, &sfinally);
|
|
if (sentry)
|
|
{
|
|
sentry = sentry.semantic(sc);
|
|
statements.insert(i, sentry);
|
|
i++;
|
|
}
|
|
if (sexception)
|
|
sexception = sexception.semantic(sc);
|
|
if (sexception)
|
|
{
|
|
if (i + 1 == statements.dim && !sfinally)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
/* Rewrite:
|
|
* s; s1; s2;
|
|
* As:
|
|
* s;
|
|
* try { s1; s2; }
|
|
* catch (Throwable __o)
|
|
* { sexception; throw __o; }
|
|
*/
|
|
auto a = new Statements();
|
|
foreach (j; i + 1 .. statements.dim)
|
|
{
|
|
a.push((*statements)[j]);
|
|
}
|
|
Statement _body = new CompoundStatement(Loc(), a);
|
|
_body = new ScopeStatement(Loc(), _body);
|
|
Identifier id = Identifier.generateId("__o");
|
|
Statement handler = new PeelStatement(sexception);
|
|
if (sexception.blockExit(sc.func, false) & BEfallthru)
|
|
{
|
|
auto ts = new ThrowStatement(Loc(), new IdentifierExp(Loc(), id));
|
|
ts.internalThrow = true;
|
|
handler = new CompoundStatement(Loc(), handler, ts);
|
|
}
|
|
auto catches = new Catches();
|
|
auto ctch = new Catch(Loc(), null, id, handler);
|
|
ctch.internalCatch = true;
|
|
catches.push(ctch);
|
|
s = new TryCatchStatement(Loc(), _body, catches);
|
|
if (sfinally)
|
|
s = new TryFinallyStatement(Loc(), s, sfinally);
|
|
s = s.semantic(sc);
|
|
statements.setDim(i + 1);
|
|
statements.push(s);
|
|
break;
|
|
}
|
|
}
|
|
else if (sfinally)
|
|
{
|
|
if (0 && i + 1 == statements.dim)
|
|
{
|
|
statements.push(sfinally);
|
|
}
|
|
else
|
|
{
|
|
/* Rewrite:
|
|
* s; s1; s2;
|
|
* As:
|
|
* s; try { s1; s2; } finally { sfinally; }
|
|
*/
|
|
auto a = new Statements();
|
|
foreach (j; i + 1 .. statements.dim)
|
|
{
|
|
a.push((*statements)[j]);
|
|
}
|
|
Statement _body = new CompoundStatement(Loc(), a);
|
|
s = new TryFinallyStatement(Loc(), _body, sfinally);
|
|
s = s.semantic(sc);
|
|
statements.setDim(i + 1);
|
|
statements.push(s);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Remove NULL statements from the list.
|
|
*/
|
|
statements.remove(i);
|
|
continue;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
foreach (i; 0 .. statements.dim)
|
|
{
|
|
Lagain:
|
|
Statement s = (*statements)[i];
|
|
if (!s)
|
|
continue;
|
|
Statement se = s.isErrorStatement();
|
|
if (se)
|
|
return se;
|
|
/* Bugzilla 11653: 'semantic' may return another CompoundStatement
|
|
* (eg. CaseRangeStatement), so flatten it here.
|
|
*/
|
|
Statements* flt = s.flatten(sc);
|
|
if (flt)
|
|
{
|
|
statements.remove(i);
|
|
statements.insert(i, flt);
|
|
if (statements.dim <= i)
|
|
break;
|
|
goto Lagain;
|
|
}
|
|
}
|
|
//IN_LLVM replaced: if (statements.dim == 1)
|
|
if (statements.dim == 1 && !isCompoundAsmBlockStatement())
|
|
{
|
|
return (*statements)[0];
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override Statements* flatten(Scope* sc)
|
|
{
|
|
return statements;
|
|
}
|
|
|
|
override final ReturnStatement isReturnStatement()
|
|
{
|
|
ReturnStatement rs = null;
|
|
foreach (s; *statements)
|
|
{
|
|
if (s)
|
|
{
|
|
rs = s.isReturnStatement();
|
|
if (rs)
|
|
break;
|
|
}
|
|
}
|
|
return rs;
|
|
}
|
|
|
|
override final Statement last()
|
|
{
|
|
Statement s = null;
|
|
for (size_t i = statements.dim; i; --i)
|
|
{
|
|
s = (*statements)[i - 1];
|
|
if (s)
|
|
{
|
|
s = s.last();
|
|
if (s)
|
|
break;
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// IN_LLVM removed: final
|
|
override CompoundStatement isCompoundStatement()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
override CompoundAsmStatement endsWithAsm()
|
|
{
|
|
// make the last inner statement decide
|
|
if (statements && statements.dim) {
|
|
size_t last = statements.dim - 1;
|
|
Statement s = (*statements)[last];
|
|
if (s) {
|
|
return s.endsWithAsm();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class CompoundDeclarationStatement : CompoundStatement
|
|
{
|
|
public:
|
|
extern (D) this(Loc loc, Statements* s)
|
|
{
|
|
super(loc, s);
|
|
statements = s;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
auto a = new Statements();
|
|
a.setDim(statements.dim);
|
|
foreach (i, s; *statements)
|
|
{
|
|
(*a)[i] = s ? s.syntaxCopy() : null;
|
|
}
|
|
return new CompoundDeclarationStatement(loc, a);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* The purpose of this is so that continue will go to the next
|
|
* of the statements, and break will go to the end of the statements.
|
|
*/
|
|
extern (C++) final class UnrolledLoopStatement : Statement
|
|
{
|
|
public:
|
|
Statements* statements;
|
|
|
|
extern (D) this(Loc loc, Statements* s)
|
|
{
|
|
super(loc);
|
|
statements = s;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
auto a = new Statements();
|
|
a.setDim(statements.dim);
|
|
foreach (i, s; *statements)
|
|
{
|
|
(*a)[i] = s ? s.syntaxCopy() : null;
|
|
}
|
|
return new UnrolledLoopStatement(loc, a);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", this, sc);
|
|
Scope* scd = sc.push();
|
|
scd.sbreak = this;
|
|
scd.scontinue = this;
|
|
Statement serror = null;
|
|
foreach (i, ref s; *statements)
|
|
{
|
|
if (s)
|
|
{
|
|
//printf("[%d]: %s\n", i, s->toChars());
|
|
s = s.semantic(scd);
|
|
if (s && !serror)
|
|
serror = s.isErrorStatement();
|
|
}
|
|
}
|
|
scd.pop();
|
|
return serror ? serror : this;
|
|
}
|
|
|
|
override bool hasBreak()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool hasContinue()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class ScopeStatement : Statement
|
|
{
|
|
public:
|
|
Statement statement;
|
|
|
|
extern (D) this(Loc loc, Statement s)
|
|
{
|
|
super(loc);
|
|
this.statement = s;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new ScopeStatement(loc, statement ? statement.syntaxCopy() : null);
|
|
}
|
|
|
|
override ScopeStatement isScopeStatement()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override ReturnStatement isReturnStatement()
|
|
{
|
|
if (statement)
|
|
return statement.isReturnStatement();
|
|
return null;
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
ScopeDsymbol sym;
|
|
//printf("ScopeStatement::semantic(sc = %p)\n", sc);
|
|
if (statement)
|
|
{
|
|
sym = new ScopeDsymbol();
|
|
sym.parent = sc.scopesym;
|
|
sc = sc.push(sym);
|
|
Statements* a = statement.flatten(sc);
|
|
if (a)
|
|
{
|
|
statement = new CompoundStatement(loc, a);
|
|
}
|
|
statement = statement.semantic(sc);
|
|
if (statement)
|
|
{
|
|
if (statement.isErrorStatement())
|
|
{
|
|
sc.pop();
|
|
return statement;
|
|
}
|
|
Statement sentry;
|
|
Statement sexception;
|
|
Statement sfinally;
|
|
statement = statement.scopeCode(sc, &sentry, &sexception, &sfinally);
|
|
assert(!sentry);
|
|
assert(!sexception);
|
|
if (sfinally)
|
|
{
|
|
//printf("adding sfinally\n");
|
|
sfinally = sfinally.semantic(sc);
|
|
statement = new CompoundStatement(loc, statement, sfinally);
|
|
}
|
|
}
|
|
sc.pop();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override bool hasBreak()
|
|
{
|
|
//printf("ScopeStatement::hasBreak() %s\n", toChars());
|
|
return statement ? statement.hasBreak() : false;
|
|
}
|
|
|
|
override bool hasContinue()
|
|
{
|
|
return statement ? statement.hasContinue() : false;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class WhileStatement : Statement
|
|
{
|
|
public:
|
|
Expression condition;
|
|
Statement _body;
|
|
Loc endloc; // location of closing curly bracket
|
|
|
|
extern (D) this(Loc loc, Expression c, Statement b, Loc endloc)
|
|
{
|
|
super(loc);
|
|
condition = c;
|
|
_body = b;
|
|
this.endloc = endloc;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new WhileStatement(loc,
|
|
condition.syntaxCopy(),
|
|
_body ? _body.syntaxCopy() : null,
|
|
endloc);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
/* Rewrite as a for(;condition;) loop
|
|
*/
|
|
Statement s = new ForStatement(loc, null, condition, null, _body, endloc);
|
|
s = s.semantic(sc);
|
|
return s;
|
|
}
|
|
|
|
override bool hasBreak()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool hasContinue()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class DoStatement : Statement
|
|
{
|
|
public:
|
|
Statement _body;
|
|
Expression condition;
|
|
|
|
extern (D) this(Loc loc, Statement b, Expression c)
|
|
{
|
|
super(loc);
|
|
_body = b;
|
|
condition = c;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new DoStatement(loc,
|
|
_body ? _body.syntaxCopy() : null,
|
|
condition.syntaxCopy());
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
sc.noctor++;
|
|
if (_body)
|
|
_body = _body.semanticScope(sc, this, this);
|
|
sc.noctor--;
|
|
|
|
// check in syntax level
|
|
condition = checkAssignmentAsCondition(condition);
|
|
|
|
condition = condition.semantic(sc);
|
|
condition = resolveProperties(sc, condition);
|
|
if (checkNonAssignmentArrayOp(condition))
|
|
condition = new ErrorExp();
|
|
condition = condition.optimize(WANTvalue);
|
|
condition = checkGC(sc, condition);
|
|
|
|
condition = condition.toBoolean(sc);
|
|
|
|
if (condition.op == TOKerror)
|
|
return new ErrorStatement();
|
|
if (_body && _body.isErrorStatement())
|
|
return _body;
|
|
|
|
return this;
|
|
}
|
|
|
|
override bool hasBreak()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool hasContinue()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class ForStatement : Statement
|
|
{
|
|
public:
|
|
Statement _init;
|
|
Expression condition;
|
|
Expression increment;
|
|
Statement _body;
|
|
Loc endloc; // location of closing curly bracket
|
|
|
|
// When wrapped in try/finally clauses, this points to the outermost one,
|
|
// which may have an associated label. Internal break/continue statements
|
|
// treat that label as referring to this loop.
|
|
Statement relatedLabeled;
|
|
|
|
extern (D) this(Loc loc, Statement _init, Expression condition, Expression increment, Statement _body, Loc endloc)
|
|
{
|
|
super(loc);
|
|
this._init = _init;
|
|
this.condition = condition;
|
|
this.increment = increment;
|
|
this._body = _body;
|
|
this.endloc = endloc;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new ForStatement(loc,
|
|
_init ? _init.syntaxCopy() : null,
|
|
condition ? condition.syntaxCopy() : null,
|
|
increment ? increment.syntaxCopy() : null,
|
|
_body.syntaxCopy(),
|
|
endloc);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("ForStatement::semantic %s\n", toChars());
|
|
|
|
if (_init)
|
|
{
|
|
/* Rewrite:
|
|
* for (auto v1 = i1, v2 = i2; condition; increment) { ... }
|
|
* to:
|
|
* { auto v1 = i1, v2 = i2; for (; condition; increment) { ... } }
|
|
* then lowered to:
|
|
* auto v1 = i1;
|
|
* try {
|
|
* auto v2 = i2;
|
|
* try {
|
|
* for (; condition; increment) { ... }
|
|
* } finally { v2.~this(); }
|
|
* } finally { v1.~this(); }
|
|
*/
|
|
auto ainit = new Statements();
|
|
ainit.push(_init), _init = null;
|
|
ainit.push(this);
|
|
Statement s = new CompoundStatement(loc, ainit);
|
|
s = new ScopeStatement(loc, s);
|
|
s = s.semantic(sc);
|
|
if (!s.isErrorStatement())
|
|
{
|
|
if (LabelStatement ls = checkLabeledLoop(sc, this))
|
|
ls.gotoTarget = this;
|
|
relatedLabeled = s;
|
|
}
|
|
return s;
|
|
}
|
|
assert(_init is null);
|
|
|
|
auto sym = new ScopeDsymbol();
|
|
sym.parent = sc.scopesym;
|
|
sc = sc.push(sym);
|
|
|
|
sc.noctor++;
|
|
if (condition)
|
|
{
|
|
// check in syntax level
|
|
condition = checkAssignmentAsCondition(condition);
|
|
|
|
condition = condition.semantic(sc);
|
|
condition = resolveProperties(sc, condition);
|
|
if (checkNonAssignmentArrayOp(condition))
|
|
condition = new ErrorExp();
|
|
condition = condition.optimize(WANTvalue);
|
|
condition = checkGC(sc, condition);
|
|
|
|
condition = condition.toBoolean(sc);
|
|
}
|
|
if (increment)
|
|
{
|
|
increment = increment.semantic(sc);
|
|
increment = resolveProperties(sc, increment);
|
|
if (checkNonAssignmentArrayOp(increment))
|
|
increment = new ErrorExp();
|
|
increment = increment.optimize(WANTvalue);
|
|
increment = checkGC(sc, increment);
|
|
}
|
|
|
|
sc.sbreak = this;
|
|
sc.scontinue = this;
|
|
if (_body)
|
|
_body = _body.semanticNoScope(sc);
|
|
sc.noctor--;
|
|
|
|
sc.pop();
|
|
|
|
if (condition && condition.op == TOKerror ||
|
|
increment && increment.op == TOKerror ||
|
|
_body && _body.isErrorStatement())
|
|
{
|
|
return new ErrorStatement();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override Statement scopeCode(Scope* sc, Statement* sentry, Statement* sexception, Statement* sfinally)
|
|
{
|
|
//printf("ForStatement::scopeCode()\n");
|
|
Statement.scopeCode(sc, sentry, sexception, sfinally);
|
|
return this;
|
|
}
|
|
|
|
override Statement getRelatedLabeled()
|
|
{
|
|
return relatedLabeled ? relatedLabeled : this;
|
|
}
|
|
|
|
override bool hasBreak()
|
|
{
|
|
//printf("ForStatement::hasBreak()\n");
|
|
return true;
|
|
}
|
|
|
|
override bool hasContinue()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class ForeachStatement : Statement
|
|
{
|
|
public:
|
|
TOK op; // TOKforeach or TOKforeach_reverse
|
|
Parameters* parameters; // array of Parameter*'s
|
|
Expression aggr;
|
|
Statement _body;
|
|
Loc endloc; // location of closing curly bracket
|
|
|
|
VarDeclaration key;
|
|
VarDeclaration value;
|
|
|
|
FuncDeclaration func; // function we're lexically in
|
|
|
|
Statements* cases; // put breaks, continues, gotos and returns here
|
|
ScopeStatements* gotos; // forward referenced goto's go here
|
|
|
|
extern (D) this(Loc loc, TOK op, Parameters* parameters, Expression aggr, Statement _body, Loc endloc)
|
|
{
|
|
super(loc);
|
|
this.op = op;
|
|
this.parameters = parameters;
|
|
this.aggr = aggr;
|
|
this._body = _body;
|
|
this.endloc = endloc;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new ForeachStatement(loc, op,
|
|
Parameter.arraySyntaxCopy(parameters),
|
|
aggr.syntaxCopy(),
|
|
_body ? _body.syntaxCopy() : null,
|
|
endloc);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("ForeachStatement::semantic() %p\n", this);
|
|
ScopeDsymbol sym;
|
|
Statement s = this;
|
|
size_t dim = parameters.dim;
|
|
TypeAArray taa = null;
|
|
Dsymbol sapply = null;
|
|
|
|
Type tn = null;
|
|
Type tnv = null;
|
|
|
|
func = sc.func;
|
|
if (func.fes)
|
|
func = func.fes.func;
|
|
|
|
VarDeclaration vinit = null;
|
|
aggr = aggr.semantic(sc);
|
|
aggr = resolveProperties(sc, aggr);
|
|
aggr = aggr.optimize(WANTvalue);
|
|
if (aggr.op == TOKerror)
|
|
return new ErrorStatement();
|
|
Expression oaggr = aggr;
|
|
if (aggr.type && aggr.type.toBasetype().ty == Tstruct &&
|
|
aggr.op != TOKtype && !aggr.isLvalue())
|
|
{
|
|
// Bugzilla 14653: Extend the life of rvalue aggregate till the end of foreach.
|
|
vinit = new VarDeclaration(loc, aggr.type, Identifier.generateId("__aggr"), new ExpInitializer(loc, aggr));
|
|
vinit.storage_class |= STCtemp;
|
|
vinit.semantic(sc);
|
|
aggr = new VarExp(aggr.loc, vinit);
|
|
}
|
|
|
|
if (!inferAggregate(this, sc, sapply))
|
|
{
|
|
const(char)* msg = "";
|
|
if (aggr.type && isAggregate(aggr.type))
|
|
{
|
|
msg = ", define opApply(), range primitives, or use .tupleof";
|
|
}
|
|
error("invalid foreach aggregate %s%s", oaggr.toChars(), msg);
|
|
return new ErrorStatement();
|
|
}
|
|
|
|
Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors
|
|
|
|
/* Check for inference errors
|
|
*/
|
|
if (!inferApplyArgTypes(this, sc, sapply))
|
|
{
|
|
/**
|
|
Try and extract the parameter count of the opApply callback function, e.g.:
|
|
int opApply(int delegate(int, float)) => 2 args
|
|
*/
|
|
bool foundMismatch = false;
|
|
size_t foreachParamCount = 0;
|
|
if (sapplyOld)
|
|
{
|
|
if (FuncDeclaration fd = sapplyOld.isFuncDeclaration())
|
|
{
|
|
int fvarargs; // ignored (opApply shouldn't take variadics)
|
|
Parameters* fparameters = fd.getParameters(&fvarargs);
|
|
|
|
if (Parameter.dim(fparameters) == 1)
|
|
{
|
|
// first param should be the callback function
|
|
Parameter fparam = Parameter.getNth(fparameters, 0);
|
|
if ((fparam.type.ty == Tpointer ||
|
|
fparam.type.ty == Tdelegate) &&
|
|
fparam.type.nextOf().ty == Tfunction)
|
|
{
|
|
TypeFunction tf = cast(TypeFunction)fparam.type.nextOf();
|
|
foreachParamCount = Parameter.dim(tf.parameters);
|
|
foundMismatch = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//printf("dim = %d, parameters->dim = %d\n", dim, parameters->dim);
|
|
if (foundMismatch && dim != foreachParamCount)
|
|
{
|
|
const(char)* plural = foreachParamCount > 1 ? "s" : "";
|
|
error("cannot infer argument types, expected %d argument%s, not %d",
|
|
foreachParamCount, plural, dim);
|
|
}
|
|
else
|
|
error("cannot uniquely infer foreach argument types");
|
|
|
|
return new ErrorStatement();
|
|
}
|
|
|
|
Type tab = aggr.type.toBasetype();
|
|
|
|
if (tab.ty == Ttuple) // don't generate new scope for tuple loops
|
|
{
|
|
if (dim < 1 || dim > 2)
|
|
{
|
|
error("only one (value) or two (key,value) arguments for tuple foreach");
|
|
return new ErrorStatement();
|
|
}
|
|
|
|
Type paramtype = (*parameters)[dim - 1].type;
|
|
if (paramtype)
|
|
{
|
|
paramtype = paramtype.semantic(loc, sc);
|
|
if (paramtype.ty == Terror)
|
|
return new ErrorStatement();
|
|
}
|
|
|
|
TypeTuple tuple = cast(TypeTuple)tab;
|
|
auto statements = new Statements();
|
|
//printf("aggr: op = %d, %s\n", aggr->op, aggr->toChars());
|
|
size_t n;
|
|
TupleExp te = null;
|
|
if (aggr.op == TOKtuple) // expression tuple
|
|
{
|
|
te = cast(TupleExp)aggr;
|
|
n = te.exps.dim;
|
|
}
|
|
else if (aggr.op == TOKtype) // type tuple
|
|
{
|
|
n = Parameter.dim(tuple.arguments);
|
|
}
|
|
else
|
|
assert(0);
|
|
foreach (j; 0 .. n)
|
|
{
|
|
size_t k = (op == TOKforeach) ? j : n - 1 - j;
|
|
Expression e = null;
|
|
Type t = null;
|
|
if (te)
|
|
e = (*te.exps)[k];
|
|
else
|
|
t = Parameter.getNth(tuple.arguments, k).type;
|
|
Parameter p = (*parameters)[0];
|
|
auto st = new Statements();
|
|
|
|
if (dim == 2)
|
|
{
|
|
// Declare key
|
|
if (p.storageClass & (STCout | STCref | STClazy))
|
|
{
|
|
error("no storage class for key %s", p.ident.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
p.type = p.type.semantic(loc, sc);
|
|
TY keyty = p.type.ty;
|
|
if (keyty != Tint32 && keyty != Tuns32)
|
|
{
|
|
if (global.params.isLP64)
|
|
{
|
|
if (keyty != Tint64 && keyty != Tuns64)
|
|
{
|
|
error("foreach: key type must be int or uint, long or ulong, not %s", p.type.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error("foreach: key type must be int or uint, not %s", p.type.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
}
|
|
Initializer ie = new ExpInitializer(Loc(), new IntegerExp(k));
|
|
auto var = new VarDeclaration(loc, p.type, p.ident, ie);
|
|
var.storage_class |= STCmanifest;
|
|
st.push(new ExpStatement(loc, var));
|
|
p = (*parameters)[1]; // value
|
|
}
|
|
// Declare value
|
|
if (p.storageClass & (STCout | STClazy) ||
|
|
p.storageClass & STCref && !te)
|
|
{
|
|
error("no storage class for value %s", p.ident.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
Dsymbol var;
|
|
if (te)
|
|
{
|
|
Type tb = e.type.toBasetype();
|
|
Dsymbol ds = null;
|
|
if ((tb.ty == Tfunction || tb.ty == Tsarray) && e.op == TOKvar)
|
|
ds = (cast(VarExp)e).var;
|
|
else if (e.op == TOKtemplate)
|
|
ds = (cast(TemplateExp)e).td;
|
|
else if (e.op == TOKscope)
|
|
ds = (cast(ScopeExp)e).sds;
|
|
else if (e.op == TOKfunction)
|
|
{
|
|
auto fe = cast(FuncExp)e;
|
|
ds = fe.td ? cast(Dsymbol)fe.td : fe.fd;
|
|
}
|
|
|
|
if (ds)
|
|
{
|
|
var = new AliasDeclaration(loc, p.ident, ds);
|
|
if (p.storageClass & STCref)
|
|
{
|
|
error("symbol %s cannot be ref", s.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
if (paramtype)
|
|
{
|
|
error("cannot specify element type for symbol %s", ds.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
}
|
|
else if (e.op == TOKtype)
|
|
{
|
|
var = new AliasDeclaration(loc, p.ident, e.type);
|
|
if (paramtype)
|
|
{
|
|
error("cannot specify element type for type %s", e.type.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
p.type = e.type;
|
|
if (paramtype)
|
|
p.type = paramtype;
|
|
Initializer ie = new ExpInitializer(Loc(), e);
|
|
auto v = new VarDeclaration(loc, p.type, p.ident, ie);
|
|
if (p.storageClass & STCref)
|
|
v.storage_class |= STCref | STCforeach;
|
|
if (e.isConst() ||
|
|
e.op == TOKstring ||
|
|
e.op == TOKstructliteral ||
|
|
e.op == TOKarrayliteral)
|
|
{
|
|
if (v.storage_class & STCref)
|
|
{
|
|
error("constant value %s cannot be ref", ie.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
else
|
|
v.storage_class |= STCmanifest;
|
|
}
|
|
var = v;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var = new AliasDeclaration(loc, p.ident, t);
|
|
if (paramtype)
|
|
{
|
|
error("cannot specify element type for symbol %s", s.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
}
|
|
st.push(new ExpStatement(loc, var));
|
|
|
|
st.push(_body.syntaxCopy());
|
|
s = new CompoundStatement(loc, st);
|
|
s = new ScopeStatement(loc, s);
|
|
statements.push(s);
|
|
}
|
|
|
|
s = new UnrolledLoopStatement(loc, statements);
|
|
if (LabelStatement ls = checkLabeledLoop(sc, this))
|
|
ls.gotoTarget = s;
|
|
if (te && te.e0)
|
|
s = new CompoundStatement(loc, new ExpStatement(te.e0.loc, te.e0), s);
|
|
if (vinit)
|
|
s = new CompoundStatement(loc, new ExpStatement(loc, vinit), s);
|
|
s = s.semantic(sc);
|
|
return s;
|
|
}
|
|
|
|
sym = new ScopeDsymbol();
|
|
sym.parent = sc.scopesym;
|
|
auto sc2 = sc.push(sym);
|
|
sc2.noctor++;
|
|
switch (tab.ty)
|
|
{
|
|
case Tarray:
|
|
case Tsarray:
|
|
{
|
|
if (checkForArgTypes())
|
|
return this;
|
|
|
|
if (dim < 1 || dim > 2)
|
|
{
|
|
error("only one or two arguments for array foreach");
|
|
goto Lerror2;
|
|
}
|
|
|
|
/* Look for special case of parsing char types out of char type
|
|
* array.
|
|
*/
|
|
tn = tab.nextOf().toBasetype();
|
|
if (tn.ty == Tchar || tn.ty == Twchar || tn.ty == Tdchar)
|
|
{
|
|
int i = (dim == 1) ? 0 : 1; // index of value
|
|
Parameter p = (*parameters)[i];
|
|
p.type = p.type.semantic(loc, sc2);
|
|
p.type = p.type.addStorageClass(p.storageClass);
|
|
tnv = p.type.toBasetype();
|
|
if (tnv.ty != tn.ty &&
|
|
(tnv.ty == Tchar || tnv.ty == Twchar || tnv.ty == Tdchar))
|
|
{
|
|
if (p.storageClass & STCref)
|
|
{
|
|
error("foreach: value of UTF conversion cannot be ref");
|
|
goto Lerror2;
|
|
}
|
|
if (dim == 2)
|
|
{
|
|
p = (*parameters)[0];
|
|
if (p.storageClass & STCref)
|
|
{
|
|
error("foreach: key cannot be ref");
|
|
goto Lerror2;
|
|
}
|
|
}
|
|
goto Lapply;
|
|
}
|
|
}
|
|
|
|
foreach (i; 0 .. dim)
|
|
{
|
|
// Declare parameterss
|
|
Parameter p = (*parameters)[i];
|
|
p.type = p.type.semantic(loc, sc2);
|
|
p.type = p.type.addStorageClass(p.storageClass);
|
|
VarDeclaration var;
|
|
|
|
if (dim == 2 && i == 0)
|
|
{
|
|
var = new VarDeclaration(loc, p.type.mutableOf(), Identifier.generateId("__key"), null);
|
|
var.storage_class |= STCtemp | STCforeach;
|
|
if (var.storage_class & (STCref | STCout))
|
|
var.storage_class |= STCnodtor;
|
|
|
|
key = var;
|
|
if (p.storageClass & STCref)
|
|
{
|
|
if (var.type.constConv(p.type) <= MATCHnomatch)
|
|
{
|
|
error("key type mismatch, %s to ref %s",
|
|
var.type.toChars(), p.type.toChars());
|
|
goto Lerror2;
|
|
}
|
|
}
|
|
if (tab.ty == Tsarray)
|
|
{
|
|
TypeSArray ta = cast(TypeSArray)tab;
|
|
IntRange dimrange = getIntRange(ta.dim);
|
|
if (!IntRange.fromType(var.type).contains(dimrange))
|
|
{
|
|
error("index type '%s' cannot cover index range 0..%llu",
|
|
p.type.toChars(), ta.dim.toInteger());
|
|
goto Lerror2;
|
|
}
|
|
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;
|
|
|
|
value = var;
|
|
if (var.storage_class & STCref)
|
|
{
|
|
if (aggr.checkModifiable(sc2, 1) == 2)
|
|
var.storage_class |= STCctorinit;
|
|
|
|
Type t = tab.nextOf();
|
|
if (t.constConv(p.type) <= MATCHnomatch)
|
|
{
|
|
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");
|
|
auto ie = new ExpInitializer(loc, new SliceExp(loc, aggr, null, null));
|
|
VarDeclaration tmp;
|
|
if (aggr.op == TOKarrayliteral &&
|
|
!((*parameters)[dim - 1].storageClass & STCref))
|
|
{
|
|
auto ale = cast(ArrayLiteralExp)aggr;
|
|
size_t edim = ale.elements ? ale.elements.dim : 0;
|
|
aggr.type = tab.nextOf().sarrayOf(edim);
|
|
|
|
// for (T[edim] tmp = a, ...)
|
|
tmp = new VarDeclaration(loc, aggr.type, id, ie);
|
|
}
|
|
else
|
|
tmp = new VarDeclaration(loc, tab.nextOf().arrayOf(), id, ie);
|
|
tmp.storage_class |= STCtemp;
|
|
|
|
Expression tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id.length);
|
|
|
|
if (!key)
|
|
{
|
|
Identifier idkey = Identifier.generateId("__key");
|
|
key = new VarDeclaration(loc, Type.tsize_t, idkey, null);
|
|
key.storage_class |= STCtemp;
|
|
}
|
|
if (op == TOKforeach_reverse)
|
|
key._init = new ExpInitializer(loc, tmp_length);
|
|
else
|
|
key._init = new ExpInitializer(loc, new IntegerExp(loc, 0, key.type));
|
|
|
|
auto cs = new Statements();
|
|
if (vinit)
|
|
cs.push(new ExpStatement(loc, vinit));
|
|
cs.push(new ExpStatement(loc, tmp));
|
|
cs.push(new ExpStatement(loc, key));
|
|
Statement forinit = new CompoundDeclarationStatement(loc, cs);
|
|
|
|
Expression cond;
|
|
if (op == TOKforeach_reverse)
|
|
{
|
|
// key--
|
|
cond = new PostExp(TOKminusminus, loc, new VarExp(loc, key));
|
|
}
|
|
else
|
|
{
|
|
// key < tmp.length
|
|
cond = new CmpExp(TOKlt, loc, new VarExp(loc, key), tmp_length);
|
|
}
|
|
|
|
Expression increment = null;
|
|
if (op == TOKforeach)
|
|
{
|
|
// key += 1
|
|
increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(loc, 1, key.type));
|
|
}
|
|
|
|
// T value = tmp[key];
|
|
IndexExp indexExp = new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, key));
|
|
indexExp.indexIsInBounds = true; // disabling bounds checking in foreach statements.
|
|
value._init = new ExpInitializer(loc, indexExp);
|
|
Statement ds = new ExpStatement(loc, value);
|
|
|
|
if (dim == 2)
|
|
{
|
|
Parameter p = (*parameters)[0];
|
|
if ((p.storageClass & STCref) && p.type.equals(key.type))
|
|
{
|
|
key.range = null;
|
|
auto v = new AliasDeclaration(loc, p.ident, key);
|
|
_body = new CompoundStatement(loc, new ExpStatement(loc, v), _body);
|
|
}
|
|
else
|
|
{
|
|
auto ei = new ExpInitializer(loc, new IdentifierExp(loc, key.ident));
|
|
auto v = new VarDeclaration(loc, p.type, p.ident, ei);
|
|
v.storage_class |= STCforeach | (p.storageClass & STCref);
|
|
_body = new CompoundStatement(loc, new ExpStatement(loc, v), _body);
|
|
if (key.range && !p.type.isMutable())
|
|
{
|
|
/* Limit the range of the key to the specified range
|
|
*/
|
|
v.range = new IntRange(key.range.imin, key.range.imax - SignExtendedNumber(1));
|
|
}
|
|
}
|
|
}
|
|
_body = new CompoundStatement(loc, ds, _body);
|
|
|
|
s = new ForStatement(loc, forinit, cond, increment, _body, endloc);
|
|
if (auto ls = checkLabeledLoop(sc, this)) // Bugzilla 15450: don't use sc2
|
|
ls.gotoTarget = s;
|
|
s = s.semantic(sc2);
|
|
break;
|
|
}
|
|
case Taarray:
|
|
if (op == TOKforeach_reverse)
|
|
warning("cannot use foreach_reverse with an associative array");
|
|
if (checkForArgTypes())
|
|
return this;
|
|
|
|
taa = cast(TypeAArray)tab;
|
|
if (dim < 1 || dim > 2)
|
|
{
|
|
error("only one or two arguments for associative array foreach");
|
|
goto Lerror2;
|
|
}
|
|
goto Lapply;
|
|
|
|
case Tclass:
|
|
case Tstruct:
|
|
/* Prefer using opApply, if it exists
|
|
*/
|
|
if (sapply)
|
|
goto Lapply;
|
|
{
|
|
/* Look for range iteration, i.e. the properties
|
|
* .empty, .popFront, .popBack, .front and .back
|
|
* foreach (e; aggr) { ... }
|
|
* translates to:
|
|
* for (auto __r = aggr[]; !__r.empty; __r.popFront()) {
|
|
* auto e = __r.front;
|
|
* ...
|
|
* }
|
|
*/
|
|
auto ad = (tab.ty == Tclass) ?
|
|
cast(AggregateDeclaration)(cast(TypeClass)tab).sym :
|
|
cast(AggregateDeclaration)(cast(TypeStruct)tab).sym;
|
|
Identifier idfront;
|
|
Identifier idpopFront;
|
|
if (op == TOKforeach)
|
|
{
|
|
idfront = Id.Ffront;
|
|
idpopFront = Id.FpopFront;
|
|
}
|
|
else
|
|
{
|
|
idfront = Id.Fback;
|
|
idpopFront = Id.FpopBack;
|
|
}
|
|
auto 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 && aggr.op == TOKvar && (cast(VarExp)aggr).var == vinit)
|
|
{
|
|
r = vinit;
|
|
_init = new ExpStatement(loc, vinit);
|
|
}
|
|
else
|
|
{
|
|
auto rid = Identifier.generateId("__r");
|
|
r = new VarDeclaration(loc, null, rid, new ExpInitializer(loc, aggr));
|
|
r.storage_class |= STCtemp;
|
|
_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)
|
|
{
|
|
auto p = (*parameters)[0];
|
|
auto 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
|
|
{
|
|
auto id = Identifier.generateId("__front");
|
|
auto ei = new ExpInitializer(loc, einit);
|
|
auto vd = new VarDeclaration(loc, null, id, ei);
|
|
vd.storage_class |= STCtemp | STCctfe | STCref | STCforeach;
|
|
|
|
makeargs = new ExpStatement(loc, vd);
|
|
|
|
Type tfront;
|
|
if (auto fd = sfront.isFuncDeclaration())
|
|
{
|
|
if (!fd.functionSemantic())
|
|
goto Lrangeerr;
|
|
tfront = fd.type;
|
|
}
|
|
else if (auto td = sfront.isTemplateDeclaration())
|
|
{
|
|
Expressions a;
|
|
if (auto f = resolveFuncCall(loc, sc, td, null, tab, &a, 1))
|
|
tfront = f.type;
|
|
}
|
|
else if (auto 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)
|
|
{
|
|
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;
|
|
|
|
auto exps = new Expressions();
|
|
exps.push(ve);
|
|
int pos = 0;
|
|
while (exps.dim < dim)
|
|
{
|
|
pos = expandAliasThisTuples(exps, pos);
|
|
if (pos == -1)
|
|
break;
|
|
}
|
|
if (exps.dim != dim)
|
|
{
|
|
const(char)* plural = exps.dim > 1 ? "s" : "";
|
|
error("cannot infer argument types, expected %d argument%s, not %d",
|
|
exps.dim, plural, dim);
|
|
goto Lerror2;
|
|
}
|
|
|
|
foreach (i; 0 .. dim)
|
|
{
|
|
auto p = (*parameters)[i];
|
|
auto exp = (*exps)[i];
|
|
version (none)
|
|
{
|
|
printf("[%d] p = %s %s, exp = %s %s\n", i,
|
|
p.type ? p.type.toChars() : "?", p.ident.toChars(),
|
|
exp.type.toChars(), exp.toChars());
|
|
}
|
|
if (!p.type)
|
|
p.type = exp.type;
|
|
p.type = p.type.addStorageClass(p.storageClass).semantic(loc, sc2);
|
|
if (!exp.implicitConvTo(p.type))
|
|
goto Lrangeerr;
|
|
|
|
auto 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, this._body);
|
|
|
|
s = new ForStatement(loc, _init, condition, increment, forbody, endloc);
|
|
if (auto ls = checkLabeledLoop(sc, this))
|
|
ls.gotoTarget = s;
|
|
|
|
version (none)
|
|
{
|
|
printf("init: %s\n", _init.toChars());
|
|
printf("condition: %s\n", condition.toChars());
|
|
printf("increment: %s\n", increment.toChars());
|
|
printf("body: %s\n", forbody.toChars());
|
|
}
|
|
s = s.semantic(sc2);
|
|
break;
|
|
|
|
Lrangeerr:
|
|
error("cannot infer argument types");
|
|
goto Lerror2;
|
|
}
|
|
case Tdelegate:
|
|
if (op == TOKforeach_reverse)
|
|
deprecation("cannot use foreach_reverse with a delegate");
|
|
Lapply:
|
|
{
|
|
if (checkForArgTypes())
|
|
{
|
|
_body = _body.semanticNoScope(sc2);
|
|
return this;
|
|
}
|
|
|
|
TypeFunction tfld = null;
|
|
if (sapply)
|
|
{
|
|
FuncDeclaration fdapply = sapply.isFuncDeclaration();
|
|
if (fdapply)
|
|
{
|
|
assert(fdapply.type && fdapply.type.ty == Tfunction);
|
|
tfld = cast(TypeFunction)fdapply.type.semantic(loc, sc2);
|
|
goto Lget;
|
|
}
|
|
else if (tab.ty == Tdelegate)
|
|
{
|
|
tfld = cast(TypeFunction)tab.nextOf();
|
|
Lget:
|
|
//printf("tfld = %s\n", tfld->toChars());
|
|
if (tfld.parameters.dim == 1)
|
|
{
|
|
Parameter p = Parameter.getNth(tfld.parameters, 0);
|
|
if (p.type && p.type.ty == Tdelegate)
|
|
{
|
|
auto t = p.type.semantic(loc, sc2);
|
|
assert(t.ty == Tdelegate);
|
|
tfld = cast(TypeFunction)t.nextOf();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Turn body into the function literal:
|
|
* int delegate(ref T param) { body }
|
|
*/
|
|
auto params = new Parameters();
|
|
foreach (i; 0 .. dim)
|
|
{
|
|
Parameter p = (*parameters)[i];
|
|
StorageClass stc = STCref;
|
|
Identifier id;
|
|
|
|
p.type = p.type.semantic(loc, sc2);
|
|
p.type = p.type.addStorageClass(p.storageClass);
|
|
version(IN_LLVM)
|
|
{
|
|
// Type of parameter may be different; see below
|
|
auto para_type = p.type;
|
|
}
|
|
if (tfld)
|
|
{
|
|
Parameter prm = Parameter.getNth(tfld.parameters, i);
|
|
//printf("\tprm = %s%s\n", (prm->storageClass&STCref?"ref ":""), prm->ident->toChars());
|
|
stc = prm.storageClass & STCref;
|
|
id = p.ident; // argument copy is not need.
|
|
if ((p.storageClass & STCref) != stc)
|
|
{
|
|
if (!stc)
|
|
{
|
|
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", cast(int)i);
|
|
version(IN_LLVM)
|
|
{
|
|
// In case of a foreach loop on an array the index passed
|
|
// to the delegate is always of type size_t. The type of
|
|
// the parameter must be changed to size_t and a cast to
|
|
// the type used must be inserted. Otherwise the index is
|
|
// always 0 on a big endian architecture. This fixes
|
|
// issue #326.
|
|
Initializer ie;
|
|
if (dim == 2 && i == 0 && (tab.ty == Tarray || tab.ty == Tsarray))
|
|
{
|
|
para_type = Type.tsize_t;
|
|
ie = new ExpInitializer(Loc(),
|
|
new CastExp(Loc(),
|
|
new IdentifierExp(Loc(), id), p.type));
|
|
}
|
|
else
|
|
{
|
|
ie = new ExpInitializer(Loc(), new IdentifierExp(Loc(), id));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Initializer ie = new ExpInitializer(Loc(), new IdentifierExp(Loc(), id));
|
|
}
|
|
auto v = new VarDeclaration(Loc(), p.type, p.ident, ie);
|
|
v.storage_class |= STCtemp;
|
|
s = new ExpStatement(Loc(), v);
|
|
_body = new CompoundStatement(loc, s, _body);
|
|
}
|
|
version(IN_LLVM)
|
|
params.push(new Parameter(stc, para_type, id, null));
|
|
else
|
|
params.push(new Parameter(stc, p.type, id, null));
|
|
}
|
|
// Bugzilla 13840: Throwable nested function inside nothrow function is acceptable.
|
|
StorageClass stc = mergeFuncAttrs(STCsafe | STCpure | STCnogc, func);
|
|
tfld = new TypeFunction(params, Type.tint32, 0, LINKd, stc);
|
|
cases = new Statements();
|
|
gotos = new ScopeStatements();
|
|
auto fld = new FuncLiteralDeclaration(loc, Loc(), tfld, TOKdelegate, this);
|
|
fld.fbody = _body;
|
|
Expression flde = new FuncExp(loc, fld);
|
|
flde = flde.semantic(sc2);
|
|
fld.tookAddressOf = 0;
|
|
|
|
// Resolve any forward referenced goto's
|
|
foreach (i; 0 .. gotos.dim)
|
|
{
|
|
GotoStatement gs = cast(GotoStatement)(*gotos)[i].statement;
|
|
if (!gs.label.statement)
|
|
{
|
|
// 'Promote' it to this scope, and replace with a return
|
|
cases.push(gs);
|
|
s = new ReturnStatement(Loc(), new IntegerExp(cases.dim + 1));
|
|
(*gotos)[i].statement = s;
|
|
}
|
|
}
|
|
|
|
Expression e = null;
|
|
Expression ec;
|
|
if (vinit)
|
|
{
|
|
e = new DeclarationExp(loc, vinit);
|
|
e = e.semantic(sc2);
|
|
if (e.op == TOKerror)
|
|
goto Lerror2;
|
|
}
|
|
|
|
if (taa)
|
|
{
|
|
// Check types
|
|
Parameter p = (*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))
|
|
{
|
|
error("foreach: index must be type %s, not %s",
|
|
ti.toChars(), ta.toChars());
|
|
goto Lerror2;
|
|
}
|
|
p = (*parameters)[1];
|
|
isRef = (p.storageClass & STCref) != 0;
|
|
ta = p.type;
|
|
}
|
|
Type taav = taa.nextOf();
|
|
if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta))
|
|
{
|
|
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 __gshared const(char)** name = ["_aaApply", "_aaApply2"];
|
|
static __gshared FuncDeclaration* fdapply = [null, null];
|
|
static __gshared TypeDelegate* fldeTy = [null, null];
|
|
|
|
ubyte i = (dim == 2 ? 1 : 0);
|
|
if (!fdapply[i])
|
|
{
|
|
params = new Parameters();
|
|
params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null));
|
|
params.push(new Parameter(STCin, Type.tsize_t, null, null));
|
|
auto dgparams = new Parameters();
|
|
dgparams.push(new Parameter(0, Type.tvoidptr, null, null));
|
|
if (dim == 2)
|
|
dgparams.push(new Parameter(0, Type.tvoidptr, null, null));
|
|
fldeTy[i] = new TypeDelegate(new TypeFunction(dgparams, Type.tint32, 0, LINKd));
|
|
params.push(new Parameter(0, fldeTy[i], null, null));
|
|
fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, name[i]);
|
|
}
|
|
|
|
auto exps = new Expressions();
|
|
exps.push(aggr);
|
|
size_t keysize = cast(size_t)taa.index.size();
|
|
keysize = (keysize + (cast(size_t)Target.ptrsize - 1)) & ~(cast(size_t)Target.ptrsize - 1);
|
|
// paint delegate argument to the type runtime expects
|
|
if (!fldeTy[i].equals(flde.type))
|
|
{
|
|
flde = new CastExp(loc, flde, flde.type);
|
|
flde.type = fldeTy[i];
|
|
}
|
|
exps.push(new IntegerExp(Loc(), keysize, Type.tsize_t));
|
|
exps.push(flde);
|
|
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 __gshared const(char)** fntab =
|
|
[
|
|
"cc", "cw", "cd",
|
|
"wc", "cc", "wd",
|
|
"dc", "dw", "dd"
|
|
];
|
|
|
|
const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1;
|
|
char[BUFFER_LEN] fdname;
|
|
int flag;
|
|
|
|
switch (tn.ty)
|
|
{
|
|
case Tchar: flag = 0; break;
|
|
case Twchar: flag = 3; break;
|
|
case Tdchar: flag = 6; break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
switch (tnv.ty)
|
|
{
|
|
case Tchar: flag += 0; break;
|
|
case Twchar: flag += 1; break;
|
|
case Tdchar: flag += 2; break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
const(char)* r = (op == TOKforeach_reverse) ? "R" : "";
|
|
int j = sprintf(fdname.ptr, "_aApply%s%.*s%llu", r, 2, fntab[flag], cast(ulong)dim);
|
|
assert(j < BUFFER_LEN);
|
|
|
|
FuncDeclaration fdapply;
|
|
TypeDelegate dgty;
|
|
params = new Parameters();
|
|
params.push(new Parameter(STCin, tn.arrayOf(), null, null));
|
|
auto dgparams = new Parameters();
|
|
dgparams.push(new Parameter(0, Type.tvoidptr, null, null));
|
|
if (dim == 2)
|
|
dgparams.push(new Parameter(0, Type.tvoidptr, null, null));
|
|
dgty = new TypeDelegate(new TypeFunction(dgparams, Type.tint32, 0, LINKd));
|
|
params.push(new Parameter(0, dgty, null, null));
|
|
fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr);
|
|
|
|
if (tab.ty == Tsarray)
|
|
aggr = 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, aggr, flde);
|
|
ec.type = Type.tint32; // don't run semantic() on ec
|
|
}
|
|
else if (tab.ty == Tdelegate)
|
|
{
|
|
/* Call:
|
|
* aggr(flde)
|
|
*/
|
|
if (aggr.op == TOKdelegate && (cast(DelegateExp)aggr).func.isNested())
|
|
{
|
|
// See Bugzilla 3560
|
|
aggr = (cast(DelegateExp)aggr).e1;
|
|
}
|
|
ec = new CallExp(loc, aggr, flde);
|
|
ec = ec.semantic(sc2);
|
|
if (ec.op == TOKerror)
|
|
goto Lerror2;
|
|
if (ec.type != Type.tint32)
|
|
{
|
|
error("opApply() function for %s must return an int", tab.toChars());
|
|
goto Lerror2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(tab.ty == Tstruct || tab.ty == Tclass);
|
|
assert(sapply);
|
|
/* Call:
|
|
* aggr.apply(flde)
|
|
*/
|
|
ec = new DotIdExp(loc, aggr, sapply.ident);
|
|
ec = new CallExp(loc, ec, flde);
|
|
ec = ec.semantic(sc2);
|
|
if (ec.op == TOKerror)
|
|
goto Lerror2;
|
|
if (ec.type != Type.tint32)
|
|
{
|
|
error("opApply() function for %s must return an int", tab.toChars());
|
|
goto Lerror2;
|
|
}
|
|
}
|
|
e = Expression.combine(e, ec);
|
|
|
|
if (!cases.dim)
|
|
{
|
|
// Easy case, a clean exit from the loop
|
|
e = new CastExp(loc, e, Type.tvoid); // Bugzilla 13899
|
|
s = new ExpStatement(loc, e);
|
|
}
|
|
else
|
|
{
|
|
// Construct a switch statement around the return value
|
|
// of the apply function.
|
|
auto 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...
|
|
foreach (i, c; *cases)
|
|
{
|
|
s = new CaseStatement(Loc(), new IntegerExp(i + 2), c);
|
|
a.push(s);
|
|
}
|
|
|
|
s = new CompoundStatement(loc, a);
|
|
s = new SwitchStatement(loc, e, s, false);
|
|
}
|
|
s = s.semantic(sc2);
|
|
break;
|
|
}
|
|
case Terror:
|
|
Lerror2:
|
|
s = new ErrorStatement();
|
|
break;
|
|
|
|
default:
|
|
error("foreach: %s is not an aggregate type", aggr.type.toChars());
|
|
goto Lerror2;
|
|
}
|
|
sc2.noctor--;
|
|
sc2.pop();
|
|
return s;
|
|
}
|
|
|
|
bool checkForArgTypes()
|
|
{
|
|
bool result = false;
|
|
foreach (p; *parameters)
|
|
{
|
|
if (!p.type)
|
|
{
|
|
error("cannot infer type for %s", p.ident.toChars());
|
|
p.type = Type.terror;
|
|
result = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
override bool hasBreak()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool hasContinue()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class ForeachRangeStatement : Statement
|
|
{
|
|
public:
|
|
TOK op; // TOKforeach or TOKforeach_reverse
|
|
Parameter prm; // loop index variable
|
|
Expression lwr;
|
|
Expression upr;
|
|
Statement _body;
|
|
Loc endloc; // location of closing curly bracket
|
|
|
|
VarDeclaration key;
|
|
|
|
extern (D) this(Loc loc, TOK op, Parameter prm, Expression lwr, Expression upr, Statement _body, Loc endloc)
|
|
{
|
|
super(loc);
|
|
this.op = op;
|
|
this.prm = prm;
|
|
this.lwr = lwr;
|
|
this.upr = upr;
|
|
this._body = _body;
|
|
this.endloc = endloc;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new ForeachRangeStatement(loc, op, prm.syntaxCopy(), lwr.syntaxCopy(), upr.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("ForeachRangeStatement::semantic() %p\n", this);
|
|
lwr = lwr.semantic(sc);
|
|
lwr = resolveProperties(sc, lwr);
|
|
lwr = lwr.optimize(WANTvalue);
|
|
if (!lwr.type)
|
|
{
|
|
error("invalid range lower bound %s", lwr.toChars());
|
|
Lerror:
|
|
return new ErrorStatement();
|
|
}
|
|
upr = upr.semantic(sc);
|
|
upr = resolveProperties(sc, upr);
|
|
upr = upr.optimize(WANTvalue);
|
|
if (!upr.type)
|
|
{
|
|
error("invalid range upper bound %s", upr.toChars());
|
|
goto Lerror;
|
|
}
|
|
if (prm.type)
|
|
{
|
|
prm.type = prm.type.semantic(loc, sc);
|
|
prm.type = prm.type.addStorageClass(prm.storageClass);
|
|
lwr = lwr.implicitCastTo(sc, prm.type);
|
|
if (upr.implicitConvTo(prm.type) || (prm.storageClass & STCref))
|
|
{
|
|
upr = upr.implicitCastTo(sc, prm.type);
|
|
}
|
|
else
|
|
{
|
|
// See if upr-1 fits in prm->type
|
|
Expression limit = new MinExp(loc, upr, new IntegerExp(1));
|
|
limit = limit.semantic(sc);
|
|
limit = limit.optimize(WANTvalue);
|
|
if (!limit.implicitConvTo(prm.type))
|
|
{
|
|
upr = upr.implicitCastTo(sc, prm.type);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Must infer types from lwr and upr
|
|
*/
|
|
Type tlwr = lwr.type.toBasetype();
|
|
if (tlwr.ty == Tstruct || tlwr.ty == Tclass)
|
|
{
|
|
/* Just picking the first really isn't good enough.
|
|
*/
|
|
prm.type = lwr.type;
|
|
}
|
|
else if (lwr.type == upr.type)
|
|
{
|
|
/* Same logic as CondExp ?lwr:upr
|
|
*/
|
|
prm.type = lwr.type;
|
|
}
|
|
else
|
|
{
|
|
scope AddExp ea = new AddExp(loc, lwr, upr);
|
|
if (typeCombine(ea, sc))
|
|
return new ErrorStatement();
|
|
prm.type = ea.type;
|
|
lwr = ea.e1;
|
|
upr = ea.e2;
|
|
}
|
|
prm.type = prm.type.addStorageClass(prm.storageClass);
|
|
}
|
|
if (prm.type.ty == Terror || lwr.op == TOKerror || upr.op == TOKerror)
|
|
{
|
|
return new ErrorStatement();
|
|
}
|
|
/* Convert to a for loop:
|
|
* foreach (key; lwr .. upr) =>
|
|
* for (auto key = lwr, auto tmp = upr; key < tmp; ++key)
|
|
*
|
|
* foreach_reverse (key; lwr .. upr) =>
|
|
* for (auto tmp = lwr, auto key = upr; key-- > tmp;)
|
|
*/
|
|
auto ie = new ExpInitializer(loc, (op == TOKforeach) ? lwr : upr);
|
|
key = new VarDeclaration(loc, upr.type.mutableOf(), Identifier.generateId("__key"), ie);
|
|
key.storage_class |= STCtemp;
|
|
SignExtendedNumber lower = getIntRange(lwr).imin;
|
|
SignExtendedNumber upper = getIntRange(upr).imax;
|
|
if (lower <= upper)
|
|
{
|
|
key.range = new IntRange(lower, upper);
|
|
}
|
|
Identifier id = Identifier.generateId("__limit");
|
|
ie = new ExpInitializer(loc, (op == TOKforeach) ? upr : lwr);
|
|
auto tmp = new VarDeclaration(loc, upr.type, id, ie);
|
|
tmp.storage_class |= STCtemp;
|
|
auto cs = new Statements();
|
|
// Keep order of evaluation as lwr, then upr
|
|
if (op == TOKforeach)
|
|
{
|
|
cs.push(new ExpStatement(loc, key));
|
|
cs.push(new ExpStatement(loc, tmp));
|
|
}
|
|
else
|
|
{
|
|
cs.push(new ExpStatement(loc, tmp));
|
|
cs.push(new ExpStatement(loc, key));
|
|
}
|
|
Statement forinit = new CompoundDeclarationStatement(loc, cs);
|
|
Expression cond;
|
|
if (op == TOKforeach_reverse)
|
|
{
|
|
cond = new PostExp(TOKminusminus, loc, new VarExp(loc, key));
|
|
if (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 (prm.type.isscalar())
|
|
{
|
|
// key < tmp
|
|
cond = new CmpExp(TOKlt, loc, new VarExp(loc, key), new VarExp(loc, tmp));
|
|
}
|
|
else
|
|
{
|
|
// key != tmp
|
|
cond = new EqualExp(TOKnotequal, loc, new VarExp(loc, key), new VarExp(loc, tmp));
|
|
}
|
|
}
|
|
Expression increment = null;
|
|
if (op == TOKforeach)
|
|
{
|
|
// key += 1
|
|
//increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(1));
|
|
increment = new PreExp(TOKpreplusplus, loc, new VarExp(loc, key));
|
|
}
|
|
if ((prm.storageClass & STCref) && prm.type.equals(key.type))
|
|
{
|
|
key.range = null;
|
|
auto v = new AliasDeclaration(loc, prm.ident, key);
|
|
_body = new CompoundStatement(loc, new ExpStatement(loc, v), _body);
|
|
}
|
|
else
|
|
{
|
|
ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, key), prm.type));
|
|
auto v = new VarDeclaration(loc, prm.type, prm.ident, ie);
|
|
v.storage_class |= STCtemp | STCforeach | (prm.storageClass & STCref);
|
|
_body = new CompoundStatement(loc, new ExpStatement(loc, v), _body);
|
|
if (key.range && !prm.type.isMutable())
|
|
{
|
|
/* Limit the range of the key to the specified range
|
|
*/
|
|
v.range = new IntRange(key.range.imin, key.range.imax - SignExtendedNumber(1));
|
|
}
|
|
}
|
|
if (prm.storageClass & STCref)
|
|
{
|
|
if (key.type.constConv(prm.type) <= MATCHnomatch)
|
|
{
|
|
error("prmument type mismatch, %s to ref %s", key.type.toChars(), prm.type.toChars());
|
|
goto Lerror;
|
|
}
|
|
}
|
|
auto s = new ForStatement(loc, forinit, cond, increment, _body, endloc);
|
|
if (LabelStatement ls = checkLabeledLoop(sc, this))
|
|
ls.gotoTarget = s;
|
|
return s.semantic(sc);
|
|
}
|
|
|
|
override bool hasBreak()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override bool hasContinue()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class IfStatement : Statement
|
|
{
|
|
public:
|
|
Parameter prm;
|
|
Expression condition;
|
|
Statement ifbody;
|
|
Statement elsebody;
|
|
VarDeclaration match; // for MatchExpression results
|
|
|
|
extern (D) this(Loc loc, Parameter prm, Expression condition, Statement ifbody, Statement elsebody)
|
|
{
|
|
super(loc);
|
|
this.prm = prm;
|
|
this.condition = condition;
|
|
this.ifbody = ifbody;
|
|
this.elsebody = elsebody;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new IfStatement(loc,
|
|
prm ? prm.syntaxCopy() : null,
|
|
condition.syntaxCopy(),
|
|
ifbody ? ifbody.syntaxCopy() : null,
|
|
elsebody ? elsebody.syntaxCopy() : null);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
// Evaluate at runtime
|
|
uint cs0 = sc.callSuper;
|
|
uint cs1;
|
|
uint* fi0 = sc.saveFieldInit();
|
|
uint* fi1 = null;
|
|
|
|
// check in syntax level
|
|
condition = checkAssignmentAsCondition(condition);
|
|
|
|
auto sym = new ScopeDsymbol();
|
|
sym.parent = sc.scopesym;
|
|
Scope* scd = sc.push(sym);
|
|
if (prm)
|
|
{
|
|
/* Declare prm, which we will set to be the
|
|
* result of condition.
|
|
*/
|
|
auto ei = new ExpInitializer(loc, condition);
|
|
match = new VarDeclaration(loc, prm.type, prm.ident, ei);
|
|
match.parent = scd.func;
|
|
match.storage_class |= prm.storageClass;
|
|
match.semantic(scd);
|
|
|
|
auto de = new DeclarationExp(loc, match);
|
|
auto ve = new VarExp(loc, match);
|
|
condition = new CommaExp(loc, de, ve);
|
|
condition = condition.semantic(scd);
|
|
|
|
if (match.edtor)
|
|
{
|
|
Statement sdtor = new DtorExpStatement(loc, match.edtor, match);
|
|
sdtor = new OnScopeStatement(loc, TOKon_scope_exit, sdtor);
|
|
ifbody = new CompoundStatement(loc, sdtor, ifbody);
|
|
match.noscope = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
condition = condition.semantic(scd);
|
|
condition = resolveProperties(scd, condition);
|
|
condition = condition.addDtorHook(scd);
|
|
}
|
|
if (checkNonAssignmentArrayOp(condition))
|
|
condition = new ErrorExp();
|
|
condition = checkGC(scd, condition);
|
|
|
|
// Convert to boolean after declaring prm so this works:
|
|
// if (S prm = S()) {}
|
|
// where S is a struct that defines opCast!bool.
|
|
condition = condition.toBoolean(scd);
|
|
|
|
// If we can short-circuit evaluate the if statement, don't do the
|
|
// semantic analysis of the skipped code.
|
|
// This feature allows a limited form of conditional compilation.
|
|
condition = condition.optimize(WANTvalue);
|
|
|
|
ifbody = ifbody.semanticNoScope(scd);
|
|
scd.pop();
|
|
|
|
cs1 = sc.callSuper;
|
|
fi1 = sc.fieldinit;
|
|
sc.callSuper = cs0;
|
|
sc.fieldinit = fi0;
|
|
if (elsebody)
|
|
elsebody = elsebody.semanticScope(sc, null, null);
|
|
sc.mergeCallSuper(loc, cs1);
|
|
sc.mergeFieldInit(loc, fi1);
|
|
|
|
if (condition.op == TOKerror ||
|
|
(ifbody && ifbody.isErrorStatement()) ||
|
|
(elsebody && elsebody.isErrorStatement()))
|
|
{
|
|
return new ErrorStatement();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override IfStatement isIfStatement()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class ConditionalStatement : Statement
|
|
{
|
|
public:
|
|
Condition condition;
|
|
Statement ifbody;
|
|
Statement elsebody;
|
|
|
|
extern (D) this(Loc loc, Condition condition, Statement ifbody, Statement elsebody)
|
|
{
|
|
super(loc);
|
|
this.condition = condition;
|
|
this.ifbody = ifbody;
|
|
this.elsebody = elsebody;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new ConditionalStatement(loc, condition.syntaxCopy(), ifbody.syntaxCopy(), elsebody ? elsebody.syntaxCopy() : null);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("ConditionalStatement::semantic()\n");
|
|
// If we can short-circuit evaluate the if statement, don't do the
|
|
// semantic analysis of the skipped code.
|
|
// This feature allows a limited form of conditional compilation.
|
|
if (condition.include(sc, null))
|
|
{
|
|
DebugCondition dc = condition.isDebugCondition();
|
|
if (dc)
|
|
{
|
|
sc = sc.push();
|
|
sc.flags |= SCOPEdebug;
|
|
ifbody = ifbody.semantic(sc);
|
|
sc.pop();
|
|
}
|
|
else
|
|
ifbody = ifbody.semantic(sc);
|
|
return ifbody;
|
|
}
|
|
else
|
|
{
|
|
if (elsebody)
|
|
elsebody = elsebody.semantic(sc);
|
|
return elsebody;
|
|
}
|
|
}
|
|
|
|
override Statements* flatten(Scope* sc)
|
|
{
|
|
Statement s;
|
|
//printf("ConditionalStatement::flatten()\n");
|
|
if (condition.include(sc, null))
|
|
{
|
|
DebugCondition dc = condition.isDebugCondition();
|
|
if (dc)
|
|
s = new DebugStatement(loc, ifbody);
|
|
else
|
|
s = ifbody;
|
|
}
|
|
else
|
|
s = elsebody;
|
|
auto a = new Statements();
|
|
a.push(s);
|
|
return a;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class PragmaStatement : Statement
|
|
{
|
|
public:
|
|
Identifier ident;
|
|
Expressions* args; // array of Expression's
|
|
Statement _body;
|
|
|
|
extern (D) this(Loc loc, Identifier ident, Expressions* args, Statement _body)
|
|
{
|
|
super(loc);
|
|
this.ident = ident;
|
|
this.args = args;
|
|
this._body = _body;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new PragmaStatement(loc, ident, Expression.arraySyntaxCopy(args), _body ? _body.syntaxCopy() : null);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
// Should be merged with PragmaDeclaration
|
|
//printf("PragmaStatement::semantic() %s\n", toChars());
|
|
//printf("body = %p\n", body);
|
|
if (ident == Id.msg)
|
|
{
|
|
if (args)
|
|
{
|
|
foreach (arg; *args)
|
|
{
|
|
sc = sc.startCTFE();
|
|
auto e = arg.semantic(sc);
|
|
e = resolveProperties(sc, e);
|
|
sc = sc.endCTFE();
|
|
// pragma(msg) is allowed to contain types as well as expressions
|
|
e = ctfeInterpretForPragmaMsg(e);
|
|
if (e.op == TOKerror)
|
|
{
|
|
errorSupplemental(loc, "while evaluating pragma(msg, %s)", arg.toChars());
|
|
goto Lerror;
|
|
}
|
|
StringExp se = e.toStringExp();
|
|
if (se)
|
|
{
|
|
se = se.toUTF8(sc);
|
|
fprintf(stderr, "%.*s", cast(int)se.len, se.string);
|
|
}
|
|
else
|
|
fprintf(stderr, "%s", e.toChars());
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
else if (ident == Id.lib)
|
|
{
|
|
version (all)
|
|
{
|
|
/* Should this be allowed?
|
|
*/
|
|
error("pragma(lib) not allowed as statement");
|
|
goto Lerror;
|
|
}
|
|
else
|
|
{
|
|
if (!args || args.dim != 1)
|
|
{
|
|
error("string expected for library name");
|
|
goto Lerror;
|
|
}
|
|
else
|
|
{
|
|
Expression e = (*args)[0];
|
|
sc = sc.startCTFE();
|
|
e = e.semantic(sc);
|
|
e = resolveProperties(sc, e);
|
|
sc = sc.endCTFE();
|
|
e = e.ctfeInterpret();
|
|
(*args)[0] = e;
|
|
StringExp se = e.toStringExp();
|
|
if (!se)
|
|
{
|
|
error("string expected for library name, not '%s'", e.toChars());
|
|
goto Lerror;
|
|
}
|
|
else if (global.params.verbose)
|
|
{
|
|
fprintf(global.stdmsg, "library %.*s\n", cast(int)se.len, se.string);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// IN_LLVM. FIXME Move to pragma.cpp
|
|
else if (ident == Id.LDC_allow_inline)
|
|
{
|
|
sc.func.allowInlining = true;
|
|
}
|
|
// IN_LLVM. FIXME Move to pragma.cpp
|
|
else if (ident == Id.LDC_never_inline)
|
|
{
|
|
sc.func.neverInline = true;
|
|
}
|
|
// IN_LLVM. FIXME Move to pragma.cpp
|
|
else if (ident == Id.LDC_profile_instr)
|
|
{
|
|
bool emitInstr = true;
|
|
if (!args || args.dim != 1 || !DtoCheckProfileInstrPragma((*args)[0], emitInstr))
|
|
{
|
|
error("pragma(LDC_profile_instr, true or false) expected");
|
|
goto Lerror;
|
|
}
|
|
else
|
|
{
|
|
FuncDeclaration fd = sc.func;
|
|
if (fd is null)
|
|
{
|
|
error("pragma(LDC_profile_instr, ...) is not inside a function");
|
|
goto Lerror;
|
|
}
|
|
fd.emitInstrumentation = emitInstr;
|
|
}
|
|
}
|
|
else if (ident == Id.startaddress)
|
|
{
|
|
if (!args || args.dim != 1)
|
|
error("function name expected for start address");
|
|
else
|
|
{
|
|
Expression e = (*args)[0];
|
|
sc = sc.startCTFE();
|
|
e = e.semantic(sc);
|
|
e = resolveProperties(sc, e);
|
|
sc = sc.endCTFE();
|
|
e = e.ctfeInterpret();
|
|
(*args)[0] = e;
|
|
Dsymbol sa = getDsymbol(e);
|
|
if (!sa || !sa.isFuncDeclaration())
|
|
{
|
|
error("function name expected for start address, not '%s'", e.toChars());
|
|
goto Lerror;
|
|
}
|
|
if (_body)
|
|
{
|
|
_body = _body.semantic(sc);
|
|
if (_body.isErrorStatement())
|
|
return _body;
|
|
}
|
|
return this;
|
|
}
|
|
}
|
|
else if (ident == Id.Pinline)
|
|
{
|
|
PINLINE inlining = PINLINEdefault;
|
|
if (!args || args.dim == 0)
|
|
inlining = PINLINEdefault;
|
|
else if (!args || args.dim != 1)
|
|
{
|
|
error("boolean expression expected for pragma(inline)");
|
|
goto Lerror;
|
|
}
|
|
else
|
|
{
|
|
Expression e = (*args)[0];
|
|
if (e.op != TOKint64 || !e.type.equals(Type.tbool))
|
|
{
|
|
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)
|
|
{
|
|
error("pragma(inline) is not inside a function");
|
|
goto Lerror;
|
|
}
|
|
fd.inlining = inlining;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error("unrecognized pragma(%s)", ident.toChars());
|
|
goto Lerror;
|
|
}
|
|
if (_body)
|
|
{
|
|
_body = _body.semantic(sc);
|
|
}
|
|
return _body;
|
|
Lerror:
|
|
return new ErrorStatement();
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class StaticAssertStatement : Statement
|
|
{
|
|
public:
|
|
StaticAssert sa;
|
|
|
|
extern (D) this(StaticAssert sa)
|
|
{
|
|
super(sa.loc);
|
|
this.sa = sa;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new StaticAssertStatement(cast(StaticAssert)sa.syntaxCopy(null));
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
sa.semantic2(sc);
|
|
return null;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class SwitchStatement : Statement
|
|
{
|
|
public:
|
|
Expression condition;
|
|
Statement _body;
|
|
bool isFinal;
|
|
|
|
DefaultStatement sdefault;
|
|
TryFinallyStatement tf;
|
|
GotoCaseStatements gotoCases; // array of unresolved GotoCaseStatement's
|
|
CaseStatements* cases; // array of CaseStatement's
|
|
int hasNoDefault; // !=0 if no default statement
|
|
int hasVars; // !=0 if has variable case values
|
|
version(IN_LLVM)
|
|
{
|
|
bool hasGotoDefault; // true iff there is a `goto default` statement for this switch
|
|
}
|
|
|
|
extern (D) this(Loc loc, Expression c, Statement b, bool isFinal)
|
|
{
|
|
super(loc);
|
|
this.condition = c;
|
|
this._body = b;
|
|
this.isFinal = isFinal;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new SwitchStatement(loc, condition.syntaxCopy(), _body.syntaxCopy(), isFinal);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("SwitchStatement::semantic(%p)\n", this);
|
|
tf = sc.tf;
|
|
if (cases)
|
|
return this; // already run
|
|
|
|
bool conditionError = false;
|
|
condition = condition.semantic(sc);
|
|
condition = resolveProperties(sc, condition);
|
|
|
|
Type att = null;
|
|
TypeEnum te = null;
|
|
while (condition.op != TOKerror)
|
|
{
|
|
// preserve enum type for final switches
|
|
if (condition.type.ty == Tenum)
|
|
te = cast(TypeEnum)condition.type;
|
|
if (condition.type.isString())
|
|
{
|
|
// If it's not an array, cast it to one
|
|
if (condition.type.ty != Tarray)
|
|
{
|
|
condition = condition.implicitCastTo(sc, condition.type.nextOf().arrayOf());
|
|
}
|
|
condition.type = condition.type.constOf();
|
|
break;
|
|
}
|
|
condition = integralPromotions(condition, sc);
|
|
if (condition.op != TOKerror && condition.type.isintegral())
|
|
break;
|
|
|
|
auto ad = isAggregate(condition.type);
|
|
if (ad && ad.aliasthis && condition.type != att)
|
|
{
|
|
if (!att && condition.type.checkAliasThisRec())
|
|
att = condition.type;
|
|
if (auto e = resolveAliasThis(sc, condition, true))
|
|
{
|
|
condition = e;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (condition.op != TOKerror)
|
|
{
|
|
error("'%s' must be of integral or string type, it is a %s",
|
|
condition.toChars(), condition.type.toChars());
|
|
conditionError = true;
|
|
break;
|
|
}
|
|
}
|
|
if (checkNonAssignmentArrayOp(condition))
|
|
condition = new ErrorExp();
|
|
condition = condition.optimize(WANTvalue);
|
|
condition = checkGC(sc, condition);
|
|
if (condition.op == TOKerror)
|
|
conditionError = true;
|
|
|
|
bool needswitcherror = false;
|
|
sc = sc.push();
|
|
sc.sbreak = this;
|
|
sc.sw = this;
|
|
cases = new CaseStatements();
|
|
sc.noctor++; // BUG: should use Scope::mergeCallSuper() for each case instead
|
|
_body = _body.semantic(sc);
|
|
sc.noctor--;
|
|
if (conditionError || _body.isErrorStatement())
|
|
goto Lerror;
|
|
// Resolve any goto case's with exp
|
|
foreach (gcs; gotoCases)
|
|
{
|
|
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;
|
|
foreach (cs; *scx.sw.cases)
|
|
{
|
|
if (cs.exp.equals(gcs.exp))
|
|
{
|
|
gcs.cs = cs;
|
|
version(IN_LLVM)
|
|
{
|
|
cs.gototarget = true;
|
|
}
|
|
goto Lfoundcase;
|
|
}
|
|
}
|
|
}
|
|
gcs.error("case %s not found", gcs.exp.toChars());
|
|
goto Lerror;
|
|
Lfoundcase:
|
|
}
|
|
if (isFinal)
|
|
{
|
|
Type t = condition.type;
|
|
Dsymbol ds;
|
|
EnumDeclaration ed = null;
|
|
if (t && ((ds = t.toDsymbol(sc)) !is null))
|
|
ed = ds.isEnumDeclaration(); // typedef'ed enum
|
|
if (!ed && te && ((ds = te.toDsymbol(sc)) !is null))
|
|
ed = ds.isEnumDeclaration();
|
|
if (ed)
|
|
{
|
|
foreach (es; *ed.members)
|
|
{
|
|
EnumMember em = es.isEnumMember();
|
|
if (em)
|
|
{
|
|
foreach (cs; *cases)
|
|
{
|
|
if (cs.exp.equals(em.value) || (!cs.exp.type.isString() && !em.value.type.isString() && cs.exp.toInteger() == em.value.toInteger()))
|
|
goto L1;
|
|
}
|
|
error("enum member %s not represented in final switch", em.toChars());
|
|
goto Lerror;
|
|
}
|
|
L1:
|
|
}
|
|
}
|
|
else
|
|
needswitcherror = true;
|
|
}
|
|
if (!sc.sw.sdefault && (!isFinal || needswitcherror || global.params.useAssert))
|
|
{
|
|
hasNoDefault = 1;
|
|
if (!isFinal && !_body.isErrorStatement())
|
|
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
|
|
auto a = new Statements();
|
|
CompoundStatement cs;
|
|
Statement s;
|
|
if (global.params.useSwitchError)
|
|
s = new SwitchErrorStatement(loc);
|
|
else
|
|
s = new ExpStatement(loc, new HaltExp(loc));
|
|
a.reserve(2);
|
|
sc.sw.sdefault = new DefaultStatement(loc, s);
|
|
a.push(_body);
|
|
if (_body.blockExit(sc.func, false) & BEfallthru)
|
|
a.push(new BreakStatement(Loc(), null));
|
|
a.push(sc.sw.sdefault);
|
|
cs = new CompoundStatement(loc, a);
|
|
_body = cs;
|
|
}
|
|
version(IN_LLVM)
|
|
{
|
|
/+ hasGotoDefault is set by GotoDefaultStatement.semantic
|
|
+ at which point sdefault may still be null, therefore
|
|
+ set sdefault.gototarget here.
|
|
+/
|
|
if (hasGotoDefault) {
|
|
assert(sdefault);
|
|
sdefault.gototarget = true;
|
|
}
|
|
}
|
|
sc.pop();
|
|
return this;
|
|
Lerror:
|
|
sc.pop();
|
|
return new ErrorStatement();
|
|
}
|
|
|
|
override bool hasBreak()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class CaseStatement : Statement
|
|
{
|
|
public:
|
|
Expression exp;
|
|
Statement statement;
|
|
int index; // which case it is (since we sort this)
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
bool gototarget; // true iff this is the target of a 'goto case'
|
|
void* bodyBB; // llvm::BasicBlock*
|
|
void* llvmIdx; // llvm::Value*
|
|
}
|
|
|
|
extern (D) this(Loc loc, Expression exp, Statement s)
|
|
{
|
|
super(loc);
|
|
this.exp = exp;
|
|
this.statement = s;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new CaseStatement(loc, exp.syntaxCopy(), statement.syntaxCopy());
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
SwitchStatement sw = sc.sw;
|
|
bool errors = false;
|
|
//printf("CaseStatement::semantic() %s\n", toChars());
|
|
sc = sc.startCTFE();
|
|
exp = exp.semantic(sc);
|
|
exp = resolveProperties(sc, exp);
|
|
sc = sc.endCTFE();
|
|
if (sw)
|
|
{
|
|
exp = exp.implicitCastTo(sc, sw.condition.type);
|
|
exp = exp.optimize(WANTvalue | WANTexpand);
|
|
/* This is where variables are allowed as case expressions.
|
|
*/
|
|
if (exp.op == TOKvar)
|
|
{
|
|
VarExp ve = cast(VarExp)exp;
|
|
VarDeclaration v = ve.var.isVarDeclaration();
|
|
Type t = exp.type.toBasetype();
|
|
if (v && (t.isintegral() || t.ty == Tclass))
|
|
{
|
|
/* Flag that we need to do special code generation
|
|
* for this, i.e. generate a sequence of if-then-else
|
|
*/
|
|
sw.hasVars = 1;
|
|
if (sw.isFinal)
|
|
{
|
|
error("case variables not allowed in final switch statements");
|
|
errors = true;
|
|
}
|
|
goto L1;
|
|
}
|
|
}
|
|
else
|
|
exp = exp.ctfeInterpret();
|
|
if (StringExp se = exp.toStringExp())
|
|
exp = se;
|
|
else if (exp.op != TOKint64 && exp.op != TOKerror)
|
|
{
|
|
error("case must be a string or an integral constant, not %s", exp.toChars());
|
|
errors = true;
|
|
}
|
|
L1:
|
|
foreach (cs; *sw.cases)
|
|
{
|
|
//printf("comparing '%s' with '%s'\n", exp->toChars(), cs->exp->toChars());
|
|
if (cs.exp.equals(exp))
|
|
{
|
|
error("duplicate case %s in switch statement", exp.toChars());
|
|
errors = true;
|
|
break;
|
|
}
|
|
}
|
|
sw.cases.push(this);
|
|
// Resolve any goto case's with no exp to this case statement
|
|
for (size_t i = 0; i < sw.gotoCases.dim;)
|
|
{
|
|
GotoCaseStatement gcs = sw.gotoCases[i];
|
|
if (!gcs.exp)
|
|
{
|
|
gcs.cs = this;
|
|
sw.gotoCases.remove(i); // remove from array
|
|
continue;
|
|
}
|
|
i++;
|
|
}
|
|
if (sc.sw.tf != sc.tf)
|
|
{
|
|
error("switch and case are in different finally blocks");
|
|
errors = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error("case not in switch statement");
|
|
errors = true;
|
|
}
|
|
statement = statement.semantic(sc);
|
|
if (statement.isErrorStatement())
|
|
return statement;
|
|
if (errors || exp.op == TOKerror)
|
|
return new ErrorStatement();
|
|
return this;
|
|
}
|
|
|
|
override int compare(RootObject obj)
|
|
{
|
|
// Sort cases so we can do an efficient lookup
|
|
CaseStatement cs2 = cast(CaseStatement)obj;
|
|
return exp.compare(cs2.exp);
|
|
}
|
|
|
|
override CaseStatement isCaseStatement()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class CaseRangeStatement : Statement
|
|
{
|
|
public:
|
|
Expression first;
|
|
Expression last;
|
|
Statement statement;
|
|
|
|
extern (D) this(Loc loc, Expression first, Expression last, Statement s)
|
|
{
|
|
super(loc);
|
|
this.first = first;
|
|
this.last = last;
|
|
this.statement = s;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new CaseRangeStatement(loc, first.syntaxCopy(), last.syntaxCopy(), statement.syntaxCopy());
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
SwitchStatement sw = sc.sw;
|
|
if (sw is null)
|
|
{
|
|
error("case range not in switch statement");
|
|
return new ErrorStatement();
|
|
}
|
|
//printf("CaseRangeStatement::semantic() %s\n", toChars());
|
|
bool errors = false;
|
|
if (sw.isFinal)
|
|
{
|
|
error("case ranges not allowed in final switch");
|
|
errors = true;
|
|
}
|
|
sc = sc.startCTFE();
|
|
first = first.semantic(sc);
|
|
first = resolveProperties(sc, first);
|
|
sc = sc.endCTFE();
|
|
first = first.implicitCastTo(sc, sw.condition.type);
|
|
first = first.ctfeInterpret();
|
|
sc = sc.startCTFE();
|
|
last = last.semantic(sc);
|
|
last = resolveProperties(sc, last);
|
|
sc = sc.endCTFE();
|
|
last = last.implicitCastTo(sc, sw.condition.type);
|
|
last = last.ctfeInterpret();
|
|
if (first.op == TOKerror || last.op == TOKerror || errors)
|
|
{
|
|
if (statement)
|
|
statement.semantic(sc);
|
|
return new ErrorStatement();
|
|
}
|
|
uinteger_t fval = first.toInteger();
|
|
uinteger_t lval = last.toInteger();
|
|
if ((first.type.isunsigned() && fval > lval) || (!first.type.isunsigned() && cast(sinteger_t)fval > cast(sinteger_t)lval))
|
|
{
|
|
error("first case %s is greater than last case %s", first.toChars(), last.toChars());
|
|
errors = true;
|
|
lval = fval;
|
|
}
|
|
if (lval - fval > 256)
|
|
{
|
|
error("had %llu cases which is more than 256 cases in case range", lval - fval);
|
|
errors = true;
|
|
lval = fval + 256;
|
|
}
|
|
if (errors)
|
|
return new ErrorStatement();
|
|
/* This works by replacing the CaseRange with an array of Case's.
|
|
*
|
|
* case a: .. case b: s;
|
|
* =>
|
|
* case a:
|
|
* [...]
|
|
* case b:
|
|
* s;
|
|
*/
|
|
auto statements = new Statements();
|
|
for (uinteger_t i = fval; i != lval + 1; i++)
|
|
{
|
|
Statement s = statement;
|
|
if (i != lval) // if not last case
|
|
s = new ExpStatement(loc, cast(Expression)null);
|
|
Expression e = new IntegerExp(loc, i, first.type);
|
|
Statement cs = new CaseStatement(loc, e, s);
|
|
statements.push(cs);
|
|
}
|
|
Statement s = new CompoundStatement(loc, statements);
|
|
s = s.semantic(sc);
|
|
return s;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class DefaultStatement : Statement
|
|
{
|
|
public:
|
|
Statement statement;
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
bool gototarget; // true iff this is the target of a 'goto default'
|
|
void* bodyBB; // llvm::BasicBlock*
|
|
}
|
|
|
|
extern (D) this(Loc loc, Statement s)
|
|
{
|
|
super(loc);
|
|
this.statement = s;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new DefaultStatement(loc, statement.syntaxCopy());
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("DefaultStatement::semantic()\n");
|
|
bool errors = false;
|
|
if (sc.sw)
|
|
{
|
|
if (sc.sw.sdefault)
|
|
{
|
|
error("switch statement already has a default");
|
|
errors = true;
|
|
}
|
|
sc.sw.sdefault = this;
|
|
if (sc.sw.tf != sc.tf)
|
|
{
|
|
error("switch and default are in different finally blocks");
|
|
errors = true;
|
|
}
|
|
if (sc.sw.isFinal)
|
|
{
|
|
error("default statement not allowed in final switch statement");
|
|
errors = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error("default not in switch statement");
|
|
errors = true;
|
|
}
|
|
statement = statement.semantic(sc);
|
|
if (errors || statement.isErrorStatement())
|
|
return new ErrorStatement();
|
|
return this;
|
|
}
|
|
|
|
override DefaultStatement isDefaultStatement()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class GotoDefaultStatement : Statement
|
|
{
|
|
public:
|
|
SwitchStatement sw;
|
|
|
|
extern (D) this(Loc loc)
|
|
{
|
|
super(loc);
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new GotoDefaultStatement(loc);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
sw = sc.sw;
|
|
if (!sw)
|
|
{
|
|
error("goto default not in switch statement");
|
|
return new ErrorStatement();
|
|
}
|
|
if (sw.isFinal)
|
|
{
|
|
error("goto default not allowed in final switch statement");
|
|
return new ErrorStatement();
|
|
}
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
sw.hasGotoDefault = true;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class GotoCaseStatement : Statement
|
|
{
|
|
public:
|
|
Expression exp; // null, or which case to goto
|
|
CaseStatement cs; // case statement it resolves to
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
SwitchStatement sw;
|
|
}
|
|
|
|
extern (D) this(Loc loc, Expression exp)
|
|
{
|
|
super(loc);
|
|
this.exp = exp;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new GotoCaseStatement(loc, exp ? exp.syntaxCopy() : null);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
if (!sc.sw)
|
|
{
|
|
error("goto case not in switch statement");
|
|
return new ErrorStatement();
|
|
}
|
|
version(IN_LLVM)
|
|
{
|
|
sw = sc.sw;
|
|
}
|
|
if (exp)
|
|
{
|
|
exp = exp.semantic(sc);
|
|
exp = exp.implicitCastTo(sc, sc.sw.condition.type);
|
|
exp = exp.optimize(WANTvalue);
|
|
if (exp.op == TOKerror)
|
|
return new ErrorStatement();
|
|
}
|
|
sc.sw.gotoCases.push(this);
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class SwitchErrorStatement : Statement
|
|
{
|
|
public:
|
|
extern (D) this(Loc loc)
|
|
{
|
|
super(loc);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class ReturnStatement : Statement
|
|
{
|
|
public:
|
|
Expression exp;
|
|
size_t caseDim;
|
|
|
|
extern (D) this(Loc loc, Expression exp)
|
|
{
|
|
super(loc);
|
|
this.exp = exp;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new ReturnStatement(loc, exp ? exp.syntaxCopy() : null);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//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 = cast(TypeFunction)fd.type;
|
|
assert(tf.ty == Tfunction);
|
|
if (exp && exp.op == TOKvar && (cast(VarExp)exp).var == fd.vresult)
|
|
{
|
|
// return vresult;
|
|
if (sc.fes)
|
|
{
|
|
assert(caseDim == 0);
|
|
sc.fes.cases.push(this);
|
|
return new ReturnStatement(Loc(), new IntegerExp(sc.fes.cases.dim + 1));
|
|
}
|
|
if (fd.returnLabel)
|
|
{
|
|
auto gs = new GotoStatement(loc, Id.returnLabel);
|
|
gs.label = fd.returnLabel;
|
|
return gs;
|
|
}
|
|
if (!fd.returns)
|
|
fd.returns = new ReturnStatements();
|
|
fd.returns.push(this);
|
|
return this;
|
|
}
|
|
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)
|
|
{
|
|
error("return statements cannot be in contracts");
|
|
errors = true;
|
|
}
|
|
if (sc.os && sc.os.tok != TOKon_scope_failure)
|
|
{
|
|
error("return statements cannot be in %s bodies", Token.toChars(sc.os.tok));
|
|
errors = true;
|
|
}
|
|
if (sc.tf)
|
|
{
|
|
error("return statements cannot be in finally bodies");
|
|
errors = true;
|
|
}
|
|
if (fd.isCtorDeclaration())
|
|
{
|
|
if (exp)
|
|
{
|
|
error("cannot return expression from constructor");
|
|
errors = true;
|
|
}
|
|
// Constructors implicitly do:
|
|
// return this;
|
|
exp = new ThisExp(Loc());
|
|
exp.type = tret;
|
|
}
|
|
else if (exp)
|
|
{
|
|
fd.hasReturnExp |= 1;
|
|
FuncLiteralDeclaration fld = fd.isFuncLiteralDeclaration();
|
|
if (tret)
|
|
exp = inferType(exp, tret);
|
|
else if (fld && fld.treq)
|
|
exp = inferType(exp, fld.treq.nextOf().nextOf());
|
|
|
|
exp = exp.semantic(sc);
|
|
exp = resolveProperties(sc, exp);
|
|
if (exp.checkType())
|
|
exp = new ErrorExp();
|
|
if (auto f = isFuncAddress(exp))
|
|
{
|
|
if (fd.inferRetType && f.checkForwardRef(exp.loc))
|
|
exp = new ErrorExp();
|
|
}
|
|
if (checkNonAssignmentArrayOp(exp))
|
|
exp = new ErrorExp();
|
|
|
|
// Extract side-effect part
|
|
exp = Expression.extractLast(exp, &e0);
|
|
if (exp.op == TOKcall)
|
|
exp = valueNoDtor(exp);
|
|
|
|
/* Void-return function can have void typed expression
|
|
* on return statement.
|
|
*/
|
|
if (tbret && tbret.ty == Tvoid || exp.type.ty == Tvoid)
|
|
{
|
|
if (exp.type.ty != Tvoid)
|
|
{
|
|
error("cannot return non-void from void function");
|
|
errors = true;
|
|
exp = new CastExp(loc, exp, Type.tvoid);
|
|
exp = exp.semantic(sc);
|
|
}
|
|
|
|
/* Replace:
|
|
* return exp;
|
|
* with:
|
|
* exp; return;
|
|
*/
|
|
e0 = Expression.combine(e0, exp);
|
|
exp = null;
|
|
}
|
|
if (e0)
|
|
e0 = checkGC(sc, e0);
|
|
}
|
|
if (exp)
|
|
{
|
|
if (fd.inferRetType) // infer return type
|
|
{
|
|
if (!tret)
|
|
{
|
|
tf.next = exp.type;
|
|
}
|
|
else if (tret.ty != Terror && !exp.type.equals(tret))
|
|
{
|
|
int m1 = exp.type.implicitConvTo(tret);
|
|
int m2 = tret.implicitConvTo(exp.type);
|
|
//printf("exp->type = %s m2<-->m1 tret %s\n", exp->type->toChars(), tret->toChars());
|
|
//printf("m1 = %d, m2 = %d\n", m1, m2);
|
|
if (m1 && m2)
|
|
{
|
|
}
|
|
else if (!m1 && m2)
|
|
tf.next = exp.type;
|
|
else if (m1 && !m2)
|
|
{
|
|
}
|
|
else if (exp.op != TOKerror)
|
|
{
|
|
error("mismatched function return type inference of %s and %s", exp.type.toChars(), 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 (exp.isLvalue())
|
|
{
|
|
/* May return by ref
|
|
*/
|
|
if (checkEscapeRef(sc, exp, true))
|
|
tf.isref = false; // return by value
|
|
}
|
|
else
|
|
tf.isref = false; // return by value
|
|
/* The "refness" is determined by all of return statements.
|
|
* This means:
|
|
* return 3; return x; // ok, x can be a value
|
|
* return x; return 3; // ok, x can be a value
|
|
*/
|
|
}
|
|
// handle NRVO
|
|
if (fd.nrvo_can && exp.op == TOKvar)
|
|
{
|
|
VarExp ve = cast(VarExp)exp;
|
|
VarDeclaration v = ve.var.isVarDeclaration();
|
|
if (tf.isref)
|
|
{
|
|
// Function returns a reference
|
|
if (!inferRef)
|
|
fd.nrvo_can = 0;
|
|
}
|
|
else if (!v || v.isOut() || v.isRef())
|
|
fd.nrvo_can = 0;
|
|
else if (fd.nrvo_var is null)
|
|
{
|
|
if (!v.isDataseg() && !v.isParameter() && v.toParent2() == fd)
|
|
{
|
|
//printf("Setting nrvo to %s\n", v->toChars());
|
|
fd.nrvo_var = v;
|
|
}
|
|
else
|
|
fd.nrvo_can = 0;
|
|
}
|
|
else if (fd.nrvo_var != v)
|
|
fd.nrvo_can = 0;
|
|
}
|
|
else //if (!exp->isLvalue()) // keep NRVO-ability
|
|
fd.nrvo_can = 0;
|
|
}
|
|
else
|
|
{
|
|
// handle NRVO
|
|
fd.nrvo_can = 0;
|
|
// infer return type
|
|
if (fd.inferRetType)
|
|
{
|
|
if (tf.next && tf.next.ty != Tvoid)
|
|
{
|
|
if (tf.next.ty != Terror)
|
|
{
|
|
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)
|
|
error("return expression expected");
|
|
errors = true;
|
|
}
|
|
else if (fd.isMain())
|
|
{
|
|
// main() returns 0, even if it returns void
|
|
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)))
|
|
{
|
|
error("return without calling constructor");
|
|
errors = true;
|
|
}
|
|
sc.callSuper |= CSXreturn;
|
|
if (sc.fieldinit)
|
|
{
|
|
AggregateDeclaration ad = fd.isAggregateMember2();
|
|
assert(ad);
|
|
size_t dim = sc.fieldinit_dim;
|
|
foreach (i; 0 .. dim)
|
|
{
|
|
VarDeclaration v = ad.fields[i];
|
|
bool mustInit = (v.storage_class & STCnodefaultctor || v.type.needsNested());
|
|
if (mustInit && !(sc.fieldinit[i] & CSXthis_ctor))
|
|
{
|
|
error("an earlier return statement skips field %s initialization", v.toChars());
|
|
errors = true;
|
|
}
|
|
sc.fieldinit[i] |= CSXreturn;
|
|
}
|
|
}
|
|
if (errors)
|
|
return new ErrorStatement();
|
|
if (sc.fes)
|
|
{
|
|
if (!exp)
|
|
{
|
|
// Send out "case receiver" statement to the foreach.
|
|
// return exp;
|
|
Statement s = new ReturnStatement(Loc(), exp);
|
|
sc.fes.cases.push(s);
|
|
// Immediately rewrite "this" return statement as:
|
|
// return cases->dim+1;
|
|
this.exp = new IntegerExp(sc.fes.cases.dim + 1);
|
|
if (e0)
|
|
return new CompoundStatement(loc, new ExpStatement(loc, e0), this);
|
|
return this;
|
|
}
|
|
else
|
|
{
|
|
fd.buildResultVar(null, 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;
|
|
caseDim = sc.fes.cases.dim + 1;
|
|
}
|
|
}
|
|
if (exp)
|
|
{
|
|
if (!fd.returns)
|
|
fd.returns = new ReturnStatements();
|
|
fd.returns.push(this);
|
|
}
|
|
if (e0)
|
|
return new CompoundStatement(loc, new ExpStatement(loc, e0), this);
|
|
return this;
|
|
}
|
|
|
|
override ReturnStatement isReturnStatement()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class BreakStatement : Statement
|
|
{
|
|
public:
|
|
Identifier ident;
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
// LDC: only set if ident is set: label statement to jump to
|
|
LabelStatement target;
|
|
}
|
|
|
|
extern (D) this(Loc loc, Identifier ident)
|
|
{
|
|
super(loc);
|
|
this.ident = ident;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new BreakStatement(loc, ident);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("BreakStatement::semantic()\n");
|
|
// If:
|
|
// break Identifier;
|
|
if (ident)
|
|
{
|
|
ident = fixupLabelName(sc, ident);
|
|
FuncDeclaration thisfunc = sc.func;
|
|
for (Scope* scx = sc; scx; scx = scx.enclosing)
|
|
{
|
|
if (scx.func != thisfunc) // if in enclosing function
|
|
{
|
|
if (sc.fes) // if this is the body of a foreach
|
|
{
|
|
/* Post this statement to the fes, and replace
|
|
* it with a return value that caller will put into
|
|
* a switch. Caller will figure out where the break
|
|
* label actually is.
|
|
* Case numbers start with 2, not 0, as 0 is continue
|
|
* and 1 is break.
|
|
*/
|
|
sc.fes.cases.push(this);
|
|
Statement s = new ReturnStatement(Loc(), new IntegerExp(sc.fes.cases.dim + 1));
|
|
return s;
|
|
}
|
|
break;
|
|
// can't break to it
|
|
}
|
|
LabelStatement ls = scx.slabel;
|
|
if (ls && ls.ident == ident)
|
|
{
|
|
Statement s = ls.statement;
|
|
if (!s || !s.hasBreak())
|
|
error("label '%s' has no break", ident.toChars());
|
|
else if (ls.tf != sc.tf)
|
|
error("cannot break out of finally block");
|
|
else
|
|
{
|
|
version(IN_LLVM)
|
|
target = ls;
|
|
|
|
ls.breaks = true;
|
|
return this;
|
|
}
|
|
return new ErrorStatement();
|
|
}
|
|
}
|
|
error("enclosing label '%s' for break not found", ident.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
else if (!sc.sbreak)
|
|
{
|
|
if (sc.os && sc.os.tok != TOKon_scope_failure)
|
|
{
|
|
error("break is not inside %s bodies", Token.toChars(sc.os.tok));
|
|
}
|
|
else if (sc.fes)
|
|
{
|
|
// Replace break; with return 1;
|
|
Statement s = new ReturnStatement(Loc(), new IntegerExp(1));
|
|
return s;
|
|
}
|
|
else
|
|
error("break is not inside a loop or switch");
|
|
return new ErrorStatement();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class ContinueStatement : Statement
|
|
{
|
|
public:
|
|
Identifier ident;
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
// LDC: only set if ident is set: label statement to jump to
|
|
LabelStatement target;
|
|
}
|
|
|
|
extern (D) this(Loc loc, Identifier ident)
|
|
{
|
|
super(loc);
|
|
this.ident = ident;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new ContinueStatement(loc, ident);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("ContinueStatement::semantic() %p\n", this);
|
|
if (ident)
|
|
{
|
|
ident = fixupLabelName(sc, ident);
|
|
Scope* scx;
|
|
FuncDeclaration thisfunc = sc.func;
|
|
for (scx = sc; scx; scx = scx.enclosing)
|
|
{
|
|
LabelStatement ls;
|
|
if (scx.func != thisfunc) // if in enclosing function
|
|
{
|
|
if (sc.fes) // if this is the body of a foreach
|
|
{
|
|
for (; scx; scx = scx.enclosing)
|
|
{
|
|
ls = scx.slabel;
|
|
if (ls && ls.ident == ident && ls.statement == sc.fes)
|
|
{
|
|
// Replace continue ident; with return 0;
|
|
return new ReturnStatement(Loc(), new IntegerExp(0));
|
|
}
|
|
}
|
|
/* Post this statement to the fes, and replace
|
|
* it with a return value that caller will put into
|
|
* a switch. Caller will figure out where the break
|
|
* label actually is.
|
|
* Case numbers start with 2, not 0, as 0 is continue
|
|
* and 1 is break.
|
|
*/
|
|
sc.fes.cases.push(this);
|
|
Statement s = new ReturnStatement(Loc(), new IntegerExp(sc.fes.cases.dim + 1));
|
|
return s;
|
|
}
|
|
break;
|
|
// can't continue to it
|
|
}
|
|
ls = scx.slabel;
|
|
if (ls && ls.ident == ident)
|
|
{
|
|
Statement s = ls.statement;
|
|
if (!s || !s.hasContinue())
|
|
error("label '%s' has no continue", ident.toChars());
|
|
else if (ls.tf != sc.tf)
|
|
error("cannot continue out of finally block");
|
|
else
|
|
{
|
|
version(IN_LLVM)
|
|
target = ls;
|
|
|
|
return this;
|
|
}
|
|
return new ErrorStatement();
|
|
}
|
|
}
|
|
error("enclosing label '%s' for continue not found", ident.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
else if (!sc.scontinue)
|
|
{
|
|
if (sc.os && sc.os.tok != TOKon_scope_failure)
|
|
{
|
|
error("continue is not inside %s bodies", Token.toChars(sc.os.tok));
|
|
}
|
|
else if (sc.fes)
|
|
{
|
|
// Replace continue; with return 0;
|
|
Statement s = new ReturnStatement(Loc(), new IntegerExp(0));
|
|
return s;
|
|
}
|
|
else
|
|
error("continue is not inside a loop");
|
|
return new ErrorStatement();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class SynchronizedStatement : Statement
|
|
{
|
|
public:
|
|
Expression exp;
|
|
Statement _body;
|
|
|
|
extern (D) this(Loc loc, Expression exp, Statement _body)
|
|
{
|
|
super(loc);
|
|
this.exp = exp;
|
|
this._body = _body;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new SynchronizedStatement(loc, exp ? exp.syntaxCopy() : null, _body ? _body.syntaxCopy() : null);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
if (exp)
|
|
{
|
|
exp = exp.semantic(sc);
|
|
exp = resolveProperties(sc, exp);
|
|
exp = exp.optimize(WANTvalue);
|
|
exp = checkGC(sc, exp);
|
|
if (exp.op == TOKerror)
|
|
goto Lbody;
|
|
|
|
ClassDeclaration cd = exp.type.isClassHandle();
|
|
if (!cd)
|
|
{
|
|
error("can only synchronize on class objects, not '%s'", exp.type.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
else if (cd.isInterfaceDeclaration())
|
|
{
|
|
/* Cast the interface to an object, as the object has the monitor,
|
|
* not the interface.
|
|
*/
|
|
if (!ClassDeclaration.object)
|
|
{
|
|
error("missing or corrupt object.d");
|
|
fatal();
|
|
}
|
|
|
|
Type t = ClassDeclaration.object.type;
|
|
t = t.semantic(Loc(), sc).toBasetype();
|
|
assert(t.ty == Tclass);
|
|
|
|
exp = new CastExp(loc, exp, t);
|
|
exp = exp.semantic(sc);
|
|
}
|
|
version (all)
|
|
{
|
|
/* Rewrite as:
|
|
* auto tmp = exp;
|
|
* _d_monitorenter(tmp);
|
|
* try { body } finally { _d_monitorexit(tmp); }
|
|
*/
|
|
Identifier id = Identifier.generateId("__sync");
|
|
auto ie = new ExpInitializer(loc, exp);
|
|
auto tmp = new VarDeclaration(loc, exp.type, id, ie);
|
|
tmp.storage_class |= STCtemp;
|
|
|
|
auto cs = new Statements();
|
|
cs.push(new ExpStatement(loc, tmp));
|
|
|
|
auto args = new Parameters();
|
|
args.push(new Parameter(0, ClassDeclaration.object.type, null, null));
|
|
|
|
FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter);
|
|
Expression e = new CallExp(loc, new VarExp(loc, fdenter, false), new VarExp(loc, tmp));
|
|
e.type = Type.tvoid; // do not run semantic on e
|
|
|
|
cs.push(new ExpStatement(loc, e));
|
|
FuncDeclaration fdexit = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorexit);
|
|
e = new CallExp(loc, new VarExp(loc, fdexit, false), new VarExp(loc, tmp));
|
|
e.type = Type.tvoid; // do not run semantic on e
|
|
Statement s = new ExpStatement(loc, e);
|
|
s = new TryFinallyStatement(loc, _body, s);
|
|
cs.push(s);
|
|
|
|
s = new CompoundStatement(loc, cs);
|
|
return s.semantic(sc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Generate our own critical section, then rewrite as:
|
|
* __gshared byte[CriticalSection.sizeof] critsec;
|
|
* _d_criticalenter(critsec.ptr);
|
|
* try { body } finally { _d_criticalexit(critsec.ptr); }
|
|
*/
|
|
Identifier id = Identifier.generateId("__critsec");
|
|
Type t = new TypeSArray(Type.tint8, new IntegerExp(Target.ptrsize + Target.critsecsize()));
|
|
auto tmp = new VarDeclaration(loc, t, id, null);
|
|
tmp.storage_class |= STCtemp | STCgshared | STCstatic;
|
|
|
|
auto cs = new Statements();
|
|
cs.push(new ExpStatement(loc, tmp));
|
|
|
|
/* This is just a dummy variable for "goto skips declaration" error.
|
|
* Backend optimizer could remove this unused variable.
|
|
*/
|
|
auto v = new VarDeclaration(loc, Type.tvoidptr, Identifier.generateId("__sync"), null);
|
|
v.semantic(sc);
|
|
cs.push(new ExpStatement(loc, v));
|
|
|
|
auto args = new Parameters();
|
|
args.push(new Parameter(0, t.pointerTo(), null, null));
|
|
|
|
FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.criticalenter, STCnothrow);
|
|
Expression e = new DotIdExp(loc, new VarExp(loc, tmp), Id.ptr);
|
|
e = e.semantic(sc);
|
|
e = new CallExp(loc, new VarExp(loc, fdenter, false), e);
|
|
e.type = Type.tvoid; // do not run semantic on e
|
|
cs.push(new ExpStatement(loc, e));
|
|
|
|
FuncDeclaration fdexit = FuncDeclaration.genCfunc(args, Type.tvoid, Id.criticalexit, STCnothrow);
|
|
e = new DotIdExp(loc, new VarExp(loc, tmp), Id.ptr);
|
|
e = e.semantic(sc);
|
|
e = new CallExp(loc, new VarExp(loc, fdexit, false), e);
|
|
e.type = Type.tvoid; // do not run semantic on e
|
|
Statement s = new ExpStatement(loc, e);
|
|
s = new TryFinallyStatement(loc, _body, s);
|
|
cs.push(s);
|
|
|
|
s = new CompoundStatement(loc, cs);
|
|
return s.semantic(sc);
|
|
}
|
|
Lbody:
|
|
if (_body)
|
|
_body = _body.semantic(sc);
|
|
if (_body && _body.isErrorStatement())
|
|
return _body;
|
|
return this;
|
|
}
|
|
|
|
override bool hasBreak()
|
|
{
|
|
return false; //true;
|
|
}
|
|
|
|
override bool hasContinue()
|
|
{
|
|
return false; //true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class WithStatement : Statement
|
|
{
|
|
public:
|
|
Expression exp;
|
|
Statement _body;
|
|
VarDeclaration wthis;
|
|
|
|
extern (D) this(Loc loc, Expression exp, Statement _body)
|
|
{
|
|
super(loc);
|
|
this.exp = exp;
|
|
this._body = _body;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new WithStatement(loc, exp.syntaxCopy(), _body ? _body.syntaxCopy() : null);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
ScopeDsymbol sym;
|
|
Initializer _init;
|
|
//printf("WithStatement::semantic()\n");
|
|
exp = exp.semantic(sc);
|
|
exp = resolveProperties(sc, exp);
|
|
exp = exp.optimize(WANTvalue);
|
|
exp = checkGC(sc, exp);
|
|
if (exp.op == TOKerror)
|
|
return new ErrorStatement();
|
|
if (exp.op == TOKscope)
|
|
{
|
|
sym = new WithScopeSymbol(this);
|
|
sym.parent = sc.scopesym;
|
|
}
|
|
else if (exp.op == TOKtype)
|
|
{
|
|
Dsymbol s = (cast(TypeExp)exp).type.toDsymbol(sc);
|
|
if (!s || !s.isScopeDsymbol())
|
|
{
|
|
error("with type %s has no members", exp.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
sym = new WithScopeSymbol(this);
|
|
sym.parent = sc.scopesym;
|
|
}
|
|
else
|
|
{
|
|
Type t = exp.type.toBasetype();
|
|
Expression olde = exp;
|
|
if (t.ty == Tpointer)
|
|
{
|
|
exp = new PtrExp(loc, exp);
|
|
exp = exp.semantic(sc);
|
|
t = exp.type.toBasetype();
|
|
}
|
|
assert(t);
|
|
t = t.toBasetype();
|
|
if (t.isClassHandle())
|
|
{
|
|
_init = new ExpInitializer(loc, exp);
|
|
wthis = new VarDeclaration(loc, exp.type, Id.withSym, _init);
|
|
wthis.semantic(sc);
|
|
sym = new WithScopeSymbol(this);
|
|
sym.parent = sc.scopesym;
|
|
}
|
|
else if (t.ty == Tstruct)
|
|
{
|
|
if (!exp.isLvalue())
|
|
{
|
|
/* Re-write to
|
|
* {
|
|
* auto __withtmp = exp
|
|
* with(__withtmp)
|
|
* {
|
|
* ...
|
|
* }
|
|
* }
|
|
*/
|
|
_init = new ExpInitializer(loc, exp);
|
|
wthis = new VarDeclaration(loc, exp.type, Identifier.generateId("__withtmp"), _init);
|
|
wthis.storage_class |= STCtemp;
|
|
auto es = new ExpStatement(loc, wthis);
|
|
exp = new VarExp(loc, wthis);
|
|
Statement ss = new ScopeStatement(loc, new CompoundStatement(loc, es, this));
|
|
return ss.semantic(sc);
|
|
}
|
|
Expression e = exp.addressOf();
|
|
_init = new ExpInitializer(loc, e);
|
|
wthis = new VarDeclaration(loc, e.type, Id.withSym, _init);
|
|
wthis.semantic(sc);
|
|
sym = new WithScopeSymbol(this);
|
|
// Need to set the scope to make use of resolveAliasThis
|
|
sym.setScope(sc);
|
|
sym.parent = sc.scopesym;
|
|
}
|
|
else
|
|
{
|
|
error("with expressions must be aggregate types or pointers to them, not '%s'", olde.type.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
}
|
|
if (_body)
|
|
{
|
|
sym._scope = sc;
|
|
sc = sc.push(sym);
|
|
sc.insert(sym);
|
|
_body = _body.semantic(sc);
|
|
sc.pop();
|
|
if (_body && _body.isErrorStatement())
|
|
return _body;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TryCatchStatement : Statement
|
|
{
|
|
public:
|
|
Statement _body;
|
|
Catches* catches;
|
|
|
|
extern (D) this(Loc loc, Statement _body, Catches* catches)
|
|
{
|
|
super(loc);
|
|
this._body = _body;
|
|
this.catches = catches;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
auto a = new Catches();
|
|
a.setDim(catches.dim);
|
|
foreach (i, c; *catches)
|
|
{
|
|
(*a)[i] = c.syntaxCopy();
|
|
}
|
|
return new TryCatchStatement(loc, _body.syntaxCopy(), a);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
uint flags;
|
|
enum FLAGcpp = 1;
|
|
enum FLAGd = 2;
|
|
|
|
_body = _body.semanticScope(sc, null, null);
|
|
assert(_body);
|
|
/* Even if body is empty, still do semantic analysis on catches
|
|
*/
|
|
bool catchErrors = false;
|
|
foreach (i, c; *catches)
|
|
{
|
|
c.semantic(sc);
|
|
if (c.errors)
|
|
{
|
|
catchErrors = true;
|
|
continue;
|
|
}
|
|
auto cd = c.type.toBasetype().isClassHandle();
|
|
flags |= cd.isCPPclass() ? FLAGcpp : FLAGd;
|
|
|
|
// Determine if current catch 'hides' any previous catches
|
|
foreach (j; 0 .. i)
|
|
{
|
|
Catch cj = (*catches)[j];
|
|
const si = c.loc.toChars();
|
|
const sj = cj.loc.toChars();
|
|
if (c.type.toBasetype().implicitConvTo(cj.type.toBasetype()))
|
|
{
|
|
error("catch at %s hides catch at %s", sj, si);
|
|
catchErrors = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sc.func)
|
|
{
|
|
if (flags == (FLAGcpp | FLAGd))
|
|
{
|
|
error("cannot mix catching D and C++ exceptions in the same try-catch");
|
|
catchErrors = true;
|
|
}
|
|
}
|
|
|
|
if (catchErrors)
|
|
return new ErrorStatement();
|
|
if (_body.isErrorStatement())
|
|
return _body;
|
|
/* If the try body never throws, we can eliminate any catches
|
|
* of recoverable exceptions.
|
|
*/
|
|
if (!(_body.blockExit(sc.func, false) & BEthrow) && ClassDeclaration.exception)
|
|
{
|
|
foreach_reverse (i; 0 .. catches.dim)
|
|
{
|
|
Catch c = (*catches)[i];
|
|
/* If catch exception type is derived from Exception
|
|
*/
|
|
if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) && (!c.handler || !c.handler.comeFrom()))
|
|
{
|
|
// Remove c from the array of catches
|
|
catches.remove(i);
|
|
}
|
|
}
|
|
}
|
|
if (catches.dim == 0)
|
|
return _body.hasCode() ? _body : null;
|
|
return this;
|
|
}
|
|
|
|
override bool hasBreak()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class Catch : RootObject
|
|
{
|
|
public:
|
|
Loc loc;
|
|
Type type;
|
|
Identifier ident;
|
|
VarDeclaration var;
|
|
Statement handler;
|
|
|
|
bool errors; // set if semantic processing errors
|
|
|
|
// was generated by the compiler, wasn't present in source code
|
|
bool internalCatch;
|
|
|
|
extern (D) this(Loc loc, Type t, Identifier id, Statement handler)
|
|
{
|
|
//printf("Catch(%s, loc = %s)\n", id->toChars(), loc.toChars());
|
|
this.loc = loc;
|
|
this.type = t;
|
|
this.ident = id;
|
|
this.handler = handler;
|
|
}
|
|
|
|
Catch syntaxCopy()
|
|
{
|
|
auto c = new Catch(loc, type ? type.syntaxCopy() : null, ident, (handler ? handler.syntaxCopy() : null));
|
|
c.internalCatch = internalCatch;
|
|
return c;
|
|
}
|
|
|
|
void semantic(Scope* sc)
|
|
{
|
|
//printf("Catch::semantic(%s)\n", ident->toChars());
|
|
static if (!IN_GCC)
|
|
{
|
|
if (sc.os && sc.os.tok != TOKon_scope_failure)
|
|
{
|
|
// If enclosing is scope(success) or scope(exit), this will be placed in finally block.
|
|
error(loc, "cannot put catch statement inside %s", Token.toChars(sc.os.tok));
|
|
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(loc, "cannot put catch statement inside finally block");
|
|
errors = true;
|
|
}
|
|
}
|
|
auto sym = new ScopeDsymbol();
|
|
sym.parent = sc.scopesym;
|
|
sc = sc.push(sym);
|
|
if (!type)
|
|
{
|
|
// reference .object.Throwable
|
|
auto tid = new TypeIdentifier(Loc(), Id.empty);
|
|
tid.addIdent(Id.object);
|
|
tid.addIdent(Id.Throwable);
|
|
type = tid;
|
|
}
|
|
type = type.semantic(loc, sc);
|
|
if (type == Type.terror)
|
|
errors = true;
|
|
else
|
|
{
|
|
auto cd = type.toBasetype().isClassHandle();
|
|
if (!cd)
|
|
{
|
|
error(loc, "can only catch class objects, not '%s'", type.toChars());
|
|
errors = true;
|
|
}
|
|
else if (cd.isCPPclass())
|
|
{
|
|
if (!Target.cppExceptions)
|
|
{
|
|
error(loc, "catching C++ class objects not supported for this target");
|
|
errors = true;
|
|
}
|
|
if (sc.func && !sc.intypeof && !internalCatch && sc.func.setUnsafe())
|
|
{
|
|
error(loc, "cannot catch C++ class objects in @safe code");
|
|
errors = true;
|
|
}
|
|
}
|
|
else if (cd != ClassDeclaration.throwable && !ClassDeclaration.throwable.isBaseOf(cd, null))
|
|
{
|
|
error(loc, "can only catch class objects derived from Throwable, not '%s'", type.toChars());
|
|
errors = true;
|
|
}
|
|
else if (sc.func && !sc.intypeof && !internalCatch &&
|
|
cd != ClassDeclaration.exception && !ClassDeclaration.exception.isBaseOf(cd, null) &&
|
|
sc.func.setUnsafe())
|
|
{
|
|
error(loc, "can only catch class objects derived from Exception in @safe code, not '%s'", type.toChars());
|
|
errors = true;
|
|
}
|
|
|
|
if (ident)
|
|
{
|
|
var = new VarDeclaration(loc, type, ident, null);
|
|
var.semantic(sc);
|
|
sc.insert(var);
|
|
}
|
|
handler = handler.semantic(sc);
|
|
if (handler && handler.isErrorStatement())
|
|
errors = true;
|
|
}
|
|
sc.pop();
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class TryFinallyStatement : Statement
|
|
{
|
|
public:
|
|
Statement _body;
|
|
Statement finalbody;
|
|
|
|
extern (D) this(Loc loc, Statement _body, Statement finalbody)
|
|
{
|
|
super(loc);
|
|
this._body = _body;
|
|
this.finalbody = finalbody;
|
|
}
|
|
|
|
static TryFinallyStatement create(Loc loc, Statement _body, Statement finalbody)
|
|
{
|
|
return new TryFinallyStatement(loc, _body, finalbody);
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new TryFinallyStatement(loc, _body.syntaxCopy(), finalbody.syntaxCopy());
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("TryFinallyStatement::semantic()\n");
|
|
_body = _body.semantic(sc);
|
|
sc = sc.push();
|
|
sc.tf = this;
|
|
sc.sbreak = null;
|
|
sc.scontinue = null; // no break or continue out of finally block
|
|
finalbody = finalbody.semanticNoScope(sc);
|
|
sc.pop();
|
|
if (!_body)
|
|
return finalbody;
|
|
if (!finalbody)
|
|
return _body;
|
|
if (_body.blockExit(sc.func, false) == BEfallthru)
|
|
{
|
|
Statement s = new CompoundStatement(loc, _body, finalbody);
|
|
return s;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override bool hasBreak()
|
|
{
|
|
return false; //true;
|
|
}
|
|
|
|
override bool hasContinue()
|
|
{
|
|
return false; //true;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class OnScopeStatement : Statement
|
|
{
|
|
public:
|
|
TOK tok;
|
|
Statement statement;
|
|
|
|
extern (D) this(Loc loc, TOK tok, Statement statement)
|
|
{
|
|
super(loc);
|
|
this.tok = tok;
|
|
this.statement = statement;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new OnScopeStatement(loc, tok, statement.syntaxCopy());
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
static if (!IN_GCC)
|
|
{
|
|
if (tok != TOKon_scope_exit)
|
|
{
|
|
// scope(success) and scope(failure) are rewritten to try-catch(-finally) statement,
|
|
// so the generated catch block cannot be placed in finally block.
|
|
// See also Catch::semantic.
|
|
if (sc.os && sc.os.tok != TOKon_scope_failure)
|
|
{
|
|
// If enclosing is scope(success) or scope(exit), this will be placed in finally block.
|
|
error("cannot put %s statement inside %s", Token.toChars(tok), Token.toChars(sc.os.tok));
|
|
return new ErrorStatement();
|
|
}
|
|
if (sc.tf)
|
|
{
|
|
error("cannot put %s statement inside finally block", Token.toChars(tok));
|
|
return new ErrorStatement();
|
|
}
|
|
}
|
|
}
|
|
sc = sc.push();
|
|
sc.tf = null;
|
|
sc.os = this;
|
|
if (tok != TOKon_scope_failure)
|
|
{
|
|
// Jump out from scope(failure) block is allowed.
|
|
sc.sbreak = null;
|
|
sc.scontinue = null;
|
|
}
|
|
statement = statement.semanticNoScope(sc);
|
|
sc.pop();
|
|
if (!statement || statement.isErrorStatement())
|
|
return statement;
|
|
return this;
|
|
}
|
|
|
|
override Statement scopeCode(Scope* sc, Statement* sentry, Statement* sexception, Statement* sfinally)
|
|
{
|
|
//printf("OnScopeStatement::scopeCode()\n");
|
|
//print();
|
|
*sentry = null;
|
|
*sexception = null;
|
|
*sfinally = null;
|
|
Statement s = new PeelStatement(statement);
|
|
switch (tok)
|
|
{
|
|
case TOKon_scope_exit:
|
|
*sfinally = s;
|
|
break;
|
|
case TOKon_scope_failure:
|
|
*sexception = s;
|
|
break;
|
|
case TOKon_scope_success:
|
|
{
|
|
/* Create:
|
|
* sentry: bool x = false;
|
|
* sexception: x = true;
|
|
* sfinally: if (!x) statement;
|
|
*/
|
|
Identifier id = Identifier.generateId("__os");
|
|
auto ie = new ExpInitializer(loc, new IntegerExp(Loc(), 0, Type.tbool));
|
|
auto v = new VarDeclaration(loc, Type.tbool, id, ie);
|
|
v.storage_class |= STCtemp;
|
|
*sentry = new ExpStatement(loc, v);
|
|
Expression e = new IntegerExp(Loc(), 1, Type.tbool);
|
|
e = new AssignExp(Loc(), new VarExp(Loc(), v), e);
|
|
*sexception = new ExpStatement(Loc(), e);
|
|
e = new VarExp(Loc(), v);
|
|
e = new NotExp(Loc(), e);
|
|
*sfinally = new IfStatement(Loc(), null, e, s, null);
|
|
break;
|
|
}
|
|
default:
|
|
assert(0);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class ThrowStatement : Statement
|
|
{
|
|
public:
|
|
Expression exp;
|
|
|
|
// was generated by the compiler, wasn't present in source code
|
|
bool internalThrow;
|
|
|
|
extern (D) this(Loc loc, Expression exp)
|
|
{
|
|
super(loc);
|
|
this.exp = exp;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
auto s = new ThrowStatement(loc, exp.syntaxCopy());
|
|
s.internalThrow = internalThrow;
|
|
return s;
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("ThrowStatement::semantic()\n");
|
|
FuncDeclaration fd = sc.parent.isFuncDeclaration();
|
|
fd.hasReturnExp |= 2;
|
|
exp = exp.semantic(sc);
|
|
exp = resolveProperties(sc, exp);
|
|
exp = checkGC(sc, exp);
|
|
if (exp.op == TOKerror)
|
|
return new ErrorStatement();
|
|
ClassDeclaration cd = exp.type.toBasetype().isClassHandle();
|
|
if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null)))
|
|
{
|
|
error("can only throw class objects derived from Throwable, not type %s", exp.type.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class DebugStatement : Statement
|
|
{
|
|
public:
|
|
Statement statement;
|
|
|
|
extern (D) this(Loc loc, Statement statement)
|
|
{
|
|
super(loc);
|
|
this.statement = statement;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new DebugStatement(loc, statement ? statement.syntaxCopy() : null);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
if (statement)
|
|
{
|
|
sc = sc.push();
|
|
sc.flags |= SCOPEdebug;
|
|
statement = statement.semantic(sc);
|
|
sc.pop();
|
|
}
|
|
return statement;
|
|
}
|
|
|
|
override Statements* flatten(Scope* sc)
|
|
{
|
|
Statements* a = statement ? statement.flatten(sc) : null;
|
|
if (a)
|
|
{
|
|
foreach (ref s; *a)
|
|
{
|
|
s = new DebugStatement(loc, s);
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class GotoStatement : Statement
|
|
{
|
|
public:
|
|
Identifier ident;
|
|
LabelDsymbol label;
|
|
TryFinallyStatement tf;
|
|
OnScopeStatement os;
|
|
VarDeclaration lastVar;
|
|
|
|
extern (D) this(Loc loc, Identifier ident)
|
|
{
|
|
super(loc);
|
|
this.ident = ident;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new GotoStatement(loc, ident);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("GotoStatement::semantic()\n");
|
|
FuncDeclaration fd = sc.func;
|
|
ident = fixupLabelName(sc, ident);
|
|
label = fd.searchLabel(ident);
|
|
tf = sc.tf;
|
|
os = sc.os;
|
|
lastVar = sc.lastVar;
|
|
if (!label.statement && sc.fes)
|
|
{
|
|
/* Either the goto label is forward referenced or it
|
|
* is in the function that the enclosing foreach is in.
|
|
* Can't know yet, so wrap the goto in a scope statement
|
|
* so we can patch it later, and add it to a 'look at this later'
|
|
* list.
|
|
*/
|
|
auto ss = new ScopeStatement(loc, this);
|
|
sc.fes.gotos.push(ss); // 'look at this later' list
|
|
return ss;
|
|
}
|
|
// Add to fwdref list to check later
|
|
if (!label.statement)
|
|
{
|
|
if (!fd.gotos)
|
|
fd.gotos = new GotoStatements();
|
|
fd.gotos.push(this);
|
|
}
|
|
else if (checkLabel())
|
|
return new ErrorStatement();
|
|
return this;
|
|
}
|
|
|
|
bool checkLabel()
|
|
{
|
|
if (!label.statement)
|
|
{
|
|
error("label '%s' is undefined", label.toChars());
|
|
return true;
|
|
}
|
|
if (label.statement.os != os)
|
|
{
|
|
if (os && os.tok == TOKon_scope_failure && !label.statement.os)
|
|
{
|
|
// Jump out from scope(failure) block is allowed.
|
|
}
|
|
else
|
|
{
|
|
if (label.statement.os)
|
|
error("cannot goto in to %s block", Token.toChars(label.statement.os.tok));
|
|
else
|
|
error("cannot goto out of %s block", Token.toChars(os.tok));
|
|
return true;
|
|
}
|
|
}
|
|
// IN_LLVM replaced: if (label.statement.tf != tf)
|
|
if ( (label.statement !is null) && label.statement.tf != tf)
|
|
{
|
|
error("cannot goto in or out of finally block");
|
|
return true;
|
|
}
|
|
VarDeclaration vd = label.statement.lastVar;
|
|
if (!vd || vd.isDataseg() || (vd.storage_class & STCmanifest))
|
|
return false;
|
|
VarDeclaration last = lastVar;
|
|
while (last && last != vd)
|
|
last = last.lastVar;
|
|
if (last == vd)
|
|
{
|
|
// All good, the label's scope has no variables
|
|
}
|
|
else if (vd.ident == Id.withSym)
|
|
{
|
|
error("goto skips declaration of with temporary at %s", vd.loc.toChars());
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
error("goto skips declaration of variable %s at %s", vd.toPrettyChars(), vd.loc.toChars());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class LabelStatement : Statement
|
|
{
|
|
public:
|
|
Identifier ident;
|
|
Statement statement;
|
|
TryFinallyStatement tf;
|
|
OnScopeStatement os;
|
|
VarDeclaration lastVar;
|
|
Statement gotoTarget; // interpret
|
|
bool breaks; // someone did a 'break ident'
|
|
|
|
extern (D) this(Loc loc, Identifier ident, Statement statement)
|
|
{
|
|
super(loc);
|
|
this.ident = ident;
|
|
this.statement = statement;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
return new LabelStatement(loc, ident, statement ? statement.syntaxCopy() : null);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
//printf("LabelStatement::semantic()\n");
|
|
FuncDeclaration fd = sc.parent.isFuncDeclaration();
|
|
ident = fixupLabelName(sc, ident);
|
|
tf = sc.tf;
|
|
os = sc.os;
|
|
lastVar = sc.lastVar;
|
|
LabelDsymbol ls = fd.searchLabel(ident);
|
|
if (ls.statement)
|
|
{
|
|
error("label '%s' already defined", ls.toChars());
|
|
return new ErrorStatement();
|
|
}
|
|
else
|
|
ls.statement = this;
|
|
sc = sc.push();
|
|
sc.scopesym = sc.enclosing.scopesym;
|
|
sc.callSuper |= CSXlabel;
|
|
if (sc.fieldinit)
|
|
{
|
|
size_t dim = sc.fieldinit_dim;
|
|
foreach (i; 0 .. dim)
|
|
sc.fieldinit[i] |= CSXlabel;
|
|
}
|
|
sc.slabel = this;
|
|
if (statement)
|
|
statement = statement.semantic(sc);
|
|
sc.pop();
|
|
return this;
|
|
}
|
|
|
|
override Statements* flatten(Scope* sc)
|
|
{
|
|
Statements* a = null;
|
|
if (statement)
|
|
{
|
|
a = statement.flatten(sc);
|
|
if (a)
|
|
{
|
|
if (!a.dim)
|
|
{
|
|
a.push(new ExpStatement(loc, cast(Expression)null));
|
|
}
|
|
// reuse 'this' LabelStatement
|
|
this.statement = (*a)[0];
|
|
(*a)[0] = this;
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
|
|
override Statement scopeCode(Scope* sc, Statement* sentry, Statement* sexit, Statement* sfinally)
|
|
{
|
|
//printf("LabelStatement::scopeCode()\n");
|
|
if (statement)
|
|
statement = statement.scopeCode(sc, sentry, sexit, sfinally);
|
|
else
|
|
{
|
|
*sentry = null;
|
|
*sexit = null;
|
|
*sfinally = null;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override LabelStatement isLabelStatement()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class LabelDsymbol : Dsymbol
|
|
{
|
|
public:
|
|
LabelStatement statement;
|
|
|
|
extern (D) this(Identifier ident)
|
|
{
|
|
super(ident);
|
|
}
|
|
|
|
static LabelDsymbol create(Identifier ident)
|
|
{
|
|
return new LabelDsymbol(ident);
|
|
}
|
|
|
|
// is this a LabelDsymbol()?
|
|
override LabelDsymbol isLabel()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class AsmStatement : Statement
|
|
{
|
|
public:
|
|
Token* tokens;
|
|
code* asmcode;
|
|
uint asmalign; // alignment of this statement
|
|
uint regs; // mask of registers modified (must match regm_t in back end)
|
|
bool refparam; // true if function parameter is referenced
|
|
bool naked; // true if function is to be naked
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
// non-zero if this is a branch, contains the target label
|
|
LabelDsymbol isBranchToLabel;
|
|
}
|
|
|
|
extern (D) this(Loc loc, Token* tokens)
|
|
{
|
|
super(loc);
|
|
this.tokens = tokens;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
version(IN_LLVM)
|
|
{
|
|
auto a_s = new AsmStatement(loc, tokens);
|
|
a_s.refparam = refparam;
|
|
a_s.naked = naked;
|
|
return a_s;
|
|
}
|
|
else
|
|
{
|
|
return new AsmStatement(loc, tokens);
|
|
}
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
return asmSemantic(this, sc);
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
* a complete asm {} block
|
|
*/
|
|
extern (C++) final class CompoundAsmStatement : CompoundStatement
|
|
{
|
|
public:
|
|
StorageClass stc; // postfix attributes like nothrow/pure/@trusted
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
void* abiret; // llvm::Value*
|
|
}
|
|
|
|
extern (D) this(Loc loc, Statements* s, StorageClass stc)
|
|
{
|
|
super(loc, s);
|
|
this.stc = stc;
|
|
}
|
|
|
|
override CompoundAsmStatement syntaxCopy()
|
|
{
|
|
auto a = new Statements();
|
|
a.setDim(statements.dim);
|
|
foreach (i, s; *statements)
|
|
{
|
|
(*a)[i] = s ? s.syntaxCopy() : null;
|
|
}
|
|
return new CompoundAsmStatement(loc, a, stc);
|
|
}
|
|
|
|
override CompoundAsmStatement semantic(Scope* sc)
|
|
{
|
|
foreach (ref s; *statements)
|
|
{
|
|
s = s ? s.semantic(sc) : null;
|
|
}
|
|
assert(sc.func);
|
|
// use setImpure/setGC when the deprecation cycle is over
|
|
PURE purity;
|
|
if (!(stc & STCpure) && (purity = sc.func.isPureBypassingInference()) != PUREimpure && purity != PUREfwdref)
|
|
deprecation("asm statement is assumed to be impure - mark it with 'pure' if it is not");
|
|
if (!(stc & STCnogc) && sc.func.isNogcBypassingInference())
|
|
deprecation("asm statement is assumed to use the GC - mark it with '@nogc' if it does not");
|
|
if (!(stc & (STCtrusted | STCsafe)) && sc.func.setUnsafe())
|
|
error("asm statement is assumed to be @system - mark it with '@trusted' if it is not");
|
|
return this;
|
|
}
|
|
|
|
override Statements* flatten(Scope* sc)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
|
|
version(IN_LLVM)
|
|
{
|
|
override final CompoundStatement isCompoundStatement()
|
|
{
|
|
return null;
|
|
}
|
|
override final CompoundAsmStatement isCompoundAsmBlockStatement()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override final CompoundAsmStatement endsWithAsm()
|
|
{
|
|
// yes this is inline asm
|
|
return this;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************
|
|
*/
|
|
extern (C++) final class ImportStatement : Statement
|
|
{
|
|
public:
|
|
Dsymbols* imports; // Array of Import's
|
|
|
|
extern (D) this(Loc loc, Dsymbols* imports)
|
|
{
|
|
super(loc);
|
|
this.imports = imports;
|
|
}
|
|
|
|
override Statement syntaxCopy()
|
|
{
|
|
auto m = new Dsymbols();
|
|
m.setDim(imports.dim);
|
|
foreach (i, s; *imports)
|
|
{
|
|
(*m)[i] = s.syntaxCopy(null);
|
|
}
|
|
return new ImportStatement(loc, m);
|
|
}
|
|
|
|
override Statement semantic(Scope* sc)
|
|
{
|
|
foreach (i; 0 .. imports.dim)
|
|
{
|
|
Import s = (*imports)[i].isImport();
|
|
assert(!s.aliasdecls.dim);
|
|
foreach (j, name; s.names)
|
|
{
|
|
Identifier _alias = s.aliases[j];
|
|
if (!_alias)
|
|
_alias = name;
|
|
auto tname = new TypeIdentifier(s.loc, name);
|
|
auto ad = new AliasDeclaration(s.loc, _alias, tname);
|
|
ad._import = s;
|
|
s.aliasdecls.push(ad);
|
|
}
|
|
s.semantic(sc);
|
|
//s->semantic2(sc); // Bugzilla 14666
|
|
sc.insert(s);
|
|
foreach (aliasdecl; s.aliasdecls)
|
|
{
|
|
sc.insert(aliasdecl);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|