www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Delegate is left with a destroyed stack object

reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
Continuing the conversation from the following thread:

   http://forum.dlang.org/post/l4mi8l$1r1$1 digitalmars.com

(Note: The OP's example there behaves as expected on git head.)

The programmer thinks that the following delegate returned by foo() will 
print S(1) because the delegate uses the local 's' which is supposed to 
live long enough:

import std.stdio;

struct S
{
     int i;
     ~this() { i = 666; }
}

auto foo()
{
     S s = S(1);
     return { writeln(s); } ;  // <-- Uses local s
}

void main()
{
     foo()();
}

However, 's' gets destroyed upon leaving foo() and the delegate is left 
with a destroyed object; so the program prints S(666).

Is that by design or a bug?

Aside: Never mind that the destroyed object gets destroyed again, and 
again, and again. :) (Well, presumably after being copied to other 
object in its dead state, because the address of the object is not the 
same.) (Put a writeln() inside the destructor to see that in action.)

Ali
Oct 29 2013
next sibling parent reply "Craig Dillabaugh" <cdillaba cg.scs.carleton.ca> writes:
On Tuesday, 29 October 2013 at 17:55:45 UTC, Ali Çehreli wrote:
 Continuing the conversation from the following thread:

   http://forum.dlang.org/post/l4mi8l$1r1$1 digitalmars.com

 (Note: The OP's example there behaves as expected on git head.)

 The programmer thinks that the following delegate returned by 
 foo() will print S(1) because the delegate uses the local 's' 
 which is supposed to live long enough:

 import std.stdio;

 struct S
 {
     int i;
     ~this() { i = 666; }
 }

 auto foo()
 {
     S s = S(1);
     return { writeln(s); } ;  // <-- Uses local s
 }

 void main()
 {
     foo()();
 }

 However, 's' gets destroyed upon leaving foo() and the delegate 
 is left with a destroyed object; so the program prints S(666).

 Is that by design or a bug?

 Aside: Never mind that the destroyed object gets destroyed 
 again, and again, and again. :) (Well, presumably after being 
 copied to other object in its dead state, because the address 
 of the object is not the same.) (Put a writeln() inside the 
 destructor to see that in action.)

 Ali
According to the Book of Revelations, 666 is the number of the Beast (Rev. 13:18), and later (Revelations 19:20) it is stated that the Beast will be thrown into a lake of fire and brimstone. That may explain the repeated destruction of s :o) Whether that is a bug or not, I am unsure. Craig
Oct 29 2013
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/29/2013 12:02 PM, Craig Dillabaugh wrote:

 the Beast will be thrown into a lake of fire and brimstone.

 That may explain the repeated destruction of s :o)
That's awesome! :) Ali
Oct 29 2013
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-10-29 18:55, Ali Çehreli wrote:
 Continuing the conversation from the following thread:

    http://forum.dlang.org/post/l4mi8l$1r1$1 digitalmars.com

 (Note: The OP's example there behaves as expected on git head.)

 The programmer thinks that the following delegate returned by foo() will
 print S(1) because the delegate uses the local 's' which is supposed to
 live long enough:

 import std.stdio;

 struct S
 {
      int i;
      ~this() { i = 666; }
 }

 auto foo()
 {
      S s = S(1);
      return { writeln(s); } ;  // <-- Uses local s
 }

 void main()
 {
      foo()();
 }

 However, 's' gets destroyed upon leaving foo() and the delegate is left
 with a destroyed object; so the program prints S(666).

 Is that by design or a bug?

 Aside: Never mind that the destroyed object gets destroyed again, and
 again, and again. :) (Well, presumably after being copied to other
 object in its dead state, because the address of the object is not the
 same.) (Put a writeln() inside the destructor to see that in action.)

 Ali
Shouldn't "s" be moved to the heap and not destroyed. -- /Jacob Carlborg
Oct 29 2013
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/29/2013 01:23 PM, Jacob Carlborg wrote:

 Shouldn't "s" be moved to the heap and not destroyed.
