www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D needs first-class lifetimes before it can get ownership and

reply Olivier FAURE <couteaubleu gmail.com> writes:
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.
Aug 10
next sibling parent reply Dennis <dkorpel gmail.com> writes:
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.
Aug 10
next sibling parent reply Dennis <dkorpel gmail.com> writes:
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.
Aug 10
parent Mike Franklin <slavo5150 yahoo.com> writes:
 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
Aug 10
prev sibling parent Olivier FAURE <couteaubleu gmail.com> writes:
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.
Aug 10
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
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.
Aug 10
parent reply a11e99z <black80 bk.ru> writes:
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.
Aug 10
parent Nick Treleaven <nick geany.org> writes:
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).
Aug 11