www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Quantum Superposition Observed in DMD at Compile Time

reply Meta <jared771 gmail.com> writes:
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.
Jun 30 2019
next sibling parent reply Exil <Exil gmall.com> writes:
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.
Jun 30 2019
parent Meta <jared771 gmail.com> writes:
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.
Jun 30 2019
prev sibling next sibling parent reply John Colvin <john.loughran.colvin gmail.com> writes:
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]
Jun 30 2019
parent reply Ataide <ataide gmail.com> writes:
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.
Jul 02 2019
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Jul 02 2019
parent Ataide <ataide gmail.com> writes:
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 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.
Jul 02 2019
prev sibling parent reply Iain Buclaw <ibuclaw gdcproject.org> writes:
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
Jul 02 2019
parent Meta <jared771 gmail.com> writes:
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.
Jul 02 2019