$(DDOC $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(SPEC_S Statements, $(DDOC_BLANKLINE ) $(HEADERNAV_TOC $(HEADERNAV_ITEM scope-statement, Scope Statements) $(HEADERNAV_ITEM scope-block-statement, Scope Block Statements) $(HEADERNAV_ITEM labeled-statement, Labeled Statements) $(HEADERNAV_ITEM block-statement, Block Statement) $(HEADERNAV_ITEM expression-statement, Expression Statement) $(HEADERNAV_ITEM declaration-statement, Declaration Statement) $(HEADERNAV_ITEM if-statement, If Statement) $(HEADERNAV_ITEM while-statement, While Statement) $(HEADERNAV_ITEM do-statement, Do Statement) $(HEADERNAV_ITEM for-statement, For Statement) $(HEADERNAV_SUBITEMS foreach-statement, Foreach Statement, $(HEADERNAV_ITEM foreach_over_arrays, Foreach over Arrays) $(HEADERNAV_ITEM foreach_over_arrays_of_characters, Foreach over Arrays of Characters) $(HEADERNAV_ITEM foreach_over_associative_arrays, Foreach over Associative Arrays) $(HEADERNAV_ITEM foreach_over_struct_and_classes, Foreach over Structs and Classes with opApply) $(HEADERNAV_ITEM foreach-with-ranges, Foreach over Structs and Classes with Ranges) $(HEADERNAV_ITEM foreach_over_delegates, Foreach over Delegates) $(HEADERNAV_ITEM foreach_over_tuples, Foreach over Sequences) $(HEADERNAV_ITEM foreach_ref_parameters, Foreach Ref Parameters) $(HEADERNAV_ITEM foreach_restrictions, Foreach Restrictions) $(HEADERNAV_ITEM foreach-range-statement, Foreach Range Statement) ) $(HEADERNAV_SUBITEMS switch-statement, Switch Statement, $(HEADERNAV_ITEM case-range, Case Range Statement) $(HEADERNAV_ITEM no-implicit-fallthrough, No Implicit Fall-Through) $(HEADERNAV_ITEM string-switch, String Switch) ) $(HEADERNAV_ITEM final-switch-statement, Final Switch Statement) $(HEADERNAV_ITEM continue-statement, Continue Statement) $(HEADERNAV_ITEM break-statement, Break Statement) $(HEADERNAV_ITEM return-statement, Return Statement) $(HEADERNAV_ITEM goto-statement, Goto Statement) $(HEADERNAV_ITEM with-statement, With Statement) $(HEADERNAV_ITEM synchronized-statement, Synchronized Statement) $(HEADERNAV_ITEM try-statement, Try Statement) $(HEADERNAV_SUBITEMS scope-guard-statement, Scope Guard Statement, $(HEADERNAV_ITEM catching_cpp_class_objects, Catching C++ Class Objects) ) $(HEADERNAV_ITEM asm, Asm Statement) $(HEADERNAV_ITEM pragma-statement, Pragma Statement) $(HEADERNAV_ITEM mixin-statement, Mixin Statement) ) $(DDOC_BLANKLINE ) $(P The order of execution within a function is controlled by $(GLINK Statement)s. A function's body consists of a sequence of zero or more $(I Statement)s. Execution occurs in lexical order, though certain statements may have deferred effects. A $(I Statement) has no value; it is executed for its effects. ) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME Statement): $(GLINK EmptyStatement) $(GLINK NonEmptyStatement) $(GLINK ScopeBlockStatement) $(DDOC_BLANKLINE ) $(GNAME EmptyStatement): $(D ;) $(DDOC_BLANKLINE ) $(GNAME NoScopeNonEmptyStatement): $(GLINK NonEmptyStatement) $(GLINK BlockStatement) $(DDOC_BLANKLINE ) $(GNAME NoScopeStatement): $(GLINK EmptyStatement) $(GLINK NonEmptyStatement) $(GLINK BlockStatement) $(DDOC_BLANKLINE ) $(GNAME NonEmptyOrScopeBlockStatement): $(GLINK NonEmptyStatement) $(GLINK ScopeBlockStatement) $(DDOC_BLANKLINE ) $(GNAME NonEmptyStatement): $(GLINK NonEmptyStatementNoCaseNoDefault) $(GLINK CaseStatement) $(GLINK CaseRangeStatement) $(GLINK DefaultStatement) $(DDOC_BLANKLINE ) $(GNAME NonEmptyStatementNoCaseNoDefault): $(GLINK LabeledStatement) $(GLINK ExpressionStatement) $(GLINK DeclarationStatement) $(GLINK IfStatement) $(GLINK WhileStatement) $(GLINK DoStatement) $(GLINK ForStatement) $(GLINK ForeachStatement) $(GLINK SwitchStatement) $(GLINK FinalSwitchStatement) $(GLINK ContinueStatement) $(GLINK BreakStatement) $(GLINK ReturnStatement) $(GLINK GotoStatement) $(GLINK WithStatement) $(GLINK SynchronizedStatement) $(GLINK TryStatement) $(GLINK ScopeGuardStatement) $(GLINK AsmStatement) $(GLINK MixinStatement) $(GLINK ForeachRangeStatement) $(GLINK2 pragma, PragmaStatement) $(GLINK2 version, ConditionalStatement) $(GLINK2 version, StaticForeachStatement) $(GLINK2 module, ImportDeclaration) ) $(DDOC_BLANKLINE ) $(P Any ambiguities in the grammar between $(I Statement)s and $(GLINK2 declaration, Declaration)s are resolved by the declarations taking precedence. Wrapping such a statement in parentheses will disambiguate it in favor of being a $(I Statement). ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 ScopeStatement, scope-statement, Scope Statements)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME ScopeStatement): $(GLINK NonEmptyStatement) $(GLINK BlockStatement) ) $(DDOC_BLANKLINE ) $(P A new scope for local symbols is introduced for the $(I NonEmptyStatement) or $(GLINK BlockStatement). ) $(DDOC_BLANKLINE ) $(P Even though a new scope is introduced, local symbol declarations cannot shadow (hide) other local symbol declarations in the same function. ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD void) func1($(D_KEYWORD int) x) { $(D_KEYWORD int) x; $(D_COMMENT // illegal, x shadows parameter x ) $(D_KEYWORD int) y; { $(D_KEYWORD int) y; } $(D_COMMENT // illegal, y shadows enclosing scope's y ) $(D_KEYWORD void) $(D_KEYWORD delegate)() dg; dg = { $(D_KEYWORD int) y; }; $(D_COMMENT // ok, this y is not in the same function ) $(D_KEYWORD struct) S { $(D_KEYWORD int) y; $(D_COMMENT // ok, this y is a member, not a local ) } { $(D_KEYWORD int) z; } { $(D_KEYWORD int) z; } $(D_COMMENT // ok, this z is not shadowing the other z ) { $(D_KEYWORD int) t; } { t++; } $(D_COMMENT // illegal, t is undefined )} ) $(DDOC_BLANKLINE ) $(BEST_PRACTICE Local declarations within a function should all have unique names, even if they are in non-overlapping scopes. ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 ScopeBlockStatement, scope-block-statement, Scope Block Statements)

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

$(LEGACY_LNAME2 LabeledStatement, labeled-statement, Labeled Statements)

$(DDOC_BLANKLINE ) $(P Statements can be labeled. A label is an identifier that precedes a statement. ) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME LabeledStatement): $(GLINK_LEX Identifier) $(D :) $(GLINK_LEX Identifier) $(D :) $(GLINK Statement) ) $(DDOC_BLANKLINE ) $(P Any statement can be labeled, including empty statements, and so can serve as the target of a $(RELATIVE_LINK2 goto-statement, goto statement). Labeled statements can also serve as the target of a $(RELATIVE_LINK2 break-statement, break) or $(RELATIVE_LINK2 continue-statement, continue) statement. ) $(P A label can appear without a following statement at the end of a block. ) $(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. ) $(DDOC_BLANKLINE ) $(P Labels in one function cannot be referenced from another function.) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 BlockStatement, block-statement, Block Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME BlockStatement): $(D { }) $(D {) $(GLINK StatementList) $(D }) $(DDOC_BLANKLINE ) $(GNAME StatementList): $(GLINK Statement) $(GLINK Statement) $(GSELF StatementList) ) $(DDOC_BLANKLINE ) $(P A block statement is a sequence of statements enclosed by { }. The statements are executed in lexical order, until the end of the block is reached or a statement transfers control elsewhere. ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 ExpressionStatement, expression-statement, Expression Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME ExpressionStatement): $(GLINK2 expression, Expression) $(D ;) ) $(DDOC_BLANKLINE ) $(P The expression is evaluated.) $(DDOC_BLANKLINE ) $(P Expressions that have no effect, like $(D (x + x)), are illegal as expression statements unless they are cast to $(D_KEYWORD void).) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) x; x++; $(D_COMMENT // ok )x; $(D_COMMENT // illegal )1+1; $(D_COMMENT // illegal )$(D_KEYWORD cast)($(D_KEYWORD void))(x + x); $(D_COMMENT // ok )) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 DeclarationStatement, declaration-statement, Declaration Statement)

$(DDOC_BLANKLINE ) $(P Declaration statements define variables, and declare types, templates, functions, imports, conditionals, static foreaches, and static asserts. ) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME DeclarationStatement): $(GLINK2 declaration, StorageClasses)$(OPT ) $(GLINK2 declaration, Declaration) ) $(DDOC_BLANKLINE ) $(P Some declaration statements:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) a; $(D_COMMENT // declare a as type int and initialize it to 0 )$(D_KEYWORD struct) S { } $(D_COMMENT // declare struct s )$(D_KEYWORD alias) myint = $(D_KEYWORD int); ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 IfStatement, if-statement, If Statement)

$(DDOC_BLANKLINE ) $(P If statements provide simple conditional execution of statements.) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME IfStatement): $(D if $(LPAREN)) $(GLINK IfCondition) $(D $(RPAREN )) $(GLINK ThenStatement) $(D if $(LPAREN)) $(GLINK IfCondition) $(D $(RPAREN )) $(GLINK ThenStatement) $(D else) $(GLINK ElseStatement) $(DDOC_BLANKLINE ) $(GNAME IfCondition): $(GLINK2 expression, Expression) $(D auto) $(GLINK_LEX Identifier) $(D =) $(GLINK2 expression, Expression) $(D scope) $(GLINK_LEX Identifier) $(D =) $(GLINK2 expression, Expression) $(GLINK2 type, TypeCtors) $(GLINK_LEX Identifier) $(D =) $(GLINK2 expression, Expression) $(GLINK2 type, TypeCtors)$(OPT ) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) $(D =) $(GLINK2 expression, Expression) $(DDOC_BLANKLINE ) $(GNAME ThenStatement): $(GLINK ScopeStatement) $(DDOC_BLANKLINE ) $(GNAME ElseStatement): $(GLINK ScopeStatement) ) $(DDOC_BLANKLINE ) $(P If there is a declared Identifier variable, it is evaluated. Otherwise, Expression is evaluated. The result is converted to a boolean, using $(DDSUBLINK spec/operatoroverloading, cast, opCast!bool()) if the method is defined. If the boolean is true, the $(I ThenStatement) is transferred to, otherwise the $(I ElseStatement) is transferred to.) $(DDOC_BLANKLINE ) $(P The $(I ElseStatement) is associated with the innermost if statement which does not already have an associated $(I ElseStatement).) $(DDOC_BLANKLINE ) $(PANEL When an $(I Identifier) form of IfCondition is used, a variable is declared with that name and initialized to the value of the Expression. $(UL $(LI If $(D auto) $(I Identifier) is provided, the type of the variable is the same as Expression. ) $(LI If $(I TypeCtors) $(I Identifier) is provided, the variable is declared to be the type of Expression but with $(I TypeCtors) applied. ) $(LI If the $(I BasicType) form is provided, it declares the type of the variable as it would for a normal $(DDSUBLINK spec/declaration, variable-declarations, variable declaration). ) ) $(P The scope of the variable is the ThenStatement only.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.regex; $(D_KEYWORD if) ($(D_KEYWORD auto) m = matchFirst($(D_STRING "abcdef"), $(D_STRING "b$(LPAREN)c$(RPAREN )d"))) { writefln($(D_STRING "[%s]"), m.pre); $(D_COMMENT // prints [a] ) writefln($(D_STRING "[%s]"), m.post); $(D_COMMENT // prints [ef] ) writefln($(D_STRING "[%s]"), m[0]); $(D_COMMENT // prints [bcd] ) writefln($(D_STRING "[%s]"), m[1]); $(D_COMMENT // prints [c] )} $(D_KEYWORD else) { writeln($(D_STRING "no match")); $(D_COMMENT //writeln$(LPAREN)m.post$(RPAREN ); // Error: undefined identifier 'm' )} $(D_COMMENT //writeln$(LPAREN)m.pre$(RPAREN ); // Error: undefined identifier 'm' )) ) ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 WhileStatement, while-statement, While Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME WhileStatement): $(D while $(LPAREN)) $(GLINK IfCondition) $(D $(RPAREN )) $(GLINK ScopeStatement) ) $(DDOC_BLANKLINE ) $(P A $(I While Statement) implements a simple loop.) $(DDOC_BLANKLINE ) $(P If the $(I IfCondition) is an Expression, it is evaluated and must have a type that can be converted to a boolean. If it's true the ScopeStatement is executed. After the ScopeStatement is executed, the Expression is evaluated again, and if true the ScopeStatement is executed again. This continues until the Expression evaluates to false.) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) i = 0; $(D_KEYWORD while) (i < 10) { foo(i); ++i; } ) $(DDOC_BLANKLINE ) $(P If an $(D 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 ScopeStatement.) $(DDOC_BLANKLINE ) $(P If a $(I TypeCtors) $(I Identifier) is provided, it is declared to be of the type specified by $(I TypeCtors) and is initialized with the value of the Expression. Its scope extends from when it is initialized to the end of the ScopeStatement.) $(DDOC_BLANKLINE ) $(P 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 ScopeStatement.) $(DDOC_BLANKLINE ) $(P A $(GLINK BreakStatement) will exit the loop.) $(DDOC_BLANKLINE ) $(P A $(GLINK ContinueStatement) will transfer directly to evaluating $(I IfCondition) again.) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 DoStatement, do-statement, Do Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME DoStatement): $(D do) $(GLINK ScopeStatement) $(D while $(LPAREN)) $(GLINK2 expression, Expression) $(D $(RPAREN )) $(D ;) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(P Do while statements implement simple loops.) $(DDOC_BLANKLINE ) $(P ScopeStatement 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.) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) i = 0; $(D_KEYWORD do) { foo(i); } $(D_KEYWORD while) (++i < 10); ) $(DDOC_BLANKLINE ) $(P A $(GLINK BreakStatement) will exit the loop. A $(GLINK ContinueStatement) will transfer directly to evaluating Expression again.) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 ForStatement, for-statement, For Statement)

$(DDOC_BLANKLINE ) $(P For statements implement loops with initialization, test, and increment clauses.) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME ForStatement): $(D for $(LPAREN)) $(GLINK Initialize) $(GLINK Test)$(OPT ) $(D ;) $(GLINK Increment)$(OPT ) $(D $(RPAREN )) $(GLINK ScopeStatement) $(DDOC_BLANKLINE ) $(GNAME Initialize): $(D ;) $(GLINK NoScopeNonEmptyStatement) $(DDOC_BLANKLINE ) $(GNAME Test): $(GLINK2 expression, Expression) $(DDOC_BLANKLINE ) $(GNAME Increment): $(GLINK2 expression, Expression) ) $(DDOC_BLANKLINE ) $(P $(I Initialize) is executed. $(I Test) is evaluated and must have a type that can be converted to a boolean. If Test is true the ScopeStatement is executed. After execution, $(I Increment) is executed. Then $(I Test) is evaluated again, and if true the ScopeStatement is executed again. This continues until the $(I Test) evaluates to false. ) $(DDOC_BLANKLINE ) $(P A $(GLINK BreakStatement) will exit the loop. A $(GLINK ContinueStatement) will transfer directly to the $(I Increment). ) $(DDOC_BLANKLINE ) $(P A $(I ForStatement) creates a new scope. If $(I Initialize) declares a variable, that variable's scope extends through ScopeStatement. For example: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD for) ($(D_KEYWORD int) i = 0; i < 10; i++) foo(i); ) $(DDOC_BLANKLINE ) is equivalent to: $(DDOC_BLANKLINE ) $(D_CODE { $(D_KEYWORD int) i; $(D_KEYWORD for) (i = 0; i < 10; i++) foo(i); } ) $(DDOC_BLANKLINE ) $(P ScopeStatement cannot be an empty statement:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD for) ($(D_KEYWORD int) i = 0; i < 10; i++) ; $(D_COMMENT // illegal )) $(DDOC_BLANKLINE ) Use instead: $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD for) ($(D_KEYWORD int) i = 0; i < 10; i++) { } ) $(DDOC_BLANKLINE ) $(P $(I Initialize) may be just $(D ;). $(I Test) may be omitted, and if so, it is treated as if it evaluated to true.) $(DDOC_BLANKLINE ) $(BEST_PRACTICE Consider replacing $(I ForStatements) with $(DDSUBLINK spec/statement, foreach-statement, Foreach Statements) or $(DDSUBLINK spec/statement, ForeachRangeStatement, Foreach Range Statements). Foreach loops are easier to understand, less prone to error, and easier to refactor. ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 ForeachStatement, foreach-statement, Foreach Statement)

$(DDOC_BLANKLINE ) $(P A foreach statement iterates a series of values.) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME AggregateForeach): $(GLINK Foreach) $(D $(LPAREN)) $(GLINK ForeachTypeList) $(D ;) $(GLINK ForeachAggregate) $(D $(RPAREN )) $(DDOC_BLANKLINE ) $(GNAME ForeachStatement): $(GLINK AggregateForeach) $(GLINK NoScopeNonEmptyStatement) $(DDOC_BLANKLINE ) $(GNAME Foreach): $(D foreach) $(D foreach_reverse) $(DDOC_BLANKLINE ) $(GNAME ForeachTypeList): $(GLINK ForeachType) $(GLINK ForeachType) $(D ,) $(GSELF ForeachTypeList) $(DDOC_BLANKLINE ) $(GNAME ForeachType): $(GLINK ForeachTypeAttributes)$(OPT ) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) $(GLINK ForeachTypeAttributes)$(OPT ) $(GLINK_LEX Identifier) $(GLINK ForeachTypeAttributes)$(OPT ) $(D alias) $(GLINK_LEX Identifier) $(DDOC_BLANKLINE ) $(GNAME ForeachTypeAttributes): $(GLINK ForeachTypeAttribute) $(GLINK ForeachTypeAttribute) $(GSELF ForeachTypeAttributes) $(DDOC_BLANKLINE ) $(GNAME ForeachTypeAttribute): $(D enum) $(D ref) $(D scope) $(GLINK2 type, TypeCtor) $(DDOC_BLANKLINE ) $(GNAME ForeachAggregate): $(GLINK2 expression, Expression) ) $(DDOC_BLANKLINE ) $(P $(I ForeachAggregate) is evaluated. It must evaluate to an expression which is a static array, dynamic array, associative array, struct, class, delegate, or sequence. The NoScopeNonEmptyStatement is executed, once for each element of the aggregate. ) $(P The number of variables declared in $(I ForeachTypeList) depends on the kind of aggregate. The declared variables are set at the start of each iteration. ) $(UL $(LI By default a single declared variable is a copy of the current element.) $(LI If the $(I ForeachTypeAttribute) is $(D ref), that variable will be a reference to the current element of the aggregate.) $(LI If the $(I ForeachTypeAttribute) is $(D scope), the variable will have $(DDSUBLINK spec/function, scope-parameters, scope) semantics.) ) $(P If not specified, the type of a $(I ForeachType) variable can be inferred from the type of the $(I ForeachAggregate). Note that auto is not a valid $(I ForeachTypeAttribute). The two foreach statements below are equivalent: ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int)[] arr = [1, 2, 3]; $(D_KEYWORD foreach) ($(D_KEYWORD int) n; arr) writeln(n); $(D_KEYWORD foreach) (n; arr) $(D_COMMENT // ok, n is an int ) writeln(n); ) ) $(P The aggregate must be $(RELATIVE_LINK2 foreach_restrictions, loop invariant), meaning that elements cannot be added or removed from it in the NoScopeNonEmptyStatement. ) $(DDOC_BLANKLINE ) $(P A $(GLINK BreakStatement) in the body of the foreach will exit the loop. A $(GLINK ContinueStatement) will immediately start the next iteration. ) $(DDOC_BLANKLINE )

$(LNAME2 foreach_over_arrays, Foreach over Arrays)

$(DDOC_BLANKLINE ) $(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), which is set successively to each element of the array. The type of the variable, if specified, must be compatible with the array element type (except for the special handling of character elements outlined below). The value variable can modify array elements when $(RELATIVE_LINK2 foreach_ref_parameters, declared with ref). ) $(P 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) as above. $(I index) cannot be declared with ref. It is set to the index of the array element on each iteration. The index type can be inferred: ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD char)[] a = ['h', 'i']; $(D_KEYWORD foreach) (i, $(D_KEYWORD char) c; a) { writefln($(D_STRING "a[%d] = '%c'"), i, c); } ) ) $(P For a dynamic array, the $(I index) type must be compatible with size_t. Static arrays may use any integral type that spans the length of the array.) $(DDOC_BLANKLINE ) $(P For $(D foreach), the elements for the array are iterated over starting at index 0 and continuing to the last element of the array. For $(D foreach_reverse), the array elements are visited in the reverse order. ) $(DDOC_BLANKLINE )

