I have a function with a switch, with an inner foreach to generate cases based on a template parameters .tupleof. It still works in 2.080.0 and even with "dmd-nightly" on run.dlang.io, but not with dmd from git as of 180527.
---
struct Foo
{
string abc, def;
}
void applyConfiguration(Thing)(ref Thing thing)
{
switch ("asdf")
{
foreach (immutable n, ref member; thing.tupleof)
{
enum memberstring = __traits(identifier, Thing.tupleof[n]);
case memberstring:
// ...
break;
}
default:
break;
}
}
void main()
{
Foo foo;
applyConfiguration(foo);
}
---
> switch.d(8): Error: `switch` skips declaration of variable `switch.applyConfiguration!(Foo).applyConfiguration.member` at switch.d(11)
> switch.d(27): Error: template instance `switch.applyConfiguration!(Foo)` error instantiating
The offending commit is eabc6a62b1d2f5924637f1e61464b9a975341dd4, "fix Issue 18858 - switch 'skips declaration' test only checks last declaration".
Is this a regression or was my code always broken?
Comment #1 by ag0aep6g — 2018-05-27T19:37:45Z
(In reply to JR from comment #0)
> Is this a regression or was my code always broken?
As far as I see, the error is good, and your code should not be allowed. So I'm closing this as invalid. But please feel free to reopen if you disagree with my reasoning.
Note that the compiler complains about the `member` variable, not `memberstring`. You're not using `member` in your code, so you don't see that it's broken, but it is.
For example, you can try printing `member` in the case statement:
----
struct Foo
{
string abc, def;
}
void main()
{
Foo foo = Foo("hello", "world");
switch ("abc")
{
foreach (immutable n, ref member; foo.tupleof)
{
enum memberstring = __traits(identifier, Foo.tupleof[n]);
case memberstring:
import std.stdio;
writeln(member); /* prints garbage and/or crashes */
break;
}
default:
break;
}
}
----
You can use `static foreach` to get a compile-time loop without declaring a broken `member` variable:
----
struct Foo
{
string abc, def;
}
void main()
{
Foo foo = Foo("hello", "world");
sw: switch ("abc")
{
static foreach (immutable n; 0 .. foo.tupleof.length)
{{
enum memberstring = __traits(identifier, Foo.tupleof[n]);
case memberstring:
import std.stdio;
writeln(foo.tupleof[n]); /* Prints "hello". */
break sw;
}}
default:
break;
}
}
----
Comment #2 by zorael — 2018-05-27T20:59:33Z
I see. The code was naturally heavily reduced.
I have up until now been using the foreach without any issues, though mostly indexing the .tupleof directly to access the underlying symbol rather than using the member variable. I only use member once with std.traits.isType.
https://github.com/zorael/kameloso/blob/f4617a5e5c796fcc9797e1f556b0861f44930f40/source/kameloso/config.d#L519
Slightly less reduced:
---
thingloop:
foreach (immutable i, thing; things)
{
switch (hits["entry"])
{
foreach (immutable n, ref member; things[i].tupleof)
{
static if (!isType!member &&
!hasUDA!(Things[i].tupleof[n], Unconfigurable))
{
enum memberstring = __traits(identifier,
Things[i].tupleof[n]);
case memberstring:
things[i].setMemberByName(hits["entry"],
hits["value"]);
continue thingloop;
}
}
default:
// Unknown setting in known section
invalidEntries[section] ~= hits["entry"].length ? hits["entry"] : line;
break;
}
}
---
Thanks, I will try your other approaches.