We are waiting for Walter to say the same thing and Kenji Hara to fix it last month. ;) Ali
Oct 29 2013
prev sibling next sibling parent reply "qznc" <qznc web.de> writes:
On Tuesday, 29 October 2013 at 17:55:45 UTC, Ali Çehreli wrote:
 Continuing the conversation from the following thread:

   http://forum.dlang.org/post/l4mi8l$1r1$1 digitalmars.com

 (Note: The OP's example there behaves as expected on git head.)

 The programmer thinks that the following delegate returned by 
 foo() will print S(1) because the delegate uses the local 's' 
 which is supposed to live long enough:

 import std.stdio;

 struct S
 {
     int i;
     ~this() { i = 666; }
 }

 auto foo()
 {
     S s = S(1);
     return { writeln(s); } ;  // <-- Uses local s
 }

 void main()
 {
     foo()();
 }

 However, 's' gets destroyed upon leaving foo() and the delegate 
 is left with a destroyed object; so the program prints S(666).

 Is that by design or a bug?
Seems reasonable to me. S s = S(1); // allocates on the stack, because value type, i is now 1 { writeln(s); } // creates closure with reference to stack memory "&s" return ... // returns and destroys local variable s, i is now 666 foo()() // calls closure with reference to invalid memory "&s" Returning closures with reference to stack memory is a bad idea. Maybe safe should prohibit this?
Oct 29 2013
next sibling parent "Dicebot" <public dicebot.lv> writes:
http://dlang.org/function.html

 The stack variables referenced by a nested function are still 
 valid even after the function exits (this is different from D 
 1.0). This is called a closure.
Oct 29 2013
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Tuesday, 29 October 2013 at 21:14:39 UTC, qznc wrote:
 Returning closures with reference to stack memory is a bad 
 idea. Maybe  safe should prohibit this?
According to http://dlang.org/function.html "The stack variables referenced by a nested function are still valid even after the function exits (this is different from D 1.0). This is called a closure." It it completely valid to reference that memory. It gets moved onto the heap when you create the closure. The problem is that the destructor still gets called, so the variable is no longer in a valid state post return.
Oct 29 2013
prev sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
On Tuesday, 29 October 2013 at 21:14:39 UTC, qznc wrote:
 { writeln(s); } // creates closure with reference to stack 
 memory "&s"
 return ... // returns and destroys local variable s, i is now 
 666
 foo()() // calls closure with reference to invalid memory "&s"
That's not how closures work in D – the local variables referenced by nested functions/closures will be allocated inside the "nested context" for the function, which is placed on the GC heap if a reference to it can be escaped. Play around with the example some more if you want: Barring any further DMD bugs, you are not going to get the contents of i to be corrupted (i.e. set to another garbage value). The problem with the compiler behavior for the code in question is that it destructs a live object, and thus clearly breaks the type system. I think the only correct resolution is to not destruct the object at the end of foo() – which incidentally also means that the destructor will never be invoked, just as for any other GC-finalized memory. David
Oct 29 2013
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/29/2013 02:32 PM, David Nadlinger wrote:

 The problem with the compiler behavior for the code in question is that
 it destructs a live object, and thus clearly breaks the type system.

 I think the only correct resolution is to not destruct the object at the
 end of foo() – which incidentally also means that the destructor will
 never be invoked, just as for any other GC-finalized memory.
To add to that, Maxim Fomin notes in the D.learn forum that there are the following conflicting requirements: 1) need to allocate struct into heap due to lambda 2) need to put dtor invocation in the end as usual. The first one is supported by the closure spec. The second one is the well-know struct destruction upon leaving a scope. Ali
Oct 29 2013
next sibling parent reply Lionello Lunesu <lionello lunesu.remove.com> writes:
On 10/29/13, 22:37, Ali Çehreli wrote:
 To add to that, Maxim Fomin notes in the D.learn forum that there are
 the following conflicting requirements:
So a copy should have been made of the live object, not the destructed one. Correct? Sounds like the destruction and the copy are happening in the wrong order.
Oct 29 2013
parent reply "David Nadlinger" <code klickverbot.at> writes:
On Tuesday, 29 October 2013 at 21:41:25 UTC, Lionello Lunesu 
wrote:
 So a copy should have been made of the live object, not the 
 destructed one. Correct?
No. There should only be one struct instance, the one living (forever) in the closure context. David
Oct 29 2013
parent reply Lionello Lunesu <lionello lunesu.remove.com> writes:
On 10/29/13, 22:42, David Nadlinger wrote:
 On Tuesday, 29 October 2013 at 21:41:25 UTC, Lionello Lunesu wrote:
 So a copy should have been made of the live object, not the destructed
 one. Correct?
