Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

static foreach

Version 1
Created 2014-03-09
StatusDraft
Last modified 2014-03-09
Author Timon Gehr

Abstract

The goal of this DIP is to propose semantics for the ‘static foreach’ construct. In short, ‘static foreach’ relates to ‘foreach’ like ‘static if’ relates to ‘if’.

Scope

This proposal suggests an additive language change. The only code that will change behaviour is code that contains the new construct. The new language construct is specified economically in terms of the existing foreach construct. Specification will be informal (in order to be able to leverage the existing informal specification of foreach statements) but quite precise.

Proposal

Grammar

The language grammar is updated in the following way:

  ForeachTypeAttribute: +     alias +     enum

  ForeachStatement: -     Foreach ( ForeachTypeList ; ForeachAggregate ) NoScopeNonEmptyStatement +     Foreach NoScopeNonEmptyStatement \ + Foreach +     ForeachKeyword ( ForeachTypeList ; ForeachAggregate )

+ StaticForeach       static Foreach

- Foreach: + ForeachKeyword:       foreach       foreach_reverse

+ StaticForeachDeclaration +     StaticForeach DeclarationBlock +     StaticForeach : DeclDefs[opt]

+ StaticForeachStatement +     StaticForeach NoScopeNonEmptyStatement

The current grammar does not specify locations where ‘static if’ is valid. The ‘static foreach’ rules should be fitted into the corrected grammar analogously to the ‘static if’ rules.

Semantics

The syntax tree parsed under rule Foreach is subject to all type checking performed on that part of a corresponding foreach statement except creation of a new nested scope and insertion of symbols into that scope. Additionally, CTFE is invoked on all expressions occurring in the ForeachAggregate. The body of the static foreach statement or static foreach declaration is duplicated once for each iteration which the corresponding foreach statement with an empty body would perform when executed in CTFE. If CTFE would fail, then expansion of the static foreach construct will fail as well. The duplicated bodies are then compiled in in order. Every such body obtains access to names that the corresponding foreach statement would have introduced. (For static foreach statements, the usual restrictions on shadowing for declarations in function scope apply.) For the i-th duplicated body, the names are bound to manifest constants (or alias declarations) describing the value (symbol) that the corresponding foreach statement would have assigned to a variable (alias) of this name in the i-th iteration. Declarations introduced in the body itself are inserted into this enclosing scope, however, the names introduced by the loop itself are not accessible from outside or in distinct iterations unless explicitly aliased. For static foreach statements, break and continue are supported and treated like for foreach statements over seqs.

The grammar changes add ‘alias’ and ‘enum’ as valid foreach type attributes. They explicitly specify the loop index to be an alias or enum declaration respectively. Those attributes can be used in both foreach over seq and static foreach.

Examples

This is an incomplete set of examples for the purpose of illustration.

Method declarations

 enum toDeclare = ["foo", "bar", "baz"];

 class C{
     static foreach(m;toDeclare){
         mixin("void "~m~"(D d){ d."~m~"(false); }");
     }
 }

Variable declarations

 import std.conv, std.range;
 static foreach(x;0..10){
     mixin("int i" ~ to!string(x) ~ " = " ~ to!string(x)~";");
 }
 static assert(is(typeof(i0)==int));
 
 static foreach(x;iota(1,10)){
     static assert(is(typeof(mixin(i~to!string(x)))==int));
 }

Overloads

 alias Seq(T...)=T;
 static foreach(T;Seq!(int, double, float)){
     T foo(T arg){ return arg; }
 }

Iterating over members of a scope

 static foreach(m;__traits(allMembers, C)){
     static if(someCondition!(__traits(getMember, C, m))):
     mixin(generateBoilerplate!(__traits(getMember, C, m)));
 }

Function scope

 void foo(){
     static foreach(i;0..2)
     static foreach(i;0..2){
         int x = 2; // error: multiple declarations of x
         writeln(x);
         break;
     }
 }
 void bar(){
     static foreach_reverse(i;0..2){
         writeln(i);
     }
 }

This document has been placed in the Public Domain.