RFC: reference counted Throwable

via Digitalmars-d digitalmars-d at puremagic.com
Sat Sep 20 04:50:25 PDT 2014


On Saturday, 20 September 2014 at 09:19:21 UTC, Dicebot wrote:
> On Saturday, 20 September 2014 at 09:05:24 UTC, Marc Schütz 
> wrote:
>> On Saturday, 20 September 2014 at 08:32:55 UTC, Dicebot wrote:
>>> On Saturday, 20 September 2014 at 08:20:47 UTC, Marc Schütz 
>>> wrote:
>>>> I don't think ARC is needed.
>>>>
>>>> library RC + borrowing + uniqueness/moving = WIN
>>>
>>> You can't do polymorphic entity RC (like exceptions) without 
>>> at least some help from compiler because language currently 
>>> does not provide tools for lifetime control of classes. At 
>>> least _some_ form of ARC is necessary.
>>
>> I think we can, using templated alias this. We're not there 
>> yet, but it's probably feasable, seeing that Igor Stepanov 
>> already implemented multiple alias this [1]. With that and 
>> maybe a little opDispatch magic, it should be possible to 
>> making wrappers that are implicitly convertible to wrappers of 
>> parents of their inner types.
>>
>> Granted, for exceptions there's more needed: There needs to be 
>> support for throwing and catching these wrappers, and for 
>> catching wrappers of derived types, too. But note that 
>> throw/catch itself doesn't involve copying, it's a moving 
>> operation, which might make it easier.
>>
>> [1] https://github.com/D-Programming-Language/dmd/pull/3998
>
> Yeah but implicitly convertible to what? If you convert it to 
> `Throwable` your reference counting facilities are circumvented 
> resulting in dangling exception reference at point where 
> `Throwable` gets caught.

As I said, throw/catch is at its core a moving operation. For 
classes this isn't important (they are references), but for an RC 
wrapper it would be, so we could specify that.

Move-constructing from an lvalue in the context of D can be seen 
as a three-step process:

1) create a temporary, initialize with T.init
2) bit-swap the variable with the temporary
3) destroy the variable

It can be seen that the value that is to be moved (the RC 
wrapper) must be non-const (only at the head, it may still be 
tail-const).

Now, any generic RC type that wants to be implicitly convertible 
to its payload type must do this via borrowing in order to be 
safe (see my proposal at [1]). Using const-borrowing, we can 
guarantee that the wrapper will not be thrown (or moved, in 
general) as long as borrowed references to its payload exist:

     struct RC(T) {
         // ...
         T _payload;
         scope!(const this) borrow() {
             return _payload;
         }
         alias borrow this;
     }

This already solves avoiding dangling references to the 
exception: No references can be left behind when the exception is 
thrown, and the wrapper will not be destroyed, but moved, thus 
not releasing the exception's memory.

The second part is really "just" some way to transport the 
wrapper via the exception mechanism, including support for 
catching wrappers of derived exception types.

>
> Special casing catching such wrappers to still preserve 
> original ref-counted type while pretending to be Throwable at 
> call site sounds like a terrible hack, much worse than any sort 
> of ARC complexity.

IMO it would only a hack _if_ they were indeed special cased. I'm 
sure we can find a more generic mechanism that would allow this 
to be implemented cleanly. Note that the other requirements I 
described above (borrowing, move semantics) are also things that 
just happen to be usable here: they are desirable in general, 
exceptions are just one possible application.

[1] http://wiki.dlang.org/User:Schuetzm/scope


More information about the Digitalmars-d mailing list