On 09/08/2011 11:56 AM, bearophile wrote:
At the moment I don't have further ideas on this.

Sorry, another solution I've used to solve that problem is to split the array 
arr of structs in two parallel arrays, one with just the mutable fields and one 
with just the immutable ones. But this increases the program complexity, makes 
the program a bit more bug-prone, and in your program all your contracts have 
to assert the two parallel arrays have the same length:


struct FooA {
     int x;
     // other mutable fields here
}
struct FooB {
     bool b;
     // other immutable fields here
}
FooA[] generateA() pure nothrow {
     auto arra = new FooA[10];
     foreach (i; 0 .. 10)
         arra[i].x = i;
     return arra;
}
immutable(FooB[]) generateB() pure nothrow {
     auto arrb = new FooB[10];
     arrb[3].b = true;
     return arrb;
}

void main() {
     auto a1 = generateA();
     auto a2 = generateB();
}

--------------------

In the end what's the difference between code like this:



struct Foo {
     immutable bool b;
}
Foo[] generate() pure nothrow {
     Foo[] arr;
     foreach (i; 0 .. 10) {
         if (i == 3)
             arr ~= Foo(true);
         else
             arr ~= Foo(false);
     }
     return arr;
}
void main() {
     auto a = generate();
}


And code like this?

struct Foo {
     immutable bool b;
}
Foo[] generate() pure nothrow {
     auto arr = new Foo[10];
     arr[3].b = true;
     return arr;
}
void main() {
     auto a = generate();
}


In the first case, by code construction the compiler has guarantees that you 
will not write each struct more than one time (because appending never appends 
two times on the same array slot), and you will not read array slots that are 
not yet initialized (because the array items yet to be appended can't be read 
in any way, they don't exist yet). For the compiler it's hard to understand 
that the second program has the same qualities (well, arr[3] gets initialized 
twice, but structs don't allow argument-less constructors, so I think the 
constructor is like a pure function, so what matters is just the latest value 
assigned to arr[3]).


I think the compiler just needs to be smart enough to understand that after the execution of code of the form:

T[] a;
foreach(i;x..y) {
    if(c1) a~=v1;
    else if(c2) a~=v2;
    ... // exactly one one-element-append per branch
}

the array a will have length y-x. Maybe value range propagation can help.

then it can be optimized not to use the runtime that heavily. This would be useful in general.

(alternatively, something like this could be allowed:

Foo[] generate() {
Foo[10] arr = void; // no way to create an uninitialized array on the heap =(
    foreach(i,ref x; arr){
        x = Foo(i == 3);
    }
    return arr.dup; // only one runtime call
})

That would be fine, because using the contents of uninitialized memory is disallowed anyways. (?)

What about

void generate(this Foo[] self, int len) this{ // maybe there is a better syntax
    // init self
}

?

This would just have the same restrictions as a class/struct constructor that initializes immutable fields.

Reply via email to