Proposal 2: Exceptions and @nogc

MysticZach via Digitalmars-d digitalmars-d at puremagic.com
Thu Apr 13 06:19:10 PDT 2017


On Wednesday, 12 April 2017 at 19:01:25 UTC, Walter Bright wrote:
> On 4/11/2017 10:24 AM, MysticZach wrote:
>> Hi guys. Hey Walter. So, about this point. On the lifetime 
>> study thread,
>> http://forum.dlang.org/post/56301A8C.1060808@erdani.com , the 
>> following two
>> problems were stated by Andrei, but I don't think they were 
>> adequately addressed
>> in the subsequent posts:
>
> The way they will be addressed is to increment the reference 
> count in the call to the function that takes a reference to an 
> RC object.

Makes sense, and I thought the same thing until after I wrote my 
post — but now I realize it's not quite good enough. Merely 
incrementing the refcount (and decrementing it afterwards) has 
the following problem:

@rc class RC;

void fun(ref RC a, RC b) {
    a = new RC; // (1)
    // b...
}

void main() {
    auto r = new RC; // (2)
    --> r.opInc(); // compiler insert
    fun(r, r);
    --> r.opDec();
}

Let's assume the reference counting scheme involves the methods 
opInc and opDec, inserted automatically by the compiler as the 
result of detecting a duplicate parameter. If you read closely, 
you'll realize that at mark 1 above, the program will leak the 
data acquired at mark 2. The assign statement of the refcounted 
object will decrement the data it points to before it is 
reassigned. But since the data at (2)'s refcount was prematurely 
incremented, it will fall to 1 and never get deleted. 
Furthermore, the opDec in main() will decrement and delete the 
data acquired at mark 1, thinking it was the mark 2 data.

The problem is that the calling context at "fun(r,r);" fails to 
keep a real reference to the mark 2 data. If one of the 
parameters is sent by reference, we can't assume it points to the 
same data upon returning as when it was sent. And the mark 2 data 
can't be deleted before fun() returns, or it will invalidate 'b' 
in fun(). This suggests we need to save a real, separate 
reference to 'r' before sending two or more versions of it.

That said, I believe the following compiler inserts in main() 
would do the trick:

void main() {
    auto r = new RC; // (2)
    --> RC __rsave = r; // compiler insert
    --> scope(exit) __rsave = null;
    fun(r, r);
}

This solution uses only the assign method of RC to inc and dec 
the refcount, suggesting that opInc and opDec are ultimately 
unnecessary, except as (very rare) optimizations. But you still 
need for the compiler to distinguish a refcounted class from a 
regular one, so maybe the presence of an opDec() could indicate 
that, opDec being the quasi-destroyer that turns a refcounted 
class variable into an RAII type. Otherwise, you might need 
something like an @rc attribute.

At any rate, your original claim was, "a general mechanism for 
safe refcounting of classes has eluded us." Is this still the 
case, considering the above?

As far as the original post, even if a general mechanism were 
found, it doesn't mean you'd have to use it in the case of 
Exception anyway. To generalize the expression 'throw new ...', 
you'd have to redefine 'new' to be different with refcounted 
classes than with regular ones. Exceptions are probably worth a 
more specialized solution like the one you proposed. Otherwise 
everyone will have to change their 'throw new' code to 
accommodate '@nogc'. Adam D. Ruppe gave us his solution here:

http://arsdnet.net/exception.d

tl;dr Everyone would now have to say things like 'throw 
emplace!...' or 'raise!"my_exception"(...)'.

I guess the proposed hack of 'throw new Exception' is simply the 
shadow of D's original philosophy of wanting the GC to do too 
much.



More information about the Digitalmars-d mailing list