$(LNAME2 foreach_over_arrays_of_characters, Foreach over Arrays of Characters)

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

$(LNAME2 foreach_over_associative_arrays, Foreach over Associative Arrays)

$(DDOC_BLANKLINE ) $(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. If the type of the variable is provided, it must implicitly convert from the array element type.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_COMMENT // value type is int )$(D_KEYWORD int)[string] userAges = [$(D_STRING "john"):30, $(D_STRING "sue"):32]; $(D_KEYWORD foreach) ($(D_KEYWORD ref) age; userAges) { age++; } $(D_KEYWORD assert)(userAges == [$(D_STRING "john"):31, $(D_STRING "sue"):33]); ) ) $(DDOC_BLANKLINE ) $(P 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 compatible with the indexing type of the associative array. It cannot be ref, and it is set to be the index of the array element.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_COMMENT // index type is string, value type is double )$(D_KEYWORD double)[string] aa = [$(D_STRING "pi"):3.14, $(D_STRING "e"):2.72]; $(D_KEYWORD foreach) (string s, $(D_KEYWORD double) d; aa) { writefln($(D_STRING "aa['%s'] = %g"), s, d); } ) ) $(DDOC_BLANKLINE ) $(P The order in which the elements of the array are iterated over is unspecified for $(D foreach). This is why $(D foreach_reverse) for associative arrays is illegal.) $(DDOC_BLANKLINE )

$(LNAME2 foreach_over_struct_and_classes, Foreach over Structs and Classes with opApply)

$(DDOC_BLANKLINE ) $(P If the aggregate expression is a struct or class object, the $(D foreach) is defined by the special $(LEGACY_LNAME2 opApply, op-apply, $(D opApply)) member function, and the foreach_reverse behavior is defined by the special $(LEGACY_LNAME2 opApplyReverse, op-apply-reverse, $(D opApplyReverse)) member function. These functions must each have the signature below: ) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME OpApplyDeclaration): int opApply $(LPAREN) scope int delegate $(LPAREN) $(I OpApplyParameters) $(RPAREN ) dg $(RPAREN ) ; $(DDOC_BLANKLINE ) $(GNAME OpApplyParameters): OpApplyParameter OpApplyParameter, OpApplyParameters $(DDOC_BLANKLINE ) $(GNAME OpApplyParameter): $(GLINK ForeachTypeAttributes)$(OPT ) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) ) $(DDOC_BLANKLINE ) $(P where each $(I OpApplyParameter) of dg must match a $(GLINK ForeachType) in a ForeachStatement, otherwise the ForeachStatement will cause an error.) $(DDOC_BLANKLINE ) $(P Any ForeachTypeAttribute cannot be enum.) $(DDOC_BLANKLINE ) $(PANEL To support a ref iteration variable, the delegate must take a ref parameter: $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) opApply($(D_KEYWORD scope) $(D_KEYWORD int) $(D_KEYWORD delegate)($(D_KEYWORD ref) $(D_KEYWORD uint) n) dg); } $(D_KEYWORD void) f(S s) { $(D_KEYWORD foreach) ($(D_KEYWORD ref) $(D_KEYWORD uint) i; s) i++; } ) ) Above, opApply is still matched when i is not ref, so by using a ref delegate parameter both forms are supported. ) $(DDOC_BLANKLINE ) $(P There can be multiple $(D opApply) and $(D opApplyReverse) functions - one is selected by matching each parameter of dg to each $(I ForeachType) declared in the $(I ForeachStatement).) $(DDOC_BLANKLINE ) $(P The body of the apply function iterates over the elements it aggregates, passing each one in successive calls to the dg delegate. The delegate return value determines whether to interrupt iteration:) $(DDOC_BLANKLINE ) $(UL $(LI If the result is nonzero, apply must cease iterating and return that value.) $(LI If the result is 0, then iteration should continue. If there are no more elements to iterate, apply must return 0.) ) $(DDOC_BLANKLINE ) $(P For example, consider a class that is a container for two elements:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD class) Foo { $(D_KEYWORD uint)[2] array; $(D_KEYWORD int) opApply($(D_KEYWORD scope) $(D_KEYWORD int) $(D_KEYWORD delegate)($(D_KEYWORD ref) $(D_KEYWORD uint)) dg) { $(D_KEYWORD foreach) (e; array) { $(D_KEYWORD int) result = dg(e); $(D_KEYWORD if) (result) $(D_KEYWORD return) result; } $(D_KEYWORD return) 0; } } $(D_KEYWORD void) main() { $(D_KEYWORD import) std.stdio; Foo a = $(D_KEYWORD new) Foo(); a.array = [73, 82]; $(D_KEYWORD foreach) ($(D_KEYWORD uint) u; a) { writeln(u); } } ) ) $(DDOC_BLANKLINE ) $(P This would print:) $(DDOC_BLANKLINE ) $(CONSOLE 73 82 ) $(PANEL The scope storage class on the $(D dg) parameter means that the delegate does not escape the scope of the $(D opApply) function (an example would be assigning $(D dg) to a global variable). If it cannot be statically guaranteed that $(D dg) does not escape, a closure may be allocated for it on the heap instead of the stack. $(DDOC_BLANKLINE ) $(BEST_PRACTICE Annotate delegate parameters to opApply functions with scope when possible.) ) $(DDOC_BLANKLINE ) $(P $(B Important:) If $(D opApply) catches any exceptions, ensure that those exceptions did not originate from the delegate passed to $(D opApply). The user would expect exceptions thrown from a foreach body to both terminate the loop, and propagate outside the foreach body. ) $(DDOC_BLANKLINE )

