DIP69 - Implement scope for escape proof references
via Digitalmars-d
digitalmars-d at puremagic.com
Fri Dec 5 08:48:43 PST 2014
There are limitations this proposal has in comparison to my
original one. These limitations might of course be harmless and
play no role in practice, but on the other hand, they may, so I
think it's good to list them here.
Additionally I have to agree with Steven Schveighoffer: This DIP
is very complicated to understand. It's not obvious how the
various parts play together, and why/to which degree it "works",
and which are the limitations. I don't think that's only because
my brain is already locked on my proposal...
1) Escape detection is limited to `ref`.
T* evil;
ref T func(scope ref T t, ref T u) @safe {
return t; // Error: escaping scope ref t
return u; // ok
evil = &u; // Error: escaping reference
}
vs.
T[] evil;
T[] func(scope T[] t, T[] u) @safe {
return t; // Error: cannot return scope
return u; // ok
evil = u; // !!! not good
}
As can be seen, `ref T u` is protected from escaping (apart from
returning it), while `T[] u` in the second example is not.
There's no general way to express that `u` can only be returned
from the function, but will not be retained otherwise by storing
it in a global variable. Adding `pure` can express this in many
cases, but is, of course, not always possible.
Another workaround is passing the parameters as `ref`, but this
would introduce an additional indirection and has different
semantics (e.g. when the lengths of the slices are modified).
2) `scope ref` return values cannot be stored.
scope ref int foo();
void bar(scope ref int a);
foo().bar(); // allowed
scope tmp = foo(); // not allowed
tmp.bar();
Another example:
struct Container(T) {
scope ref T opIndex(size_t index);
}
void bar(scope ref int a);
Container c;
bar(c[42]); // ok
scope ref tmp = c[42]; // nope
Both cases should be fine theoretically; the "real" owner lives
longer than `tmp`. Unfortunately the compiler doesn't know about
this.
Both restrictions 1) and 2) are because there are no explicit
lifetime/owner designations (the scope!identifier thingy in my
proposal).
3) `scope` cannot be used for value types.
I can think of a few use cases for scoped value types (RC and
file descriptors), but they might only be marginal.
4) No overloading on `scope`.
This is at least partially a consequence of `scope` inference. I
think overloading can be made to work in the presence of
inference, but I haven't thought it through.
5) `scope` is a storage class.
Manu complained about `ref` being a storage class. If I
understand him right, one reason is that we have a large toolkit
for dealing with type modifiers, but almost nothing for storage
classes. I have to agree with him there. But I haven't understood
his point fully, maybe he himself can post more about his
problems with this?
6) There seem to be problems with chaining.
scope ref int foo();
scope ref int bar1(ref int a) {
return a;
}
scope ref int bar2(scope ref int a) {
return a;
}
ref int bar3(ref int a) {
return a;
}
ref int bar4(scope ref int a) {
return a;
}
void baz(scope ref int a);
Which of the following calls would work?
foo().bar1().baz();
foo().bar2().baz();
foo().bar3().baz();
foo().bar4().baz();
I'm not sure I understand this fully yet, but it could be that
none of them work...
More information about the Digitalmars-d
mailing list