scope escaping

Meta jared771 at gmail.com
Thu Feb 6 14:41:33 PST 2014


On Thursday, 6 February 2014 at 15:53:01 UTC, Adam D. Ruppe wrote:
> Sorry, my lines got mangled, let me try pasting it again.
>
>
> Making scope the default
> =======================
>
>
> There's five points to discuss:
>
> 1) All variables are assumed to be marked with scope implicitly
>
> 2) The exception is structs with a special annotation which 
> marks that they encapsulate a resource. An encapsulated 
> resource explicitly marked scope at the usage site is STILL 
> scope, but it will not implicitly inherit the scopiness of the 
> member reference/
>
> @encapsulated_resource
> struct RefCounted(T) {
>     T t; // the scopiness of this would not propagated to
>          // refcounted itself
> }
>
> This lets us write structs to manage raw pointers (etc.) as an 
> escape from the rules. Note you may also write 
> @encaspulated_resource struct Borrowed(T){} as an escape from 
> the rules. Using this would of course be at your own risk, 
> analogous to @trusted code.
>
> 3) Built-in allocations return GC!T instead of T. GC!T's 
> definition is:
>
> @encapsulated_resource
> struct GC(T) {
>     private T _managed_payload;
>     /* @force_inline */
>     /* implicit scope return value */
>     @safe nothrow inout(T) borrow() { return _managed_payload; }
>     alias borrow this;
> }
>
> NOTE: if inout(T) there doesn't work for const correctness, we 
> need to fix const on wrapped types; an orthogonal issue.
>
> If you don't care about ownership, the alias this gives you a 
> naked borrowed reference whenever needed. If you do care about 
> ownership:
>
> auto foo = new Foo();
> static assert(is(typeof(foo) == GC!Foo));
>
> letting you store it with confidence without additional steps 
> or assumptions.
>
> When passing to a template, if you want to explicitly borrow 
> it, you might write borrow. Otherwise, IFTI will see the whole 
> GC!T type.  This is important if we want to write owned 
> identity templates.
>
> If an argument is scope, ownership is irrelevant. We might 
> strip it off but I don't think that's necessary... might help 
> avoid template bloat though.
>
> 4) All other types remain the same. Yes, typeof(this) == T, 
> NEVER GC!T.  Again, remember the rule of thumb: would this work 
> with as static stack buffer?
>
>    class Foo { Foo getMe() { return this; } }
>    ubyte[__traits(classInstanceSize, Foo)] buffer;
>    Foo f = emplace!Foo(buffer); // ok so far, f is scope
>    GC!Foo gc = f.getMe(); // obviously wrong, f is not GC
>
>    The object does not control its own allocation, so it does 
> not own its own memory. Thus, `this` is *always* borrowed.
>
>    Does this work if building a tree:
>
>    class Tree { Tree[] children; Tree addChild(Tree t) {
> children ~= t; } }
>
>    addChild there would *not* compile, since it escapes the t 
> into the object's scope. Tree would need to know ownership: 
> make children and addChild take GC!Tree instead, for example, 
> then it will work.
>
>    What if addChild wants to set t.parent = this; ? That 
> wouldn't be possible (without using a trust-me borrowed!T 
> wrapper)... and while this would break some of my code... I say 
> unto you, such code was already broken, because the parent 
> might be emplaced on a stack buffer!
>
>    GC!Tree child = new Tree();
>    {
>        ubyte[...] stack;
>        Owned!Tree parent = emplace!Tree(stack[]);
>        parent.addChild(child);
>    }
>    child.parent; // bug city
>
>
>    Instead, addChild should request its own ownership.
>
>    Tree addChild(GC!Tree child, GC!Tree _this) {
>        children ~= child;
>        child.parent = _this;
>    }
>
>
>    Then, the buggy above scenario does not compile, while 
> making it possible to do the correct thing, storing a 
> (verified) GC reference in the object graph.
>
>
>    I understand that would be a bit of a pain, but you agree it 
> is more correct, yes? So that might be worthwhile breakage 
> (especailly since we're talking about potentially large 
> breakage already.)
>
>
> 5) Interaction with @safe is something we can debate. @safe 
> works best with the GC, but if we play our scope cards right, 
> memory corruption via stack stuff can be statically eliminated 
> too, thus making some varaints of emplace @safe too. So I don't 
> think even @safe functions can assume this == GC, and even if 
> they could, we shouldn't since it limits us from legitimate 
> optimizations.
>
>    So I think the @safe rules should stay exactly as they are 
> now. Wrapper structs that do things like malloc/realloc might 
> be @system because it would still be possible for a borrowed 
> pointer to be invalidated when they realloc (note this is not 
> the case with GC, which is @safe even through growth 
> reallocations). So @safe and scope are separate issues.

This, along with an actual implementation of scope would be a 
really neat thing to have, but it has 2 problems.

1. It would significantly increase language complexity (although 
the return on investment would also be quite high).

2. Walter would be dead-set against it. He's said before that 
implementing scope would require flow-analysis in the compiler, 
which would increase the implementation complexity by a lot. On 
the flipside of that, if someone ever convinces him and 
flow-analysis *is* added, it opens up the door to a whole new 
world of other possible enhancements.


More information about the Digitalmars-d mailing list