Thread overview
Quantum Superposition Observed in DMD at Compile Time
Jul 01, 2019
Meta
Jul 01, 2019
Exil
Jul 01, 2019
Meta
Jul 01, 2019
John Colvin
Jul 02, 2019
Ataide
Jul 02, 2019
Jonathan M Davis
Jul 02, 2019
Ataide
Jul 02, 2019
Iain Buclaw
Jul 02, 2019
Meta
July 01, 2019
Jokes aside, this is very strange behaviour.

struct Test(T...)
{
    this(T ts)
    {
        foreach (i, t; ts)
        {
            int[ts.length] nums;
            if (i % 2 == 0)
            {
                nums[i / 2] = i;
            }
            else
            {
                nums[(i - 1) / 2] = i; // Error:array index 9223372036854775807 is out of bounds nums[0 .. 4]
            }
        }
    }
}

void main()
{
    auto t = Test!(int, string, double, bool)(0, "", 0, false);
}

Of course, the only possible way (i - 1) / 2 can be 9223372036854775807 is if i == 0 and thus i - 1 underflows. However, this should never be possible because the else branch should only be taken when i > 0.

After some testing, I realized that this is a very weird side-effect of constant folding. Regardless of whether the "else" branch will be taken at runtime, nums[(i - 1) / 2] is computed *at compile time* for each loop... but, it's not actually a loop. This is a static foreach (old-style, but this problem occurs with `static foreach` as well). The loop body is unrolled N times (where N == ts.length), and as expected, when I change the foreach to a regular for-loop, this problem goes away. Also changing the runtime if to static if fixes it.

This is very strange behaviour, but understandable after some investigation. What I don't quite know is whether there should be a bug report opened for this. It's not technically a bug, I don't think, but it is certainly unexpected and arguably should be fixed.
July 01, 2019
On Monday, 1 July 2019 at 01:42:36 UTC, Meta wrote:
> Jokes aside, this is very strange behaviour.
>
> struct Test(T...)
> {
>     this(T ts)
>     {
>         foreach (i, t; ts)
>         {
>             int[ts.length] nums;
>             if (i % 2 == 0)
>             {
>                 nums[i / 2] = i;
>             }
>             else
>             {
>                 nums[(i - 1) / 2] = i; // Error:array index 9223372036854775807 is out of bounds nums[0 .. 4]
>             }
>         }
>     }
> }
>
> void main()
> {
>     auto t = Test!(int, string, double, bool)(0, "", 0, false);
> }
>
> Of course, the only possible way (i - 1) / 2 can be 9223372036854775807 is if i == 0 and thus i - 1 underflows. However, this should never be possible because the else branch should only be taken when i > 0.
>
> After some testing, I realized that this is a very weird side-effect of constant folding. Regardless of whether the "else" branch will be taken at runtime, nums[(i - 1) / 2] is computed *at compile time* for each loop... but, it's not actually a loop. This is a static foreach (old-style, but this problem occurs with `static foreach` as well). The loop body is unrolled N times (where N == ts.length), and as expected, when I change the foreach to a regular for-loop, this problem goes away. Also changing the runtime if to static if fixes it.
>
> This is very strange behaviour, but understandable after some investigation. What I don't quite know is whether there should be a bug report opened for this. It's not technically a bug, I don't think, but it is certainly unexpected and arguably should be fixed.

If anything the foreach should be a regular loop. The only time CTFE should happen is with static foreach(). But that didn't exist until recently. Have no doubt this will break code, though they would just need to stick static infront of the loop to fix it? Possibly a deprecation? Doesn't make sense to have foreach() behave like static foreach() now that it exists.
July 01, 2019
On Monday, 1 July 2019 at 02:29:57 UTC, Exil wrote:
> If anything the foreach should be a regular loop. The only time CTFE should happen is with static foreach(). But that didn't exist until recently. Have no doubt this will break code, though they would just need to stick static infront of the loop to fix it? Possibly a deprecation? Doesn't make sense to have foreach() behave like static foreach() now that it exists.

