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.