Thread overview
Surprise destructor call...
Aug 14
Manu
Aug 14
Dennis
Aug 14
user1234
Aug 14
Dennis
Aug 14
user1234
Aug 14
Manu
Aug 14
kinke
Aug 14
Manu
August 14
Can anyone illuminate me as to where this destructor call is coming from?

struct Thing
{
  static int x;
  this(typeof(null)) pure {} // <-- comment this line and the error goes
away
  ~this() { ++x; }
}

Thing fun() pure
{
   return Thing(null);
}

error : `pure` function `urt.string.string.fun` cannot call impure destructor `urt.string.string.Thing.~this`

Shouldn't NVRO construct the result in place and elide the copy/move? I
would not expect any call to the destructor in fun()...
Also surprisingly, if I comment out the constructor, the compile error
about the destructor goes away. I can't see why the constructor's existence
affects the destruction semantics in fun()?


August 14

On Wednesday, 14 August 2024 at 09:19:55 UTC, Manu wrote:

>

Also surprisingly, if I comment out the constructor, the compile error
about the destructor goes away. I can't see why the constructor's existence
affects the destruction semantics in fun()?

The compiler's logic goes as follows:

return Thing(null);

Since Thing has a constructor, this gets rewritten to:

return Thing().this(null);

Now we have a 'DotVarExp' with a struct literal on the left, and the struct has a destructor. Since internally expression temporaries can't have destructors, it gets extracted into a temporary variable:

return ((Thing __slThing3 = Thing();) , __slThing3).this(null);

Then __slThing3 goes out of scope and needs a destructor call.

So clearly, in this case you don't want the temporary, but in other cases (return Thing(null).field;) you do need it, so I'm thinking about what the right conditions should be for the temporary.

August 14

On Wednesday, 14 August 2024 at 11:52:51 UTC, Dennis wrote:

>

[...]
Now we have a 'DotVarExp' with a struct literal on the left, and the struct has a destructor. Since internally expression temporaries can't have destructors, it gets extracted into a temporary variable:

return ((Thing __slThing3 = Thing();) , __slThing3).this(null);

Then __slThing3 goes out of scope and needs a destructor call.

So clearly, in this case you don't want the temporary, but in other cases (return Thing(null).field;) you do need it, so I'm thinking about what the right conditions should be for the temporary.

The hidden temporary is not represented in the -vcg-ast output. That would have helped to understand the issue.

August 14

On Wednesday, 14 August 2024 at 12:42:13 UTC, user1234 wrote:

>

The hidden temporary is not represented in the -vcg-ast output. That would have helped to understand the issue.

https://github.com/dlang/dmd/pull/16782

August 14
On Wed, 14 Aug 2024 at 21:56, Dennis via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Wednesday, 14 August 2024 at 09:19:55 UTC, Manu wrote:
> > Also surprisingly, if I comment out the constructor, the
> > compile error
> > about the destructor goes away. I can't see why the
> > constructor's existence
> > affects the destruction semantics in fun()?
>
> The compiler's logic goes as follows:
>
> ```D
> return Thing(null);
> ```
>
> Since `Thing` has a constructor, this gets rewritten to:
>
> ```D
> return Thing().this(null);
> ```
>
> Now we have a 'DotVarExp' with a struct literal on the left, and the struct has a destructor. Since internally expression temporaries can't have destructors, it gets extracted into a temporary variable:
>
> ```D
> return ((Thing __slThing3 = Thing();) , __slThing3).this(null);
> ```
>
> Then `__slThing3` goes out of scope and needs a destructor call.
>
> So clearly, in this case you don't want the temporary, but in
> other cases (`return Thing(null).field;`) you do need it, so I'm
> thinking about what the right conditions should be for the
> temporary.
>

Well, the condition is that NRVO should elide the copy/move... it should be
constructed at the caller's scope in this case.
Your example `Thing(null).field` is not the same thing, because it's not
NRVO at all.

I guess this is a bug then?


August 14

On Wednesday, 14 August 2024 at 13:56:08 UTC, Manu wrote:

>

Well, the condition is that NRVO should elide the copy/move... it should be
constructed at the caller's scope in this case.
Your example Thing(null).field is not the same thing, because it's not
NRVO at all.

I guess this is a bug then?

When actually using NRVO (auto r = Thing(null); return r;), one even gets the error twice. So that's definitely a bug, the destruction is handled by the caller; just for the attributes check etc., there's no real dtor call in fun().

What you have is a case for RVO; AFAIK, LDC and GDC implement that (for non-POD types at least), no idea about DMD. Meaning that the temporary in the return expression isn't destructed by foo either; it's emplaced directly into the caller-allocated return value, as the NRVO case.

August 15
On Thu, 15 Aug 2024 at 00:31, kinke via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Wednesday, 14 August 2024 at 13:56:08 UTC, Manu wrote:
> > Well, the condition is that NRVO should elide the copy/move...
> > it should be
> > constructed at the caller's scope in this case.
> > Your example `Thing(null).field` is not the same thing, because
> > it's not
> > NRVO at all.
> >
> > I guess this is a bug then?
>
> When actually using NRVO (`auto r = Thing(null); return r;`), one
> even gets the error *twice*. So that's definitely a bug, the
> destruction is handled by the caller; just for the attributes
> check etc., there's no real dtor call in `fun()`.
>
> What you have is a case for RVO; AFAIK, LDC and GDC implement that (for non-POD types at least), no idea about DMD. Meaning that the temporary in the return expression isn't destructed by `foo` either; it's emplaced directly into the caller-allocated return value, as the NRVO case.
>

My understanding is that RVO is in the D spec and NOT simply an optimisation. The language shouldn't attempt to call a destructor here in my case under any circumstances...

Walter?


August 14

On Wednesday, 14 August 2024 at 13:25:18 UTC, Dennis wrote:

>

On Wednesday, 14 August 2024 at 12:42:13 UTC, user1234 wrote:

>

The hidden temporary is not represented in the -vcg-ast output. That would have helped to understand the issue.

https://github.com/dlang/dmd/pull/16782

let's get the merge 🤙