I'm inclined to agree, except for the fact that changing it would be a metric ton of code. More likely the constant folding needs to be fixed, though I don't know if that's even possible.
July 01, 2019
On Monday, 1 July 2019 at 01:42:36 UTC, Meta wrote:
> Jokes aside, this is very strange behaviour.
>
> struct Test(T...)
> {
>     this(T ts)
>     {
>         foreach (i, t; ts)
>         {
>             int[ts.length] nums;
>             if (i % 2 == 0)
>             {
>                 nums[i / 2] = i;
>             }
>             else
>             {
>                 nums[(i - 1) / 2] = i; // Error:array index 9223372036854775807 is out of bounds nums[0 .. 4]
>             }
>         }
>     }
> }
>
> void main()
> {
>     auto t = Test!(int, string, double, bool)(0, "", 0, false);
> }
>
> Of course, the only possible way (i - 1) / 2 can be 9223372036854775807 is if i == 0 and thus i - 1 underflows. However, this should never be possible because the else branch should only be taken when i > 0.
>
> After some testing, I realized that this is a very weird side-effect of constant folding. Regardless of whether the "else" branch will be taken at runtime, nums[(i - 1) / 2] is computed *at compile time* for each loop... but, it's not actually a loop. This is a static foreach (old-style, but this problem occurs with `static foreach` as well). The loop body is unrolled N times (where N == ts.length), and as expected, when I change the foreach to a regular for-loop, this problem goes away. Also changing the runtime if to static if fixes it.
>
> This is very strange behaviour, but understandable after some investigation. What I don't quite know is whether there should be a bug report opened for this. It's not technically a bug, I don't think, but it is certainly unexpected and arguably should be fixed.

Pretty sure that's a compiler bug. Please report.

Here's a simpler example, which for extra buggy joy prints the error message twice!

void foo()
{
  size_t[1] nums;
  static foreach (i; 0 .. 1)
    if (false)
      auto a = nums[i - 1];
}

onlineapp.d(6): Error: array index 18446744073709551615 is out of bounds nums[0 .. 1]
onlineapp.d(6): Error: array index 18446744073709551615 is out of bounds nums[0 .. 1]
July 02, 2019
On Mon, 1 Jul 2019 at 03:45, Meta via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> Jokes aside, this is very strange behaviour.
>
> struct Test(T...)
> {
>      this(T ts)
>      {
>          foreach (i, t; ts)
>          {
>              int[ts.length] nums;
>              if (i % 2 == 0)
>              {
>                  nums[i / 2] = i;
>              }
>              else
>              {
>                  nums[(i - 1) / 2] = i; // Error:array index
> 9223372036854775807 is out of bounds nums[0 .. 4]
>              }
>          }
>      }
> }
>
> void main()
> {
>      auto t = Test!(int, string, double, bool)(0, "", 0, false);
> }
>
> Of course, the only possible way (i - 1) / 2 can be 9223372036854775807 is if i == 0 and thus i - 1 underflows. However, this should never be possible because the else branch should only be taken when i > 0.
>
> After some testing, I realized that this is a very weird side-effect of constant folding. Regardless of whether the "else" branch will be taken at runtime, nums[(i - 1) / 2] is computed *at compile time* for each loop... but, it's not actually a loop. This is a static foreach (old-style, but this problem occurs with `static foreach` as well). The loop body is unrolled N times (where N == ts.length), and as expected, when I change the foreach to a regular for-loop, this problem goes away. Also changing the runtime if to static if fixes it.
>
> This is very strange behaviour, but understandable after some investigation. What I don't quite know is whether there should be a bug report opened for this. It's not technically a bug, I don't think, but it is certainly unexpected and arguably should be fixed.

You could have just raised a bugzilla ticket.

