Issue 21989 - [REG 2.096] Double destruction of new'ed aggregates since `-preview=dtorfields` became the default
Summary: [REG 2.096] Double destruction of new'ed aggregates since `-preview=dtorfield...
Status: NEW
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P1 regression
Assignee: No Owner
URL:
Keywords: industry, pull, wrong-code
Depends on:
Blocks:
 
Reported: 2021-06-02 11:53 UTC by Mathias LANG
Modified: 2023-11-11 06:46 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Mathias LANG 2021-06-02 11:53:51 UTC
```D
import std;

pragma(msg, __VERSION__);

class Test {
    S s;
    
    this()
    {
        if (uniform(1, 10) != 0)
        	throw new Exception("oops");
        s = S(13);
    }
}

struct S {
    int x = 42;
    int y;
    this(int y) { this.y = y; };
    this(this) { assert(x == 42); }
    ~this()
    {
        if (x != 42) writeln("OH NO!");
        x = 0; // omitting this makes "OH NO!" go away
    }
}

void main()
{
    try new Test;
    catch (Exception e) writeln("Expected fail: ", e.msg);
}
```

Result:
```
2096L
Expected fail: oops
OH NO!
```

This happens because the class ctor destructs it, then the GC, upon collecting the object, finalize the class again.
Comment 1 Mathias LANG 2021-06-02 11:54:25 UTC
Bug found in a large application using Vibe.d: https://github.com/vibe-d/vibe-core/issues/283
Comment 2 moonlightsentinel 2021-06-02 12:40:42 UTC
Thats bad. I guess the destructed class instance should be GC.free'd if the ctor is aborted by an exception. Alternatively could also reset the instance to it's init value.
Comment 3 moonlightsentinel 2021-06-06 11:30:07 UTC
Not restricted to classes, also affects GC-allocated structs
Comment 4 Dlang Bot 2021-06-06 15:09:57 UTC
@MoonlightSentinel created dlang/dmd pull request #12641 "Fix 21989 - Reset destructed instance with preview=dtorfields" fixing this issue:

- Fix 21989 - Reset destructed instance with preview=dtorfields
  
  Emplacing the initializer ensures that the GC won't run the destructor
  on an invalid instance.
  
  Preventing the destructor call from the GC doesn't seem to be feasible
  due to the potentually lesser qualified GC interface (not `pure`, ...).

https://github.com/dlang/dmd/pull/12641
Comment 5 Mai Lapyst 2023-11-11 06:46:46 UTC
It seems that in 2.103.0 this bug was fixed; atleast the provided example doesn't produces the "OH NO!" anymore:

```
2103L
Expected fail: oops
```