// REQUIRED_ARGS: -o- // PERMUTE_ARGS: // EXTRA_FILES: imports/imp12242a1.d imports/imp12242a2.d /* TEST_OUTPUT: --- 9 8 7 6 5 4 3 2 1 0 S(1, 2, 3, [0, 1, 2]) x0: 1 x1: 2 x2: 3 a: [0, 1, 2] (int[], char[], bool[], Object[]) [0, 0] x0: int x1: double x2: char test(0)→ 0 test(1)→ 1 test(2)→ 2 test(3)→ 3 test(4)→ 4 test(5)→ 5 test(6)→ 6 test(7)→ 7 test(8)→ 8 test(9)→ 9 test(10)→ -1 test(11)→ -1 test(12)→ -1 test(13)→ -1 test(14)→ -1 1 [1, 2, 3] 2 [1, 2, 3] 3 [1, 2, 3] 0 1 1 2 2 3 1 3 4 object Tuple tuple main front popFront empty back popBack Iota iota map to text all any join S s Seq Overloads Parameters forward foo A B C D E Types Visitor testVisitor staticMap arrayOf StaticForeachLoopVariable StaticForeachScopeExit StaticForeachReverseHiding UnrolledForeachReverse StaticForeachReverse StaticForeachByAliasDefault NestedStaticForeach TestAliasOutsideFunctionScope OpApplyMultipleStaticForeach OpApplyMultipleStaticForeachLowered RangeStaticForeach OpApplySingleStaticForeach TypeStaticForeach AliasForeach EnumForeach TestUninterpretable SeqForeachConstant SeqForeachBreakContinue TestStaticForeach testtest fun testEmpty bug17660 breakContinueBan MixinTemplate testToStatement bug17688 T foo2 T2 1 2 '3' 2 3 '4' 0 1 1 2 2 3 --- */ module staticforeach; struct Tuple(T...){ T expand; alias expand this; } auto tuple(T...)(T t){ return Tuple!T(t); } /+struct TupleStaticForeach{ // should work, but is not the fault of the static foreach implementation. //pragma(msg, [tuple(1,"2",'3'),tuple(2,"3",'4')].map!((x)=>x)); static foreach(a,b,c;[tuple(1,"2",'3'),tuple(2,"3",'4')].map!((x)=>x)){ pragma(msg,a," ",b," ",c); } }+/ void main(){ static foreach(a,b,c;[tuple(1,"2",'3'),tuple(2,"3",'4')].map!((x)=>x)){ pragma(msg, a," ",b," ",c); } static struct S{ // (aggregate scope, forward referencing possible) static assert(stripA("123")==1); static assert(stripA([1],2)==2); static foreach(i;0..2){ mixin(`import imports.imp12242a`~text(i+1)~`;`); static assert(stripA("123")==1); static assert(stripA([1],2)==2); } static assert(stripA("123")==1); static assert(stripA([1],2)==2); } static foreach(i;0..2){ // (function scope, no forward referencing) mixin(`import imports.imp12242a`~text(i+1)~`;`); static assert(stripA("123")==1); static if(i) static assert(stripA([1],2)==2); } static assert(stripA("123")==1); static assert(stripA([1],2)==2); } auto front(T)(T[] a){ return a[0]; } auto popFront(T)(ref T[] a){ a=a[1..$]; } auto empty(T)(T[] a){ return !a.length; } auto back(T)(T[] a){ return a[$-1]; } auto popBack(T)(ref T[] a){ a=a[0..$-1]; } struct Iota(T){ T s,e; @property bool empty(){ return s>=e; } @property T front(){ return s; } @property T back(){ return cast(T)(e-1); } void popFront(){ s++; } void popBack(){ e--; } } auto iota(T)(T s, T e){ return Iota!T(s,e); } template map(alias a){ struct Map(R){ R r; @property front(){ return a(r.front); } @property back(){ return a(r.back); } @property bool empty(){ return r.empty; } void popFront(){ r.popFront(); } void popBack(){ r.popBack(); } } auto map(R)(R r){ return Map!R(r); } } template to(T:string){ string to(S)(S x)if(is(S:int)||is(S:size_t)||is(S:char)){ static if(is(S==char)) return cast(string)[x]; if(x<0) return "-"~to(-1 * x); if(x==0) return "0"; return (x>=10?to(x/10):"")~cast(char)(x%10+'0'); } } auto text(T)(T arg){ return to!string(arg); }; template all(alias a){ bool all(R)(R r){ foreach(x;r) if(!a(x)) return false; return true; } } template any(alias a){ bool any(R)(R r){ foreach(x;r) if(a(x)) return true; return false; } } auto join(R)(R r,string sep=""){ string a; int first=0; foreach(x;r){ if(first++) a~=sep; a~=x; } return a; } static foreach_reverse(x;iota(0,10).map!(to!string)){ pragma(msg, x); } // create struct members iteratively struct S{ static foreach(i;a){ mixin("int x"~to!string(i)~";"); } immutable int[] a = [0,1,2]; } enum s=S(1,2,3); pragma(msg, s); // loop over struct members static foreach(member;__traits(allMembers,S)){ pragma(msg, member,": ",mixin("s."~member)); } // print prime numbers using overload sets as state variables. /+ static assert(is(typeof(bad57))); static assert(!is(typeof(bad53))); static foreach(x;iota(2,100)){ static foreach(y;iota(2,x)){ static if(!(x%y)){ mixin("void bad"~to!string(x)~"();"); } } static if(!is(typeof(mixin("bad"~to!string(x))))){ static assert(iota(2,x).all!(y=>!!(x%y))); pragma(msg, x); }else{ static assert(iota(2,x).any!(y=>!(x%y))); } } +/ alias Seq(T...)=T; alias Overloads(alias a) = Seq!(__traits(getOverloads, __traits(parent, a), __traits(identifier, a))); template Parameters(alias f){ static if(is(typeof(f) P == function)) alias Parameters=P; } template forward(alias a){ enum x=2; static foreach(f;Overloads!a){ auto ref forward(Parameters!f args){ return f(args); } } enum y=3; } int foo(int x){ return x; } string foo(string x){ return x; } static assert(forward!foo(2)==2 && forward!foo("hi") == "hi"); // simple boilerplate-free visitor pattern static foreach(char T;'A'..'F'){ mixin("class "~T~q{{ void accept(Visitor v){ return v.visit(this); } }}); } alias Types = Seq!(mixin("Seq!("~iota('A','F').map!(to!string).join(", ")~")")); abstract class Visitor{ static foreach(T;Types){ abstract void visit(T); } } string testVisitor(){ string r; void writeln(T...)(T args){ static foreach(x;args) r~=x; r~='\n'; } class Visitor: .Visitor{ static foreach(T;Types){ override void visit(T){ writeln("visited: ",T.stringof); } } } void main(){ auto v=new Visitor; static foreach(T;Types){ v.visit(new T); } } main(); return r; } static assert(testVisitor()=="visited: A visited: B visited: C visited: D visited: E "); // iterative computation over AliasSeq: template staticMap(alias F,T...){ alias state0=Seq!(); static foreach(i,A;T){ mixin("alias state"~to!string(i+1)~" = Seq!(state"~to!string(i)~",F!A);"); } alias staticMap = Seq!(mixin("state"~to!string(T.length))); } alias arrayOf(T)=T[]; static assert(is(staticMap!(arrayOf,int,char,bool,Object)==Seq!(int[], char[], bool[], Object[]))); pragma(msg, staticMap!(arrayOf,int,char,bool,Object)); struct StaticForeachLoopVariable{ int x; static foreach(i;0..1){ mixin("enum x"~text(i)~" = i;"); } int y; static assert(__traits(allMembers, StaticForeachLoopVariable).length==3); static assert(!is(typeof(StaticForeachLoopVariable.i))); static assert(!is(typeof(__traits(getMember, StaticForeachLoopVariable, "i")))); } struct StaticForeachScopeExit{ static: int[] test(){ int[] r; scope(exit) r ~= 1234; { static foreach(i;0..5){ scope(exit) r ~= i; } r ~= 5; } return r; } static assert(test()==[5,4,3,2,1,0]); } struct StaticForeachReverseHiding{ static foreach(i;[0]){ enum i = 1; // TODO: disallow? static assert(i==0); } } struct UnrolledForeachReverse{ static: alias Seq(T...)=T; int[] test(){ int[] r; foreach_reverse(i;Seq!(0,1,2,3)){ r~=i; } return r; } static assert(test()==[3,2,1,0]); } struct StaticForeachReverse{ static: alias Seq(T...)=T; int[] test(){ int[] r; static foreach_reverse(i;0..4){ r~=i; } return r; } static assert(test()==[3,2,1,0]); int[] test2(){ int[] r; static foreach_reverse(i;[0,1,2,3]){ r~=i; } return r; } static assert(test2()==[3,2,1,0]); int[] test3(){ static struct S{ int opApplyReverse(scope int delegate(int) dg){ foreach_reverse(i;0..4) if(auto r=dg(i)) return r; return 0; } } int[] r; static foreach_reverse(i;S()){ r~=i; } return r; } static assert(test3()==[3,2,1,0]); int[] test4(){ int[] r; static foreach_reverse(i;Seq!(0,1,2,3)){ r~=i; } return r; } static assert(test()==[3,2,1,0]); } struct StaticForeachByAliasDefault{ static: alias Seq(T...)=T; int[] test(){ int a,b,c; static foreach(i,x;Seq!(a,b,c)) x=i; return [a,b,c]; } static assert(test()==[0,1,2]); int[] test2(){ int x=0; int foo(){ return ++x; } static foreach(y;Seq!foo) return [y,y,y]; } static assert(test2()==[1,2,3]); void test3(){ int x=0; int foo(){ return ++x; } static assert(!is(typeof({ static foreach(enum y;Seq!foo) return [y,y,y]; }))); } } struct NestedStaticForeach{ static: static foreach(i,name;["a"]){ static foreach(j,name2;["d"]){ mixin("enum int[] "~name~name2~"=[i, j];"); } } pragma(msg, ad); } struct TestAliasOutsideFunctionScope{ static: alias Seq(T...)=T; int a; static foreach(alias x;Seq!(a)){ } } struct OpApplyMultipleStaticForeach{ static: struct OpApply{ int opApply(scope int delegate(int,int) dg){ foreach(i;0..10) if(auto r=dg(i,i*i)) return r; return 0; } } static foreach(a,b;OpApply()){ mixin(`enum x`~cast(char)('0'+a)~"=b;"); } static foreach(i;0..10){ static assert(mixin(`x`~cast(char)('0'+i))==i*i); } } struct OpApplyMultipleStaticForeachLowered{ static: struct OpApply{ int opApply(scope int delegate(int,int) dg){ foreach(i;0..10) if(auto r=dg(i,i*i)) return r; return 0; } } static foreach(x;{ static struct S(T...){ this(T k){ this.x=k; } T x; } static s(T...)(T a){ return S!T(a); } typeof({ foreach(a,b;OpApply()){ return s(a,b); } assert(0);}())[] r; foreach(a,b;OpApply()) r~=s(a,b); return r; }()){ mixin(`enum x`~cast(char)('0'+x.x[0])~"=x.x[1];"); } static foreach(i;0..10){ static assert(mixin(`x`~cast(char)('0'+i))==i*i); } } struct RangeStaticForeach{ static: struct Range{ int x=0; this(int x){ this.x=x; } @property int front(){ return x; } void popFront(){ x += 2; } @property bool empty(){ return x>=10; } } static foreach(i;Range()){ mixin(`enum x`~cast(char)('0'+i)~"=i;"); } static foreach(i;0..5){ static assert(mixin(`x`~cast(char)('0'+2*i))==2*i); } static assert(!is(typeof({ struct S{ static foreach(i,k;Range()){} } }))); static foreach(k;Range()){} // ok } struct OpApplySingleStaticForeach{ static: struct OpApply{ int opApply(scope int delegate(int) dg){ foreach(i;0..10) if(auto r=dg(i)) return r; return 0; } } static foreach(b;OpApply()){ mixin(`enum x`~cast(char)('0'+b)~"=b;"); } static foreach(i;0..10){ static assert(mixin(`x`~cast(char)('0'+i))==i); } } struct TypeStaticForeach{ static: alias Seq(T...)=T; static foreach(i,alias T;Seq!(int,double,char)){ mixin(`T x`~cast(char)('0'+i)~";"); } pragma(msg, "x0: ",typeof(x0)); pragma(msg, "x1: ",typeof(x1)); pragma(msg, "x2: ",typeof(x2)); static assert(is(typeof(x0)==int)); static assert(is(typeof(x1)==double)); static assert(is(typeof(x2)==char)); } struct AliasForeach{ static: alias Seq(T...)=T; int[] test(){ int a,b,c; static foreach(x;Seq!(a,b,c,2)){ static if(is(typeof({x=2;}))) x=2; } int x,y,z; static foreach(alias k;Seq!(x,y,z,2)){ static if(is(typeof({k=2;}))) k=2; } int j,k,l; static assert(!is(typeof({ static foreach(ref x;Seq!(j,k,l,2)){ static if(is(typeof({x=2;}))) x=2; } }))); return [x,y,z]; } static assert(test()==[2,2,2]); } struct EnumForeach{ static: alias Seq(T...)=T; int a=1; int fun(){ return 1; } int gun(){ return 2; } int hun(){ return 3;} auto test(){ static foreach(i,enum x;Seq!(fun,gun,hun)){ static assert(i+1==x); } foreach(i,enum x;Seq!(fun,gun,hun)){ static assert(i+1==x); } } } struct TestUninterpretable{ static: alias Seq(T...)=T; auto test(){ int k; static assert(!is(typeof({ static foreach(x;[k]){} }))); static assert(!is(typeof({ foreach(enum x;[1,2,3]){} }))); static assert(!is(typeof({ foreach(alias x;[1,2,3]){} }))); foreach(enum x;Seq!(1,2,3)){} // ok foreach(alias x;Seq!(1,2,3)){} // ok static foreach(enum x;[1,2,3]){} // ok static foreach(alias x;[1,2,3]){} // ok static assert(!is(typeof({ static foreach(enum alias x;[1,2,3]){} }))); int x; static foreach(i;Seq!x){ } // ok static foreach(i,j;Seq!(1,2,x)){ } // ok static assert(!is(typeof({ static foreach(ref x;[1,2,3]){} }))); } } struct SeqForeachConstant{ static: alias Seq(T...)=T; static assert(!is(typeof({ foreach(x;Seq!1) x=2; }))); int test2(){ int r=0; foreach(x;Seq!(1,2,3)){ enum k=x; r+=k; } return r; } static assert(test2()==6); } struct SeqForeachBreakContinue{ static: alias Seq(T...)=T; int[] test(){ int[] r; foreach(i;Seq!(0,1,2,3,4,5)){ if(i==2) continue; if(i==4) break; r~=i; } return r; } static assert(test()==[0,1,3]); } struct TestStaticForeach{ static: int test(int x){ int r=0; label: switch(x){ static foreach(i;0..10){ case i: r=i; break label; // TODO: remove label when restriction is lifted } default: r=-1; break label; } return r; } static foreach(i;0..15){ pragma(msg, "test(",i,")→ ",test(i)); static assert(test(i)==(i<10?i:-1)); } enum x=[1,2,3]; static foreach(i;x){ mixin("enum x"~cast(char)('0'+i)~"="~cast(char)('0'+i)~";"); } static foreach(i;x){ pragma(msg, mixin("x"~cast(char)('0'+i))); pragma(msg,x); } int[] noBreakNoContinue(){ int[] r; static foreach(i;0..1){ // if(i==3) continue; // TODO: error? // if(i==7) break; // TODO: error? r~=i; } return r; } mixin("enum k=3;"); } static foreach(i,j;[1,2,3]){ pragma(msg, int(i)," ",j); } void testtest(){ static foreach(i,v;[1,2,3]){ pragma(msg, int(i)," ",v); static assert(i+1 == v); } } static foreach(i;Seq!(1,2,3,4,int)){ static if(!is(i) && i!=2){ pragma(msg, i); } } int fun(int x){ int r=0; label: switch(x){ static foreach(i;Seq!(0,1,2,3,4,5,6)){ static if (i < 5) case i: r=i; break label; // TODO: remove label when restriction is lifted } default: r=-1; break label; } return r; } static foreach(i;0..10) static assert(fun(i)==(i<5?i:-1)); static foreach(i;0..0) { } void testEmpty(){ static foreach(i;0..0) { } } auto bug17660(){ int x; static foreach (i; 0 .. 1) { return 3; } return x; } static assert(bug17660()==3); int breakContinueBan(){ static assert(!is(typeof({ for(;;){ static foreach(i;0..1){ break; } } }))); static assert(!is(typeof({ for(;;){ static foreach(i;0..1){ continue; } } }))); Louter1: for(;;){ static foreach(i;0..1){ break Louter1; } } Louter2: foreach(i;0..10){ static foreach(j;0..1){ continue Louter2; } return 0; } static foreach(i;0..1){ for(;;){ break; } // ok } return 1; } static assert(breakContinueBan()==1); mixin template MixinTemplate(){ static foreach(i;0..2){ mixin(`enum x`~cast(char)('0'+i)~"=i;"); } static foreach(i;[0,1]){ mixin(`enum y`~cast(char)('0'+i)~"=i;"); } } void testToStatement(){ mixin MixinTemplate; static assert(x0==0 && x1==1); static assert(y0==0 && y1==1); } void bug17688(){ final switch(1) static foreach(x;0..1){ int y=3; case 1: return; } static assert(!is(typeof(y))); } struct T{ enum n = 1; } T foo(T v)@nogc{ static foreach(x;0..v.n){ } return T.init; } T foo2(T v)@nogc{ static foreach(_;0..typeof(return).n){ } return T.init; } //https://issues.dlang.org/show_bug.cgi?id=18698 static foreach(m; __traits(allMembers, staticforeach)) { pragma(msg, m); } //https://issues.dlang.org/show_bug.cgi?id=20072 struct T2{ static foreach(i;0..1) struct S{} } static assert(is(__traits(parent,T2.S)==T2));