$(LNAME2 template-op-apply, Template opApply)

$(DDOC_BLANKLINE ) $(P $(D opApply) can also be a templated function, which will infer the types of parameters based on the $(I ForeachStatement). For example:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD import) std.traits : ParameterTypeTuple; $(D_COMMENT // introspection template ) $(D_KEYWORD import) std.stdio; $(D_KEYWORD int) opApply(Dg)($(D_KEYWORD scope) Dg dg) $(D_KEYWORD if) (ParameterTypeTuple!Dg.length == 2) $(D_COMMENT // foreach with 2 parameters ) { writeln(2); $(D_KEYWORD return) 0; } $(D_KEYWORD int) opApply(Dg)($(D_KEYWORD scope) Dg dg) $(D_KEYWORD if) (ParameterTypeTuple!Dg.length == 3) $(D_COMMENT // foreach with 3 parameters ) { writeln(3); $(D_KEYWORD return) 0; } } $(D_KEYWORD void) main() { $(D_KEYWORD foreach) ($(D_KEYWORD int) a, $(D_KEYWORD int) b; S()) { } $(D_COMMENT // calls first opApply function ) $(D_KEYWORD foreach) ($(D_KEYWORD int) a, $(D_KEYWORD int) b, $(D_KEYWORD float) c; S()) { } $(D_COMMENT // calls second opApply function )} ) ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 foreach_with_ranges, foreach-with-ranges, Foreach over Structs and Classes with Ranges)

$(DDOC_BLANKLINE ) $(P If the aggregate expression is a struct or class object, but the $(D opApply) for $(D foreach), or $(D opApplyReverse) for $(D foreach_reverse) do not exist, then iteration can be done with $(LINK2 $(ROOT_DIR )phobos/std_range.html, range) primitives. For $(D foreach), this means the following properties and methods must be defined: ) $(DDOC_BLANKLINE ) $(TABLE2 Foreach Range Properties, Property, Purpose $(TROW $(ARGS $(D .empty)), $(ARGS returns true if no more elements)) $(TROW $(ARGS $(D .front)), $(ARGS return the leftmost element of the range)) ) $(DDOC_BLANKLINE ) $(TABLE2 Foreach Range Methods, Method, Purpose $(TROW $(ARGS $(D .popFront())), $(ARGS move the left edge of the range right by one)) ) $(DDOC_BLANKLINE ) $(P Meaning:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD foreach) (e; range) { ... } ) $(DDOC_BLANKLINE ) $(P translates to:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD for) ($(D_KEYWORD auto) __r = range; !__r.empty; __r.popFront()) { $(D_KEYWORD auto) e = __r.front; ... } )$(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(P Similarly, for $(D foreach_reverse), the following properties and methods must be defined: ) $(DDOC_BLANKLINE ) $(TABLE2 Foreach$(UNDERSCORE )reverse Range Properties, Property, Purpose $(TROW $(ARGS $(D .empty)), $(ARGS returns true if no more elements)) $(TROW $(ARGS $(D .back)), $(ARGS return the rightmost element of the range)) ) $(DDOC_BLANKLINE ) $(TABLE2 Foreach$(UNDERSCORE )reverse Range Methods, Method, Purpose $(TROW $(ARGS $(D .popBack())), $(ARGS move the right edge of the range left by one)) ) $(DDOC_BLANKLINE ) $(P Meaning:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD foreach_reverse) (e; range) { ... } ) $(DDOC_BLANKLINE ) $(P translates to:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD for) ($(D_KEYWORD auto) __r = range; !__r.empty; __r.popBack()) { $(D_KEYWORD auto) e = __r.back; ... } )$(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(P Example with a linked list:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) Node { $(D_KEYWORD int) i; Node* next; } $(D_COMMENT // range )$(D_KEYWORD struct) List { Node* node; $(D_KEYWORD bool) empty() { $(D_KEYWORD return) node == $(D_KEYWORD null); } $(D_KEYWORD ref) $(D_KEYWORD int) front() { $(D_KEYWORD return) node.i; } $(D_KEYWORD void) popFront() { node = node.next; } } $(D_KEYWORD void) main() { $(D_KEYWORD import) std.stdio; $(D_KEYWORD auto) l = $(D_KEYWORD new) Node(1, $(D_KEYWORD new) Node(2, $(D_KEYWORD null))); $(D_KEYWORD auto) r = List(l); $(D_KEYWORD foreach) (e; r) { writeln(e); } } ) ) $(DDOC_BLANKLINE )

$(LNAME2 front-seq, Multiple Element Values)

$(DDOC_BLANKLINE ) $(P Multiple loop variables are allowed if the front property returns a type that expands to a $(DDSUBLINK spec/template, homogeneous_sequences, value sequence) whose length matches the number of variables. Each variable is assigned to the corresponding value in the sequence. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) Tuple(Types...) $(D_COMMENT // takes a TypeSeq ){ Types items; $(D_COMMENT // ValueSeq ) $(D_KEYWORD alias) items $(D_KEYWORD this); $(D_COMMENT // decay to a value sequence )} $(D_COMMENT // Infinite range with a repeating element, which is a tuple )$(D_KEYWORD struct) TupleRange { $(D_KEYWORD enum) front = Tuple!($(D_KEYWORD char), $(D_KEYWORD bool), $(D_KEYWORD int))('a', $(D_KEYWORD true), 2); $(D_KEYWORD enum) $(D_KEYWORD bool) empty = $(D_KEYWORD false); $(D_KEYWORD void) popFront() {} } $(D_KEYWORD void) main() { $(D_COMMENT // Tuple destructuring ) $(D_KEYWORD foreach) (a, b, c; TupleRange()) { $(D_KEYWORD assert)(a == 'a'); $(D_KEYWORD assert)(b == $(D_KEYWORD true)); $(D_KEYWORD assert)(c == 2); $(D_KEYWORD break); } $(D_COMMENT // Tuple variable ) $(D_KEYWORD foreach) (tup; TupleRange()) { $(D_KEYWORD assert)(tup[0] == 'a'); $(D_KEYWORD assert)(tup == TupleRange.front); $(D_KEYWORD break); } } ) ) $(P See also: $(REF Tuple, std,typecons).) $(DDOC_BLANKLINE )

$(LNAME2 foreach_over_delegates, Foreach over Delegates)

$(DDOC_BLANKLINE ) $(P If $(I ForeachAggregate) is a delegate, the type signature of the delegate is of the same as for $(RELATIVE_LINK2 foreach_over_struct_and_classes, opApply). This enables many different named looping strategies to coexist in the same class or struct.) $(DDOC_BLANKLINE ) $(P The delegate can generate the elements on the fly:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_COMMENT // Custom loop implementation, that iterates over powers of 2 with )$(D_COMMENT // alternating sign. The foreach loop body is passed in dg. )$(D_KEYWORD int) myLoop($(D_KEYWORD int) $(D_KEYWORD delegate)($(D_KEYWORD int)) dg) { $(D_KEYWORD for) ($(D_KEYWORD int) z = 1; z < 128; z *= -2) { $(D_KEYWORD auto) ret = dg(z); $(D_COMMENT // If the loop body contains a break, ret will be non-zero. ) $(D_KEYWORD if) (ret != 0) $(D_KEYWORD return) ret; } $(D_KEYWORD return) 0; } $(D_COMMENT // Append each value in the iteration to an array )$(D_KEYWORD int)[] result; $(D_KEYWORD foreach) (x; &myLoop) { result ~= x; } $(D_KEYWORD assert)(result == [1, -2, 4, -8, 16, -32, 64, -128]); ) ) $(DDOC_BLANKLINE ) $(P $(B Note:) When $(I ForeachAggregate) is a delegate, the compiler does not try to implement reverse traversal of the results returned by the delegate when $(D foreach_reverse) is used. This may result in code that is confusing to read. Therefore, using $(D foreach_reverse) with a delegate is now deprecated, and will be rejected in the future.) $(DDOC_BLANKLINE )

$(LNAME2 foreach_over_tuples, Foreach over Sequences)

$(DDOC_BLANKLINE ) $(P If the aggregate expression is a $(DDSUBLINK spec/template, variadic-templates, sequence), the loop body is statically expanded once for each element. This is the same as $(DDSUBLINK spec/version, staticforeach, Static Foreach) on a sequence. ) $(P There can be one or two iteration symbols declared. If one, then the symbol is an $(I element alias) of each element in the sequence in turn. ) $(UL $(LI If the sequence is a $(I TypeSeq), the element alias is set to each type in turn. )$(LI If the sequence is a $(I ValueSeq), the element alias is set to each value in turn. If the type of the element alias is given, it must be compatible with the type of every sequence element. If no type is given, the type of the element alias will match the type of each sequence element, which may change between elements. )) $(P If there are two symbols declared, the first is the $(I index variable) and the second is the $(I element alias). The index must be of int, uint, long or ulong type, it cannot be ref, and it is set to the index of each sequence element. ) $(P Example:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD import) std.meta : AliasSeq; $(D_KEYWORD void) main() { $(D_KEYWORD alias) Seq = AliasSeq!($(D_KEYWORD int), $(D_STRING "literal"), main); $(D_KEYWORD foreach) ($(D_KEYWORD int) i, sym; Seq) { $(D_KEYWORD pragma)(msg, i, $(D_STRING ": "), sym.stringof); } } ) ) $(P Output:) $(DDOC_BLANKLINE ) $(CONSOLE 0: int 1: "literal" 2: main() ) $(DDOC_BLANKLINE ) $(P Example:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) fun() { $(D_KEYWORD import) std.meta : AliasSeq; $(D_KEYWORD alias) values = AliasSeq!(7.4, $(D_STRING "hi"), [2,5]); $(D_KEYWORD foreach) (sym; values) { $(D_KEYWORD pragma)(msg, sym, $(D_STRING " has type "), $(D_KEYWORD typeof)(sym)); } } ) ) $(DDOC_BLANKLINE ) $(P Output:) $(DDOC_BLANKLINE ) $(CONSOLE 7.4 has type double hi has type string [2, 5] has type int[] ) $(DDOC_BLANKLINE )

