On Wednesday, 5 February 2014 at 00:49:25 UTC, Walter Bright wrote:
What happens with this:

    T identity(T t) { return t; }

My previous post was with regard to nullability. This one is about ownership.

The answer is the same: if you need this, use a template, otherwise, you always deal with a specific type (if you are allocating a new copy or need to store the reference somewhere outside the owner's scope) or a borrowed type:

GC!Object obj;
static assert(typeof(identity(obj) == GC!Object);

and so on, you can test this today, D already has all these options.


If you don't have a template, how do you approach this? Well, this is really nothing new for one:

class Foo {}

Foo foo = new Foo();
Object obj = foo; // legal

Object identity(Object obj) { return obj; }

foo = identity(foo); // won't compile without an explicit cast



When you work with interfaces, some information is lost. That's a pain sometimes, but it is a feature too - the function that works on the interface doesn't need to care about anything else.

With class inheritance, we can address this with covariant overrides.... but only of it is a member function (method).



So let's just accept that the free function loses data. You can't get a container back from a range in the general case.


So, what about the method:

class Foo {
   typeof(this) identity() { return this; }
}


What is typeof(this)? I'll tell you: Foo! Here's why:

*) The internal this pointer is never null, and thus never needs to be nullable, so that's out of consideration (see my last message)

*) The internal this pointer does *not* own its memory. Calling "delete this;" is (I think) undefined behavior in D: the class might exist on the stack, or be allocated with malloc, and calling freeing the memory inside a method is likely to lead to a crash when the invariant is called upon returning anyway!


Since the object does not own its own memory, it must always assume this is a borrowed reference. Therefore:

* Escaping `this` is prohibited.

Foo globalFoo;
class Foo {
   // illegal, escapes a reference to a borrowed reference
   void storeThis() { globalFoo = this; }
}

Consider that this is wrong if the class is made with std.conv.emplace (or the deprecated scope storage class) on a stack buffer.

This is silently wrong today, it can leave a dangling reference. (though since emplace is not @safe, we do have that protection against such problems... though since we're talking about avoiding the GC here, emplace is something we probably want anyway)


* Returning this is always a borrowed pointer, unless you explicitly make a copy.

class Foo {
   Foo identity() { return this; }
}

GC!Foo foo = new Foo(); // owned by the Gc

Foo foo2 = foo.identity(); // borrowed reference, NOT GC!Foo


Therefore, we cannot escape it to a global that way either. If we want that, we have to work with foo directly, bypassing foo.identity.

A clone method would create a new owned thing, so it returns something more specific:

class Foo {
GC!Foo clone() { auto foo = new GC; /* copy methods */ return foo; }
}


A clone method might also be templated on an allocator, and can thus change the return type as needed by the allocator. This wouldn't be virtual... but it kinda has to be.

class Foo {
    virtual GC!Foo clone() {...}
}
class Bar : Foo {
    override RC!Foo clone() {...}
}

You wouldn't want that anyway, since if you called it through the foo interface, you wouldn't know how to free it (does it need to run a struct dtor? The GC? The caller needs to know.)


Gotta pick an allocation method in the base class if you want it to work as a virtual function. So... yeah do that or use a non-virtual template. You can make it work.

A clone method typically wouldn't even be inout anyway so meh.

* A class is responsible for its member references but not itself. When the class' dtor is called, it needs to call member struct dtors, but does not call free(this) - leave that to the outside allcoator. This is already reality today (the GC calls the dtor before releasing the memory).

Reply via email to