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