DIP69 - Implement scope for escape proof references

Walter Bright via Digitalmars-d digitalmars-d at puremagic.com
Fri Dec 5 15:58:27 PST 2014


On 12/5/2014 8:48 AM, "Marc Schütz" <schuetzm at gmx.net>" wrote:
> 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.

Good idea. Certainly, this is less powerful than your proposal. The question, 
obviously, is what is good enough to get the job done. By "the job", I mean 
reference counting, migrating many allocations to RAII (meaning the stack), and 
eliminating a lot of closure GC allocations.


> 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...

I'm still looking for an easier way to explain it. The good news in this, 
however, is if it is correctly implement the compiler should be a big help in 
using scope correctly.


> 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

right, although:
      evil = t;  // Error: not allowed

>      }
>
> 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.

As you point out, 'ref' is designed for this.


> 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();

Right


> 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.

Right, though the compiler can optimize to produce the equivalent.

> 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.

I suspect that one can encapsulate such values in a struct where access to them 
is strictly controlled.


> 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.

Right. Different overloads can have different semantic implementations, so what 
should inference do? I also suspect it is bad style to overload on 'scope'.


> 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?

I didn't fully understand Manu's issue, but it was about 'ref' not being 
inferred by template type deduction. I didn't understand why 'auto ref' did not 
work for him. I got the impression that he was trying to program in D the same 
way he'd do things in C++, and that's where the trouble came in.


> 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();

yes

>      foo().bar2().baz();

no - cannot return scope ref parameter

>      foo().bar3().baz();

yes

>      foo().bar4().baz();

no, cannot return scope ref parameter


> I'm not sure I understand this fully yet, but it could be that none of them work...

Well, you're half right :-)


More information about the Digitalmars-d mailing list