Bug 9704 – Destructor not called on function calls if postblit throws

Status
RESOLVED
Resolution
WORKSFORME
Severity
normal
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2013-03-12T11:35:39Z
Last change time
2022-09-08T14:39:08Z
Assigned to
No Owner
Creator
Johannes Pfau
See also
http://d.puremagic.com/issues/show_bug.cgi?id=10409, https://issues.dlang.org/show_bug.cgi?id=14903

Attachments

IDFilenameSummaryContent-TypeSize
1201test99.dtest casetext/x-dsrc330

Comments

Comment #0 by johannespfau — 2013-03-12T11:35:39Z
See the attached testcase: If the throw statement in B's postblit is commented we see the expected result: A:this(this) B:this(this) A:~this B:~this B:~this A:~this The function call creates two copies, then the destructors are called for the originals and for the copies. But if the throw statement is in place: A:this(this) B:this(this) B:~this A:~this [email protected](12): Boom We see that the destructors for the copies are not called. It can be argued that this is valid for the B copy, as it's postblit threw. But for the copy of A the destructor must be called as the postblit was called and succeeded. The problem is that the destructors are currently called in the callee but the postblits in the caller. If an exception is thrown in between those calls, the destructor can't be called. A possible solution is to move the destructor to the caller. But in this case the parameter (the copy) must be passed by invisible reference so that the destructor actually sees the updated value after the callee returns. (Note that it might be useful to make an exception for non-PODs without destructors here. Those shouldn't be affected by this issue and could be treated like PODs when being passed to a function as an optimization.)
Comment #1 by johannespfau — 2013-03-12T11:36:29Z
Created attachment 1201 test case
Comment #2 by maxim — 2013-03-12T13:13:41Z
(In reply to comment #0) > The problem is that the destructors are currently called in the callee but the > postblits in the caller. If an exception is thrown in between those calls, the > destructor can't be called. To be more precise, it happens during evaluation of function argument which follows some struct argument and throws exception. extern(C) int printf(const char* fmt, ...); struct A { this(this) { printf("A:this(this)\n");} ~this() { printf("A:~this\n"); } uint data; } int foo() { throw new Exception(""); } void func(A a, int i) {} void main() { A a; func(a, foo()); } Here should be two destructors: for "a" and " __cpcttmpXXX" structs. And calling dtor is unavoidable since postblit may deep copy some memory. > A possible solution is to move the destructor to the caller. But in this case > the parameter (the copy) must be passed by invisible reference so that the > destructor actually sees the updated value after the callee returns. > > (Note that it might be useful to make an exception for non-PODs without > destructors here. Those shouldn't be affected by this issue and could be > treated like PODs when being passed to a function as an optimization.) Alternatively a copy operation can be moved into callee or D runtime can be boosted to cope with such problem.
Comment #3 by monarchdodra — 2013-07-12T11:05:06Z
*** Issue 10623 has been marked as a duplicate of this issue. ***
Comment #4 by monarchdodra — 2013-07-12T11:07:53Z
(In reply to comment #3) > *** Issue 10623 has been marked as a duplicate of this issue. *** For reference, this was also discussed in learn. http://forum.dlang.org/group/digitalmars.D.learn http://forum.dlang.org/thread/20130628005448.00000969@unknown
Comment #5 by andrei — 2013-07-12T14:17:12Z
Pasting the additional example here for reference: import std.stdio; struct S1 { this(int) { writeln("constructed"); } ~this() { writeln("destroyed"); } } struct S2 { this(int) { throw new Exception("a"); } } void fun(S1, S2, S1) {} void main() { fun(S1(2), S2(2), S1(2)); }
Comment #6 by razvan.nitu1305 — 2022-09-08T14:39:08Z
Running the code in in comment 2 yields: A:this(this) A:~this A:~this [email protected](10) So we can see that both the temporary and `a` are destroyed. This seems to be fixed.