digitalmars.D - Better handling of noncopyable objects and objects with this(this)
- Andrei Alexandrescu (4/4) May 31 2015 FYI I just created https://issues.dlang.org/show_bug.cgi?id=14638 as one...
- Jonathan M Davis (18/23) Jun 01 2015 On a related note, as was brought up recently (by Ketmar IIRC),
- Namespace (6/31) Jun 01 2015 What about
- Jonathan M Davis (8/13) Jun 01 2015 hasLvalueElements checks whether you can pass the return value of
- ketmar (6/9) Jun 02 2015 there is funnier trick:
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (16/21) Jun 01 2015 What do "static use" and "dynamic use" mean here?
- Jonathan M Davis (16/30) Jun 01 2015 Wouldn't the compiler just do something like
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (11/43) Jun 01 2015 When this can be done, it's of course a good solution. But when
- Jonathan M Davis (7/53) Jun 01 2015 Then, like you suggested, that would indeed appear to require
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (11/65) Jun 01 2015 Yes, and I think an optimizer can even eliminate the cost in some
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (29/29) Jun 01 2015 There is another solution that doesn't require a hidden variable,
- Andrei Alexandrescu (5/24) Jun 01 2015 static = as you read the code
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (6/42) Jun 01 2015 Huh? What does that have to do with the current topic?
- Jonathan M Davis (13/46) Jun 01 2015 Obviously, only Andrei can say for sure what he meant, but I
- Andrei Alexandrescu (4/8) Jun 01 2015 At language level the compiler has control over generating the
- Timon Gehr (4/13) Jun 01 2015 Static analysis is perfectly capable of figuring this case out. He is
- Andrei Alexandrescu (3/17) Jun 01 2015 It should be enough to do the move only if all paths of execution end in...
- IgorStepanov (3/8) Jun 01 2015 My last year's thoughts about that:
- Joseph Rushton Wakeling via Digitalmars-d (4/8) Jun 01 2015 Having just been playing with noncopyable types at the suggestion of Dic...
FYI I just created https://issues.dlang.org/show_bug.cgi?id=14638 as one of possibly several language enhancements to improve usability of noncopyable types (most allocators are not copyable) and to enhance performance of objects that define this(this). -- Andrei
May 31 2015
On Monday, 1 June 2015 at 04:43:20 UTC, Andrei Alexandrescu wrote:FYI I just created https://issues.dlang.org/show_bug.cgi?id=14638 as one of possibly several language enhancements to improve usability of noncopyable types (most allocators are not copyable) and to enhance performance of objects that define this(this). -- AndreiOn a related note, as was brought up recently (by Ketmar IIRC), ranges currently forbid noncopyable objects thanks to auto h = r.front; // can get the front of the range in isInputRange. If we want to fix that, then we're probably going to need to change isInputRange so that it checks that we can access front but doesn't require copying and then add something like hasCopyableElements for the algorithms that do need to copy front. I'm not a huge fan of that idea given how much code exists which copies front and how that would likely require that a lot of range-based functions add even more checks to their template constraints, but I'm not sure what else we can reasonably do if we want noncopyable elements to work in ranges, and the change wouldn't break existing code, just make it so that much of it would need updated template constraints in order to avoid compilation errors if anyone ever tries to use a range with noncopyable elements with it. - Jonathan M Davis
Jun 01 2015
On Monday, 1 June 2015 at 10:18:35 UTC, Jonathan M Davis wrote:On Monday, 1 June 2015 at 04:43:20 UTC, Andrei Alexandrescu wrote:What about ---- auto h = &r.front; // can get the front of the range ---- ?FYI I just created https://issues.dlang.org/show_bug.cgi?id=14638 as one of possibly several language enhancements to improve usability of noncopyable types (most allocators are not copyable) and to enhance performance of objects that define this(this). -- AndreiOn a related note, as was brought up recently (by Ketmar IIRC), ranges currently forbid noncopyable objects thanks to auto h = r.front; // can get the front of the range in isInputRange. If we want to fix that, then we're probably going to need to change isInputRange so that it checks that we can access front but doesn't require copying and then add something like hasCopyableElements for the algorithms that do need to copy front. I'm not a huge fan of that idea given how much code exists which copies front and how that would likely require that a lot of range-based functions add even more checks to their template constraints, but I'm not sure what else we can reasonably do if we want noncopyable elements to work in ranges, and the change wouldn't break existing code, just make it so that much of it would need updated template constraints in order to avoid compilation errors if anyone ever tries to use a range with noncopyable elements with it. - Jonathan M Davis
Jun 01 2015
On Monday, 1 June 2015 at 11:03:30 UTC, Namespace wrote:What about ---- auto h = &r.front; // can get the front of the range ---- ?hasLvalueElements checks whether you can pass the return value of r.front by ref and take its address. So, if you want to take the address of r.front, you should be using hasLvalueElements in your template constraint to prevent ranges which won't allow you to take the address of their front from being use with that function and resulting in compilation errors inside of the function. - Jonathan M Davis
Jun 01 2015
On Mon, 01 Jun 2015 11:03:29 +0000, Namespace wrote:What about ---- auto h =3D &r.front; // can get the front of the range ---- ?there is funnier trick: static void testfront(T) (auto ref T n) {} testfront(r.front); the thing is that we don't really want to check if we can take an address=20 of front value, we want to keep `ref` if it's there. with this magic=20 template `ref` is not stripped, but we still don't require "&" to work.=
Jun 02 2015
On Monday, 1 June 2015 at 04:43:20 UTC, Andrei Alexandrescu wrote:FYI I just created https://issues.dlang.org/show_bug.cgi?id=14638 as one of possibly several language enhancements to improve usability of noncopyable types (most allocators are not copyable) and to enhance performance of objects that define this(this). -- AndreiWhat do "static use" and "dynamic use" mean here? Also, object with destructors need to have more restrictions: S { ~this(); } void foo() { S s; if(condition) bar(s); // <- should we run the destructor here? } This can either be solved by making such cases non-eligible, or by "remembering" whether an object was moved using a hidden boolean variable. AFAIK the latter is incidentally the solution Rust chose.
Jun 01 2015
On Monday, 1 June 2015 at 12:50:28 UTC, Marc Schütz wrote:Also, object with destructors need to have more restrictions: S { ~this(); } void foo() { S s; if(condition) bar(s); // <- should we run the destructor here? } This can either be solved by making such cases non-eligible, or by "remembering" whether an object was moved using a hidden boolean variable. AFAIK the latter is incidentally the solution Rust chose.Wouldn't the compiler just do something like if(condition) { bar(s); // Do a move; the destructor is run inside bar return; } else { s.__dtor(); return; } In which case, the destructor is always run in the right spot, and the compiler can guarantee that a move is done rather than a copy? - Jonathan M Davis
Jun 01 2015
On Monday, 1 June 2015 at 14:20:46 UTC, Jonathan M Davis wrote:On Monday, 1 June 2015 at 12:50:28 UTC, Marc Schütz wrote:When this can be done, it's of course a good solution. But when there is additional code between the move and the destruction, it usually doesn't work: void foo() { S s; if(condition) bar(s); baz(); // <- should we run the destructor here? }Also, object with destructors need to have more restrictions: S { ~this(); } void foo() { S s; if(condition) bar(s); // <- should we run the destructor here? } This can either be solved by making such cases non-eligible, or by "remembering" whether an object was moved using a hidden boolean variable. AFAIK the latter is incidentally the solution Rust chose.Wouldn't the compiler just do something like if(condition) { bar(s); // Do a move; the destructor is run inside bar return; } else { s.__dtor(); return; } In which case, the destructor is always run in the right spot, and the compiler can guarantee that a move is done rather than a copy?
Jun 01 2015
On Monday, 1 June 2015 at 14:43:22 UTC, Marc Schütz wrote:On Monday, 1 June 2015 at 14:20:46 UTC, Jonathan M Davis wrote:Then, like you suggested, that would indeed appear to require either an invisible bool that indicates whether a move was done or not or disallowing the move. I think that I'd favor the bool, since moving is likely to save more than the extra bool is going to cost. - Jonathan M DavisOn Monday, 1 June 2015 at 12:50:28 UTC, Marc Schütz wrote:When this can be done, it's of course a good solution. But when there is additional code between the move and the destruction, it usually doesn't work: void foo() { S s; if(condition) bar(s); baz(); // <- should we run the destructor here? }Also, object with destructors need to have more restrictions: S { ~this(); } void foo() { S s; if(condition) bar(s); // <- should we run the destructor here? } This can either be solved by making such cases non-eligible, or by "remembering" whether an object was moved using a hidden boolean variable. AFAIK the latter is incidentally the solution Rust chose.Wouldn't the compiler just do something like if(condition) { bar(s); // Do a move; the destructor is run inside bar return; } else { s.__dtor(); return; } In which case, the destructor is always run in the right spot, and the compiler can guarantee that a move is done rather than a copy?
Jun 01 2015
On Monday, 1 June 2015 at 14:59:38 UTC, Jonathan M Davis wrote:On Monday, 1 June 2015 at 14:43:22 UTC, Marc Schütz wrote:Yes, and I think an optimizer can even eliminate the cost in some cases, e.g. do the rewrite you suggested. For reference, here are some discussions the Rust community had about this topic (didn't read them completely): https://github.com/rust-lang/rfcs/pull/320 https://github.com/rust-lang/rfcs/blob/master/text/0320-nonzeroing-dynamic-drop.md https://github.com/pnkfelix/rfcs/blob/a773ba113ba135ddb7f481c4829882733eaa5355/active/0000-remove-drop-flag-and-zeroing.md#require-explicit-drops-rather-than-injecting-them Some of the suggested solutions are not applicable for us, because we don't have to deal with explicit moves like they do. But we can certainly learn from their discussions.On Monday, 1 June 2015 at 14:20:46 UTC, Jonathan M Davis wrote:Then, like you suggested, that would indeed appear to require either an invisible bool that indicates whether a move was done or not or disallowing the move. I think that I'd favor the bool, since moving is likely to save more than the extra bool is going to cost.On Monday, 1 June 2015 at 12:50:28 UTC, Marc Schütz wrote:When this can be done, it's of course a good solution. But when there is additional code between the move and the destruction, it usually doesn't work: void foo() { S s; if(condition) bar(s); baz(); // <- should we run the destructor here? }Also, object with destructors need to have more restrictions: S { ~this(); } void foo() { S s; if(condition) bar(s); // <- should we run the destructor here? } This can either be solved by making such cases non-eligible, or by "remembering" whether an object was moved using a hidden boolean variable. AFAIK the latter is incidentally the solution Rust chose.Wouldn't the compiler just do something like if(condition) { bar(s); // Do a move; the destructor is run inside bar return; } else { s.__dtor(); return; } In which case, the destructor is always run in the right spot, and the compiler can guarantee that a move is done rather than a copy?
Jun 01 2015
There is another solution that doesn't require a hidden variable, at the cost of duplicating generated code: void foo() { S s; if(cond) bar(s); some(); more(); code(); } Can be rewritten as: void foo() { S s; if(cond) { bar(s); some(); more(); code(); // don't destroy here } else { some(); more(); code(); s.~this(); // destroy here } } But I think this is best left to an optimizer which has more info about the costs of the additional code vs the hidden var to decide whether its worth it.
Jun 01 2015
On 6/1/15 5:50 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:On Monday, 1 June 2015 at 04:43:20 UTC, Andrei Alexandrescu wrote:static = as you read the code dynamic = as you run the codeFYI I just created https://issues.dlang.org/show_bug.cgi?id=14638 as one of possibly several language enhancements to improve usability of noncopyable types (most allocators are not copyable) and to enhance performance of objects that define this(this). -- AndreiWhat do "static use" and "dynamic use" mean here?Also, object with destructors need to have more restrictions: S { ~this(); } void foo() { S s; if(condition) bar(s); // <- should we run the destructor here? } This can either be solved by making such cases non-eligible, or by "remembering" whether an object was moved using a hidden boolean variable. AFAIK the latter is incidentally the solution Rust chose.This has been solved, we have move which obliterates the source with .init. Andrei
Jun 01 2015
On Monday, 1 June 2015 at 15:45:11 UTC, Andrei Alexandrescu wrote:On 6/1/15 5:50 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:Huh? What does that have to do with the current topic? The question is not what should happen when someone does a conditional _explicit_ move, but whether a move should be done _implicitly_ by the compiler given the above constellation, and how it deals with the destructor if yes.On Monday, 1 June 2015 at 04:43:20 UTC, Andrei Alexandrescu wrote:static = as you read the code dynamic = as you run the codeFYI I just created https://issues.dlang.org/show_bug.cgi?id=14638 as one of possibly several language enhancements to improve usability of noncopyable types (most allocators are not copyable) and to enhance performance of objects that define this(this). -- AndreiWhat do "static use" and "dynamic use" mean here?Also, object with destructors need to have more restrictions: S { ~this(); } void foo() { S s; if(condition) bar(s); // <- should we run the destructor here? } This can either be solved by making such cases non-eligible, or by "remembering" whether an object was moved using a hidden boolean variable. AFAIK the latter is incidentally the solution Rust chose.This has been solved, we have move which obliterates the source with .init.
Jun 01 2015
On Monday, 1 June 2015 at 16:40:38 UTC, Marc Schütz wrote:On Monday, 1 June 2015 at 15:45:11 UTC, Andrei Alexandrescu wrote:Obviously, only Andrei can say for sure what he meant, but I would guess that he was suggesting that in the case of if(condition) bar(s); it would set s to S.init when it's moved for the call to bar so that the destructor can be run when s exits scope regardless of which branch was executed - though that does mean that the destructor is run twice when bar(s) is called (once inside of Bar for the original value and once for the init value) - so while that solution is correct and straightforward, I'm not sure that it's the best one. - Jonathan M DavisOn 6/1/15 5:50 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:Huh? What does that have to do with the current topic? The question is not what should happen when someone does a conditional _explicit_ move, but whether a move should be done _implicitly_ by the compiler given the above constellation, and how it deals with the destructor if yes.On Monday, 1 June 2015 at 04:43:20 UTC, Andrei Alexandrescu wrote: Also, object with destructors need to have more restrictions: S { ~this(); } void foo() { S s; if(condition) bar(s); // <- should we run the destructor here? } This can either be solved by making such cases non-eligible, or by "remembering" whether an object was moved using a hidden boolean variable. AFAIK the latter is incidentally the solution Rust chose.This has been solved, we have move which obliterates the source with .init.
Jun 01 2015
On 6/1/15 9:40 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:The question is not what should happen when someone does a conditional _explicit_ move, but whether a move should be done _implicitly_ by the compiler given the above constellation, and how it deals with the destructor if yes.At language level the compiler has control over generating the destructor, so it won't if it's doing a move. Note that this is STATIC analysis. -- Andrei
Jun 01 2015
On 06/01/2015 07:30 PM, Andrei Alexandrescu wrote:On 6/1/15 9:40 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:Static analysis is perfectly capable of figuring this case out. He is asking about how much sophistication will be guaranteed by the language specification, and what the exact rules are.The question is not what should happen when someone does a conditional _explicit_ move, but whether a move should be done _implicitly_ by the compiler given the above constellation, and how it deals with the destructor if yes.At language level the compiler has control over generating the destructor, so it won't if it's doing a move. Note that this is STATIC analysis. -- Andrei
Jun 01 2015
On 6/1/15 10:38 AM, Timon Gehr wrote:On 06/01/2015 07:30 PM, Andrei Alexandrescu wrote:It should be enough to do the move only if all paths of execution end in the same conversion to rvalue. -- AndreiOn 6/1/15 9:40 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" wrote:Static analysis is perfectly capable of figuring this case out. He is asking about how much sophistication will be guaranteed by the language specification, and what the exact rules are.The question is not what should happen when someone does a conditional _explicit_ move, but whether a move should be done _implicitly_ by the compiler given the above constellation, and how it deals with the destructor if yes.At language level the compiler has control over generating the destructor, so it won't if it's doing a move. Note that this is STATIC analysis. -- Andrei
Jun 01 2015
On Monday, 1 June 2015 at 04:43:20 UTC, Andrei Alexandrescu wrote:FYI I just created https://issues.dlang.org/show_bug.cgi?id=14638 as one of possibly several language enhancements to improve usability of noncopyable types (most allocators are not copyable) and to enhance performance of objects that define this(this). -- AndreiMy last year's thoughts about that: https://issues.dlang.org/show_bug.cgi?id=13492
Jun 01 2015
On 01/06/15 06:43, Andrei Alexandrescu via Digitalmars-d wrote:FYI I just created https://issues.dlang.org/show_bug.cgi?id=14638 as one of possibly several language enhancements to improve usability of noncopyable types (most allocators are not copyable) and to enhance performance of objects that define this(this). -- AndreiHaving just been playing with noncopyable types at the suggestion of Dicebot, Steve Schweighoffer and Martin Nowak, thanks for drawing attention to this. I can see it being very useful to address.
Jun 01 2015