On 09/08/2011 03:02 PM, Timon Gehr wrote:
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
}

Better:

void generate(out this Foo[] self, int len){}



?

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

Reply via email to