www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Static foreach pull request

reply Timon Gehr <timon.gehr gmx.ch> writes:
Code: https://github.com/dlang/dmd/pull/6760

Some examples: 
https://github.com/tgehr/dmd/blob/71ab1280c88f9f0922fabf89ab3e7e1164b70e8b/src/test_staticforeach.d

This is a complete proof-of-concept implementation of "static foreach". 
The semantics of the construct are given by merging the ones of static 
if and runtime foreach. This allows declarations to be generated using 
an imperative loop.

If you are interested in static foreach making it into the language, 
please play with the implementation and tell me how to break it. It 
would also be nice to get some code reviews (the implementation is the 
result of two days of exhausting trial-and-error figuring out how the 
DMD frontend works).
May 08 2017
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09.05.2017 05:06, Timon Gehr wrote:
 ...

 Some examples:
 https://github.com/tgehr/dmd/blob/71ab1280c88f9f0922fabf89ab3e7e1164b70e8b/src/test_staticforeach.d
Better link: https://github.com/tgehr/dmd/blob/static-foreach/src/test_staticforeach.d
May 08 2017
next sibling parent reply Daniel N <no public.email> writes:
On Tuesday, 9 May 2017 at 03:42:48 UTC, Timon Gehr wrote:
 On 09.05.2017 05:06, Timon Gehr wrote:
 ...

 Some examples:
 https://github.com/tgehr/dmd/blob/71ab1280c88f9f0922fabf89ab3e7e1164b70e8b/src/test_staticforeach.d
Better link: https://github.com/tgehr/dmd/blob/static-foreach/src/test_staticforeach.d
EPIC! Thank you so much for doing this!
 adds 'enum' and 'alias' on foreach loop variables to force one 
 of the two.
 (Also works with existing foreach over AliasSeq.)
I was wondering, would it be possible to move this to a separate pull request and get it merged before the rest? It would be useful on its own and probably would stand a chance of getting merged much faster... before every detail of static-foreach is agreed upon?
May 09 2017
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09.05.2017 09:26, Daniel N wrote:
 On Tuesday, 9 May 2017 at 03:42:48 UTC, Timon Gehr wrote:
 On 09.05.2017 05:06, Timon Gehr wrote:
 ...

 Some examples:
 https://github.com/tgehr/dmd/blob/71ab1280c88f9f0922fabf89ab3e7e1164b70e8b/src/test_staticforeach.d
Better link: https://github.com/tgehr/dmd/blob/static-foreach/src/test_staticforeach.d
EPIC! Thank you so much for doing this! ...
Well, there was a hackaton, and I needed to do _something_. :) I thought this will have the highest impact.
 adds 'enum' and 'alias' on foreach loop variables to force one of the
 two.
 (Also works with existing foreach over AliasSeq.)
I was wondering, would it be possible to move this to a separate pull request and get it merged before the rest? It would be useful on its own and probably would stand a chance of getting merged much faster... before every detail of static-foreach is agreed upon?
Yes, I'll do that.
May 09 2017
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09.05.2017 05:42, Timon Gehr wrote:
 On 09.05.2017 05:06, Timon Gehr wrote:
 ...

 Some examples:
 https://github.com/tgehr/dmd/blob/71ab1280c88f9f0922fabf89ab3e7e1164b70e8b/src/test_staticforeach.d
Better link: https://github.com/tgehr/dmd/blob/static-foreach/src/test_staticforeach.d
Moved: https://github.com/tgehr/dmd/blob/static-foreach/test_staticforeach.d
May 09 2017
prev sibling next sibling parent reply Jack Stouffer <jack jackstouffer.com> writes:
On Tuesday, 9 May 2017 at 03:06:37 UTC, Timon Gehr wrote:
 ...
I'm going to save you some time and tell you that Andrei and Walter are going to require a DIP for this.
May 08 2017
parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 09/05/2017 7:10 AM, Jack Stouffer wrote:
 On Tuesday, 9 May 2017 at 03:06:37 UTC, Timon Gehr wrote:
 ...
I'm going to save you some time and tell you that Andrei and Walter are going to require a DIP for this.
http://forum.dlang.org/thread/oenjmm$lds$1 digitalmars.com
May 08 2017
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09.05.2017 08:17, rikki cattermole wrote:
 On 09/05/2017 7:10 AM, Jack Stouffer wrote:
 On Tuesday, 9 May 2017 at 03:06:37 UTC, Timon Gehr wrote:
 ...
