Comment #0 by johannes.loher — 2016-01-09T23:48:02Z
Created attachment 1573
code example
When compiling the attached code with DMD and the running it, core.exception.SwitchError is raised, even though all cases are covered. The conditions for this to happen seem quite random to me: Small changes in the setup that should not have any effect on the outcome actually change whether an exception is raised or not. Compiling with -g prevents an exception from being raised (i.e. it works as it should). More examples are covered in my code example.
Binaries created by gdc and ldc behave as expected and do not raise an exception.
Comment #1 by johannes.loher — 2016-06-13T14:36:37Z
Created attachment 1600
new code example
Comment #2 by johannes.loher — 2016-08-08T15:04:46Z
Everything seems to work fine when running the program from within gdb. It also does not seem to be a problem of final switch, but switch in general. I tried it with a usual switch clause, in that case It always goes to default:
Comment #3 by dfj1esp02 — 2016-08-09T14:09:08Z
Does optimization flag -O affect the behavior?
Comment #4 by ag0aep6g — 2016-08-09T15:02:03Z
Reduced a bit more:
----
struct S
{
int a = 0;
int b = 1;
}
int f1(int a)
{
switch (a)
{
case 0: return 10;
case 1: return 20;
case 2: return 30;
case 3: return 40;
default: return 99;
}
}
int f2(S s)
{
return f1(s.a);
}
void main()
{
S s;
assert(f1(s.a) == 10); /* passes */
assert(f2(s) == 10); /* fails */
}
----
(In reply to Sobirari Muhomori from comment #3)
> Does optimization flag -O affect the behavior?
No. -inline has an effect, presumably because f2 is inlined.
Comment #5 by johannes.loher — 2016-10-15T13:34:54Z
In the reduced example, the bug also appears when running in gdb.
Comment #6 by ag0aep6g — 2017-04-24T16:26:41Z
*** Issue 17347 has been marked as a duplicate of this issue. ***
Comment #7 by alexander.breckel — 2017-04-25T09:38:41Z
Further reduced:
----
struct S
{
int a = 0;
int b = 1;
}
int f1(S s)
{
switch (s.a)
{
case 0: return 10;
case 1: return 20;
case 2: return 30;
case 3: return 40;
default: return 99;
}
}
void main()
{
S s;
assert(f1(s) == 10); /* fails */
}
The generates assembly for f1 is this:
----
00000000004271a8 <_D3app2f1FS3app1SZi>:
4271a8: 55 push %rbp
4271a9: 48 8b ec mov %rsp,%rbp
4271ac: 48 83 ec 10 sub $0x10,%rsp
4271b0: 48 89 7d f8 mov %rdi,-0x8(%rbp)
4271b4: 48 83 ff 03 cmp $0x3,%rdi
4271b8: 77 2d ja 4271e7 <_D3app2f1FS3app1SZi+0x3f>
4271ba: 48 8d 05 a7 3f 02 00 lea 0x23fa7(%rip),%rax # 44b168 <_D3app1S6__initZ+0x8>
4271c1: 48 63 0c b8 movslq (%rax,%rdi,4),%rcx
4271c5: 48 8d 04 01 lea (%rcx,%rax,1),%rax
4271c9: ff e0 jmpq *%rax
4271cb: b8 0a 00 00 00 mov $0xa,%eax
4271d0: c9 leaveq
4271d1: c3 retq
4271d2: b8 14 00 00 00 mov $0x14,%eax
4271d7: c9 leaveq
4271d8: c3 retq
4271d9: b8 1e 00 00 00 mov $0x1e,%eax
4271de: c9 leaveq
4271df: c3 retq
4271e0: b8 28 00 00 00 mov $0x28,%eax
4271e5: c9 leaveq
4271e6: c3 retq
4271e7: b8 63 00 00 00 mov $0x63,%eax
4271ec: c9 leaveq
4271ed: c3 retq
...
Dmd packs the whole struct into a single register (%rdi). Since the switch statement contains consecutive cases, Dmd rightfully tries to skip as many of them as possible by doing a single comparison (cmp $0x3,%rdi). However, this comparison is performed on the whole register, which also contains the second field of the Struct.
Comment #8 by johannes.loher — 2017-07-01T10:47:29Z
I tracked this down a bit. It worked fine till 2.063.2 (including). From 2.064 onward it sarts to segfault. Finally, from 2.068 onward, the assert error starts hppening.
Comment #9 by ag0aep6g — 2017-07-01T11:01:57Z
(In reply to Johannes Loher from comment #8)
> I tracked this down a bit. It worked fine till 2.063.2 (including). From
> 2.064 onward it sarts to segfault. Finally, from 2.068 onward, the assert
> error starts hppening.
That makes it a regression.
Comment #10 by dlang-bugzilla — 2017-07-01T11:32:51Z
I couldn't find the point in the source history at which it started bisecting, because when building DMD from source, the test program bisects as far back as I can get source builds to work (2011), so it's likely a latent bug which became exposed by some change in 2.064. However, I did find that the assert replaced the segfault in https://github.com/dlang/dmd/pull/4666.