/** * Defines AST nodes for statements. * * Specification: $(LINK2 https://dlang.org/spec/statement.html, Statements) * * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/statement.d, _statement.d) * Documentation: https://dlang.org/phobos/dmd_statement.html * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statement.d */ module dmd.statement; import core.stdc.stdarg; import core.stdc.stdio; import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; import dmd.gluelayer; import dmd.canthrow; import dmd.cond; import dmd.dclass; import dmd.declaration; import dmd.denum; import dmd.dimport; import dmd.dscope; import dmd.dsymbol; import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.dinterpret; import dmd.mtype; import dmd.root.outbuffer; import dmd.root.rootobject; import dmd.sapply; import dmd.sideeffect; import dmd.staticassert; import dmd.tokens; import dmd.visitor; /** * Returns: * `TypeIdentifier` corresponding to `object.Throwable` */ TypeIdentifier getThrowable() { auto tid = new TypeIdentifier(Loc.initial, Id.empty); tid.addIdent(Id.object); tid.addIdent(Id.Throwable); return tid; } /** * Returns: * TypeIdentifier corresponding to `object.Exception` */ TypeIdentifier getException() { auto tid = new TypeIdentifier(Loc.initial, Id.empty); tid.addIdent(Id.object); tid.addIdent(Id.Exception); return tid; } /*********************************************************** * Specification: http://dlang.org/spec/statement.html */ extern (C++) abstract class Statement : ASTNode { const Loc loc; const STMT stmt; override final DYNCAST dyncast() const { return DYNCAST.statement; } final extern (D) this(const ref Loc loc, STMT stmt) { this.loc = loc; this.stmt = stmt; // If this is an in{} contract scope statement (skip for determining // inlineStatus of a function body for header content) } Statement syntaxCopy() { assert(0); } /************************************* * Do syntax copy of an array of Statement's. */ static Statements* arraySyntaxCopy(Statements* a) { Statements* b = null; if (a) { b = a.copy(); foreach (i, s; *a) { (*b)[i] = s ? s.syntaxCopy() : null; } } return b; } override final const(char)* toChars() const { HdrGenState hgs; OutBuffer buf; .toCBuffer(this, &buf, &hgs); buf.writeByte(0); return buf.extractSlice().ptr; } static if (__VERSION__ < 2092) { 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); } } else { pragma(printf) final void error(const(char)* format, ...) { va_list ap; va_start(ap, format); .verror(loc, format, ap); va_end(ap); } pragma(printf) final void warning(const(char)* format, ...) { va_list ap; va_start(ap, format); .vwarning(loc, format, ap); va_end(ap); } pragma(printf) final void deprecation(const(char)* format, ...) { va_list ap; va_start(ap, format); .vdeprecation(loc, format, ap); va_end(ap); } } Statement getRelatedLabeled() { return this; } /**************************** * Determine if an enclosed `break` would apply to this * statement, such as if it is a loop or switch statement. * Returns: * `true` if it does */ bool hasBreak() const pure nothrow { //printf("Statement::hasBreak()\n"); return false; } /**************************** * Determine if an enclosed `continue` would apply to this * statement, such as if it is a loop statement. * Returns: * `true` if it does */ bool hasContinue() const pure nothrow { return false; } /********************************** * Returns: * `true` if statement uses exception handling */ final bool usesEH() { extern (C++) final class UsesEH : StoppableVisitor { alias visit = typeof(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(ScopeGuardStatement s) { stop = true; } override void visit(SynchronizedStatement s) { stop = true; } } scope UsesEH ueh = new UsesEH(); return walkPostorder(this, ueh); } /********************************** * Returns: * `true` if statement 'comes from' somewhere else, like a goto */ final bool comeFrom() { extern (C++) final class ComeFrom : StoppableVisitor { alias visit = typeof(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); } /********************************** * Returns: * `true` if statement has executable code. */ final bool hasCode() { extern (C++) final class HasCode : StoppableVisitor { alias visit = typeof(super).visit; public: override void visit(Statement s) { stop = true; } override void visit(ExpStatement s) { if (s.exp !is null) { stop = s.exp.hasCode(); } } override void visit(CompoundStatement s) { } override void visit(ScopeStatement s) { } override void visit(ImportStatement s) { } } scope HasCode hc = new HasCode(); return walkPostorder(this, hc); } /******************************* * Find last statement in a sequence of statements. * Returns: * the last statement, or `null` if there isn't one */ inout(Statement) last() inout nothrow pure { return this; } /************************** * Support Visitor Pattern * Params: * v = visitor */ override void accept(Visitor v) { v.visit(this); } /************************************ * Does this statement end with a return statement? * * I.e. is it a single return statement or some compound statement * that unconditionally hits a return statement. * Returns: * return statement it ends with, otherwise null */ pure nothrow @nogc inout(ReturnStatement) endsWithReturnStatement() inout { return null; } final pure inout nothrow @nogc @safe: /******************** * A cheaper method of doing downcasting of Statements. * Returns: * the downcast statement if it can be downcasted, otherwise `null` */ inout(ErrorStatement) isErrorStatement() { return stmt == STMT.Error ? cast(typeof(return))this : null; } inout(ScopeStatement) isScopeStatement() { return stmt == STMT.Scope ? cast(typeof(return))this : null; } inout(ExpStatement) isExpStatement() { return stmt == STMT.Exp ? cast(typeof(return))this : null; } inout(CompoundStatement) isCompoundStatement() { return stmt == STMT.Compound ? cast(typeof(return))this : null; } inout(ReturnStatement) isReturnStatement() { return stmt == STMT.Return ? cast(typeof(return))this : null; } inout(IfStatement) isIfStatement() { return stmt == STMT.If ? cast(typeof(return))this : null; } inout(ConditionalStatement) isConditionalStatement() { return stmt == STMT.Conditional ? cast(typeof(return))this : null; } inout(StaticForeachStatement) isStaticForeachStatement() { return stmt == STMT.StaticForeach ? cast(typeof(return))this : null; } inout(CaseStatement) isCaseStatement() { return stmt == STMT.Case ? cast(typeof(return))this : null; } inout(DefaultStatement) isDefaultStatement() { return stmt == STMT.Default ? cast(typeof(return))this : null; } inout(LabelStatement) isLabelStatement() { return stmt == STMT.Label ? cast(typeof(return))this : null; } inout(GotoStatement) isGotoStatement() { return stmt == STMT.Goto ? cast(typeof(return))this : null; } inout(GotoDefaultStatement) isGotoDefaultStatement() { return stmt == STMT.GotoDefault ? cast(typeof(return))this : null; } inout(GotoCaseStatement) isGotoCaseStatement() { return stmt == STMT.GotoCase ? cast(typeof(return))this : null; } inout(BreakStatement) isBreakStatement() { return stmt == STMT.Break ? cast(typeof(return))this : null; } inout(DtorExpStatement) isDtorExpStatement() { return stmt == STMT.DtorExp ? cast(typeof(return))this : null; } inout(CompileStatement) isCompileStatement() { return stmt == STMT.Compile ? cast(typeof(return))this : null; } inout(ForwardingStatement) isForwardingStatement() { return stmt == STMT.Forwarding ? cast(typeof(return))this : null; } inout(DoStatement) isDoStatement() { return stmt == STMT.Do ? cast(typeof(return))this : null; } inout(WhileStatement) isWhileStatement() { return stmt == STMT.While ? cast(typeof(return))this : null; } inout(ForStatement) isForStatement() { return stmt == STMT.For ? cast(typeof(return))this : null; } inout(ForeachStatement) isForeachStatement() { return stmt == STMT.Foreach ? cast(typeof(return))this : null; } inout(SwitchStatement) isSwitchStatement() { return stmt == STMT.Switch ? cast(typeof(return))this : null; } inout(ContinueStatement) isContinueStatement() { return stmt == STMT.Continue ? cast(typeof(return))this : null; } inout(WithStatement) isWithStatement() { return stmt == STMT.With ? cast(typeof(return))this : null; } inout(TryCatchStatement) isTryCatchStatement() { return stmt == STMT.TryCatch ? cast(typeof(return))this : null; } inout(ThrowStatement) isThrowStatement() { return stmt == STMT.Throw ? cast(typeof(return))this : null; } inout(DebugStatement) isDebugStatement() { return stmt == STMT.Debug ? cast(typeof(return))this : null; } inout(TryFinallyStatement) isTryFinallyStatement() { return stmt == STMT.TryFinally ? cast(typeof(return))this : null; } inout(ScopeGuardStatement) isScopeGuardStatement() { return stmt == STMT.ScopeGuard ? cast(typeof(return))this : null; } inout(SwitchErrorStatement) isSwitchErrorStatement() { return stmt == STMT.SwitchError ? cast(typeof(return))this : null; } inout(UnrolledLoopStatement) isUnrolledLoopStatement() { return stmt == STMT.UnrolledLoop ? cast(typeof(return))this : null; } inout(ForeachRangeStatement) isForeachRangeStatement() { return stmt == STMT.ForeachRange ? cast(typeof(return))this : null; } inout(CompoundDeclarationStatement) isCompoundDeclarationStatement() { return stmt == STMT.CompoundDeclaration ? cast(typeof(return))this : 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 { extern (D) this() { super(Loc.initial, STMT.Error); assert(global.gaggedErrors || global.errors); } override ErrorStatement syntaxCopy() { return this; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class PeelStatement : Statement { Statement s; extern (D) this(Statement s) { super(s.loc, STMT.Peel); this.s = s; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#ExpressionStatement */ extern (C++) class ExpStatement : Statement { Expression exp; final extern (D) this(const ref Loc loc, Expression exp) { super(loc, STMT.Exp); this.exp = exp; } final extern (D) this(const ref Loc loc, Expression exp, STMT stmt) { super(loc, stmt); this.exp = exp; } final extern (D) this(const ref Loc loc, Dsymbol declaration) { super(loc, STMT.Exp); this.exp = new DeclarationExp(loc, declaration); } static ExpStatement create(Loc loc, Expression exp) { return new ExpStatement(loc, exp); } override ExpStatement syntaxCopy() { return new ExpStatement(loc, exp ? exp.syntaxCopy() : null); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class DtorExpStatement : ExpStatement { // Wraps an expression that is the destruction of 'var' VarDeclaration var; extern (D) this(const ref Loc loc, Expression exp, VarDeclaration var) { super(loc, exp, STMT.DtorExp); this.var = var; } override DtorExpStatement syntaxCopy() { return new DtorExpStatement(loc, exp ? exp.syntaxCopy() : null, var); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#mixin-statement */ extern (C++) final class CompileStatement : Statement { Expressions* exps; extern (D) this(const ref Loc loc, Expression exp) { Expressions* exps = new Expressions(); exps.push(exp); this(loc, exps); } extern (D) this(const ref Loc loc, Expressions* exps) { super(loc, STMT.Compile); this.exps = exps; } override CompileStatement syntaxCopy() { return new CompileStatement(loc, Expression.arraySyntaxCopy(exps)); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) class CompoundStatement : Statement { Statements* statements; /** * Construct a `CompoundStatement` using an already existing * array of `Statement`s * * Params: * loc = Instantiation information * statements = An array of `Statement`s, that will referenced by this class */ final extern (D) this(const ref Loc loc, Statements* statements) { super(loc, STMT.Compound); this.statements = statements; } final extern (D) this(const ref Loc loc, Statements* statements, STMT stmt) { super(loc, stmt); this.statements = statements; } /** * Construct a `CompoundStatement` from an array of `Statement`s * * Params: * loc = Instantiation information * sts = A variadic array of `Statement`s, that will copied in this class * The entries themselves will not be copied. */ final extern (D) this(const ref Loc loc, Statement[] sts...) { super(loc, STMT.Compound); statements = new Statements(); statements.reserve(sts.length); foreach (s; sts) statements.push(s); } static CompoundStatement create(Loc loc, Statement s1, Statement s2) { return new CompoundStatement(loc, s1, s2); } override CompoundStatement syntaxCopy() { return new CompoundStatement(loc, Statement.arraySyntaxCopy(statements)); } override final inout(ReturnStatement) endsWithReturnStatement() inout nothrow pure { foreach (s; *statements) { if (s) { if (inout rs = s.endsWithReturnStatement()) return rs; } } return null; } override final inout(Statement) last() inout nothrow pure { Statement s = null; for (size_t i = statements.dim; i; --i) { s = cast(Statement)(*statements)[i - 1]; if (s) { s = cast(Statement)s.last(); if (s) break; } } return cast(inout)s; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class CompoundDeclarationStatement : CompoundStatement { extern (D) this(const ref Loc loc, Statements* statements) { super(loc, statements, STMT.CompoundDeclaration); } override CompoundDeclarationStatement syntaxCopy() { auto a = new Statements(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 { Statements* statements; extern (D) this(const ref Loc loc, Statements* statements) { super(loc, STMT.UnrolledLoop); this.statements = statements; } override UnrolledLoopStatement syntaxCopy() { auto a = new Statements(statements.dim); foreach (i, s; *statements) { (*a)[i] = s ? s.syntaxCopy() : null; } return new UnrolledLoopStatement(loc, a); } override bool hasBreak() const pure nothrow { return true; } override bool hasContinue() const pure nothrow { return true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) class ScopeStatement : Statement { Statement statement; Loc endloc; // location of closing curly bracket extern (D) this(const ref Loc loc, Statement statement, Loc endloc) { super(loc, STMT.Scope); this.statement = statement; this.endloc = endloc; } override ScopeStatement syntaxCopy() { return new ScopeStatement(loc, statement ? statement.syntaxCopy() : null, endloc); } override inout(ReturnStatement) endsWithReturnStatement() inout nothrow pure { if (statement) return statement.endsWithReturnStatement(); return null; } override bool hasBreak() const pure nothrow { //printf("ScopeStatement::hasBreak() %s\n", toChars()); return statement ? statement.hasBreak() : false; } override bool hasContinue() const pure nothrow { return statement ? statement.hasContinue() : false; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * Statement whose symbol table contains foreach index variables in a * local scope and forwards other members to the parent scope. This * wraps a statement. * * Also see: `dmd.attrib.ForwardingAttribDeclaration` */ extern (C++) final class ForwardingStatement : Statement { /// The symbol containing the `static foreach` variables. ForwardingScopeDsymbol sym = null; /// The wrapped statement. Statement statement; extern (D) this(const ref Loc loc, ForwardingScopeDsymbol sym, Statement statement) { super(loc, STMT.Forwarding); this.sym = sym; assert(statement); this.statement = statement; } extern (D) this(const ref Loc loc, Statement statement) { auto sym = new ForwardingScopeDsymbol(null); sym.symtab = new DsymbolTable(); this(loc, sym, statement); } override ForwardingStatement syntaxCopy() { return new ForwardingStatement(loc, statement.syntaxCopy()); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#while-statement */ extern (C++) final class WhileStatement : Statement { Parameter param; Expression condition; Statement _body; Loc endloc; // location of closing curly bracket extern (D) this(const ref Loc loc, Expression condition, Statement _body, Loc endloc, Parameter param = null) { super(loc, STMT.While); this.condition = condition; this._body = _body; this.endloc = endloc; this.param = param; } override WhileStatement syntaxCopy() { return new WhileStatement(loc, condition.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc, param ? param.syntaxCopy() : null); } override bool hasBreak() const pure nothrow { return true; } override bool hasContinue() const pure nothrow { return true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#do-statement */ extern (C++) final class DoStatement : Statement { Statement _body; Expression condition; Loc endloc; // location of ';' after while extern (D) this(const ref Loc loc, Statement _body, Expression condition, Loc endloc) { super(loc, STMT.Do); this._body = _body; this.condition = condition; this.endloc = endloc; } override DoStatement syntaxCopy() { return new DoStatement(loc, _body ? _body.syntaxCopy() : null, condition.syntaxCopy(), endloc); } override bool hasBreak() const pure nothrow { return true; } override bool hasContinue() const pure nothrow { return true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#for-statement */ extern (C++) final class ForStatement : Statement { 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(const ref Loc loc, Statement _init, Expression condition, Expression increment, Statement _body, Loc endloc) { super(loc, STMT.For); this._init = _init; this.condition = condition; this.increment = increment; this._body = _body; this.endloc = endloc; } override ForStatement syntaxCopy() { return new ForStatement(loc, _init ? _init.syntaxCopy() : null, condition ? condition.syntaxCopy() : null, increment ? increment.syntaxCopy() : null, _body.syntaxCopy(), endloc); } override Statement getRelatedLabeled() { return relatedLabeled ? relatedLabeled : this; } override bool hasBreak() const pure nothrow { //printf("ForStatement::hasBreak()\n"); return true; } override bool hasContinue() const pure nothrow { return true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#foreach-statement */ extern (C++) final class ForeachStatement : Statement { TOK op; // TOK.foreach_ or TOK.foreach_reverse_ Parameters* parameters; // array of Parameters, one for each ForeachType Expression aggr; // ForeachAggregate Statement _body; // NoScopeNonEmptyStatement 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(const ref Loc loc, TOK op, Parameters* parameters, Expression aggr, Statement _body, Loc endloc) { super(loc, STMT.Foreach); this.op = op; this.parameters = parameters; this.aggr = aggr; this._body = _body; this.endloc = endloc; } override ForeachStatement syntaxCopy() { return new ForeachStatement(loc, op, Parameter.arraySyntaxCopy(parameters), aggr.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc); } override bool hasBreak() const pure nothrow { return true; } override bool hasContinue() const pure nothrow { return true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#foreach-range-statement */ extern (C++) final class ForeachRangeStatement : Statement { TOK op; // TOK.foreach_ or TOK.foreach_reverse_ Parameter prm; // loop index variable Expression lwr; Expression upr; Statement _body; Loc endloc; // location of closing curly bracket VarDeclaration key; extern (D) this(const ref Loc loc, TOK op, Parameter prm, Expression lwr, Expression upr, Statement _body, Loc endloc) { super(loc, STMT.ForeachRange); this.op = op; this.prm = prm; this.lwr = lwr; this.upr = upr; this._body = _body; this.endloc = endloc; } override ForeachRangeStatement syntaxCopy() { return new ForeachRangeStatement(loc, op, prm.syntaxCopy(), lwr.syntaxCopy(), upr.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc); } override bool hasBreak() const pure nothrow { return true; } override bool hasContinue() const pure nothrow { return true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#if-statement */ extern (C++) final class IfStatement : Statement { Parameter prm; Expression condition; Statement ifbody; Statement elsebody; VarDeclaration match; // for MatchExpression results Loc endloc; // location of closing curly bracket extern (D) this(const ref Loc loc, Parameter prm, Expression condition, Statement ifbody, Statement elsebody, Loc endloc) { super(loc, STMT.If); this.prm = prm; this.condition = condition; this.ifbody = ifbody; this.elsebody = elsebody; this.endloc = endloc; } override IfStatement syntaxCopy() { return new IfStatement(loc, prm ? prm.syntaxCopy() : null, condition.syntaxCopy(), ifbody ? ifbody.syntaxCopy() : null, elsebody ? elsebody.syntaxCopy() : null, endloc); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/version.html#ConditionalStatement */ extern (C++) final class ConditionalStatement : Statement { Condition condition; Statement ifbody; Statement elsebody; extern (D) this(const ref Loc loc, Condition condition, Statement ifbody, Statement elsebody) { super(loc, STMT.Conditional); this.condition = condition; this.ifbody = ifbody; this.elsebody = elsebody; } override ConditionalStatement syntaxCopy() { return new ConditionalStatement(loc, condition.syntaxCopy(), ifbody.syntaxCopy(), elsebody ? elsebody.syntaxCopy() : null); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/version.html#StaticForeachStatement * Static foreach statements, like: * void main() * { * static foreach(i; 0 .. 10) * { * pragma(msg, i); * } * } */ extern (C++) final class StaticForeachStatement : Statement { StaticForeach sfe; extern (D) this(const ref Loc loc, StaticForeach sfe) { super(loc, STMT.StaticForeach); this.sfe = sfe; } override StaticForeachStatement syntaxCopy() { return new StaticForeachStatement(loc, sfe.syntaxCopy()); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#pragma-statement */ extern (C++) final class PragmaStatement : Statement { const Identifier ident; Expressions* args; // array of Expression's Statement _body; extern (D) this(const ref Loc loc, const Identifier ident, Expressions* args, Statement _body) { super(loc, STMT.Pragma); this.ident = ident; this.args = args; this._body = _body; } override PragmaStatement syntaxCopy() { return new PragmaStatement(loc, ident, Expression.arraySyntaxCopy(args), _body ? _body.syntaxCopy() : null); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/version.html#StaticAssert */ extern (C++) final class StaticAssertStatement : Statement { StaticAssert sa; extern (D) this(StaticAssert sa) { super(sa.loc, STMT.StaticAssert); this.sa = sa; } override StaticAssertStatement syntaxCopy() { return new StaticAssertStatement(sa.syntaxCopy(null)); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#switch-statement */ extern (C++) final class SwitchStatement : Statement { Expression condition; /// switch(condition) Statement _body; /// bool isFinal; /// https://dlang.org/spec/statement.html#final-switch-statement DefaultStatement sdefault; /// default: Statement tryBody; /// set to TryCatchStatement or TryFinallyStatement if in _body portion TryFinallyStatement tf; /// set if in the 'finally' block of a TryFinallyStatement 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 VarDeclaration lastVar; /// last observed variable declaration in this statement extern (D) this(const ref Loc loc, Expression condition, Statement _body, bool isFinal) { super(loc, STMT.Switch); this.condition = condition; this._body = _body; this.isFinal = isFinal; } override SwitchStatement syntaxCopy() { return new SwitchStatement(loc, condition.syntaxCopy(), _body.syntaxCopy(), isFinal); } override bool hasBreak() const pure nothrow { return true; } /************************************ * Returns: * true if error */ extern (D) bool checkLabel() { /* * Checks the scope of a label for existing variable declaration. * Params: * vd = last variable declared before this case/default label * Returns: `true` if the variables declared in this label would be skipped. */ bool checkVar(VarDeclaration vd) { for (auto v = vd; v && v != lastVar; v = v.lastVar) { if (v.isDataseg() || (v.storage_class & (STC.manifest | STC.temp) && vd.ident != Id.withSym) || v._init.isVoidInitializer()) continue; if (vd.ident == Id.withSym) error("`switch` skips declaration of `with` temporary at %s", v.loc.toChars()); else error("`switch` skips declaration of variable `%s` at %s", v.toPrettyChars(), v.loc.toChars()); return true; } return false; } enum error = true; if (sdefault && checkVar(sdefault.lastVar)) return !error; // return error once fully deprecated foreach (scase; *cases) { if (scase && checkVar(scase.lastVar)) return !error; // return error once fully deprecated } return !error; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#CaseStatement */ extern (C++) final class CaseStatement : Statement { Expression exp; Statement statement; int index; // which case it is (since we sort this) VarDeclaration lastVar; void* extra; // for use by Statement_toIR() extern (D) this(const ref Loc loc, Expression exp, Statement statement) { super(loc, STMT.Case); this.exp = exp; this.statement = statement; } override CaseStatement syntaxCopy() { return new CaseStatement(loc, exp.syntaxCopy(), statement.syntaxCopy()); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#CaseRangeStatement */ extern (C++) final class CaseRangeStatement : Statement { Expression first; Expression last; Statement statement; extern (D) this(const ref Loc loc, Expression first, Expression last, Statement statement) { super(loc, STMT.CaseRange); this.first = first; this.last = last; this.statement = statement; } override CaseRangeStatement syntaxCopy() { return new CaseRangeStatement(loc, first.syntaxCopy(), last.syntaxCopy(), statement.syntaxCopy()); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#DefaultStatement */ extern (C++) final class DefaultStatement : Statement { Statement statement; VarDeclaration lastVar; extern (D) this(const ref Loc loc, Statement statement) { super(loc, STMT.Default); this.statement = statement; } override DefaultStatement syntaxCopy() { return new DefaultStatement(loc, statement.syntaxCopy()); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#GotoStatement */ extern (C++) final class GotoDefaultStatement : Statement { SwitchStatement sw; extern (D) this(const ref Loc loc) { super(loc, STMT.GotoDefault); } override GotoDefaultStatement syntaxCopy() { return new GotoDefaultStatement(loc); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#GotoStatement */ extern (C++) final class GotoCaseStatement : Statement { Expression exp; // null, or which case to goto CaseStatement cs; // case statement it resolves to extern (D) this(const ref Loc loc, Expression exp) { super(loc, STMT.GotoCase); this.exp = exp; } override GotoCaseStatement syntaxCopy() { return new GotoCaseStatement(loc, exp ? exp.syntaxCopy() : null); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class SwitchErrorStatement : Statement { Expression exp; extern (D) this(const ref Loc loc) { super(loc, STMT.SwitchError); } final extern (D) this(const ref Loc loc, Expression exp) { super(loc, STMT.SwitchError); this.exp = exp; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#return-statement */ extern (C++) final class ReturnStatement : Statement { Expression exp; size_t caseDim; extern (D) this(const ref Loc loc, Expression exp) { super(loc, STMT.Return); this.exp = exp; } override ReturnStatement syntaxCopy() { return new ReturnStatement(loc, exp ? exp.syntaxCopy() : null); } override inout(ReturnStatement) endsWithReturnStatement() inout nothrow pure { return this; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#break-statement */ extern (C++) final class BreakStatement : Statement { Identifier ident; extern (D) this(const ref Loc loc, Identifier ident) { super(loc, STMT.Break); this.ident = ident; } override BreakStatement syntaxCopy() { return new BreakStatement(loc, ident); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#continue-statement */ extern (C++) final class ContinueStatement : Statement { Identifier ident; extern (D) this(const ref Loc loc, Identifier ident) { super(loc, STMT.Continue); this.ident = ident; } override ContinueStatement syntaxCopy() { return new ContinueStatement(loc, ident); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#SynchronizedStatement */ extern (C++) final class SynchronizedStatement : Statement { Expression exp; Statement _body; extern (D) this(const ref Loc loc, Expression exp, Statement _body) { super(loc, STMT.Synchronized); this.exp = exp; this._body = _body; } override SynchronizedStatement syntaxCopy() { return new SynchronizedStatement(loc, exp ? exp.syntaxCopy() : null, _body ? _body.syntaxCopy() : null); } override bool hasBreak() const pure nothrow { return false; //true; } override bool hasContinue() const pure nothrow { return false; //true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#with-statement */ extern (C++) final class WithStatement : Statement { Expression exp; Statement _body; VarDeclaration wthis; Loc endloc; extern (D) this(const ref Loc loc, Expression exp, Statement _body, Loc endloc) { super(loc, STMT.With); this.exp = exp; this._body = _body; this.endloc = endloc; } override WithStatement syntaxCopy() { return new WithStatement(loc, exp.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#try-statement */ extern (C++) final class TryCatchStatement : Statement { Statement _body; Catches* catches; Statement tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion extern (D) this(const ref Loc loc, Statement _body, Catches* catches) { super(loc, STMT.TryCatch); this._body = _body; this.catches = catches; } override TryCatchStatement syntaxCopy() { auto a = new Catches(catches.dim); foreach (i, c; *catches) { (*a)[i] = c.syntaxCopy(); } return new TryCatchStatement(loc, _body.syntaxCopy(), a); } override bool hasBreak() const pure nothrow { return false; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#Catch */ extern (C++) final class Catch : RootObject { const Loc loc; Type type; Identifier ident; Statement handler; VarDeclaration var; bool errors; // set if semantic processing errors // was generated by the compiler, wasn't present in source code bool internalCatch; extern (D) this(const ref Loc loc, Type type, Identifier ident, Statement handler) { //printf("Catch(%s, loc = %s)\n", id.toChars(), loc.toChars()); this.loc = loc; this.type = type; this.ident = ident; this.handler = handler; } Catch syntaxCopy() { auto c = new Catch(loc, type ? type.syntaxCopy() : getThrowable(), ident, (handler ? handler.syntaxCopy() : null)); c.internalCatch = internalCatch; return c; } } /*********************************************************** * https://dlang.org/spec/statement.html#try-statement */ extern (C++) final class TryFinallyStatement : Statement { Statement _body; Statement finalbody; Statement tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion bool bodyFallsThru; /// true if _body falls through to finally extern (D) this(const ref Loc loc, Statement _body, Statement finalbody) { super(loc, STMT.TryFinally); this._body = _body; this.finalbody = finalbody; this.bodyFallsThru = true; // assume true until statementSemantic() } static TryFinallyStatement create(Loc loc, Statement _body, Statement finalbody) { return new TryFinallyStatement(loc, _body, finalbody); } override TryFinallyStatement syntaxCopy() { return new TryFinallyStatement(loc, _body.syntaxCopy(), finalbody.syntaxCopy()); } override bool hasBreak() const pure nothrow { return false; //true; } override bool hasContinue() const pure nothrow { return false; //true; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#scope-guard-statement */ extern (C++) final class ScopeGuardStatement : Statement { TOK tok; Statement statement; extern (D) this(const ref Loc loc, TOK tok, Statement statement) { super(loc, STMT.ScopeGuard); this.tok = tok; this.statement = statement; } override ScopeGuardStatement syntaxCopy() { return new ScopeGuardStatement(loc, tok, statement.syntaxCopy()); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#throw-statement */ extern (C++) final class ThrowStatement : Statement { Expression exp; // was generated by the compiler, wasn't present in source code bool internalThrow; extern (D) this(const ref Loc loc, Expression exp) { super(loc, STMT.Throw); this.exp = exp; } override ThrowStatement syntaxCopy() { auto s = new ThrowStatement(loc, exp.syntaxCopy()); s.internalThrow = internalThrow; return s; } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class DebugStatement : Statement { Statement statement; extern (D) this(const ref Loc loc, Statement statement) { super(loc, STMT.Debug); this.statement = statement; } override DebugStatement syntaxCopy() { return new DebugStatement(loc, statement ? statement.syntaxCopy() : null); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/statement.html#goto-statement */ extern (C++) final class GotoStatement : Statement { Identifier ident; LabelDsymbol label; Statement tryBody; /// set to TryCatchStatement or TryFinallyStatement if in _body portion TryFinallyStatement tf; ScopeGuardStatement os; VarDeclaration lastVar; extern (D) this(const ref Loc loc, Identifier ident) { super(loc, STMT.Goto); this.ident = ident; } override GotoStatement syntaxCopy() { return new GotoStatement(loc, ident); } extern (D) bool checkLabel() { if (!label.statement) return true; // error should have been issued for this already if (label.statement.os != os) { if (os && os.tok == TOK.onScopeFailure && !label.statement.os) { // Jump out from scope(failure) block is allowed. } else { if (label.statement.os) error("cannot `goto` in to `%s` block", Token.toChars(label.statement.os.tok)); else error("cannot `goto` out of `%s` block", Token.toChars(os.tok)); return true; } } if (label.statement.tf != tf) { error("cannot `goto` in or out of `finally` block"); return true; } Statement stbnext; for (auto stb = tryBody; stb != label.statement.tryBody; stb = stbnext) { if (!stb) { error("cannot `goto` into `try` block"); return true; } if (auto stf = stb.isTryFinallyStatement()) stbnext = stf.tryBody; else if (auto stc = stb.isTryCatchStatement()) stbnext = stc.tryBody; else assert(0); } VarDeclaration vd = label.statement.lastVar; if (!vd || vd.isDataseg() || (vd.storage_class & STC.manifest)) 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.storage_class & STC.exptemp) { // Lifetime ends at end of expression, so no issue with skipping the statement } 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); } } /*********************************************************** * https://dlang.org/spec/statement.html#LabeledStatement */ extern (C++) final class LabelStatement : Statement { Identifier ident; Statement statement; Statement tryBody; /// set to TryCatchStatement or TryFinallyStatement if in _body portion TryFinallyStatement tf; ScopeGuardStatement os; VarDeclaration lastVar; Statement gotoTarget; // interpret void* extra; // used by Statement_toIR() bool breaks; // someone did a 'break ident' extern (D) this(const ref Loc loc, Identifier ident, Statement statement) { super(loc, STMT.Label); this.ident = ident; this.statement = statement; } override LabelStatement syntaxCopy() { return new LabelStatement(loc, ident, statement ? statement.syntaxCopy() : null); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** */ extern (C++) final class LabelDsymbol : Dsymbol { LabelStatement statement; bool deleted; // set if rewritten to return in foreach delegate bool iasm; // set if used by inline assembler extern (D) this(Identifier ident, const ref Loc loc = Loc.initial) { super(loc, 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); } } /*********************************************************** * https://dlang.org/spec/statement.html#asm */ extern (C++) class AsmStatement : Statement { Token* tokens; extern (D) this(const ref Loc loc, Token* tokens) { super(loc, STMT.Asm); this.tokens = tokens; } extern (D) this(const ref Loc loc, Token* tokens, STMT stmt) { super(loc, stmt); this.tokens = tokens; } override AsmStatement syntaxCopy() { return new AsmStatement(loc, tokens); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/iasm.html */ extern (C++) final class InlineAsmStatement : AsmStatement { 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 extern (D) this(const ref Loc loc, Token* tokens) { super(loc, tokens, STMT.InlineAsm); } override InlineAsmStatement syntaxCopy() { return new InlineAsmStatement(loc, tokens); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html * Assembler instructions with D expression operands. */ extern (C++) final class GccAsmStatement : AsmStatement { StorageClass stc; // attributes of the asm {} block Expression insn; // string expression that is the template for assembler code Expressions* args; // input and output operands of the statement uint outputargs; // of the operands in 'args', the number of output operands Identifiers* names; // list of symbolic names for the operands Expressions* constraints; // list of string constants specifying constraints on operands Expressions* clobbers; // list of string constants specifying clobbers and scratch registers Identifiers* labels; // list of goto labels GotoStatements* gotos; // of the goto labels, the equivalent statements they represent extern (D) this(const ref Loc loc, Token* tokens) { super(loc, tokens, STMT.GccAsm); } override GccAsmStatement syntaxCopy() { return new GccAsmStatement(loc, tokens); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * a complete asm {} block */ extern (C++) final class CompoundAsmStatement : CompoundStatement { StorageClass stc; // postfix attributes like nothrow/pure/@trusted extern (D) this(const ref Loc loc, Statements* statements, StorageClass stc) { super(loc, statements, STMT.CompoundAsm); this.stc = stc; } override CompoundAsmStatement syntaxCopy() { auto a = new Statements(statements.dim); foreach (i, s; *statements) { (*a)[i] = s ? s.syntaxCopy() : null; } return new CompoundAsmStatement(loc, a, stc); } override void accept(Visitor v) { v.visit(this); } } /*********************************************************** * https://dlang.org/spec/module.html#ImportDeclaration */ extern (C++) final class ImportStatement : Statement { Dsymbols* imports; // Array of Import's extern (D) this(const ref Loc loc, Dsymbols* imports) { super(loc, STMT.Import); this.imports = imports; } override ImportStatement syntaxCopy() { auto m = new Dsymbols(imports.dim); foreach (i, s; *imports) { (*m)[i] = s.syntaxCopy(null); } return new ImportStatement(loc, m); } override void accept(Visitor v) { v.visit(this); } }