Re: Generating switch at Compile Time

2017-04-17 Thread Stefan Koch via Digitalmars-d-learn

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

2017-04-17 Thread ag0aep6g via Digitalmars-d-learn

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

2017-04-17 Thread Jesse Phillips via Digitalmars-d-learn

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

2017-04-13 Thread ag0aep6g via Digitalmars-d-learn

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

2017-04-13 Thread Jesse Phillips via Digitalmars-d-learn
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