Re: Generating switch at Compile Time
On Thursday, 13 April 2017 at 21:06:52 UTC, Jesse Phillips wrote: I realize that this is likely really pushing the compile time generation but a recent change to the switch statement[1] is surfacing because of this usage. uninitswitch2.d(13): Deprecation: 'switch' skips declaration of variable uninits witch2.main.li at uninitswitch2.d(14) - import std.traits; import std.typecons; import std.meta; private static immutable list = AliasSeq!( tuple("a", "q"), tuple("b", "r"), ); void main() { import std.stdio; string search; switch(search) { --->foreach(li; list) { // li initialization is skipped mixin("case li[0]:"); mixin("writeln(li[1]);"); return; } default: break; } // Works mixin(genSwitch("search")); } - I realize I can build out the entire switch and mix it in: - string genSwitch(string search) { auto ans = "switch(" ~ search ~ ") {\n"; foreach(li; list) { ans ~= "case \"" ~ li[0] ~ "\":\n"; ans ~= "writeln(\"" ~ li[1] ~ "\");\n"; ans ~= "return;\n"; } ans ~= "default:\n"; ans ~= "break;\n"; ans ~= "}"; return ans; } - But I'm just wondering if the new initialization check should not be triggered from this utilization. - // Unrolled based on // https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time description version(none) void func2243(Tuple param0, Tuple param1) { { { case param0[0]: writeln(param0[1]); return; } { case param1[0]: writeln(param1[1]); return; } } } - Thoughts? 1. https://issues.dlang.org/show_bug.cgi?id=14532 This is what is generated by your example: switch (search) { unrolled { { // does not actually open a new scope immutable immutable(Tuple!(string, string)) li = __list_field_0; case "a": { } writeln(li.__expand_field_1); return 0; } { // same here we do not actually open a new scope. immutable immutable(Tuple!(string, string)) li = __list_field_1; case "b": { } writeln(li.__expand_field_1); return 0; } } default: { break; } } return 0; it should be clear to sww why li's initialisation is referred to as skipped.
Re: Generating switch at Compile Time
On 04/17/2017 09:29 PM, Jesse Phillips wrote: On Thursday, 13 April 2017 at 21:33:28 UTC, ag0aep6g wrote: [...] By the way, in my opinion, `case li[0]:` shouldn't compile with the static immutable `list`. `list` and `li[0]` are dynamic values. The compiler only attempts (and succeeds) to evaluate them at compile time because they're typed as immutable. The way I see it, that only makes things more confusing. This is very interesting. I wonder if the compiler is still unrolling the loop at compile time since it functions as expected; It certainly needs that deprecation though. It's really weird. I thought the loop would just not be unrolled at all. However, both `case`s seem to be generated as expected. So it behaves like a static foreach in that regard. But when you use `li` as a dynamic value (e.g. `writeln(li[1])`), it's suddenly empty. Seems that dmd can't decide what to do, so it does a little bit of both. Maybe I was wrong, and the loop in your code is a static foreach, but at some point there's a bug that makes dmd think it's dealing with run-time values. The behavior is also completely inconsistent. With ints, the program compiles and the assert passes: alias AliasSeq(stuff ...) = stuff; immutable list = AliasSeq!(1, 2); void main() { switch(1) { foreach(li; list) { case li: enum e = li; assert(e == li); return; } default: } } With strings, the program doesn't compile: alias AliasSeq(stuff ...) = stuff; immutable list = AliasSeq!("foo", "bar"); void main() { switch("foo") { foreach(li; list) { case li: enum e = li; assert(e == li); return; /* "Error: case must be a string or an integral constant, not li" */ } default: } } And with structs (your case), it compiles with a deprecation warning and behaves schizophrenically: alias AliasSeq(stuff ...) = stuff; struct S { int x; } immutable list = AliasSeq!(S(1), S(2)); void main() { switch(1) /* Deprecation: 'switch' skips declaration of variable */ { foreach(li; list) { case li.x: enum e = li; assert(e == li); return; /* The assert fails. */ } default: } } In my opinion, they should all simply be rejected. But I have no idea what's intended by the compiler writers. It's a mess. With enum/alias, they all compile and work as expected, of course.
Re: Generating switch at Compile Time
On Thursday, 13 April 2017 at 21:33:28 UTC, ag0aep6g wrote: That's not a static foreach. It's a normal run-time foreach. The switch jumps into the loop body without the loop head ever executing. The compiler is correct when it says that initialization of li is being skipped. Good good. I did miss that as I was trying different things. Make `list` an enum or alias instead. Then the foreach is unrolled at compile time, you don't get a deprecation message, and it works correctly. Yes it did. By the way, in my opinion, `case li[0]:` shouldn't compile with the static immutable `list`. `list` and `li[0]` are dynamic values. The compiler only attempts (and succeeds) to evaluate them at compile time because they're typed as immutable. The way I see it, that only makes things more confusing. This is very interesting. I wonder if the compiler is still unrolling the loop at compile time since it functions as expected; It certainly needs that deprecation though.
Re: Generating switch at Compile Time
On 04/13/2017 11:06 PM, Jesse Phillips wrote: - [...] private static immutable list = AliasSeq!( tuple("a", "q"), tuple("b", "r"), ); [...] switch(search) { --->foreach(li; list) { // li initialization is skipped mixin("case li[0]:"); mixin("writeln(li[1]);"); return; } default: break; } [...] } - Thoughts? That's not a static foreach. It's a normal run-time foreach. The switch jumps into the loop body without the loop head ever executing. The compiler is correct when it says that initialization of li is being skipped. Make `list` an enum or alias instead. Then the foreach is unrolled at compile time, you don't get a deprecation message, and it works correctly. By the way, in my opinion, `case li[0]:` shouldn't compile with the static immutable `list`. `list` and `li[0]` are dynamic values. The compiler only attempts (and succeeds) to evaluate them at compile time because they're typed as immutable. The way I see it, that only makes things more confusing.
Generating switch at Compile Time
I realize that this is likely really pushing the compile time generation but a recent change to the switch statement[1] is surfacing because of this usage. uninitswitch2.d(13): Deprecation: 'switch' skips declaration of variable uninits witch2.main.li at uninitswitch2.d(14) - import std.traits; import std.typecons; import std.meta; private static immutable list = AliasSeq!( tuple("a", "q"), tuple("b", "r"), ); void main() { import std.stdio; string search; switch(search) { --->foreach(li; list) { // li initialization is skipped mixin("case li[0]:"); mixin("writeln(li[1]);"); return; } default: break; } // Works mixin(genSwitch("search")); } - I realize I can build out the entire switch and mix it in: - string genSwitch(string search) { auto ans = "switch(" ~ search ~ ") {\n"; foreach(li; list) { ans ~= "case \"" ~ li[0] ~ "\":\n"; ans ~= "writeln(\"" ~ li[1] ~ "\");\n"; ans ~= "return;\n"; } ans ~= "default:\n"; ans ~= "break;\n"; ans ~= "}"; return ans; } - But I'm just wondering if the new initialization check should not be triggered from this utilization. - // Unrolled based on // https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time description version(none) void func2243(Tuple param0, Tuple param1) { { { case param0[0]: writeln(param0[1]); return; } { case param1[0]: writeln(param1[1]); return; } } } - Thoughts? 1. https://issues.dlang.org/show_bug.cgi?id=14532