This looks like the best proposal so far. Many useful ideas, and at the first glance it looks like a big step forward. I also really like how the useful stack allocation behavior for closures and new'ed values is kept alive.

What would be nice to add is a behavior specification for 'scope' member variables (lifetime considered equal or slightly shorter than parent object lifetime). For example the `RefCountedSlice.payload` and `count` fields could be annotated with 'scope' to let the compiler actually guarantee that they won't be accessible in a way that conflicts with their lifetime (i.e. the 'scope' return of 'opIndex' would actually be enforced).

That will just leave one hole in conjunction with the @trusted destructor, which is (presumably) not easy to fix without much larger changes to the type system, as well as to how container types are built. It is still vulnerable to artificial shortening of the elements' lifetime, e.g. by using opAssign() or destroy():

@safe {
    RefCountedSlice!int s = ...;
    scope int* el;
    el = &s[0];
    s = RefCountedSlice.init;
    *el = 12; // oops
}

A similar issue affects the library implementation of isolated memory that I did a while ago:

@safe {
    class C { int* x; }

    // c is guaranteed to be only reachable through this variable
    Isolated!C c = makeIsolated!C();

    // c.x is a @property that returns a specially wrapped reference to
    // the actual C.x field - with this DIP this is similar to a 'scope'
    // return, but acts transitively
    Scoped!(int*) x = c.x;

    // one of the benefits of Isolated!T is that it allows @safe
    // conversion to immutable:
    immutable(C) ci = c.freeze();
    // c gets cleared by freeze() to disallow any further modifications

    // but, oops, x is still there and can be used to modify the now
    // immutable contents of ci.x
    *x = 12;
}

Disallowing the assignment of scope return references to local scope references (either by default, or using some form of additional inference/annotation) would solve this particular issue, but not the issue in general (the assignment/destruction could for example happen in a nested function call).

Reply via email to