Thread overview | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
August 10, 2019 D needs first-class lifetimes before it can get ownership and borrowing | ||||
---|---|---|---|---|
| ||||
I've been trying to nail down the semantics of my in-progress DIP for a `unique` qualifier, and I couldn't come up with coherent rules for double-indirection. To give you an shortened example of the problem, contemplate this: int* gptr; struct SmartPtr { int* ptr; @safe: ~this() { reset(); } @trusted: void init() { ptr = cast(int*)malloc(int.sizeof); *ptr = 0; } void reset() { free(ptr); ptr = null; } @safe: ref int get() return { assert(ptr); return *ptr; } void borrow() { gptr = this.ptr; } } @safe void main() { SmartPtr s; s.init(); s.borrow(); s.reset(); *gptr = 1; // Memory corruption } The problem above is that, under DIP-1000, the compiler assumes `borrow()` doesn't escape any data, because it takes `this` by ref; and yet it escapes this.ptr, because there's no way to mark this.ptr as scope. As long as borrow() is @safe, there's no way reset() can be @trusted. To my knowledge, the proposed @live semantics don't solve this problem (because they would also assume that `borrow()` doesn't escape data); so I think it's necessary to have lifetime annotations inside structs before any ownership-borrowing scheme can be achieved. |
August 10, 2019 Re: D needs first-class lifetimes before it can get ownership and borrowing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Olivier FAURE | On Saturday, 10 August 2019 at 13:30:17 UTC, Olivier FAURE wrote:
> The problem above is that, under DIP-1000, the compiler assumes `borrow()` doesn't escape any data, because it takes `this` by ref; and yet it escapes this.ptr, because there's no way to mark this.ptr as scope.
If I change it to:
```
void borrow() scope { gptr = this.ptr; }
```
I get:
```
Error: scope variable this assigned to non-scope gptr
```
I don't know why it compiles without scope though.
|
August 10, 2019 Re: D needs first-class lifetimes before it can get ownership and borrowing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dennis | On Saturday, 10 August 2019 at 16:48:26 UTC, Dennis wrote:
> I don't know why it compiles without scope though.
I think it's because you didn't declare it as `scope SmartPtr s;` and `reset` is wrongly @trusted.
|
August 10, 2019 Re: D needs first-class lifetimes before it can get ownership and borrowing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Olivier FAURE | On Saturday, 10 August 2019 at 13:30:17 UTC, Olivier FAURE wrote: > I've been trying to nail down the semantics of my in-progress DIP for a `unique` qualifier, and I couldn't come up with coherent rules for double-indirection. > > To give you an shortened example of the problem, contemplate this: > > int* gptr; > > struct SmartPtr { > int* ptr; > > @safe: > ~this() { reset(); } > @trusted: > void init() { ptr = cast(int*)malloc(int.sizeof); *ptr = 0; } > void reset() { free(ptr); ptr = null; } > > @safe: > ref int get() return { assert(ptr); return *ptr; } > > void borrow() { gptr = this.ptr; } > } > > @safe > void main() { > SmartPtr s; > s.init(); > s.borrow(); > s.reset(); > *gptr = 1; // Memory corruption > } > > The problem above is that, under DIP-1000, the compiler assumes `borrow()` doesn't escape any data, because it takes `this` by ref; and yet it escapes this.ptr, because there's no way to mark this.ptr as scope. We could allow scope fields: struct SmartPtr { scope int* ptr > As long as borrow() is @safe, there's no way reset() can be @trusted. That use of @trusted requires @safe code inside the struct (and in the defining module due to field access) to form part of the @trusted contract. scope fields would lessen the burden of @trusted and allow encapsulated pointers. But there still won't be an error if you don't use scope for the ptr field. |
August 10, 2019 Re: D needs first-class lifetimes before it can get ownership and borrowing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nick Treleaven | On Saturday, 10 August 2019 at 17:38:43 UTC, Nick Treleaven wrote:
> On Saturday, 10 August 2019 at 13:30:17 UTC, Olivier FAURE
>
> struct SmartPtr {
> scope int* ptr
>
should there be an opportunity to transfer ownership?
SmartPtr {
int* transfer() [@forget, @transfer, @orsomethingelse] { return ptr; /*ptr = null;*/ }
}
and then need attr @nodiscard with error when returned value is discarded cuz memory/handle leak can be.
|
August 10, 2019 Re: D needs first-class lifetimes before it can get ownership and borrowing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dennis | On Saturday, 10 August 2019 at 16:48:26 UTC, Dennis wrote: > If I change it to: > ``` > void borrow() scope { gptr = this.ptr; } > ``` > > I get: > ``` > Error: scope variable this assigned to non-scope gptr > ``` ... Huh. Okay, this is... interesting. I'm not finding obvious holes either (though I wonder if you can game it by having a SmartPtr!SmartPtr). Too bad the semantics for combining ref and scope in dip1000 aren't documented. > I don't know why it compiles without scope though. Because without scope, borrow takes ref this, and with scope, it takes ref scope this. Similarly, foo(ref scope int*) and foo(scope int**) have different semantics. I need to tweak with this. My understanding is that ref scope is a special case where the language enforces lifetime on two levels instead of the default one, so this might be enough to cobble together a working OB system. |
August 10, 2019 Re: D needs first-class lifetimes before it can get ownership and borrowing | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dennis | > I think it's because you didn't declare it as `scope SmartPtr s;` and `reset` is wrongly @trusted.
`SmartPtr` is a struct so it's implicitly `scope`. Also, I think the `@trusted` annotation is only to allow the usage of `malloc` and `free` which aren't safe. I think for this illustration, the `@trusted` annotation is OK.
Mike
|
August 11, 2019 Re: D needs first-class lifetimes before it can get ownership and borrowing | ||||
---|---|---|---|---|
| ||||
Posted in reply to a11e99z | On Saturday, 10 August 2019 at 18:11:18 UTC, a11e99z wrote:
>
> should there be an opportunity to transfer ownership?
> SmartPtr {
> int* transfer() [@forget, @transfer, @orsomethingelse] { return ptr; /*ptr = null;*/ }
> }
It would just use a trusted move constructor (or copy constructor if doing ref counting).
|
Copyright © 1999-2021 by the D Language Foundation