Ddoc $(SPEC_S Statements, C and C++ programmers will find the D statements very familiar, with a few interesting additions. $(GRAMMAR $(GNAME Statement): $(B ;) $(GLINK NonEmptyStatement) $(GLINK ScopeBlockStatement) $(GNAME NoScopeNonEmptyStatement): $(GLINK NonEmptyStatement) $(GLINK BlockStatement) $(GNAME NoScopeStatement): $(B ;) $(GLINK NonEmptyStatement) $(GLINK BlockStatement) $(GNAME NonEmptyOrScopeBlockStatement): $(GLINK NonEmptyStatement) $(GLINK ScopeBlockStatement) $(GNAME NonEmptyStatement): $(GLINK NonEmptyStatementNoCaseNoDefault) $(GLINK CaseStatement) $(V2 $(GLINK CaseRangeStatement) ) $(GLINK DefaultStatement) $(GNAME NonEmptyStatementNoCaseNoDefault): $(GLINK LabeledStatement) $(GLINK ExpressionStatement) $(GLINK DeclarationStatement) $(GLINK IfStatement) $(GLINK WhileStatement) $(GLINK DoStatement) $(GLINK ForStatement) $(GLINK ForeachStatement) $(GLINK SwitchStatement) $(V2 $(GLINK FinalSwitchStatement) ) $(GLINK ContinueStatement) $(GLINK BreakStatement) $(GLINK ReturnStatement) $(GLINK GotoStatement) $(GLINK WithStatement) $(GLINK SynchronizedStatement) $(GLINK TryStatement) $(GLINK ScopeGuardStatement) $(GLINK ThrowStatement) $(V1 $(GLINK VolatileStatement) ) $(GLINK AsmStatement) $(GLINK PragmaStatement) $(GLINK MixinStatement) $(V2 $(GLINK ForeachRangeStatement)) $(LINK2 version.html#ConditionalStatement, $(I ConditionalStatement)) $(LINK2 version.html#StaticAssert, $(I StaticAssert)) $(LINK2 template-mixin.html#TemplateMixin, $(I TemplateMixin)) ) $(P Any ambiguities in the grammar between $(I Statement)s and $(GLINK2 declaration, Declaration)s are resolved by the declarations taking precedence. If a $(I Statement) is desired instead, wrapping it in parentheses will disambiguate it in favor of being a $(I Statement). )

$(LNAME2 ScopeStatement, Scope Statements)

$(GRAMMAR $(I ScopeStatement): $(GLINK NonEmptyStatement) $(GLINK BlockStatement) ) $(P A new scope for local symbols is introduced for the $(I NonEmptyStatement) or $(GLINK BlockStatement). ) $(P Even though a new scope is introduced, local symbol declarations cannot shadow (hide) other local symbol declarations in the same function. ) -------------- void func1(int x) { int x; // illegal, x shadows parameter x int y; { int y; } // illegal, y shadows enclosing scope's y void delegate() dg; dg = { int y; }; // ok, this y is not in the same function struct S { int y; // ok, this y is a member, not a local } { int z; } { int z; } // ok, this z is not shadowing the other z { int t; } { t++; } // illegal, t is undefined } -------------- $(P The idea is to avoid bugs in complex functions caused by scoped declarations inadvertently hiding previous ones. Local names should all be unique within a function. )

$(LNAME2 ScopeBlockStatement, Scope Block Statements)

$(GRAMMAR $(I ScopeBlockStatement): $(GLINK BlockStatement) ) $(P A scope block statement introduces a new scope for the $(GLINK BlockStatement). )

$(LNAME2 LabeledStatement, Labeled Statements)

$(P Statements can be labeled. A label is an identifier that precedes a statement. ) $(GRAMMAR $(I LabeledStatement): $(I Identifier) : $(PSSEMI) ) $(P Any statement can be labeled, including empty statements, and so can serve as the target of a goto statement. Labeled statements can also serve as the target of a break or continue statement. ) $(P Labels are in a name space independent of declarations, variables, types, etc. Even so, labels cannot have the same name as local declarations. The label name space is the body of the function they appear in. Label name spaces do not nest, i.e. a label inside a block statement is accessible from outside that block. )

$(LNAME2 BlockStatement, Block Statement)

$(GRAMMAR $(I BlockStatement): $(B { }) $(B {) $(I StatementList) $(B }) $(GNAME StatementList): $(PSSEMI_PSCURLYSCOPE) $(PSSEMI_PSCURLYSCOPE) $(I StatementList) ) $(P A block statement is a sequence of statements enclosed by { }. The statements are executed in lexical order. )

$(LNAME2 ExpressionStatement, Expression Statement)

$(GRAMMAR $(I ExpressionStatement): $(EXPRESSION) $(B ;) ) The expression is evaluated.

Expressions that have no effect, like $(TT (x + x)), are illegal in expression statements. If such an expression is needed, casting it to $(D_KEYWORD void) will make it legal. ---- int x; x++; // ok x; // illegal 1+1; // illegal cast(void)(x + x); // ok ----

$(LNAME2 DeclarationStatement, Declaration Statement)

Declaration statements declare variables and types. $(GRAMMAR $(I DeclarationStatement): $(I Declaration) ) $(P Some declaration statements:) ---- int a; // declare a as type int and initialize it to 0 struct S { } // declare struct s alias int myint; ----

$(LNAME2 IfStatement, If Statement)

If statements provide simple conditional execution of statements. $(GRAMMAR $(I IfStatement): $(B if $(LPAREN)) $(I IfCondition) $(B $(RPAREN)) $(I ThenStatement) $(B if $(LPAREN)) $(I IfCondition) $(B $(RPAREN)) $(I ThenStatement) $(B else) $(I ElseStatement) $(GNAME IfCondition): $(EXPRESSION) $(B auto) $(I Identifier) $(B =) $(EXPRESSION) $(GLINK2 declaration, BasicType) $(GLINK2 declaration, Declarator) $(B =) $(EXPRESSION) $(GNAME ThenStatement): $(PSSCOPE) $(GNAME ElseStatement): $(PSSCOPE) ) $(EXPRESSION) is evaluated and must have a type that can be converted to a boolean. If it's true the $(I ThenStatement) is transferred to, else the $(I ElseStatement) is transferred to.

The 'dangling else' parsing problem is solved by associating the else with the nearest if statement.

If an $(B auto) $(I Identifier) is provided, it is declared and initialized to the value and type of the $(EXPRESSION). Its scope extends from when it is initialized to the end of the $(I ThenStatement).

If a $(I Declarator) is provided, it is declared and initialized to the value of the $(EXPRESSION). Its scope extends from when it is initialized to the end of the $(I ThenStatement). --- import std.regexp; ... if (auto m = std.regexp.search("abcdef", "b(c)d")) { writefln("[%s]", m.pre); // prints [a] writefln("[%s]", m.post); // prints [ef] writefln("[%s]", m.match(0)); // prints [bcd] writefln("[%s]", m.match(1)); // prints [c] writefln("[%s]", m.match(2)); // prints [] } else { writefln(m.post); // error, m undefined } writefln(m.pre); // error, m undefined ---

$(LNAME2 WhileStatement, While Statement)

$(GRAMMAR $(I WhileStatement): $(B while $(LPAREN)) $(EXPRESSION) $(B $(RPAREN)) $(PSSCOPE) ) While statements implement simple loops. $(EXPRESSION) is evaluated and must have a type that can be converted to a boolean. If it's true the $(PSSCOPE) is executed. After the $(PSSCOPE) is executed, the $(EXPRESSION) is evaluated again, and if true the $(PSSCOPE) is executed again. This continues until the $(EXPRESSION) evaluates to false. --- int i = 0; while (i < 10) { foo(i); i++; } --- A $(GLINK BreakStatement) will exit the loop. A $(GLINK ContinueStatement) will transfer directly to evaluating $(EXPRESSION) again.

$(LNAME2 DoStatement, Do Statement)

$(GRAMMAR $(I DoStatement): $(B do) $(PSSCOPE) $(B while $(LPAREN)) $(EXPRESSION) $(B $(RPAREN)) ) Do while statements implement simple loops. $(PSSCOPE) is executed. Then $(EXPRESSION) is evaluated and must have a type that can be converted to a boolean. If it's true the loop is iterated again. This continues until the $(EXPRESSION) evaluates to false. --- int i = 0; do { foo(i); } while (++i < 10); --- A $(GLINK BreakStatement) will exit the loop. A $(GLINK ContinueStatement) will transfer directly to evaluating $(EXPRESSION) again.

$(LNAME2 ForStatement, For Statement)

For statements implement loops with initialization, test, and increment clauses. $(GRAMMAR $(I ForStatement): $(B for $(LPAREN))$(I Initialize) $(I Test)$(OPT) $(B ;) $(I Increment)$(OPT)$(B $(RPAREN)) $(PSSCOPE) $(GNAME Initialize): $(B ;) $(PS0) $(GNAME Test): $(EXPRESSION) $(GNAME Increment): $(EXPRESSION) ) $(P $(I Initialize) is executed. $(I Test) is evaluated and must have a type that can be converted to a boolean. If it's true the statement is executed. After the statement is executed, the $(I Increment) is executed. Then $(I Test) is evaluated again, and if true the statement is executed again. This continues until the $(I Test) evaluates to false. ) $(P A $(GLINK BreakStatement) will exit the loop. A $(GLINK ContinueStatement) will transfer directly to the $(I Increment). ) $(P A $(I ForStatement) creates a new scope. If $(I Initialize) declares a variable, that variable's scope extends through the end of the for statement. For example: ) -------------- for (int i = 0; i < 10; i++) foo(i); -------------- is equivalent to: -------------- { int i; for (i = 0; i < 10; i++) foo(i); } -------------- Function bodies cannot be empty: -------------- for (int i = 0; i < 10; i++) ; // illegal -------------- Use instead: -------------- for (int i = 0; i < 10; i++) { } -------------- The $(I Initialize) may be omitted. $(I Test) may also be omitted, and if so, it is treated as if it evaluated to true.

$(LNAME2 ForeachStatement, Foreach Statement)

A foreach statement loops over the contents of an aggregate. $(GRAMMAR $(I ForeachStatement): $(I Foreach) $(B $(LPAREN))$(I ForeachTypeList) $(B ;) $(I Aggregate)$(B $(RPAREN)) $(PS0) $(GNAME Foreach): $(B foreach) $(B foreach_reverse) $(GNAME ForeachTypeList): $(I ForeachType) $(I ForeachType) , $(I ForeachTypeList) $(GNAME ForeachType): $(B ref) $(GLINK2 declaration, BasicType) $(GLINK2 declaration, Declarator) $(GLINK2 declaration, BasicType) $(GLINK2 declaration, Declarator) $(B ref) $(I Identifier) $(I Identifier) $(GNAME Aggregate): $(EXPRESSION) ) $(P $(I Aggregate) is evaluated. It must evaluate to an expression of type static array, dynamic array, associative array, struct, class, delegate, or tuple. The $(PS0) is executed, once for each element of the aggregate. At the start of each iteration, the variables declared by the $(I ForeachTypeList) are set to be a copy of the elements of the aggregate. If the variable is $(B ref), it is a reference to the contents of that aggregate. ) $(P The aggregate must be loop invariant, meaning that elements to the aggregate cannot be added or removed from it in the $(PS0). )

Foreach over Arrays

$(P If the aggregate is a static or dynamic array, there can be one or two variables declared. If one, then the variable is said to be the $(I value) set to the elements of the array, one by one. The type of the variable must match the type of the array contents, except for the special cases outlined below. If there are two variables declared, the first is said to be the $(I index) and the second is said to be the $(I value). The $(I index) must be of $(B int), $(B uint) or $(B size_t) type, it cannot be $(I ref), and it is set to be the index of the array element. ) -------------- char[] a; ... foreach (int i, char c; a) { writefln("a[%d] = '%c'", i, c); } -------------- $(P For $(B foreach), the elements for the array are iterated over starting at index 0 and continuing to the maximum of the array. For $(B foreach_reverse), the array elements are visited in the reverse order. )

Foreach over Arrays of Characters

$(P If the aggregate expression is a static or dynamic array of $(B char)s, $(B wchar)s, or $(B dchar)s, then the $(I Type) of the $(I value) can be any of $(B char), $(B wchar), or $(B dchar). In this manner any UTF array can be decoded into any UTF type: ) -------------- char[] a = "\xE2\x89\xA0"; // \u2260 encoded as 3 UTF-8 bytes foreach (dchar c; a) { writefln("a[] = %x", c); // prints 'a[] = 2260' } dchar[] b = "\u2260"; foreach (char c; b) { writef("%x, ", c); // prints 'e2, 89, a0, ' } -------------- $(P Aggregates can be string literals, which can be accessed as char, wchar, or dchar arrays: ) -------------- void test() { foreach (char c; "ab") { writefln("'%s'", c); } foreach (wchar w; "xy") { writefln("'%s'", w); } } -------------- $(P which would print: ) $(CONSOLE 'a' 'b' 'x' 'y' )

Foreach over Associative Arrays

$(P If the aggregate expression is an associative array, there can be one or two variables declared. If one, then the variable is said to be the $(I value) set to the elements of the array, one by one. The type of the variable must match the type of the array contents. If there are two variables declared, the first is said to be the $(I index) and the second is said to be the $(I value). The $(I index) must be of the same type as the indexing type of the associative array. It cannot be $(I ref), and it is set to be the index of the array element. The order in which the elements of the array is unspecified for $(B foreach). $(B foreach_reverse) for associative arrays is illegal. ) -------------- double[char[]] a; // $(I index) type is char[], $(I value) type is double ... foreach (char[] s, double d; a) { writefln("a['%s'] = %g", s, d); } -------------- $(V2

$(LNAME2 foreach_with_ranges, Foreach over Structs and Classes with Ranges)

$(P Iteration over struct and class objects can be done with ranges, which means the following properties must be defined: ) $(TABLE2 Foreach Range Properties, $(TR $(TH Property) $(TH Purpose )) $(TR $(TD $(TT .empty)) $(TD returns true if no more elements)) $(TR $(TD $(TT .popFront)) $(TD move the left edge of the range right one)) $(TR $(TD $(TT .popBack)) $(TD move the right edge of the range left one)) $(TR $(TD $(TT .front)) $(TD return the leftmost element of the range)) $(TR $(TD $(TT .back)) $(TD return the rightmost element of the range)) ) $(P Meaning:) --- foreach (e; range) { ... } --- $(P translates to:) --- for (auto __r = range; !__r.empty; __r.next) { auto e = __r.head; ... } --- $(P Similarly:) --- foreach_reverse (e; range) { ... } --- $(P translates to:) --- for (auto __r = range; !__r.empty; __r.retreat) { auto e = __r.toe; ... } --- $(P If the foreach range properties do not exist, the $(TT opApply) method will be used instead. ) )

Foreach over Structs and Classes with opApply

$(P If it is a struct or class object, the $(B foreach) is defined by the special $(LNAME2 #opApply, $(I opApply)) member function. The $(B foreach_reverse) behavior is defined by the special $(LNAME2 opApplyReverse, $(I opApplyReverse)) member function. These special functions must be defined by the type in order to use the corresponding foreach statement. The functions have the type: ) -------------- int $(B opApply)(int delegate(ref $(I Type) [, ...]) $(I dg)); int $(B opApplyReverse)(int delegate(ref $(I Type) [, ...]) $(I dg)); -------------- $(P where $(I Type) matches the $(I Type) used in the $(I ForeachType) declaration of $(I Identifier). Multiple $(I ForeachType)s correspond with multiple $(I Type)'s in the delegate type passed to $(B opApply) or $(B opApplyReverse). There can be multiple $(B opApply) and $(B opApplyReverse) functions, one is selected by matching the type of $(I dg) to the $(I ForeachType)s of the $(I ForeachStatement). The body of the apply function iterates over the elements it aggregates, passing them each to the $(I dg) function. If the $(I dg) returns 0, then apply goes on to the next element. If the $(I dg) returns a nonzero value, apply must cease iterating and return that value. Otherwise, after done iterating across all the elements, apply will return 0. ) $(P For example, consider a class that is a container for two elements: ) -------------- class Foo { uint array[2]; int $(B opApply)(int delegate(ref uint) $(I dg)) { int result = 0; for (int i = 0; i < array.length; i++) { result = $(I dg)(array[i]); if (result) break; } return result; } } -------------- An example using this might be: -------------- void test() { Foo a = new Foo(); a.array[0] = 73; a.array[1] = 82; foreach (uint u; a) { writefln("%d", u); } } -------------- which would print: $(CONSOLE 73 82 )

Foreach over Delegates

$(P If $(I Aggregate) is a delegate, the type signature of the delegate is of the same as for $(B opApply). This enables many different named looping strategies to coexist in the same class or struct.)

Foreach over Tuples

$(P If the aggregate expression is a tuple, there can be one or two variables declared. If one, then the variable is said to be the $(I value) set to the elements of the tuple, one by one. If the type of the variable is given, it must match the type of the tuple contents. If it is not given, the type of the variable is set to the type of the tuple element, which may change from iteration to iteration. If there are two variables declared, the first is said to be the $(I index) and the second is said to be the $(I value). The $(I index) must be of $(B int) or $(B uint) type, it cannot be $(I ref), and it is set to be the index of the tuple element. ) $(P If the tuple is a list of types, then the foreach statement is executed once for each type, and the value is aliased to that type. ) ----- import std.stdio; import std.typetuple; // for TypeTuple void main() { alias TypeTuple!(int, long, double) TL; foreach (T; TL) { writefln(typeid(T)); } } ----- $(P Prints:) $(CONSOLE int long double )

Foreach Ref Parameters

$(P $(B ref) can be used to update the original elements: ) -------------- void test() { static uint[2] a = [7, 8]; foreach (ref uint u; a) { u++; } foreach (uint u; a) { writefln("%d", u); } } -------------- which would print: $(CONSOLE 8 9 ) $(P $(B ref) can not be applied to the index values.) $(P If not specified, the $(I Type)s in the $(I ForeachType) can be inferred from the type of the $(I Aggregate). )

Foreach Restrictions

$(P The aggregate itself must not be resized, reallocated, free'd, reassigned or destructed while the foreach is iterating over the elements. ) -------------- int[] a; int[] b; foreach (int i; a) { a = null; // error a.length = a.length + 10; // error a = b; // error } a = null; // ok --------------

Break and Continue out of Foreach

$(P A $(GLINK BreakStatement) in the body of the foreach will exit the foreach, a $(GLINK ContinueStatement) will immediately start the next iteration. )

$(LNAME2 SwitchStatement, Switch Statement)

A switch statement goes to one of a collection of case statements depending on the value of the switch expression. $(GRAMMAR $(I SwitchStatement): $(B switch $(LPAREN)) $(EXPRESSION) $(B $(RPAREN)) $(PSSCOPE) $(GNAME CaseStatement): $(B case) $(LINK2 expression.html#ArgumentList, $(I ArgumentList)) $(B :) $(PSSEMI_PSCURLYSCOPE_LIST) $(V2 $(GNAME CaseRangeStatement): $(B case) $(I FirstExp) $(B : .. case) $(I LastExp) $(B :) $(PSSEMI_PSCURLYSCOPE_LIST) $(I FirstExp): $(ASSIGNEXPRESSION) $(I LastExp): $(ASSIGNEXPRESSION) ) $(GNAME DefaultStatement): $(B default :) $(PSSEMI_PSCURLYSCOPE_LIST) $(GNAME ScopeStatementList): $(GLINK StatementListNoCaseNoDefault) $(GNAME StatementListNoCaseNoDefault): $(GLINK StatementNoCaseNoDefault) $(GLINK StatementNoCaseNoDefault) $(I StatementListNoCaseNoDefault) $(GNAME StatementNoCaseNoDefault): $(B ;) $(GLINK NonEmptyStatementNoCaseNoDefault) $(GLINK ScopeBlockStatement) ) $(P $(EXPRESSION) is evaluated. The result type T must be of integral type or $(CODE char[]), $(CODE wchar[]) or $(CODE dchar[]). The result is compared against each of the case expressions. If there is a match, the corresponding case statement is transferred to. ) $(P The case expressions, $(LINK2 expression.html#ArgumentList, $(I ArgumentList)), are a comma separated list of expressions. ) $(V2 $(P A $(I CaseRangeStatement) is a shorthand for listing a series of case statements from $(I FirstExp) to $(I LastExp). ) ) $(P If none of the case expressions match, and there is a default statement, the default statement is transferred to. ) $(P If none of the case expressions match, and there is not a default statement, a $(LINK2 phobos/std_switcherr.html, $(CODE std.switcherr.SwitchError)) is thrown. The reason for this is to catch the common programming error of adding a new value to an enum, but failing to account for the extra value in switch statements. This behavior is unlike C or C++. ) $(P $(V1 The case expressions must all evaluate to a constant value or array. ) $(V2 The case expressions must all evaluate to a constant value or array, or a runtime initialized const or immutable variable of integral type. ) They must be implicitly convertible to the type of the switch $(EXPRESSION). ) $(P Case expressions must all evaluate to distinct values. $(V2 Const or immutable variables must all have different names. If they share a value, the first case statement with that value gets control. ) There may not be two or more default statements. ) $(P The $(GLINK ScopeStatementList) introduces a new scope. ) $(P Case statements and default statements associated with the switch can be nested within block statements; they do not have to be in the outermost block. For example, this is allowed: ) -------------- switch (i) { case 1: { case 2: } break; } -------------- $(P Case statements 'fall through' to subsequent case values. A break statement will exit the switch $(I BlockStatement). For example: ) -------------- switch (i) { case 1: x = 3; case 2: x = 4; break; case 3,4,5: x = 5; break; } -------------- $(P will set $(CODE x) to $(CODE 4) if $(CODE i) is $(CODE 1). ) $(P $(LNAME2 string-switch, Strings can be used in switch expressions). For example: ) -------------- char[] name; ... switch (name) { case "fred": case "sally": ... } -------------- $(P For applications like command line switch processing, this can lead to much more straightforward code, being clearer and less error prone. char, wchar and dchar strings are allowed. ) $(P $(B Implementation Note:) The compiler's code generator may assume that the case statements are sorted by frequency of use, with the most frequent appearing first and the least frequent last. Although this is irrelevant as far as program correctness is concerned, it is of performance interest. ) $(V2

$(LNAME2 FinalSwitchStatement, Final Switch Statement)

$(GRAMMAR $(I FinalSwitchStatement): $(B final switch $(LPAREN)) $(EXPRESSION) $(B $(RPAREN)) $(PSSCOPE) ) $(P A final switch statement is just like a switch statement, except that:) $(UL $(LI No $(GLINK DefaultStatement) is allowed.) $(LI No $(GLINK CaseRangeStatement)s are allowed.) $(LI If the switch $(EXPRESSION) is of enum type, all the enum members must appear in the $(GLINK CaseStatement)s.) $(LI The case expressions cannot evaluate to a run time initialized value.) ) )

$(LNAME2 ContinueStatement, Continue Statement)

$(GRAMMAR $(I ContinueStatement): $(B continue;) $(B continue) $(I Identifier) $(B ;) ) A continue aborts the current iteration of its enclosing loop statement, and starts the next iteration. continue executes the next iteration of its innermost enclosing while, for, foreach, or do loop. The increment clause is executed.

If continue is followed by $(I Identifier), the $(I Identifier) must be the label of an enclosing while, for, or do loop, and the next iteration of that loop is executed. It is an error if there is no such statement.

Any intervening finally clauses are executed, and any intervening synchronization objects are released.

$(B Note:) If a finally clause executes a return, throw, or goto out of the finally clause, the continue target is never reached. --- for (i = 0; i < 10; i++) { if (foo(i)) continue; bar(); } ---

$(LNAME2 BreakStatement, Break Statement)

$(GRAMMAR $(I BreakStatement): $(B break;) $(B break) $(I Identifier) $(B ;) ) A break exits the enclosing statement. break exits the innermost enclosing while, for, foreach, do, or switch statement, resuming execution at the statement following it.

If break is followed by $(I Identifier), the $(I Identifier) must be the label of an enclosing while, for, do or switch statement, and that statement is exited. It is an error if there is no such statement.

Any intervening finally clauses are executed, and any intervening synchronization objects are released.

$(B Note:) If a finally clause executes a return, throw, or goto out of the finally clause, the break target is never reached. --- for (i = 0; i < 10; i++) { if (foo(i)) break; } ---

$(LNAME2 ReturnStatement, Return Statement)

$(GRAMMAR $(I ReturnStatement): $(B return;) $(B return) $(EXPRESSION) $(B ;) ) A return exits the current function and supplies its return value. $(EXPRESSION) is required if the function specifies a return type that is not void. The $(EXPRESSION) is implicitly converted to the function return type.

At least one return statement, throw statement, or assert(0) expression is required if the function specifies a return type that is not void, unless the function contains inline assembler code.

$(EXPRESSION) is allowed even if the function specifies a $(D_KEYWORD void) return type. The $(EXPRESSION) will be evaluated, but nothing will be returned. If the $(EXPRESSION) has no side effects, and the return type is $(D_KEYWORD void), then it is illegal.

Before the function actually returns, any objects with scope storage duration are destroyed, any enclosing finally clauses are executed, any scope(exit) statements are executed, any scope(success) statements are executed, and any enclosing synchronization objects are released.

The function will not return if any enclosing finally clause does a return, goto or throw that exits the finally clause.

If there is an out postcondition (see $(LINK2 dbc.html, Contract Programming)), that postcondition is executed after the $(EXPRESSION) is evaluated and before the function actually returns. --- int foo(int x) { return x + 3; } ---

$(LNAME2 GotoStatement, Goto Statement)

$(GRAMMAR $(I GotoStatement): $(B goto) $(I Identifier) $(B ;) $(B goto) $(B default) $(B ;) $(B goto) $(B case) $(B ;) $(B goto) $(B case) $(EXPRESSION) $(B ;) ) A goto transfers to the statement labeled with $(I Identifier). --- if (foo) goto L1; x = 3; L1: x++; --- The second form, $(CODE goto default;), transfers to the innermost $(GLINK DefaultStatement) of an enclosing $(GLINK SwitchStatement).

The third form, $(CODE goto case;), transfers to the next $(GLINK CaseStatement) of the innermost enclosing $(I SwitchStatement).

The fourth form, $(CODE goto case $(EXPRESSION);), transfers to the $(GLINK CaseStatement) of the innermost enclosing $(GLINK SwitchStatement) with a matching $(EXPRESSION). --- switch (x) { case 3: goto case; case 4: goto default; case 5: goto case 4; default: x = 4; break; } --- Any intervening finally clauses are executed, along with releasing any intervening synchronization mutexes.

It is illegal for a $(I GotoStatement) to be used to skip initializations.

$(LNAME2 WithStatement, With Statement)

The with statement is a way to simplify repeated references to the same object. $(GRAMMAR $(I WithStatement): $(B with) $(B $(LPAREN)) $(EXPRESSION) $(B $(RPAREN)) $(PSSCOPE) $(B with) $(B $(LPAREN)) $(LINK2 template.html#Symbol, $(I Symbol)) $(B $(RPAREN)) $(PSSCOPE) $(B with) $(B $(LPAREN)) $(LINK2 template.html#TemplateInstance, $(I TemplateInstance)) $(B $(RPAREN)) $(PSSCOPE) ) where $(EXPRESSION) evaluates to a class reference or struct instance. Within the with body the referenced object is searched first for identifier symbols. The $(I WithStatement) -------------- $(B with) (expression) { ... ident; } -------------- is semantically equivalent to: -------------- { Object tmp; tmp = expression; ... tmp.ident; } -------------- $(P Note that $(EXPRESSION) only gets evaluated once. The with statement does not change what $(B this) or $(B super) refer to. ) $(P For $(I Symbol) which is a scope or $(I TemplateInstance), the corresponding scope is searched when looking up symbols. For example: ) -------------- struct Foo { alias int Y; } ... Y y; // error, Y undefined with (Foo) { Y y; // same as Foo.Y y; } -------------- $(V2 $(P Use of with object symbols that shadow local symbols with the same identifier are not allowed. This is to reduce the risk of inadvertant breakage of with statements when new members are added to the object declaration. ) --- struct S { float x; } void main() { int x; S s; with (s) { x++; // error, shadows the int x declaration } } --- )

$(LNAME2 SynchronizedStatement, Synchronized Statement)

$(P The synchronized statement wraps a statement with a mutex to synchronize access among multiple threads. ) $(GRAMMAR $(I SynchronizedStatement): $(B synchronized) $(PSSCOPE) $(B synchronized $(LPAREN)) $(EXPRESSION) $(B $(RPAREN)) $(PSSCOPE) ) $(P Synchronized allows only one thread at a time to execute $(I ScopeStatement) by using a mutex. ) $(P What mutex is used is determined by the $(EXPRESSION). If there is no $(EXPRESSION), then a global mutex is created, one per such synchronized statement. Different synchronized statements will have different global mutexes. ) $(P If there is an $(EXPRESSION), it must evaluate to either an Object or an instance of an $(I Interface), in which case it is cast to the Object instance that implemented that $(I Interface). The mutex used is specific to that Object instance, and is shared by all synchronized statements referring to that instance. ) $(P The synchronization gets released even if $(I ScopeStatement) terminates with an exception, goto, or return. ) $(P Example: ) -------------- synchronized { ... } -------------- $(P This implements a standard critical section. )

$(LNAME2 TryStatement, Try Statement)

Exception handling is done with the try-catch-finally statement. $(GRAMMAR $(I TryStatement): $(B try) $(PSSCOPE) $(I Catches) $(B try) $(PSSCOPE) $(I Catches) $(I FinallyStatement) $(B try) $(PSSCOPE) $(I FinallyStatement) $(GNAME Catches): $(I LastCatch) $(I Catch) $(I Catch) $(I Catches) $(GNAME LastCatch): $(B catch) $(PS0) $(GNAME Catch): $(B catch $(LPAREN)) $(I CatchParameter) $(B $(RPAREN)) $(PS0) $(GNAME CatchParameter): $(GLINK2 declaration, BasicType) $(I Identifier) $(GNAME FinallyStatement): $(B finally) $(PS0) ) $(P $(I CatchParameter) declares a variable v of type T, where T is $(V1 Object or derived from Object.) $(V2 Throwable or derived from Throwable.) v is initialized by the throw expression if T is of the same type or a base class of the throw expression. The catch clause will be executed if the exception object is of type T or derived from T. ) $(P If just type T is given and no variable v, then the catch clause is still executed. ) $(P It is an error if any $(I CatchParameter) type T1 hides a subsequent $(I Catch) with type T2, i.e. it is an error if T1 is the same type as or a base class of T2. ) $(P $(I LastCatch) catches all exceptions. ) $(P The $(I FinallyStatement) is always executed, whether the $(B try) $(I ScopeStatement) exits with a goto, break, continue, return, exception, or fall-through. ) $(V1 $(P If an exception is raised in the $(I FinallyStatement) and is not caught before the $(I FinallyStatement) is executed, the new exception replaces any existing exception: ) ) $(V2 $(P If an exception is raised in the $(I FinallyStatement) and is not caught before the original exception is caught, it is chained to the previous exception via the $(I next) member of $(I Throwable). Note that, in contrast to most other programming languages, the new exception does not replace the original exception. Instead, later exceptions are regarded as 'collateral damage' caused by the first exception. The original exception must be caught, and this results in the capture of the enture chain. ) $(P Thrown objects derived from $(I Error) are treated differently. They bypass the normal chaining mechanism, such that the chain can only be caught by catching the first $(I Error). In addition to the list of subsequent exceptions, $(I Error) also contains a pointer to the points to the original exception (the head of the chain) if a bypass occurred, so that the entire exception history is retained. ) ) -------------- import std.stdio; int main() { try { try { throw new Exception("first"); } finally { writefln("finally"); throw new Exception("second"); } } catch(Exception e) { writefln("catch %s", e.msg); } writefln("done"); return 0; } -------------- prints: $(V1 $(CONSOLE finally catch second done ) ) $(V2 $(CONSOLE finally catch first done ) ) $(P A $(I FinallyStatement) may not exit with a goto, break, continue, or return; nor may it be entered with a goto. ) $(P A $(I FinallyStatement) may not contain any $(I Catches). This restriction may be relaxed in future versions. )

$(LNAME2 ThrowStatement, Throw Statement)

Throw an exception. $(GRAMMAR $(I ThrowStatement): $(B throw) $(EXPRESSION) $(B ;) ) $(EXPRESSION) is evaluated and must be $(V1 an Object) $(V2 a Throwable) reference. The $(V1 Object) $(V2 Throwable) reference is thrown as an exception. --- throw new Exception("message"); ---

$(LNAME2 ScopeGuardStatement, Scope Guard Statement)

$(GRAMMAR $(I ScopeGuardStatement): $(B scope(exit)) $(PSCURLYSCOPE) $(B scope(success)) $(PSCURLYSCOPE) $(B scope(failure)) $(PSCURLYSCOPE) ) The $(I ScopeGuardStatement) executes $(PSCURLYSCOPE) at the close of the current scope, rather than at the point where the $(I ScopeGuardStatement) appears. $(B scope(exit)) executes $(PSCURLYSCOPE) when the scope exits normally or when it exits due to exception unwinding. $(B scope(failure)) executes $(PSCURLYSCOPE) when the scope exits due to exception unwinding. $(B scope(success)) executes $(PSCURLYSCOPE) when the scope exits normally.

If there are multiple $(I ScopeGuardStatement)s in a scope, they are executed in the reverse lexical order in which they appear. If any scope instances are to be destructed upon the close of the scope, they also are interleaved with the $(I ScopeGuardStatement)s in the reverse lexical order in which they appear. ---- writef("1"); { writef("2"); scope(exit) writef("3"); scope(exit) writef("4"); writef("5"); } writefln(); ---- writes: $(CONSOLE 12543 ) ---- { scope(exit) writef("1"); scope(success) writef("2"); scope(exit) writef("3"); scope(success) writef("4"); } writefln(); ---- writes: $(CONSOLE 4321 ) ---- class Foo { this() { writef("0"); } ~this() { writef("1"); } } try { scope(exit) writef("2"); scope(success) writef("3"); scope Foo f = new Foo(); scope(failure) writef("4"); throw new Exception("msg"); scope(exit) writef("5"); scope(success) writef("6"); scope(failure) writef("7"); } catch (Exception e) { } writefln(); ---- writes: $(CONSOLE 0412 ) A $(B scope(exit)) or $(B scope(success)) statement may not exit with a throw, goto, break, continue, or return; nor may it be entered with a goto. $(V1

$(LNAME2 VolatileStatement, Volatile Statement)

No code motion occurs across volatile statement boundaries. $(GRAMMAR $(I VolatileStatement): $(B volatile) $(PSSEMI_PSCURLYSCOPE) $(B volatile) $(B ;) ) $(PSSEMI_PSCURLYSCOPE) is evaluated. Memory writes occurring before the $(PSSEMI_PSCURLYSCOPE) are performed before any reads within or after the $(PSSEMI_PSCURLYSCOPE). Memory reads occurring after the $(PSSEMI_PSCURLYSCOPE) occur after any writes before or within $(PSSEMI_PSCURLYSCOPE) are completed.

A volatile statement does not guarantee atomicity. For that, use synchronized statements. )

$(LNAME2 asm, Asm Statement)

Inline assembler is supported with the asm statement: $(GRAMMAR $(GNAME AsmStatement): $(B asm { }) $(B asm {) $(I AsmInstructionList) $(B }) $(GNAME AsmInstructionList): $(I AsmInstruction) $(B ;) $(I AsmInstruction) $(B ;) $(I AsmInstructionList) ) An asm statement enables the direct use of assembly language instructions. This makes it easy to obtain direct access to special CPU features without resorting to an external assembler. The D compiler will take care of the function calling conventions, stack setup, etc.

The format of the instructions is, of course, highly dependent on the native instruction set of the target CPU, and so is $(LINK2 iasm.html, implementation defined). But, the format will follow the following conventions: $(UL $(LI It must use the same tokens as the D language uses.) $(LI The comment form must match the D language comments.) $(LI Asm instructions are terminated by a ;, not by an end of line.) ) These rules exist to ensure that D source code can be tokenized independently of syntactic or semantic analysis.

For example, for the Intel Pentium: -------------- int x = 3; asm { mov EAX,x; // load x and put it in register EAX } -------------- Inline assembler can be used to access hardware directly: -------------- int gethardware() { asm { mov EAX, dword ptr 0x1234; } } -------------- For some D implementations, such as a translator from D to C, an inline assembler makes no sense, and need not be implemented. The version statement can be used to account for this: -------------- version (D_InlineAsm_X86) { asm { ... } } else { /* ... some workaround ... */ } -------------- $(P Semantically consecutive $(I AsmStatement)s shall not have any other instructions (such as register save or restores) inserted between them by the compiler. )

$(LNAME2 PragmaStatement, Pragma Statement)

$(GRAMMAR $(I PragmaStatement): $(LINK2 pragma.html, $(I Pragma)) $(PSSEMI) )

$(LNAME2 MixinStatement, Mixin Statement)

$(GRAMMAR $(I MixinStatement): $(B mixin) $(B $(LPAREN)) $(ASSIGNEXPRESSION) $(B $(RPAREN)) $(B ;) ) $(P The $(ASSIGNEXPRESSION) must evaluate at compile time to a constant string. The text contents of the string must be compilable as a valid $(GLINK StatementList), and is compiled as such. ) --- import std.stdio; void main() { int j; mixin(" int x = 3; for (int i = 0; i < 3; i++) writefln(x + i, ++j); "); // ok const char[] s = "int y;"; mixin(s); // ok y = 4; // ok, mixin declared y char[] t = "y = 3;"; mixin(t); // error, t is not evaluatable at compile time mixin("y =") 4; // error, string must be complete statement mixin("y =" ~ "4;"); // ok } --- $(V2

$(LNAME2 ForeachRangeStatement, Foreach Range Statement)

A foreach range statement loops over the specified range. $(GRAMMAR $(I ForeachRangeStatement): $(GLINK Foreach) $(LPAREN)$(GLINK ForeachType) $(B ;) $(I LwrExpression) $(B ..) $(I UprExpression) $(B $(RPAREN)) $(PSSCOPE) $(GNAME LwrExpression): $(EXPRESSION) $(GNAME UprExpression): $(EXPRESSION) ) $(P $(I ForeachType) declares a variable with either an explicit type, or a type inferred from $(I LwrExpression) and $(I UprExpression). The $(I ScopeStatement) is then executed $(I n) times, where $(I n) is the result of $(I UprExpression) - $(I LwrExpression). If $(I UprExpression) is less than or equal to $(I LwrExpression), the $(I ScopeStatement) is executed zero times. If $(I Foreach) is $(B foreach), then the variable is set to $(I LwrExpression), then incremented at the end of each iteration. If $(I Foreach) is $(B foreach_reverse), then the variable is set to $(I UprExpression), then decremented before each iteration. $(I LwrExpression) and $(I UprExpression) are each evaluated exactly once, regardless of how many times the $(I ScopeStatement) is executed. ) --- import std.stdio; int foo() { writefln("foo"); return 10; } void main() { foreach (i; 0 .. foo()) { writef(i); } } --- $(P Prints:) $(CONSOLE foo0123456789 ) ) ) Macros: TITLE=Statements WIKI=Statement GLINK=$(LINK2 #$0, $(I $0)) GNAME=$(LNAME2 $0, $0) EXPRESSION=$(LINK2 expression.html#Expression, $(I Expression)) PSSEMI_PSCURLYSCOPE=$(GLINK Statement) PSSEMI_PSCURLYSCOPE_LIST=$(GLINK ScopeStatementList) PS0=$(GLINK NoScopeNonEmptyStatement) PSSCOPE=$(GLINK ScopeStatement) PSCURLY=$(GLINK BlockStatement) PSSEMI=$(GLINK NoScopeStatement) PSCURLY_PSSCOPE=$(GLINK ScopeBlockStatement) PSCURLYSCOPE=$(GLINK NonEmptyOrScopeBlockStatement) FOO=