No. There should only be one struct instance, the one living (forever) in the closure context. David
Why? It's a struct. It should be completely fine to create a copy [on the heap] for the closure context. The way I see closure is that any referenced locals become hidden arguments. Imagine an implicit ", S s" in the closure's parameter list. Everybody would expect 's' (being a value type) to be a copy. If you want to ensure there's only one instance, you either need to use class instead of struct, or allocate 's' on the heap and use a pointer to S. L.
Oct 30 2013
next sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
On Wednesday, 30 October 2013 at 09:20:40 UTC, Lionello Lunesu 
wrote:
 Why? It's a struct. It should be completely fine to create a 
 copy [on the heap] for the closure context
That's definitely not how D closures work, they always refer to local variables "by reference". One other place where this tends to crop is for code involving loop variables, but while the behavior might be unexpected to some, discussion has made clear that the code works as intended: --- void main() { import std.stdio; void delegate()[] dgs; foreach (i; 0 .. 5) dgs ~= { writeln(i); }; foreach (dg; dgs) dg(); } --- If structs behaved like you want them to, the snippet would (have to) print 0, 1, 2, 3, 4 as well, and tht's definitely too big a language change to consider at this stage. David
Oct 30 2013
next sibling parent Lionello Lunesu <lionello lunesu.remove.com> writes:
On 10/30/13, 10:56, David Nadlinger wrote:
 ---
 void main() {
    import std.stdio;

    void delegate()[] dgs;
    foreach (i; 0 .. 5) dgs ~= { writeln(i); };

    foreach (dg; dgs) dg();
 }
 ---

 If structs behaved like you want them to, the snippet would (have to)
 print 0, 1, 2, 3, 4 as well,
Did NOT know that. Thanks for letting me know :)
 and tht's definitely too big a language change to consider at this stage.
Well, that's a separate issue, whether to change the language or not. It's still valuable to discuss unexpected behavior when one runs into it. L.
Oct 30 2013
prev sibling next sibling parent reply "Max Samukha" <maxsamukha gmail.com> writes:
On Wednesday, 30 October 2013 at 09:56:08 UTC, David Nadlinger 
wrote:

 One other place where this tends to crop is for code involving 
 loop variables, but while the behavior might be unexpected to 
 some, discussion has made clear that the code works as intended:

 ---
 void main() {
   import std.stdio;

   void delegate()[] dgs;
   foreach (i; 0 .. 5) dgs ~= { writeln(i); };

   foreach (dg; dgs) dg();
 }
 ---

 If structs behaved like you want them to, the snippet would 
 (have to) print 0, 1, 2, 3, 4 as well, and tht's definitely too 
 big a language change to consider at this stage.

 David
So D managed to mess up closures, too. And that's after years of countless complaints about the same issue in JS!
Oct 30 2013
parent reply "Max Samukha" <maxsamukha gmail.com> writes:
On Wednesday, 30 October 2013 at 12:17:29 UTC, Max Samukha wrote:

 So D managed to mess up closures, too. And that's after years 
 of countless complaints about the same issue in JS!
And please no misguided arguments like http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx.
Oct 30 2013
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 30 October 2013 at 12:28:13 UTC, Max Samukha wrote:
 On Wednesday, 30 October 2013 at 12:17:29 UTC, Max Samukha 
 wrote:

 So D managed to mess up closures, too. And that's after years 
 of countless complaints about the same issue in JS!
And please no misguided arguments like http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx.
the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The "for" loop will not be changed. We return you now to our original article." Javascript is introducing the let keyword to create properly scoped variables. It seems everybody agrees on what should have been done.
Oct 30 2013
prev sibling parent Marco Leise <Marco.Leise gmx.de> writes:
Am Wed, 30 Oct 2013 13:28:12 +0100
schrieb "Max Samukha" <maxsamukha gmail.com>:

 On Wednesday, 30 October 2013 at 12:17:29 UTC, Max Samukha wrote:
 
 So D managed to mess up closures, too. And that's after years 
 of countless complaints about the same issue in JS!
And please no misguided arguments like http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx.
Without thinking much about it, why not call loop variables temporaries that you cannot close over. If you want a closure you'd have to declare the variable either inside or outside the loop. -- Marco
Nov 05 2013
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 10/30/2013 10:56 AM, David Nadlinger wrote:
 On Wednesday, 30 October 2013 at 09:20:40 UTC, Lionello Lunesu wrote:
 Why? It's a struct. It should be completely fine to create a copy [on
 the heap] for the closure context