$(LNAME2 foreach_ref_parameters, Foreach Ref Parameters)

$(DDOC_BLANKLINE ) $(P $(D ref) can be used to modify the elements of the ForeachAggregate. This works for containers that expose lvalue elements, and $(DDSUBLINK spec/template, homogeneous_sequences, lvalue sequences). ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD uint)[2] a = [7, 8]; $(D_KEYWORD foreach) ($(D_KEYWORD ref) u; a) { u++; } $(D_KEYWORD foreach) (u; a) { writeln(u); } ) ) $(DDOC_BLANKLINE ) which would print: $(DDOC_BLANKLINE ) $(CONSOLE 8 9 ) $(P $(D ref) cannot be applied to an array index variable.) $(DDOC_BLANKLINE )

$(LNAME2 foreach_restrictions, Foreach Restrictions)

$(DDOC_BLANKLINE ) $(P The aggregate itself must not be resized, reallocated, free'd, reassigned or destructed while foreach is iterating over the elements. ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int)[] a = [1, 2, 3]; $(D_KEYWORD auto) fun = { a ~= 4; }; $(D_KEYWORD foreach) ($(D_KEYWORD int) v; a) { $(D_COMMENT // resizing is unspecified! ) fun(); a ~= 4; a.length += 10; $(D_COMMENT // reallocating is unspecified! ) a.reserve(10); $(D_COMMENT // reassigning is unspecified! ) a = $(D_KEYWORD null); a = [5, 6]; } a ~= 4; $(D_COMMENT // OK )a = $(D_KEYWORD null); $(D_COMMENT // OK )) $(D_CODE $(D_KEYWORD auto) aa = [1: 1, 2: 2]; $(D_KEYWORD foreach) (v; aa) { aa[3] = 3; $(D_COMMENT // unspecified resize ) aa.rehash; $(D_COMMENT // unspecified reallocation ) aa = [4: 4]; $(D_COMMENT // unspecified reassign )} aa[3] = 3; $(D_COMMENT // OK )aa = $(D_KEYWORD null); $(D_COMMENT // OK )) $(DDOC_BLANKLINE ) $(NOTE Resizing or reassigning a dynamic or associative array during foreach is still @safe. ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 ForeachRangeStatement, foreach-range-statement, Foreach Range Statement)

$(DDOC_BLANKLINE ) $(P A foreach range statement loops over the specified range.) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME RangeForeach): $(GLINK Foreach) $(D $(LPAREN)) $(GLINK ForeachType) $(D ;) $(GLINK LwrExpression) $(D ..) $(GLINK UprExpression) $(D $(RPAREN )) $(DDOC_BLANKLINE ) $(GNAME LwrExpression): $(GLINK2 expression, Expression) $(DDOC_BLANKLINE ) $(GNAME UprExpression): $(GLINK2 expression, Expression) $(DDOC_BLANKLINE ) $(GNAME ForeachRangeStatement): $(GLINK RangeForeach) $(GLINK ScopeStatement) ) $(DDOC_BLANKLINE ) $(P $(I ForeachType) declares a variable with either an explicit type, or a common 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 not executed.) $(DDOC_BLANKLINE ) $(P If $(I Foreach) is $(D foreach), then the variable is set to $(I LwrExpression), then incremented at the end of each iteration. If $(I Foreach) is $(D 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. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio; $(D_KEYWORD int) foo() { write($(D_STRING "foo")); $(D_KEYWORD return) 10; } $(D_KEYWORD void) main() { $(D_KEYWORD foreach) (i; 0 .. foo()) { write(i); } } ) ) $(DDOC_BLANKLINE ) prints: $(DDOC_BLANKLINE ) $(CONSOLE foo0123456789 ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 SwitchStatement, switch-statement, Switch Statement)

