www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Trying to understand DIP1000

reply Dukc <ajieskola gmail.com> writes:
Walters exciting yesterday talk made me think about dip1000 
again. I found out that I do not understand this:

Containers that own their data will be able to give access to 
elements by scope ref. The compiler ensures that the references 
returned never outlive the container. Therefore, the container 
can deallocate its payload (subject to control of multiple 
container copies, e.g. by means of reference counting). A basic 
outline of a reference counted slice is shown below:

 safe struct RefCountedSlice(T) {
	private T[] payload;
	private uint* count;

	this(size_t initialSize) {
		payload = new T[initialSize];
		count = new size_t;
		*count = 1;
	}

	this(this) {
		if (count) ++*count;
	}

	// Prevent reassignment as references to payload may still exist
	 system opAssign(RefCountedSlice rhs) {
		this.__dtor();
		payload = rhs.payload;
		count = rhs.count;
		++*count;
	}

     // Interesting fact #1: destructor can be  trusted
	 trusted ~this() {
		if (count && !--*count) {
			delete payload;
			delete count;
		}
	}

     // Interesting fact #2: references to internals can be given 
away
	scope ref T opIndex(size_t i) {
		return payload[i];
	}

	// ...
}

// Prevent premature destruction as references to payload may 
still exist
// (defined in object.d)
 system void destroy(T)(ref RefCountedSlice!T rcs);


I did some hacking and found out that -dip1000 flag and scope are 
not even needed for most cases:

 safe struct RefCountedSlice(T) {
     private T[] payload;
     private uint* count;

     this(size_t initialSize) {
         payload = new T[initialSize];
         count = new size_t;
         *count = 1;
     }

     this(this) {
         if (count) ++*count;
     }

     // Prevent reassignment as references to payload may still 
exist
      system opAssign(RefCountedSlice rhs) {
         this.__dtor();
         payload = rhs.payload;
         count = rhs.count;
         ++*count;
     }

      trusted ~this() {
         if (count && !--*count) {
             delete payload;
             delete count;
             import std.stdio;
         }
     }
     ref opIndex(size_t i) {
         return payload[i];
     }

     ref front(){return payload[0];}

     void popFront(){payload = payload[1 .. $];}

     auto empty(){return payload.length == 0;}
}

I tested that this does compile and work correctly:

 safe void main()
{   auto test = RefCountedSlice!string(16);

     int i = 0;
     foreach(ref e; test)
     {   foreach(unused; 0 .. i++) e ~= "s";
     }
     foreach(j; 2 .. 10)
     {   import std.stdio;
         test[j].writeln;
     }
}

The compiler is too cunning to let you to leak test[x] out of a 
function by reference, or take an address of it. Nor you can 
assing it to another variable, because that means copy semantics. 
And that all applies without using -dip1000 or even -dip25.

There's still one way I found to fool the compiler, at least 
without flags:

int[] gcActivationAttempt;

 safe void killDMan()
{   auto outer = [RefCountedSlice!int(1)];
     foreach(ref fail; outer)
     {   outer = [RefCountedSlice!int(1)];
         gcActivationAttempt = new int[30000];
         fail[0] = 24;
     }
}

Because that's the only hole I found that prevents generic  safe 
refcounted containers, I suppose the "scope ref" keyword somehow 
deals with that. But I cannot think of how.

Can somebody explain that?
May 05
next sibling parent Dukc <ajieskola gmail.com> writes:
On Friday, 5 May 2017 at 18:04:32 UTC, Dukc wrote:
 The compiler is too cunning to let you to leak test[x] out of a 
 function by reference, or take an address of it. Nor you can 
 assing it to another variable, because that means copy 
 semantics.
And oh, the identity function won't fool the compiler either, it infers return scope, -dip25 or no.
May 05
prev sibling parent Dukc <ajieskola gmail.com> writes:
On Friday, 5 May 2017 at 18:04:32 UTC, Dukc wrote:
  safe void killDMan()
 {   auto outer = [RefCountedSlice!int(1)];
     foreach(ref fail; outer)
     {   outer = [RefCountedSlice!int(1)];
         gcActivationAttempt = new int[30000];
         fail[0] = 24;
     }
 }
should be int[] gcActivationAttempt; safe void killDMan() { auto outer = [RefCountedSlice!int(1)]; foreach(ref fail; outer[0]) { outer = [RefCountedSlice!int(1)]; gcActivationAttempt = new int[30000]; fail = 24; } } but this compiles too
May 05