That's definitely not how D closures work, they always refer to local variables "by reference". One other place where this tends to crop is for code involving loop variables, but while the behavior might be unexpected to some, discussion has made clear that the code works as intended: --- void main() { import std.stdio; void delegate()[] dgs; foreach (i; 0 .. 5) dgs ~= { writeln(i); }; foreach (dg; dgs) dg(); } --- If structs behaved like you want them to, the snippet would (have to) print 0, 1, 2, 3, 4 as well, and tht's definitely too big a language change to consider at this stage. David
No, the current behaviour is an implementation bug and the expected behaviour is indeed to print the numbers from zero to four. The foreach iteration variable is stored in loop iteration scope since 2.063. The frontend never allocates closures in such a scope even though it is required for basic type safety. Bug report: http://d.puremagic.com/issues/show_bug.cgi?id=2043 Past discussion: http://forum.dlang.org/thread/felqszcrbvtrepjtfpul forum.dlang.org Your example can be changed to use a for loop, and then it indeed illustrates your point. for(int i=0;i<5;i++) dgs ~= { writeln(i); }; foreach (dg; dgs) dg(); // prints 5 five times
Oct 30 2013
prev sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Wednesday, 30 October 2013 at 09:20:40 UTC, Lionello Lunesu 
wrote:
 On 10/29/13, 22:42, David Nadlinger wrote:
 On Tuesday, 29 October 2013 at 21:41:25 UTC, Lionello Lunesu 
 wrote:
 So a copy should have been made of the live object, not the 
 destructed
 one. Correct?
No. There should only be one struct instance, the one living (forever) in the closure context. David
Why? It's a struct. It should be completely fine to create a copy [on the heap] for the closure context.
That would be worse: void increment(ref int x) { ++x; } void run(void delegate() f) { f(); } void main() { int x = 0; run( { increment(x); } ); writeln(x); } If the closure took a copy of x then this would write 0. Certainly not what I would expect. I think not running the destructor is the best option (although to be honest, I'm not a huge fan of closures to begin with, for exactly these sorts of reasons -- they only really work well in a pure functional setting).
Oct 30 2013
next sibling parent "Brian Rogoff" <brogoff gmail.com> writes:
On Wednesday, 30 October 2013 at 20:35:14 UTC, Peter Alexander 
wrote:
 I think not running the destructor is the best option (although 
 to be honest, I'm not a huge fan of closures to begin with, for 
 exactly these sorts of reasons -- they only really work well in 
 a pure functional setting).
I disagree. Closures work well in Scheme (more generally, Lisps) and ML, which are not purely functional languages. In SICP *, the combination of closures and mutable state is used to model OO. I used closures more than I used OO in OCaml. Things are trickier in D for a trickier of reasons. You may be right about not running the destructor; I'm still thinking about it. -- Brian * The Structure and Interpretation of Computer Programs by Abelson and Sussman, for those who don't know.
Oct 30 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-10-30 21:35, Peter Alexander wrote:

 I think not running the destructor is the best option (although to be
 honest, I'm not a huge fan of closures to begin with, for exactly these
 sorts of reasons -- they only really work well in a pure functional
 setting).
I use Ruby all day with a lot of blocks (closures) and I never had any problem. In Ruby everything is an object and passed around by reference. I guess that's help. BTW, the default iteration pattern in Ruby is to use blocks: [1, 2, 3].each { |e| puts e } Closest translation in D: [1, 2, 3].each!(e => writeln(e)); But in D one would of course use a foreach loop instead. -- /Jacob Carlborg
Oct 31 2013
next sibling parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Thursday, 31 October 2013 at 07:41:30 UTC, Jacob Carlborg 
wrote:
 But in D one would of course use a foreach loop instead.
I think `std.algorithm.copy` is generally a good replacement for foreach loops in functional D code.
Oct 31 2013
prev sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Thursday, 31 October 2013 at 07:41:30 UTC, Jacob Carlborg 
wrote:
 On 2013-10-30 21:35, Peter Alexander wrote:

 I think not running the destructor is the best option 
 (although to be
 honest, I'm not a huge fan of closures to begin with, for 
 exactly these
 sorts of reasons -- they only really work well in a pure 
 functional
 setting).
I use Ruby all day with a lot of blocks (closures) and I never had any problem. In Ruby everything is an object and passed around by reference. I guess that's help. BTW, the default iteration pattern in Ruby is to use blocks: [1, 2, 3].each { |e| puts e } Closest translation in D: [1, 2, 3].each!(e => writeln(e)); But in D one would of course use a foreach loop instead.
Those aren't closures, it captures none of the environment. Those are just simple lambda functions.
Oct 31 2013
parent "Chris Nicholson-Sauls" <ibisbasenji gmail.com> writes:
On Thursday, 31 October 2013 at 20:56:11 UTC, Peter Alexander 
wrote:
 On Thursday, 31 October 2013 at 07:41:30 UTC, Jacob Carlborg 
 wrote:
 I use Ruby all day with a lot of blocks (closures) and I never 
 had any problem. In Ruby everything is an object and passed 
 around by reference. I guess that's help.

 BTW, the default iteration pattern in Ruby is to use blocks:

 [1, 2, 3].each { |e| puts e }

 Closest translation in D:

 [1, 2, 3].each!(e => writeln(e));

 But in D one would of course use a foreach loop instead.
Those aren't closures, it captures none of the environment. Those are just simple lambda functions.
Maybe this would be a better Ruby example? module App def self.run register_callbacks Other.call_all end def self.register_callbacks foo = 10 Other.register { puts foo } Other.register { puts self } foo = 20 Other.register { puts foo } foo = 30 end end module Other callbacks = [] def self.register (&blk) callbacks << blk end def self.call_all callbacks.each {|cb| cb.call } end end App.run if __FILE__ == $0 Output is: 30 App 30 -- Chris NS
Nov 02 2013
prev sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
On Tuesday, 29 October 2013 at 21:37:43 UTC, Ali Çehreli wrote:
 To add to that, Maxim Fomin notes in the D.learn forum that 
 there are the following conflicting requirements:

 1) need to allocate struct into heap due to lambda

 2) need to put dtor invocation in the end as usual.

 The first one is supported by the closure spec. The second one 
 is the well-know struct destruction upon leaving a scope.