$(DDOC_BLANKLINE ) A switch statement goes to one of a collection of case statements depending on the value of the switch expression. $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME SwitchStatement): $(D switch $(LPAREN)) $(GLINK2 expression, Expression) $(D $(RPAREN )) $(GLINK ScopeStatement) $(DDOC_BLANKLINE ) $(GNAME CaseStatement): $(D case) $(GLINK2 expression, ArgumentList) $(D :) $(GLINK ScopeStatementList)$(OPT ) $(DDOC_BLANKLINE ) $(GNAME DefaultStatement): $(D default :) $(GLINK ScopeStatementList)$(OPT ) $(DDOC_BLANKLINE ) $(GNAME ScopeStatementList): $(GLINK StatementListNoCaseNoDefault) $(DDOC_BLANKLINE ) $(GNAME StatementListNoCaseNoDefault): $(GLINK StatementNoCaseNoDefault) $(GLINK StatementNoCaseNoDefault) $(GSELF StatementListNoCaseNoDefault) $(DDOC_BLANKLINE ) $(GNAME StatementNoCaseNoDefault): $(GLINK EmptyStatement) $(GLINK NonEmptyStatementNoCaseNoDefault) $(GLINK ScopeBlockStatement) ) $(DDOC_BLANKLINE ) $(P $(I Expression) is evaluated. If the type of $(I Expression) is an enum, it is (recursively) converted to its $(GLINK2 enum, EnumBaseType). Then, if the type is an integral, $(I Expression) undergoes $(DDSUBLINK spec/type, integer-promotions, Integer Promotions). Then, the type of $(I Expression) must be either an integral or a static or dynamic array of $(CODE char), $(CODE wchar), or $(CODE dchar). ) $(DDOC_BLANKLINE ) $(P The resulting value is compared against each of the case expressions. If there is a match, the corresponding case statement is transferred to. ) $(DDOC_BLANKLINE ) $(P The case expressions in $(I ArgumentList) are a comma separated list of expressions. Each expression must evaluate to a compile-time value or array, or a runtime initialized const or immutable variable of integral type. Each expression must be implicitly convertible to the type of the switch Expression.) $(DDOC_BLANKLINE ) $(P Compile-time case values must all be distinct. Const or immutable runtime variables must all have different names. If two case expressions share a value, the first case statement with that value gets control.) $(DDOC_BLANKLINE ) $(P The $(GLINK ScopeStatementList) introduces a new scope. ) $(DDOC_BLANKLINE ) $(P A matching break statement will exit the switch $(I BlockStatement).) $(DDOC_BLANKLINE ) $(P A switch statement must have exactly one DefaultStatement. If none of the case expressions match, control is transferred to the default statement. ) $(DDOC_BLANKLINE ) $(RATIONALE This makes it clear that all possible cases are intentionally handled. See also: $(RELATIVE_LINK2 final-switch-statement, final switch).) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD foreach) (i; 2 .. 10) { $(D_KEYWORD bool) prime; $(D_KEYWORD switch) (i) { $(D_KEYWORD case) 2, 3, 5, 7: prime = $(D_KEYWORD true); $(D_KEYWORD break); $(D_KEYWORD default): prime = $(D_KEYWORD false); } writeln(i, $(D_STRING ": "), prime); } ) ) $(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: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD switch) (i) { $(D_KEYWORD case) 1: { $(D_KEYWORD case) 2: } i++; $(D_KEYWORD break); $(D_KEYWORD default): } ) $(DDOC_BLANKLINE ) $(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. ) $(DDOC_BLANKLINE )

$(LNAME2 case-range, Case Range Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME CaseRangeStatement): $(D case) $(GLINK FirstExp) $(D : .. case) $(GLINK LastExp) $(D :) $(GLINK ScopeStatementList)$(OPT ) $(DDOC_BLANKLINE ) $(GNAME FirstExp): $(ASSIGNEXPRESSION ) $(DDOC_BLANKLINE ) $(GNAME LastExp): $(ASSIGNEXPRESSION ) ) $(DDOC_BLANKLINE ) $(P A $(I CaseRangeStatement) is a shorthand for listing a series of case statements from $(I FirstExp) to $(I LastExp), inclusive.) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD case) 1: .. $(D_KEYWORD case) 3: ) $(DDOC_BLANKLINE ) $(P The above is equivalent to:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD case) 1, 2, 3: ) $(DDOC_BLANKLINE )

