On Saturday, 20 September 2014 at 11:50:28 UTC, Marc Schütz wrote:
On Saturday, 20 September 2014 at 09:19:21 UTC, Dicebot wrote:
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
The mechanism might simply be to allow anything to be thrown that
a) supports moving, e.g. provides a method `release()` that
returns a unique expression (this requires moving and uniqueness
to be specified first, which is a good idea anyway), and
b) is implicitly convertible to Throwable.