I'm going to save you some time and tell you that Andrei and Walter are going to require a DIP for this.
http://forum.dlang.org/thread/oenjmm$lds$1 digitalmars.com
Also, the DIP has existed for three years, it's just not very polished: https://wiki.dlang.org/DIP57
May 09 2017
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/9/17 3:46 PM, Timon Gehr wrote:
 On 09.05.2017 08:17, rikki cattermole wrote:
 On 09/05/2017 7:10 AM, Jack Stouffer wrote:
 On Tuesday, 9 May 2017 at 03:06:37 UTC, Timon Gehr wrote:
 ...
I'm going to save you some time and tell you that Andrei and Walter are going to require a DIP for this.
http://forum.dlang.org/thread/oenjmm$lds$1 digitalmars.com
Also, the DIP has existed for three years, it's just not very polished: https://wiki.dlang.org/DIP57
Cool! Now that you also have a proof of concept implementation, could you please flesh that up to make it solid and submit it as a PR? Thanks! -- Andrei
May 09 2017
prev sibling next sibling parent reply Corey <corey dlang.forum> writes:
On Tuesday, 9 May 2017 at 03:06:37 UTC, Timon Gehr wrote:
 (the implementation is the result of two days of exhausting 
 trial-and-error figuring out how the DMD frontend works).
First time dealing with the frontend? Heck, two days sounds fast to me.
May 09 2017
parent Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Tuesday, 9 May 2017 at 09:17:06 UTC, Corey wrote:
 On Tuesday, 9 May 2017 at 03:06:37 UTC, Timon Gehr wrote:
 (the implementation is the result of two days of exhausting 
 trial-and-error figuring out how the DMD frontend works).
First time dealing with the frontend? Heck, two days sounds fast to me.
I guess having written your own D frontend from scratch must have helped! https://github.com/tgehr/d-compiler
May 09 2017
prev sibling parent reply Guillaume Boucher <guillaume.boucher.d gmail.com> writes:
On Tuesday, 9 May 2017 at 03:06:37 UTC, Timon Gehr wrote:
 If you are interested in static foreach making it into the 
 language, please play with the implementation and tell me how 
 to break it.
Code: void main() { void f() { idonotexist(); } static foreach(j;0..0) { f(); } } Output: test_staticforeach.d(3): Error: undefined identifier 'idonotexist' Statement::blockExit(0x7f38d5cd35e0) static foreach (j; __error) { f(); } core.exception.AssertError ddmd/blockexit.d(90): Assertion failure ---------------- ??:? _d_assertp [0x72e590] ??:? _ZN9blockExit9BlockExit5visitEP9Statement [0x637bd0] ??:? _ZN7Visitor5visitEP22StaticForeachStatement [0x625b45] ??:? _ZN22StaticForeachStatement6acceptEP7Visitor [0x61ebf8] ??:? int ddmd.blockexit.blockExit(ddmd.statement.Statement, ddmd.func.FuncDeclaration, bool) [0x637b69] ??:? _ZN9blockExit9BlockExit5visitEP17CompoundStatement [0x637ef9] ??:? _ZN17CompoundStatement6acceptEP7Visitor [0x61dc61] ??:? int ddmd.blockexit.blockExit(ddmd.statement.Statement, ddmd.func.FuncDeclaration, bool) [0x637b69] ??:? _ZN15FuncDeclaration9semantic3EP5Scope [0x5bba3d] ??:? _ZN6Module9semantic3EP5Scope [0x563f31] ??:? int ddmd.mars.tryMain(ulong, const(char)**) [0x5e2b46] ??:? _Dmain [0x5e3a12] ??:? _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZ9__lambda1MFNlZv [0x7301da] ??:? scope void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate()) [0x730124] ??:? scope void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll() [0x730196] ??:? scope void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate()) [0x730124] ??:? _d_run_main [0x7300a2] ??:? main [0x5e411f] ??:? __libc_start_main [0xd4999510]
May 09 2017
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09.05.2017 23:39, Guillaume Boucher wrote:
 On Tuesday, 9 May 2017 at 03:06:37 UTC, Timon Gehr wrote:
 If you are interested in static foreach making it into the language,
 please play with the implementation and tell me how to break it.
