Bug 2569 – static arrays in CTFE functions don't compile

Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P2
Component
dmd
Product
D
Version
D1 (retired)
Platform
x86
OS
Windows
Creation time
2009-01-08T04:33:00Z
Last change time
2014-03-01T00:36:26Z
Keywords
patch, rejects-valid
Assigned to
nobody
Creator
clugdbug

Attachments

IDFilenameSummaryContent-TypeSize
434patchCTFEassign.patchPatch against DMD1.046. Works for DMD2.031 as well.text/plain14352

Comments

Comment #0 by clugdbug — 2009-01-08T04:33:16Z
int foo() { int [3] a; return 0; } static x = foo(); --- bug.d(7): Error: cannot evaluate foo() at compile time Interestingly, in D2.023, the error message is displayed twice.
Comment #1 by clugdbug — 2009-07-21T00:18:03Z
This isn't complicated. The only reason it doesn't work is that BinExp::interpretAssignCommon in interpret.c doesn't deal with array assignment AT ALL. Adding a trivial hack like: if (e1->op == TOKslice) { return e2; } is enough to make most cases work. I'm working on a proper patch which will deal with array literals, etc.
Comment #2 by clugdbug — 2009-07-22T00:36:19Z
Turned out to be slightly more involved than I thought, there are a few special cases. I think I've captured them all. Extended test case: ------------------- struct Foo { int m; } int foo() { short [4] a = [2, 3, 4, 6]; double [27] b = 2.0; cfloat[2] c; auto d = [2, 3, 4, 5]; Foo[2] e = [Foo(3), Foo(2)]; // ushort [3] f = [1, 2]; // Uncomment to generate a compile-time error. d[2..6] = 4; a[1] = 7; return a[0]-3; } static assert(foo()==-1); PATCH: interpret.c, line 1464 in BinExp::interpretAssignCommon(), before checking for the other cases. ------------ // Assignment/initialization of static arrays if (e1->op == TOKslice && ((SliceExp *)e1)->e1->op==TOKvar) { SliceExp * sexp = (SliceExp *)e1; VarExp *ve = (VarExp *)(sexp->e1); VarDeclaration *v = ve->var->isVarDeclaration(); Type *t = v->type->toBasetype(); if (t->ty == Tsarray){ size_t dim = ((TypeSArray *)t)->dim->toInteger(); if (e2->op == TOKarrayliteral) { // Static array assignment from literal ArrayLiteralExp *ae = (ArrayLiteralExp *)e2; // Ensure length is the same if (ae->elements->dim != dim) { error("Array length mismatch"); return e; } v->value = ae; return ae; } if (t->nextOf()->ty == e2->type->ty) { // Static array block assignment Expressions *elements = new Expressions(); elements->setDim(dim); for (size_t i = 0; i < dim; i++) elements->data[i] = e2; ArrayLiteralExp *ae = new ArrayLiteralExp(0, elements); ae->type = v->type; v->value = ae; return e2; } } }
Comment #3 by clugdbug — 2009-07-23T04:56:55Z
The patch I posted was incomplete, so I'm withdrawing it. Done properly, it should support slicing assignment, and arrays initialized to void.
Comment #4 by DimitarRosenovKolev — 2009-07-23T07:41:12Z
(In reply to comment #3) > The patch I posted was incomplete, so I'm withdrawing it. Done properly, it > should support slicing assignment, and arrays initialized to void. This is a nasty little bugger. Any idea when the pAtch will be ready?
Comment #5 by clugdbug — 2009-07-24T00:01:29Z
(In reply to comment #4) > (In reply to comment #3) > > The patch I posted was incomplete, so I'm withdrawing it. Done properly, it > > should support slicing assignment, and arrays initialized to void. > > This is a nasty little bugger. Any idea when the patch will be ready? The patch is now complete for array literals, and also fixes bug#1948 and bug#3205. It also gives nicer error messages when it can't compile an assignment in CTFE. There are an unbelievable number of special cases. The only thing that's missing is that it doesn't deal with assignment from string literals. Which means that: char [5] s = "abc"; // fails char [6] t = ['a', 'b', 'c']; // ok Here's my test case. All the tests below are working on my patched DMD: struct S { int x; char y; } // Functions which should fail CTFE int badfoo(){ S[2] c; c[4].x=6; // array bounds error return 7; } int badglobal = 1; int badfoo2(){ S[] c; c[7].x=6; // uninitialized error return 7; } int badfoo3(){ S[2] c; c[badglobal].x=6; // global index error return 7; } int badfoo4(){ static S[2] c; c[0].x=6; // Cannot access static return 7; } int badfoo5(){ S[] c = void; c[0].x=6; // c is uninitialized, and not a static array. return 1; } int badfoo6() { S[] b = [S(7), S(15), S(56), S(12)]; b[-2..4] = S(17); // exceeding (negative) array bounds return 1; } int badfoo7() { S[] b = [S(7), S(15), S(56), S(12), S(67)]; b[1..4] = [S(17), S(4)]; // slice mismatch in dynamic array return 1; } int badfoo8() { S[] b; b[1..3] = [S(17), S(4)]; // slice assign to uninitialized dynamic array return 1; } template Compileable(int z) { bool OK;} static assert(!is(typeof(Compileable!(badfoo()).OK))); static assert(!is(typeof(Compileable!(badfoo2()).OK))); static assert(!is(typeof(Compileable!(badfoo3()).OK))); static assert(!is(typeof(Compileable!(badfoo4()).OK))); static assert(!is(typeof(Compileable!(badfoo5()).OK))); static assert(!is(typeof(Compileable!(badfoo6()).OK))); static assert(!is(typeof(Compileable!(badfoo7()).OK))); static assert(!is(typeof(Compileable!(badfoo8()).OK))); // Functions which should pass CTFE int goodfoo1() { int[8] w; // use static array in CTFE w[]=7; // full slice assign w[$-1]=538; // use of $ in index assignment assert(w[6]==7); return w[7]; } static assert(goodfoo1()==538); int goodfoo2() { S[4] w = S(101); // Block-initialize array of structs w[$-2].x = 917; // use $ in index member assignment w[$-2].y = 58; // this must not clobber the prev assignment return w[2].x; // check we got the correct one } static assert(goodfoo2()==917); int goodfoo3() { S[4] w = void; // uninitialized array of structs w[$-2].x = 217; // initialize one member return w[2].x; } static assert(goodfoo3()==217); int goodfoo4() { S[4] b = [S(7), S(15), S(56), S(12)]; // assign from array literal assert(b[3]==S(12)); return b[2].x-55; } static assert(goodfoo4()==1); int goodfoo5() { S[4] b = [S(7), S(15), S(56), S(12)]; b[0..2] = [S(2),S(6)]; // slice assignment from array literal assert(b[3]==S(12)); assert(b[1]==S(6)); return b[0].x; } static assert(goodfoo5()==2); static assert(goodfoo5()==2); // check for memory corruption int goodfoo6() { S[6] b = void; b[2..5] = [S(2),S(6), S(17)]; // slice assign to uninitialized var assert(b[4]==S(17)); return b[3].x; } static assert(goodfoo6()==6); int goodfoo7() { S[8] b = void; b[2..5] = S(217); // slice assign to uninitialized var assert(b[4]==S(217)); return b[3].x; } static assert(goodfoo7()==217); int goodfoo8() { S[] b = [S(7), S(15), S(56), S(12), S(67)]; b[2..4] = S(17); // dynamic array block slice assign assert(b[3]==S(17)); assert(b[4]==S(67)); return b[0].x; } static assert(goodfoo8()==7);
Comment #6 by DimitarRosenovKolev — 2009-07-24T01:50:38Z
Thanks great work. You got a beer from me.
Comment #7 by clugdbug — 2009-07-24T02:19:04Z
Created attachment 434 Patch against DMD1.046. Works for DMD2.031 as well.
Comment #8 by clugdbug — 2009-07-24T02:19:43Z
Here's the patch. Cheers!
Comment #9 by samukha — 2009-07-24T03:14:39Z
Thanks a lot!
Comment #10 by clugdbug — 2009-07-24T04:32:55Z
Note that with this patch in place, bug #1330 becomes a lot more visible. (Fixing that is, I believe, the way to fix the killer bug #1382. Fixing 1330 would probably help to fix bug #2386 as well. But it would involve fixing the compiler in multiple places, which is difficult).
Comment #11 by bugzilla — 2009-09-03T13:24:02Z
Fixed dmd 1.047 and 2.032