Bug 3835 – ref foreach does not work in CTFE

Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
Other
OS
Windows
Creation time
2010-02-18T13:06:00Z
Last change time
2015-06-09T01:27:38Z
Keywords
rejects-valid, wrong-code
Assigned to
nobody
Creator
bearophile_hugs

Comments

Comment #0 by bearophile_hugs — 2010-02-18T13:06:33Z
This code (that uses CTFE to do something that can be useful, it initializes an array with values compiled at compile time) compiles and works correctly, but I think it's too much fragile: if you change the for loop a little, or other details in genFactorials(), the program stops compiling: int factorial(int n) { if (n == 0) return 1; return n * factorial(n - 1); } int[] genFactorials(int n) { int[] result; for (; n >= 0; --n) result = factorial(n) ~ result; return result; } const int N = 13; static const auto factorials = cast(int[N])genFactorials(N - 1); void main() {} If you need them, I can show many variants of this program that don't compile/work. I think to be useful in real programs CTFE has to be less fragile than this.
Comment #1 by clugdbug — 2010-02-18T21:14:53Z
(In reply to comment #0) > This code (that uses CTFE to do something that can be useful, it initializes an > array with values compiled at compile time) compiles and works correctly, but I > think it's too much fragile: if you change the for loop a little, or other > details in genFactorials(), the program stops compiling: Please put in an example that it wouldn't compile!
Comment #2 by bearophile_hugs — 2010-02-19T04:24:33Z
> Please put in an example that it wouldn't compile! OK. This two versions of genFactorials print the wrong results: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 import std.stdio: writeln; int factorial(int n) { if (n == 0) return 1; return n * factorial(n - 1); } int[] genFactorialsA(int n) { int[] result = new int[n + 1]; foreach (i, ref el; result) el = factorial(i); return result; } int[N] genFactorialsB(int N)() { int[N] result; foreach (i, ref el; result) el = factorial(i); return result; } enum int N = 13; static enum auto factorials1 = cast(int[N])genFactorialsA(N - 1); enum auto factorials2 = genFactorialsB!13; void main() { writeln(factorials1); writeln(factorials2); } This version doesn't compile, the compiler raises: test7b.d(7): Error: pure function 'factorial' cannot call impure function 'factorial' test7b.d(12): Error: pure function 'genFactorials' cannot call impure function 'factorial' test7b.d(17): Error: cannot evaluate genFactorials(12) at compile time test7b.d(17): Error: cannot evaluate genFactorials(12) at compile time import std.stdio: writeln; pure int[] genFactorials(int n) { pure static int factorial(int n) { if (n == 0) return 1; return n * factorial(n - 1); } int[] result = new int[n + 1]; foreach (i; 0 .. n+1) result[i] = factorial(i); return result; } enum int N = 13; static enum auto factorials = cast(int[N])genFactorials(N - 1); void main() { writeln(factorials); }
Comment #3 by kovrov+puremagic — 2010-03-01T15:09:52Z
Looks like foreach do not work in CTFE. Here is example with two equivalent functions, one uses 'for', other 'foreach' loops: uint[256] crc32_table_for() { uint[256] table; for (uint i = 0; i < 256; ++i) { uint crc = i; for (int j = 0; j < 8; j++) crc = crc & 1 ? (crc >> 1) ^ 0xEDB88320 : crc >> 1; table[i] = crc; } return table; } uint[256] crc32_table_foreach() { uint[256] table; foreach (n, ref i; table) { uint crc = n; foreach (j; 0 .. 8) crc = crc & 1 ? (crc >> 1) ^ 0xEDB88320 : crc >> 1; i = crc; } return table; } immutable crc32_table1 = crc32_table_for(); immutable crc32_table2 = crc32_table_foreach(); import std.stdio; void main() { writefln("%s", crc32_table1); writefln("%s", crc32_table2); assert (crc32_table1 == crc32_table2); }
Comment #4 by bearophile_hugs — 2010-03-01T15:56:48Z
I think foreach(x; items) works in CTFE, but foreach(ref x; items) doesn't work well.
Comment #5 by bearophile_hugs — 2010-03-07T10:27:14Z
The first two cases are already present in bug 2411. I don't know about the third.
Comment #6 by clugdbug — 2010-03-09T04:32:40Z
I created bug 3912 for the third bug mentioned in the comments, which has nothing to do with CTFE. Maybe this bug can now be marked as a duplicate of bug 2411?
Comment #7 by bearophile_hugs — 2010-03-09T04:45:06Z
You can mark this as duplicate. But when an improvement is created, it's necessary to try it with *all* duplicated examples too, because you must be sure they are really problems with the same cause.
Comment #8 by clugdbug — 2010-03-10T11:24:04Z
Bug 2411 is NOT the same as this. (2411 does not involve CTFE). Changing title.
Comment #9 by bearophile_hugs — 2010-04-10T16:19:46Z
In dmd 2.043 my second tests cases works: import std.stdio: writeln; pure int[] genFactorials(int n) { pure static int factorial(int n) { if (n == 0) return 1; return n * factorial(n - 1); } int[] result = new int[n + 1]; foreach (i; 0 .. n+1) result[i] = factorial(i); return result; } enum int N = 13; static enum auto factorials = cast(int[N])genFactorials(N - 1); void main() { writeln(factorials); } The first test case doesn't work yet.
Comment #10 by bearophile_hugs — 2010-04-10T16:27:42Z
A reduced test case: int foo() { int[1] arr; foreach (ref el; arr) el = 10; return arr[0]; } enum int r = foo(); void main() { assert(r == 10); }
Comment #11 by code — 2011-02-06T14:59:06Z
This bug is worse than it might seem from reading the »rejects-valid« keyword – DMD 2.051 happily executes ref foreach during compile-time, but generates wrong results silently. Should we add a »wrong-ctfe« tag or something like that?
Comment #12 by clugdbug — 2011-02-07T00:57:25Z
(In reply to comment #11) > This bug is worse than it might seem from reading the »rejects-valid« keyword – > DMD 2.051 happily executes ref foreach during compile-time, but generates wrong > results silently. > > Should we add a »wrong-ctfe« tag or something like that? No, incorrect CTFE should just be marked as wrong-code. Yup, this one's important.
Comment #13 by clugdbug — 2011-04-08T21:17:18Z