-- 
Iain
July 02, 2019
On Tuesday, 2 July 2019 at 13:37:11 UTC, Iain Buclaw wrote:
> On Mon, 1 Jul 2019 at 03:45, Meta via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>>
>> Jokes aside, this is very strange behaviour.
>>
>> struct Test(T...)
>> {
>>      this(T ts)
>>      {
>>          foreach (i, t; ts)
>>          {
>>              int[ts.length] nums;
>>              if (i % 2 == 0)
>>              {
>>                  nums[i / 2] = i;
>>              }
>>              else
>>              {
>>                  nums[(i - 1) / 2] = i; // Error:array index
>> 9223372036854775807 is out of bounds nums[0 .. 4]
>>              }
>>          }
>>      }
>> }
>>
>> void main()
>> {
>>      auto t = Test!(int, string, double, bool)(0, "", 0, false);
>> }
>>
>> Of course, the only possible way (i - 1) / 2 can be 9223372036854775807 is if i == 0 and thus i - 1 underflows. However, this should never be possible because the else branch should only be taken when i > 0.
>>
>> After some testing, I realized that this is a very weird side-effect of constant folding. Regardless of whether the "else" branch will be taken at runtime, nums[(i - 1) / 2] is computed *at compile time* for each loop... but, it's not actually a loop. This is a static foreach (old-style, but this problem occurs with `static foreach` as well). The loop body is unrolled N times (where N == ts.length), and as expected, when I change the foreach to a regular for-loop, this problem goes away. Also changing the runtime if to static if fixes it.
>>
>> This is very strange behaviour, but understandable after some investigation. What I don't quite know is whether there should be a bug report opened for this. It's not technically a bug, I don't think, but it is certainly unexpected and arguably should be fixed.
>
> You could have just raised a bugzilla ticket.

I didn't know if this was a bug or not due to the mix of compile-time and runtime stuff going on in my example.
July 02, 2019
On Monday, 1 July 2019 at 05:57:25 UTC, John Colvin wrote:
> Here's a simpler example...

Sorry to disturb here, but I tried to post over Learn but got a Forum error.

Could you tell me why I am getting error on this code:

//DMD64 D Compiler 2.072.2
import std.stdio;
void main(){
  static foreach(i;[1,2,3]){}
}

Error(s):
source_file.d(4): Error: basic type expected, not foreach
source_file.d(4): Error: no identifier for declarator _error_

Thanks.
July 02, 2019
On Tuesday, July 2, 2019 8:44:38 AM MDT Ataide via Digitalmars-d wrote:
> On Monday, 1 July 2019 at 05:57:25 UTC, John Colvin wrote:
> > Here's a simpler example...
>
> Sorry to disturb here, but I tried to post over Learn but got a Forum error.
>
> Could you tell me why I am getting error on this code:
>
> //DMD64 D Compiler 2.072.2
> import std.stdio;
> void main(){
>    static foreach(i;[1,2,3]){}
> }
>
> Error(s):
> source_file.d(4): Error: basic type expected, not foreach
> source_file.d(4): Error: no identifier for declarator _error_
>
> Thanks.

Well, static foreach wasn't implemented until 2.076, so putting static in front of foreach like that would be illegal with 2.072.2. The fact that _error_ shows up in the message like that indicates a bug with how dmd dealt with the error (probably an issue with lowering), but the code isn't legal prior to 2.076. At that point, you would have had to iterate over an AliasSeq to force the foreach to be done at compile time.

- Jonathan M Davis



July 02, 2019
On Tuesday, 2 July 2019 at 17:10:34 UTC, Jonathan M Davis wrote:
> Well, static foreach wasn't implemented until 2.076, so putting static in front of foreach like that would be illegal with 2.072.2.

In fact I had a hunch about this, but on the other hand it wouldn't be nice if docs show the minimum version required for each feature, like for example C# does with minimum frameworks?

I just remembered that in this case I even tried 2.074, and looking the docs there wasn't any mention about the minimum version.

Ataide.