From D Wiki
Jump to: navigation, search

DIP57: static foreach

Title: static foreach
DIP: 57
Version: 1
Status: Draft
Created: 2014-03-09
Last Modified: 2014-03-09
Author: Timon Gehr


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'.


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.



The language grammar is updated in the following way:

+     alias
+     enum
-     Foreach ( ForeachTypeList ; ForeachAggregate ) NoScopeNonEmptyStatement
+     Foreach NoScopeNonEmptyStatement

+ Foreach
+     ForeachKeyword ( ForeachTypeList ; ForeachAggregate )
+ StaticForeach
      static Foreach
- Foreach:
+ ForeachKeyword:
+ 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.


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.


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));


 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){{
         int x = 2; // ok
     static foreach(i;0..2){
         int x = 2; // error: multiple declarations of x
 void bar(){
     static foreach_reverse(i;0..2){


This document has been placed in the Public Domain.