Borrowing and Ownership

Timon Gehr timon.gehr at gmx.ch
Wed Nov 13 09:52:24 UTC 2019


On 13.11.19 09:08, Atila Neves wrote:
> On Sunday, 27 October 2019 at 22:36:30 UTC, Timon Gehr wrote:
>> I finally got around to writing up some thoughts on @safe borrowing 
>> and ownership in D.
> 
> Thanks! I'd been wanting to understand your thoughts on this for a 
> while. A few questions:
> 
>> ref implies scope
> 
> Is that true now??
> ...

Yes. (What I mean is that it applies to the implicit pointer that is 
generated, not the value itself.)

For example (compiled with -dip1000 switch):

int* p;
void foo(ref int x)@safe{
     x=2;
     p=&x; // error
}

void main()@safe{
     int x;
     foo(x);
}

error: address of variable `x` assigned to `p` with longer lifetime

>> `scope` should apply to all types of data equally
> 
> And how would the application of `scope` to, say, `int` affect it? What 
> would the compiler do with that?
> ...

The same things it would do with other types. You can't escape the value 
or make non-borrowed copies. It's unlikely to have useful applications 
for a plain integer, but you could have a `struct Handle{ private int 
handle; ... }` that carries the invariant that the handle is valid.

Then you can do things like:

void workWithHandle(scope Handle handle)@safe{ ... }

void unsafe()@trusted{
     auto handle=getHandle();
     workWithHandle(handle);
     disposeHandle(handle);
}

If `scope` was restricted to pointers, you would have to pass pointers 
to handles in order to get any of that type checking. (IIRC, this 
complaint was on this newsgroup not too long ago.) In general, it would 
make the rules less useful for @trusted libraries that need to restrict 
access patterns from @safe code.


> Could you please expand on the rationale for these rules?
> ...

As noted, with DIP 1021 accepted, it's pretty clear that mutable `scope` 
pointers won't be able to alias (as that DIP attempts to restrict 
aliasing of `ref` parameters). I would prefer to keep aliasing and 
lifetime restrictions separate (because you can always assign something 
with higher lifetime to something with smaller lifetime, but aliasing 
restrictions are incompatible both ways), but it's unlikely to happen.

Note that Walter's vision for @live is to restrict aliasing in this way 
for _all_ pointers.

>> Non-immutable non-scope values may not be assigned to `scope` values

Otherwise you could very easily introduce aliasing between scoped pointers:

void foo(int* p)@safe{
     scope int* q=p;
     scope int* r=p;
     assert(q !is r); // fail
}

The type system is supposed to guarantee that if this assertion 
compiles, it passes.

>> A non-`scope` pointer cannot be dereferenced if that would yield a 
>> `scope` value

A non-scope pointer doesn't satisfy any aliasing restrictions, hence if 
you dereference it and get a `scope` value, you could have multiple 
paths to access a single `scope` value, which has to be ruled out.

void foo(scope(int*)* p, scope(int*)* q)@safe{
     // p and q could alias
     scope int* r=*p;
     scope int* s=*q;
     assert(r !is s); // can fail
}

>> `scope` has to be a type constructor.
> 

One example:

import std.typecons;
int* y;
int* foo(){
     int x;
     auto t=tuple(&x,y); // type has to be Tuple!(scope(int*),int*)
     return t[1];
}

(Another key use case for type constructors is to distinguish the return 
value and the receiver, as in `const(int*) foo()const`;, but for 
`scope`, we can actually write `int* foo()scope return` for this purpose.)

> Other than that, I wonder about the teachability of these rules.

The rules follow from the simple principles lined out at the beginning 
of the post, so that should not be a problem. Note that my post was 
pretty condensed. Unfortunately, I don't have enough spare time at the 
moment.

> I had to read the proposal several times myself.
> 

Thanks for taking the time.


More information about the Digitalmars-d mailing list