$(LNAME2 no-implicit-fallthrough, No Implicit Fall-Through)

$(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(P A $(GLINK ScopeStatementList) must either be empty, or be ended with a $(GLINK ContinueStatement), $(GLINK BreakStatement), $(GLINK ReturnStatement), $(GLINK GotoStatement), $(GLINK2 expression, ThrowExpression) or assert(0) expression unless this is the last case.) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD switch) (i) { $(D_KEYWORD case) 1: message ~= $(D_STRING "one"); $(D_COMMENT // ERROR: implicit fall-through ) $(D_KEYWORD case) 2: $(D_COMMENT // valid: the body is empty ) $(D_KEYWORD default): message ~= $(D_STRING "unknown"); } ) $(DDOC_BLANKLINE ) $(P $(D goto case;) can be used for explicit fall-through:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE string message; $(D_KEYWORD foreach) (i; 1..5) { $(D_KEYWORD switch) (i) { $(D_KEYWORD default): $(D_COMMENT // valid: ends with 'throw' ) $(D_KEYWORD throw) $(D_KEYWORD new) Exception($(D_STRING "unknown number")); $(D_KEYWORD case) 3: $(D_COMMENT // valid: ends with 'break' $(LPAREN)break out of the 'switch' only$(RPAREN ) ) message ~= $(D_STRING "three"); $(D_KEYWORD break); $(D_KEYWORD case) 4: $(D_COMMENT // valid: ends with 'continue' $(LPAREN)continue the enclosing loop$(RPAREN ) ) message ~= $(D_STRING "four"); $(D_KEYWORD continue); $(D_COMMENT // don't append a comma ) $(D_KEYWORD case) 1: $(D_COMMENT // valid: ends with 'goto' $(LPAREN)explicit fall-through to next case.$(RPAREN ) ) message ~= $(D_STRING ">"); $(D_KEYWORD goto) $(D_KEYWORD case); $(D_KEYWORD case) 2: $(D_COMMENT // valid: this is the last case in the switch statement. ) message ~= $(D_STRING "one or two"); } message ~= $(D_STRING ", "); } writeln(message); ) ) $(P $(RELATIVE_LINK2 goto-statement, goto) also supports jumping to a specific case or the default case statement.) $(DDOC_BLANKLINE )

$(LNAME2 string-switch, String Switch)

$(DDOC_BLANKLINE ) $(P Strings can be used in switch expressions. For example: ) $(DDOC_BLANKLINE ) $(D_CODE string name; ... $(D_KEYWORD switch) (name) { $(D_KEYWORD case) $(D_STRING "fred"): $(D_KEYWORD case) $(D_STRING "sally"): ... } ) $(DDOC_BLANKLINE ) $(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. ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 FinalSwitchStatement, final-switch-statement, Final Switch Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME FinalSwitchStatement): $(D final switch $(LPAREN)) $(GLINK2 expression, Expression) $(D $(RPAREN )) $(GLINK ScopeStatement) ) $(DDOC_BLANKLINE ) $(P A final switch statement is just like a switch statement, except that:) $(DDOC_BLANKLINE ) $(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.) ) $(DDOC_BLANKLINE ) $(IMPLEMENTATION_DEFINED If the Expression value does not match any of the $(I CaseRangeStatements), whether that is diagnosed at compile time or at runtime.) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 ContinueStatement, continue-statement, Continue Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME ContinueStatement): $(D continue) $(GLINK_LEX Identifier)$(OPT ) $(D ;) ) $(DDOC_BLANKLINE ) $(P continue aborts the current iteration of its innermost enclosing loop statement, and starts the next iteration. If the enclosing loop is a for statement, its $(GLINK Increment) clause is executed.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE string[] words = [$(D_STRING "OK"), $(D_STRING "just"), $(D_STRING "longer"), $(D_STRING "words"), $(D_STRING "now")]; $(D_KEYWORD foreach) (w; words) { $(D_KEYWORD if) (w.length < 4) $(D_KEYWORD continue); $(D_COMMENT // skip writeln ) writeln(w); } ) ) $(P Output:) $(DDOC_BLANKLINE ) $(CONSOLE just longer words ) $(DDOC_BLANKLINE ) $(P If continue is followed by $(I Identifier), the $(I Identifier) must be the label of an enclosing loop statement, and the next iteration of that loop is executed. It is an error if there is no such statement.) $(DDOC_BLANKLINE ) $(D_CODE outer: $(D_KEYWORD foreach) (item; list) { $(D_COMMENT // try 3 times ) $(D_KEYWORD foreach) (i; 0 .. 3) { $(D_KEYWORD if) (item.buy()) $(D_KEYWORD continue) outer; $(D_COMMENT // skip to next item ) log($(D_STRING "attempt failed")); } } ) $(DDOC_BLANKLINE ) $(P Any intervening $(RELATIVE_LINK2 try-statement, finally) clauses are executed, and any intervening synchronization objects are released.) $(DDOC_BLANKLINE ) $(P $(D Note:) If a finally clause executes a throw out of the finally clause, the continue target is never reached.) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 BreakStatement, break-statement, Break Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME BreakStatement): $(D break) $(GLINK_LEX Identifier)$(OPT ) $(D ;) ) $(DDOC_BLANKLINE ) $(P break exits the innermost enclosing loop or $(RELATIVE_LINK2 switch-statement, switch) statement, resuming execution at the statement following it.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD const) n = 55; $(D_COMMENT // find the smallest factor of n )$(D_KEYWORD foreach) (i; 2 .. n) { writeln($(D_STRING "Trying: "), i); $(D_KEYWORD if) (n % i == 0) { writeln($(D_STRING "smallest factor is "), i); $(D_KEYWORD break); $(D_COMMENT // stop looking ) } } writeln($(D_STRING "finished")); ) ) $(P Output:) $(DDOC_BLANKLINE ) $(CONSOLE Trying: 2 Trying: 3 Trying: 4 Trying: 5 smallest factor is 5 finished ) $(DDOC_BLANKLINE ) $(P If break is followed by $(I Identifier), the $(I Identifier) must be the label of an enclosing loop or switch statement, and that statement is exited. It is an error if there is no such statement.) $(DDOC_BLANKLINE ) $(D_CODE $(D_COMMENT // display messages cyclically until the shop is closed )outer: $(D_KEYWORD while) ($(D_KEYWORD true)) { $(D_KEYWORD foreach) (msg; messages) { $(D_KEYWORD if) (shop.isClosed()) $(D_KEYWORD break) outer; $(D_COMMENT // end the while loop ) display(msg); } } display($(D_STRING "opens at 9am")); ) $(DDOC_BLANKLINE ) $(P Any intervening $(RELATIVE_LINK2 try-statement, finally) clauses are executed, and any intervening synchronization objects are released.) $(DDOC_BLANKLINE ) $(P $(D Note:) If a finally clause executes a throw out of the finally clause, the break target is never reached.) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 ReturnStatement, return-statement, Return Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME ReturnStatement): $(D return) $(GLINK2 expression, Expression)$(OPT ) $(D ;) ) $(DDOC_BLANKLINE ) $(P return exits the current function and supplies its $(DDSUBLINK spec/function, function-return-values, return value).) $(DDOC_BLANKLINE ) $(P Expression is required if the function specifies a return type that is not void. The Expression is implicitly converted to the function return type.) $(DDOC_BLANKLINE ) $(P An Expression of type void is allowed if the function specifies a void return type. The Expression will be evaluated, but nothing will be returned. This is useful in generic programming. ) $(P 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.) $(DDOC_BLANKLINE ) $(P The function will not return if any enclosing finally clause does a return, goto or throw that exits the finally clause.) $(DDOC_BLANKLINE ) $(P If there is an $(DDSUBLINK spec/function, postconditions, out postcondition), that postcondition is executed after the Expression is evaluated and before the function actually returns.) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) foo($(D_KEYWORD int) x) { $(D_KEYWORD return) x + 3; } ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 GotoStatement, goto-statement, Goto Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME GotoStatement): $(D goto) $(GLINK_LEX Identifier) $(D ;) $(D goto) $(D default) $(D ;) $(D goto) $(D case) $(D ;) $(D goto) $(D case) $(GLINK2 expression, Expression) $(D ;) ) $(DDOC_BLANKLINE ) $(P goto transfers to the statement labeled with $(I Identifier).) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD if) (foo) $(D_KEYWORD goto) L1; x = 3; L1: x++; ) $(DDOC_BLANKLINE ) $(P The second form, $(CODE goto default;), transfers to the innermost $(GLINK DefaultStatement) of an enclosing $(GLINK SwitchStatement).) $(DDOC_BLANKLINE ) $(P The third form, $(CODE goto case;), transfers to the next $(GLINK CaseStatement) of the innermost enclosing $(GLINK SwitchStatement).) $(DDOC_BLANKLINE ) $(P The fourth form, $(CODE goto case) Expression$(D ;), transfers to the $(GLINK CaseStatement) of the innermost enclosing $(GLINK SwitchStatement) with a matching Expression.) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD switch) (x) { $(D_KEYWORD case) 3: $(D_KEYWORD goto) $(D_KEYWORD case); $(D_KEYWORD case) 4: $(D_KEYWORD goto) $(D_KEYWORD default); $(D_KEYWORD case) 5: $(D_KEYWORD goto) $(D_KEYWORD case) 4; $(D_KEYWORD default): x = 4; $(D_KEYWORD break); } ) $(DDOC_BLANKLINE ) $(P Any intervening finally clauses are executed, along with releasing any intervening synchronization mutexes.) $(DDOC_BLANKLINE ) $(P It is illegal for a $(I GotoStatement) to be used to skip initializations.) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 WithStatement, with-statement, With Statement)

$(DDOC_BLANKLINE ) $(P The with statement is a way to simplify repeated references to the same object.) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME WithStatement): $(D with) $(D $(LPAREN)) $(GLINK2 expression, Expression) $(D $(RPAREN )) $(GLINK ScopeStatement) $(D with) $(D $(LPAREN)) $(GLINK2 template, Symbol) $(D $(RPAREN )) $(GLINK ScopeStatement) $(D with) $(D $(LPAREN)) $(GLINK2 template, TemplateInstance) $(D $(RPAREN )) $(GLINK ScopeStatement) ) $(DDOC_BLANKLINE ) where Expression evaluates to one of: $(DDOC_BLANKLINE ) $(UL $(LI a class reference) $(LI a struct instance) $(LI an enum instance) $(LI a pointer to one of the above) ) $(DDOC_BLANKLINE ) Within the with body the referenced object is searched first for identifier symbols. $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD enum) E { A, B } $(D_KEYWORD void) test(E e) { $(D_KEYWORD with) (e) $(D_COMMENT // affects the switch statement ) $(D_KEYWORD switch) (e) { $(D_KEYWORD case) A: $(D_COMMENT // no need for E.A ) $(D_KEYWORD case) B: $(D_KEYWORD default): $(D_KEYWORD break); } } ) $(DDOC_BLANKLINE ) $(P Below, if ident is a member of the type of expression, the $(I WithStatement):) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD with) (expression) { ... ident; } ) $(DDOC_BLANKLINE ) is semantically equivalent to: $(DDOC_BLANKLINE ) $(D_CODE ($(D_KEYWORD auto) $(D_KEYWORD ref) tmp) { ... tmp.ident; }(expression); ) $(DDOC_BLANKLINE ) $(P Note that Expression only gets evaluated once and is not copied. The with statement does not change what $(D this) or $(D super) refer to. ) $(DDOC_BLANKLINE ) $(P For $(I Symbol) which is a scope or $(I TemplateInstance), the corresponding scope is searched when looking up symbols. For example: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD struct) Foo { $(D_KEYWORD alias) Y = $(D_KEYWORD int); } ... Y y; $(D_COMMENT // error, Y undefined )$(D_KEYWORD with) (Foo) { Y y; $(D_COMMENT // same as Foo.Y y; )} ) $(DDOC_BLANKLINE ) $(P Use of with object symbols that shadow local symbols with the same identifier are not allowed. This is to reduce the risk of inadvertent breakage of with statements when new members are added to the object declaration. ) $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD float) x; } $(D_KEYWORD void) main() { $(D_KEYWORD int) x; S s; $(D_KEYWORD with) (s) { x++; $(D_COMMENT // error, shadows the int x declaration ) } } ) $(DDOC_BLANKLINE ) $(P In nested $(I WithStatement)s, the inner-most scope takes precedence. If a symbol cannot be resolved at the inner-most scope, resolution is forwarded incrementally up the scope hierarchy.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio; $(D_KEYWORD struct) Foo { $(D_KEYWORD void) f() { writeln($(D_STRING "Foo.f")); } } $(D_KEYWORD struct) Bar { $(D_KEYWORD void) f() { writeln($(D_STRING "Bar.f")); } } $(D_KEYWORD struct) Baz { $(D_COMMENT // f$(LPAREN)$(RPAREN ) is not implemented )} $(D_KEYWORD void) f() { writeln($(D_STRING "f")); } $(D_KEYWORD void) main() { Foo foo; Bar bar; Baz baz; f(); $(D_COMMENT // prints "f" ) $(D_KEYWORD with)(foo) { f(); $(D_COMMENT // prints "Foo.f" ) $(D_KEYWORD with)(bar) { f(); $(D_COMMENT // prints "Bar.f" ) $(D_KEYWORD with)(baz) { f(); $(D_COMMENT // prints "Bar.f". `Baz` does not implement `f$(LPAREN)$(RPAREN )` so ) $(D_COMMENT // resolution is forwarded to `with$(LPAREN)bar$(RPAREN )`'s scope ) } } $(D_KEYWORD with)(baz) { f(); $(D_COMMENT // prints "Foo.f". `Baz` does not implement `f$(LPAREN)$(RPAREN )` so ) $(D_COMMENT // resolution is forwarded to `with$(LPAREN)foo$(RPAREN )`'s scope ) } } $(D_KEYWORD with)(baz) { f(); $(D_COMMENT // prints "f". `Baz` does not implement `f$(LPAREN)$(RPAREN )` so ) $(D_COMMENT // resolution is forwarded to `main`'s scope. `f$(LPAREN)$(RPAREN )` is ) $(D_COMMENT // not implemented in `main`'s scope, so resolution is ) $(D_COMMENT // subsequently forward to module scope. ) } } ) ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 SynchronizedStatement, synchronized-statement, Synchronized Statement)

$(DDOC_BLANKLINE ) $(P The synchronized statement wraps a statement with a mutex to synchronize access among multiple threads. ) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME SynchronizedStatement): $(D synchronized) $(GLINK ScopeStatement) $(D synchronized $(LPAREN)) $(GLINK2 expression, Expression) $(D $(RPAREN )) $(GLINK ScopeStatement) ) $(DDOC_BLANKLINE ) $(P Synchronized allows only one thread at a time to execute $(I ScopeStatement) by using a mutex. ) $(DDOC_BLANKLINE ) $(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. ) $(DDOC_BLANKLINE ) $(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. ) $(DDOC_BLANKLINE ) $(P The synchronization gets released even if $(I ScopeStatement) terminates with an exception, goto, or return. ) $(DDOC_BLANKLINE ) $(P Example: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD synchronized) { ... } ) $(DDOC_BLANKLINE ) $(P This implements a standard critical section. ) $(DDOC_BLANKLINE ) $(P Synchronized statements support recursive locking; that is, a function wrapped in synchronized is allowed to recursively call itself and the behavior will be as expected: The mutex will be locked and unlocked as many times as there is recursion. ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 TryStatement, try-statement, Try Statement)