Closures follow the infinite lifetime model – the struct scope is never left, if you want. This is not exactly a new scenario, destructors on new'd structs aren't called either (unless you manually destroy them). David
Oct 29 2013
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/29/2013 02:41 PM, David Nadlinger wrote:

 Closures follow the infinite lifetime model – the struct scope is never
 left, if you want.
Agreed but the implicit nature of things is troubling. Imagine that the program depends on the destructor to be called: void foo() { auto s = S(1); // ... } Later, somebody creates a lambda that silently extends the lifetime of the stack frame (apparently, infinitely). That may be very surprising. Aside: Is it still a closure if the lambda does not refer to the stack frame? If so, it is even worse: import std.stdio; struct S { int i; ~this() { writeln("dtor"); } } void bar() {} auto foo() { S s = S(1); return &bar; // <-- all is well, this is not a closure } void main() { foo()(); } Imagine someone decides to return a lambda from foo() instead: auto foo() { S s = S(1); return {}; // <-- Should 's' be immortal now? } Too subtle for my taste! :)
 This is not exactly a new scenario, destructors on new'd structs aren't
 called either (unless you manually destroy them).
At least in the case of a new'ed structs I have a convenient way to destroy them through a pointer. I think in the case of a closure I must go through hoops to access or save that pointer. Ali
Oct 29 2013
next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 29 October 2013 at 21:57:25 UTC, Ali Çehreli wrote:
 On 10/29/2013 02:41 PM, David Nadlinger wrote:

 Closures follow the infinite lifetime model – the struct
scope is never
 left, if you want.
Agreed but the implicit nature of things is troubling. Imagine that the program depends on the destructor to be called:
It may be confusing but I agree that disabling dtor invocation at the end of function scope is a right decision. Technically scope is left, but lifetime continues.
 This is not exactly a new scenario, destructors on new'd
structs aren't
 called either (unless you manually destroy them).
At least in the case of a new'ed structs I have a convenient way to destroy them through a pointer. I think in the case of a closure I must go through hoops to access or save that pointer. Ali
This is not a big deal. Another but similar case: import std.stdio; struct S { int i; ~this() { writefln("dtor %X, %X", &this, i); } } auto foo() { S s = S(1); struct SS { void bar() { s = S(2); } } return SS(); } void main() { foo().bar(); } So, one need also to be aware of nested structs (or functions) touching local objects.
Oct 29 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-10-29 22:57, Ali Çehreli wrote:

 Imagine someone decides to return a lambda from foo() instead:

 auto foo()
 {
      S s = S(1);
      return {};    // <-- Should 's' be immortal now?
 }

 Too subtle for my taste! :)
