The next iteration of scope

Zach the Mystic via Digitalmars-d digitalmars-d at puremagic.com
Mon Mar 16 18:13:39 PDT 2015


On Monday, 16 March 2015 at 20:50:46 UTC, Marc Schütz wrote:
> On Monday, 16 March 2015 at 19:43:01 UTC, Zach the Mystic wrote:
>> I always tend to think of member functions as if they weren't:
>>
>> struct S {
>>  T t;
>>  ref T fun() return {
>>    return t;
>>  }
>> }
>>
>> In my head, I just translate fun() above to:
>>
>> ref T fun(return S* __this) {
>>  return __this.t;
>> }
>>
>> Therefore whatever the scope of `__this`, that's the scope of 
>> the return, just like it would be for any other parameter. 
>> Then:
>>
>> S s;
>> s.fun();
>>
>> ... is really just `fun(s);` in disguise. That's why it's hard 
>> for me to grasp `scope` members, because they seem to me to be 
>> just as scope as their parent, whether global or local.
>
> It works just the same:
>
> struct S {
>     private int* payload_;
>     ref int* payload() return {
>         return payload_;
>     }
> }
>
> ref int* payload(scope ref S __this) return {
>     return __this.payload_;    // well, imagine it's not private
> }

More accurately,

// `return` is moved
ref int* payload(return scope ref S __this) {
    return __this.payload_;
}

I think that if you need `return` to make it safe, there's much 
less need for `scope`.

> Both the S.payload() and the free-standing payload() do the 
> same thing.
>
> From inside the functions, `return` tells us that we're allowed 
> to a reference to our payload. From the caller's point of view, 
> it signifies that the return value is scoped to the first 
> argument, or `this` respectively.
>
> To reiterate, `scope` members are just syntactical sugar for 
> the kinds of accessor methods/functions in the example code. 
> There's nothing special about them.

That's fine, but then there's the argument that syntax sugar is 
different from "real" functionality. To add it would require a 
compelling use case.

My fundamental issue with `scope` in general is that it should be 
the safe default, which means it doesn't really need to appear 
that often. If @safe is default, the compiler would force you to 
mark any parameter `return` when it detected such a return.

>> How a member could be scope when the parent is global is hard 
>> for me to imagine.
>
> The following is clear, right?
>
> int* p;
> scope int* borrowed = p;
>
> That's clearly allowed, we're storing a reference to a global 
> or GC object into a scope variable. Now let's use `S`, which 
> contains an `int*` member:
>
> S s;
> scope S borrowed_s = s;
>
> That's also ok. Doesn't matter whether it's the pointer itself, 
> or something containing the pointer. And now the final step:
>
> scope int* p2;
> p2 = s.payload;          // OK
> p2 = borrowed_s.payload; // also OK
> static int* p3;
> p3 = s.payload;          // NOT OK!
>
> However, if `payload` were not the accessor method/function, 
> but instead a simple (non-scope) member of `S`, that last line 
> would be allowed, because there is nothing restricting its use.

See above. With `return` being forced on the implicit this 
parameter:

ref int* payload(return /*scope*/ ref S __this) { ... }

`return` covers the need for safety, unless I'm still missing 
something.

> For members that the struct owns and want's to manage itself, 
> this is not good. Therefore, we make it private and allow 
> access to it only through accessor methods/functions that are 
> annotated with `return`. But we could accidentally forget an 
> annotation, and the pointer could escape.

Same argument. Forgetting `return` in safe code == compiler 
error. I think DIP25 already does this.


More information about the Digitalmars-d mailing list