$(DDOC_BLANKLINE ) $(P Exception handling is done with the try-catch-finally statement.) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME TryStatement): $(D try) $(GLINK ScopeStatement) $(GLINK Catches) $(D try) $(GLINK ScopeStatement) $(GLINK Catches) $(GLINK FinallyStatement) $(D try) $(GLINK ScopeStatement) $(GLINK FinallyStatement) $(DDOC_BLANKLINE ) $(GNAME Catches): $(GLINK Catch) $(GLINK Catch) $(GSELF Catches) $(DDOC_BLANKLINE ) $(GNAME Catch): $(D catch $(LPAREN)) $(GLINK CatchParameter) $(D $(RPAREN )) $(GLINK NoScopeNonEmptyStatement) $(DDOC_BLANKLINE ) $(GNAME CatchParameter): $(GLINK2 type, BasicType) $(GLINK_LEX Identifier)$(OPT ) $(DDOC_BLANKLINE ) $(GNAME FinallyStatement): $(D finally) $(GLINK NoScopeNonEmptyStatement) ) $(DDOC_BLANKLINE ) $(P $(I CatchParameter) declares a variable v of type T, where T is 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.) $(DDOC_BLANKLINE ) $(P If just type T is given and no variable v, then the catch clause is still executed. ) $(DDOC_BLANKLINE ) $(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. ) $(DDOC_BLANKLINE ) $(P The $(I FinallyStatement) is always executed, whether the $(D try) $(I ScopeStatement) exits with a goto, break, continue, return, exception, or fall-through. ) $(DDOC_BLANKLINE ) $(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 entire chain. ) $(DDOC_BLANKLINE ) $(P Thrown objects derived from the $(LINK2 https://dlang.org/phobos/object.html#.Error, $(D Error)) class are treated differently. They bypass the normal chaining mechanism, such that the chain can only be caught by catching the first $(D Error). In addition to the list of subsequent exceptions, $(D Error) also contains a pointer that points to the original exception (the head of the chain) if a bypass occurred, so that the entire exception history is retained.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio; $(D_KEYWORD int) main() { $(D_KEYWORD try) { $(D_KEYWORD try) { $(D_KEYWORD throw) $(D_KEYWORD new) Exception($(D_STRING "first")); } $(D_KEYWORD finally) { writeln($(D_STRING "finally")); $(D_KEYWORD throw) $(D_KEYWORD new) Exception($(D_STRING "second")); } } $(D_KEYWORD catch) (Exception e) { writefln($(D_STRING "catch %s"), e.msg); } writeln($(D_STRING "done")); $(D_KEYWORD return) 0; } ) ) $(DDOC_BLANKLINE ) prints: $(DDOC_BLANKLINE ) $(CONSOLE finally catch first done ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(P A $(I FinallyStatement) may not exit with a goto, break, continue, or return; nor may it be entered with a goto. ) $(DDOC_BLANKLINE ) $(P A $(I FinallyStatement) may not contain any $(I Catches). This restriction may be relaxed in future versions. ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 ScopeGuardStatement, scope-guard-statement, Scope Guard Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME ScopeGuardStatement): $(D scope ( exit )) $(GLINK NonEmptyOrScopeBlockStatement) $(D scope ( success )) $(GLINK NonEmptyOrScopeBlockStatement) $(D scope ( failure )) $(GLINK NonEmptyOrScopeBlockStatement) ) $(DDOC_BLANKLINE ) $(P The $(I ScopeGuardStatement) executes NonEmptyOrScopeBlockStatement at the close of the current scope, rather than at the point where the $(I ScopeGuardStatement) appears. $(D scope(exit)) executes NonEmptyOrScopeBlockStatement when the scope exits normally or when it exits due to exception unwinding. $(D scope(failure)) executes NonEmptyOrScopeBlockStatement when the scope exits due to exception unwinding. scope(success) executes NonEmptyOrScopeBlockStatement when the scope exits normally.) $(DDOC_BLANKLINE ) $(P If there are multiple $(I ScopeGuardStatement)s in a scope, they will be executed in the reverse lexical order in which they appear. If any scope instances are to be destroyed upon the close of the scope, their destructions will be interleaved with the $(I ScopeGuardStatement)s in the reverse lexical order in which they appear.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE write($(D_STRING "1")); { write($(D_STRING "2")); $(D_KEYWORD scope)(exit) write($(D_STRING "3")); $(D_KEYWORD scope)(exit) write($(D_STRING "4")); write($(D_STRING "5")); } writeln(); ) ) $(DDOC_BLANKLINE ) writes: $(DDOC_BLANKLINE ) $(CONSOLE 12543 ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE { $(D_KEYWORD scope)(exit) write($(D_STRING "1")); $(D_KEYWORD scope)(success) write($(D_STRING "2")); $(D_KEYWORD scope)(exit) write($(D_STRING "3")); $(D_KEYWORD scope)(success) write($(D_STRING "4")); } writeln(); ) ) $(DDOC_BLANKLINE ) writes: $(DDOC_BLANKLINE ) $(CONSOLE 4321 ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) Foo { $(D_KEYWORD this)(string s) { write(s); } ~$(D_KEYWORD this)() { write($(D_STRING "1")); } } $(D_KEYWORD try) { $(D_KEYWORD scope)(exit) write($(D_STRING "2")); $(D_KEYWORD scope)(success) write($(D_STRING "3")); Foo f = Foo($(D_STRING "0")); $(D_KEYWORD scope)(failure) write($(D_STRING "4")); $(D_KEYWORD throw) $(D_KEYWORD new) Exception($(D_STRING "msg")); $(D_KEYWORD scope)(exit) write($(D_STRING "5")); $(D_KEYWORD scope)(success) write($(D_STRING "6")); $(D_KEYWORD scope)(failure) write($(D_STRING "7")); } $(D_KEYWORD catch) (Exception e) { } writeln(); ) ) $(DDOC_BLANKLINE ) writes: $(DDOC_BLANKLINE ) $(CONSOLE 0412 ) $(DDOC_BLANKLINE ) A $(D scope(exit)) or $(D scope(success)) statement may not exit with a throw, goto, break, continue, or return; nor may it be entered with a goto. A $(D scope(failure)) statement may not exit with a return. $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio; $(D_KEYWORD int) foo() { $(D_KEYWORD scope)(exit) writeln($(D_STRING "Inside foo$(LPAREN)$(RPAREN )")); $(D_KEYWORD return) bar(); } $(D_KEYWORD int) bar() { writeln($(D_STRING "Inside bar$(LPAREN)$(RPAREN )")); $(D_KEYWORD return) 0; } $(D_KEYWORD int) main() { foo(); $(D_KEYWORD return) 0; } ) ) $(DDOC_BLANKLINE ) writes: $(DDOC_BLANKLINE ) $(CONSOLE Inside bar() Inside foo() ) $(DDOC_BLANKLINE )

$(LNAME2 catching_cpp_class_objects, Catching C++ Class Objects)

$(DDOC_BLANKLINE ) $(P On many platforms, catching C++ class objects is supported. Catching C++ objects and D objects cannot both be done in the same $(I TryStatement). Upon exit from the $(I Catch), any destructors for the C++ object will be run and the storage used for it reclaimed. C++ objects cannot be caught in $(D @safe) code. ) $(DDOC_BLANKLINE )

$(LNAME2 asm, Asm Statement)

$(DDOC_BLANKLINE ) $(P Inline assembler is supported with the asm statement:) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME AsmStatement): $(D asm) $(GLINK2 function, FunctionAttributes)$(OPT ) $(D {) $(GLINK AsmInstructionList)$(OPT ) $(D }) $(DDOC_BLANKLINE ) $(GNAME AsmInstructionList): $(GLINK2 iasm, AsmInstruction) $(D ;) $(GLINK2 iasm, AsmInstruction) $(D ;) $(GSELF AsmInstructionList) ) $(DDOC_BLANKLINE ) $(P 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.) $(DDOC_BLANKLINE ) $(P The format of the instructions is, of course, highly dependent on the native instruction set of the target CPU, and so is $(DDLINK spec/iasm, Inline Assembler, implementation defined). But, the format will follow the following conventions:) $(DDOC_BLANKLINE ) $(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.) ) $(DDOC_BLANKLINE ) These rules exist to ensure that D source code can be tokenized independently of syntactic or semantic analysis. $(DDOC_BLANKLINE ) $(P For example, for the Intel Pentium:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) x = 3; $(D_KEYWORD asm) { mov EAX,x; $(D_COMMENT // load x and put it in register EAX )} ) $(DDOC_BLANKLINE ) $(P Inline assembler can be used to access hardware directly:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD int) gethardware() { $(D_KEYWORD asm) { mov EAX, dword ptr 0x1234; } } ) $(DDOC_BLANKLINE ) $(P 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:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD version) (D_InlineAsm_X86) { $(D_KEYWORD asm) { ... } } $(D_KEYWORD else) { $(D_COMMENT /* ... some workaround ... */) } ) $(DDOC_BLANKLINE ) $(P Semantically consecutive $(I AsmStatement)s shall not have any other instructions (such as register save or restores) inserted between them by the compiler. ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 PragmaStatement, pragma-statement, Pragma Statement)

$(DDOC_BLANKLINE ) $(P See $(GLINK2 pragma, PragmaStatement)). $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 MixinStatement, mixin-statement, Mixin Statement)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME MixinStatement): $(D mixin) $(D $(LPAREN)) $(GLINK2 expression, ArgumentList) $(D $(RPAREN )) $(D ;) ) $(DDOC_BLANKLINE ) $(P Each $(GLINK2 expression, AssignExpression) in the $(I ArgumentList) is evaluated at compile time, and the result must be representable as a string. The resulting strings are concatenated to form a string. The text contents of the string must be compilable as a valid $(GLINK StatementList), and is compiled as such. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio; $(D_KEYWORD void) main() { $(D_KEYWORD int) i = 0; $(D_KEYWORD mixin)($(D_STRING " int x = 3; for $(LPAREN); i < 3; i++$(RPAREN ) writeln$(LPAREN)x + i, i$(RPAREN ); ")); $(D_COMMENT // ok ) $(D_KEYWORD enum) s = $(D_STRING "int y;"); $(D_KEYWORD mixin)(s); $(D_COMMENT // ok ) y = 4; $(D_COMMENT // ok, mixin declared y ) string t = $(D_STRING "y = 3;"); $(D_COMMENT //mixin$(LPAREN)t$(RPAREN ); // error, t is not evaluatable at compile time ) $(D_COMMENT //mixin$(LPAREN)"y ="$(RPAREN ) 4; // error, string must be complete statement ) $(D_KEYWORD mixin)($(D_STRING "y =") ~ $(D_STRING "4;")); $(D_COMMENT // ok ) $(D_KEYWORD mixin)($(D_STRING "y ="), 2+2, $(D_STRING ";")); $(D_COMMENT // ok )} ) ) $(DDOC_BLANKLINE ) $(SPEC_SUBNAV_PREV_NEXT expression, Expressions, arrays, Arrays) ) )