Of course not. "s" is never referred to in the returned delegate. -- /Jacob Carlborg
Oct 30 2013
parent "David Nadlinger" <code klickverbot.at> writes:
On Wednesday, 30 October 2013 at 07:49:48 UTC, Jacob Carlborg 
wrote:
 On 2013-10-29 22:57, Ali Çehreli wrote:

 Imagine someone decides to return a lambda from foo() instead:

 auto foo()
 {
     S s = S(1);
     return {};    // <-- Should 's' be immortal now?
 }

 Too subtle for my taste! :)
Of course not. "s" is never referred to in the returned delegate.
Yes, only variables actually referenced by a nested fiction are placed in the nested context. I agree that the fact that an object's destructor is not run at the end of the parent function should ideally be more easily visible, but given the inherently implicit nature of D's closures, I'm not sure we can do much about that. David
Oct 30 2013
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/29/2013 10:55 AM, Ali Çehreli wrote:

 Continuing the conversation from the following thread:

    http://forum.dlang.org/post/l4mi8l$1r1$1 digitalmars.com
Kenji Hara added there: ===== The combination of closure variables + scoped destruction should be rejected, but currently it isn't. It's a compiler bug. http://d.puremagic.com/issues/show_bug.cgi?id=11382 Kenji Hara ===== Ali
Oct 30 2013
parent reply "Xiaoxi" <xiaoxi 163.com> writes:
On Wednesday, 30 October 2013 at 21:15:37 UTC, Ali Çehreli wrote:
 On 10/29/2013 10:55 AM, Ali Çehreli wrote:

 Continuing the conversation from the following thread:

    http://forum.dlang.org/post/l4mi8l$1r1$1 digitalmars.com
Kenji Hara added there: ===== The combination of closure variables + scoped destruction should be rejected, but currently it isn't. It's a compiler bug. http://d.puremagic.com/issues/show_bug.cgi?id=11382 Kenji Hara ===== Ali
kenji is right. compilation error is the only safe approach, if you do not wish scoped destruction, don’t request it in the first place! dangerous operations like this should be explicit to avoid surprises, not hidden in complicared implicit special cases.
Oct 30 2013
next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 31 October 2013 at 00:46:05 UTC, Xiaoxi wrote:
 On Wednesday, 30 October 2013 at 21:15:37 UTC, Ali Çehreli 
 wrote:
 On 10/29/2013 10:55 AM, Ali Çehreli wrote:

 Continuing the conversation from the following thread:

   http://forum.dlang.org/post/l4mi8l$1r1$1 digitalmars.com
Kenji Hara added there: ===== The combination of closure variables + scoped destruction should be rejected, but currently it isn't. It's a compiler bug. http://d.puremagic.com/issues/show_bug.cgi?id=11382 Kenji Hara ===== Ali
kenji is right. compilation error is the only safe approach, if you do not wish scoped destruction, don’t request it in the first place! dangerous operations like this should be explicit to avoid surprises, not hidden in complicared implicit special cases.
This is wrong in a point that accessing object or not running destructor is not safe: closures can touch any object, not only structs, making them allocated in heap, and there are many cases when struct destructors are not called.
Oct 30 2013
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 10/31/2013 01:46 AM, Xiaoxi wrote:
 =====
 The combination of closure variables + scoped destruction should
 be rejected, but currently it isn't. It's a compiler bug.

 http://d.puremagic.com/issues/show_bug.cgi?id=11382

 Kenji Hara
 =====

 Ali
kenji is right.
I think nobody is 'right' here.
 compilation error is the only safe approach, if you do
 not wish scoped destruction, don’t request it in the first place!
Destruction is implicit and it is possible to allocate structs on the heap anyway.
 dangerous operations like this should be explicit to avoid surprises,
 not hidden in complicared implicit special cases.
If failing compilation is the way to go, avoiding special cases is not why. Erroring out here is a special case.
Oct 31 2013