Code: void main() { void f() { idonotexist(); } static foreach(j;0..0) { f(); } } Output: test_staticforeach.d(3): Error: undefined identifier 'idonotexist' Statement::blockExit(0x7f38d5cd35e0) static foreach (j; __error) { f(); } core.exception.AssertError ddmd/blockexit.d(90): Assertion failure ---------------- ...
Thanks! (It's a known issue though: https://github.com/tgehr/dmd/blob/static-foreach/test_staticforeach.d#L330.) I guess the problem is that I do not propagate the error condition properly, but I'm not sure how to do it. (In my frontend, error handling control flow is automated almost completely.)
May 09 2017
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/10/17 12:56 AM, Timon Gehr wrote:
 Thanks! (It's a known issue though: 
 https://github.com/tgehr/dmd/blob/static-foreach/test_staticforeach.d#L330.) 
 
 
 I guess the problem is that I do not propagate the error condition 
 properly, but I'm not sure how to do it. (In my frontend, error handling 
 control flow is automated almost completely.)
Ignorant thought: guard the static foreach with a static if? -- Andrei
May 09 2017
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/10/17 1:00 AM, Andrei Alexandrescu wrote:
 On 5/10/17 12:56 AM, Timon Gehr wrote:
 Thanks! (It's a known issue though: 
 https://github.com/tgehr/dmd/blob/static-foreach/test_staticforeach.d#L330.) 


 I guess the problem is that I do not propagate the error condition 
 properly, but I'm not sure how to do it. (In my frontend, error 
 handling control flow is automated almost completely.)
Ignorant thought: guard the static foreach with a static if? -- Andrei
(as a lowering that is)
May 09 2017
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09.05.2017 23:56, Timon Gehr wrote:
 core.exception.AssertError ddmd/blockexit.d(90): Assertion failure
 ----------------
 ...
Thanks! (It's a known issue though: https://github.com/tgehr/dmd/blob/static-foreach/test_staticforeach.d#L330.) I guess the problem is that I do not propagate the error condition properly, but I'm not sure how to do it. (In my frontend, error handling control flow is automated almost completely.)
https://github.com/dlang/dmd/pull/6760/commits/0ac9556cb3a0e1ea1de02e97e7f05e866584de84
May 09 2017
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09.05.2017 23:56, Timon Gehr wrote:
 core.exception.AssertError ddmd/blockexit.d(90): Assertion failure
 ----------------
 ...
Thanks! (It's a known issue though: https://github.com/tgehr/dmd/blob/static-foreach/test_staticforeach.d#L330.)
Actually, yours is a different case with the same outcome (f and idonotexist do not matter at all, the issue exists even for static foreach(j;0..0){}). All static foreach loops over empty (non-AliasSeq) aggregates failed that assertion. The reason was that CTFE can return a null literal from a function that returns T[], but the constant folder cannot actually evaluate null.length for some reason.
May 10 2017
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 10 May 2017 at 09:42:53 UTC, Timon Gehr wrote:
 On 09.05.2017 23:56, Timon Gehr wrote:
 core.exception.AssertError ddmd/blockexit.d(90): Assertion 
 failure
 ----------------
 ...
Thanks! (It's a known issue though: https://github.com/tgehr/dmd/blob/static-foreach/test_staticforeach.d#L330.)
Actually, yours is a different case with the same outcome (f and idonotexist do not matter at all, the issue exists even for static foreach(j;0..0){}). All static foreach loops over empty (non-AliasSeq) aggregates failed that assertion. The reason was that CTFE can return a null literal from a function that returns T[], but the constant folder cannot actually evaluate null.length for some reason.
So here is the difference between null and []: null : () { Slice* s; s = null; return s; } [] : () { Slice* s; s = alloca(sizeof(*s)); s.base = null; s.length = 0; return s; } Therefore null.length => (cast(Slice*)null).length; which results in a segfault. and [].length => (cast(Slice*)somevalidSliceDiscriptor).length;
May 10 2017
parent reply Atila Neves <atila.neves gmail.com> writes:
On Wednesday, 10 May 2017 at 11:12:06 UTC, Stefan Koch wrote:
 On Wednesday, 10 May 2017 at 09:42:53 UTC, Timon Gehr wrote:
 [...]
So here is the difference between null and []: null : () { Slice* s; s = null; return s; } [] : () { Slice* s; s = alloca(sizeof(*s)); s.base = null; s.length = 0; return s; } Therefore null.length => (cast(Slice*)null).length; which results in a segfault. and [].length => (cast(Slice*)somevalidSliceDiscriptor).length;
That's not how "regular" D works though. Atila
May 10 2017
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 10 May 2017 at 13:11:46 UTC, Atila Neves wrote:
 On Wednesday, 10 May 2017 at 11:12:06 UTC, Stefan Koch wrote:
 null :

 () { Slice* s; s = null; return s; }

 [] :
 () { Slice* s; s = alloca(sizeof(*s)); s.base = null; s.length 
 = 0; return s; }

 Therefore

 null.length => (cast(Slice*)null).length; which results in a 
 segfault.

 and

 [].length => (cast(Slice*)somevalidSliceDiscriptor).length;
That's not how "regular" D works though. Atila
What do you mean ? Hmm this should be how it works .... They reason why assert([] == null) holds. is because base is implicitly alias thised. if you try assert([] is null), it should fail.
May 10 2017
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10.05.2017 15:18, Stefan Koch wrote:
 if you try assert([] is null), it should fail.
It doesn't. I have tried to make that point before, unsuccessfully. Empty arrays may or may not be null, but the empty array literal is always null.
May 10 2017
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 10 May 2017 at 14:13:09 UTC, Timon Gehr wrote:
 On 10.05.2017 15:18, Stefan Koch wrote:
 if you try assert([] is null), it should fail.
It doesn't. I have tried to make that point before, unsuccessfully. Empty arrays may or may not be null, but the empty array literal is always null.
cat t3.d ---- static assert([] is null); --- dmd t.d -c --- t3.d(1): Error: static assert ([] is null) is false ----
May 10 2017
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10.05.2017 16:21, Stefan Koch wrote:
 On Wednesday, 10 May 2017 at 14:13:09 UTC, Timon Gehr wrote:
 On 10.05.2017 15:18, Stefan Koch wrote:
 if you try assert([] is null), it should fail.
It doesn't. I have tried to make that point before, unsuccessfully. Empty arrays may or may not be null, but the empty array literal is always null.
cat t3.d ---- static assert([] is null); --- dmd t.d -c --- t3.d(1): Error: static assert ([] is null) is false ----
void main(){ import std.stdio; enum x = [] is null; auto y = [] is null; writeln(x," ",y); // "false true" }
May 10 2017
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 10 May 2017 at 18:41:30 UTC, Timon Gehr wrote:
 On 10.05.2017 16:21, Stefan Koch wrote:
 On Wednesday, 10 May 2017 at 14:13:09 UTC, Timon Gehr wrote:
 On 10.05.2017 15:18, Stefan Koch wrote:
 if you try assert([] is null), it should fail.
It doesn't. I have tried to make that point before, unsuccessfully. Empty arrays may or may not be null, but the empty array literal is always null.
cat t3.d ---- static assert([] is null); --- dmd t.d -c --- t3.d(1): Error: static assert ([] is null) is false ----
void main(){ import std.stdio; enum x = [] is null; auto y = [] is null; writeln(x," ",y); // "false true" }
Oh fudge. Another case where the ctfe-engine goes the right way; And the runtime version does not ... we should fix this one of these days.
May 10 2017
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/10/17 3:45 PM, Stefan Koch wrote:
 On Wednesday, 10 May 2017 at 18:41:30 UTC, Timon Gehr wrote:
 On 10.05.2017 16:21, Stefan Koch wrote:
 On Wednesday, 10 May 2017 at 14:13:09 UTC, Timon Gehr wrote:
 On 10.05.2017 15:18, Stefan Koch wrote:
 if you try assert([] is null), it should fail.
It doesn't. I have tried to make that point before, unsuccessfully. Empty arrays may or may not be null, but the empty array literal is always null.
cat t3.d ---- static assert([] is null); --- dmd t.d -c --- t3.d(1): Error: static assert ([] is null) is false ----
void main(){ import std.stdio; enum x = [] is null; auto y = [] is null; writeln(x," ",y); // "false true" }
Oh fudge. Another case where the ctfe-engine goes the right way; And the runtime version does not ... we should fix this one of these days.
[] lowers to a d runtime call (in lifetime, the function to allocate an array) with no elements. The function must return a valid array with 0 length. null is such an array. It's not an error at all, and should not be fixed or changed IMO. You almost never want to use 'is' on an array, as it compares just the pointer and length. Usually you want '=='. For instance, you would never do: [1, 2, 3] is [1, 2, 3] And expect any sane result. It might actually be true on some compiler that's clever enough! -Steve
May 11 2017