Jump to page: 1 2
Thread overview
[Issue 15848] Identity opAssign not called on out parameters
[Issue 15848] out doesn't call opAssign()
Mar 29, 2016
ag0aep6g@gmail.com
Mar 31, 2016
Alex Parrill
Apr 03, 2016
Marc Schütz
Jun 15, 2016
Mathias Lang
Jun 15, 2016
Mathias Lang
Jul 05, 2017
Vladimir Panteleev
Nov 07, 2022
RazvanN
Nov 07, 2022
ag0aep6g
Dec 17, 2022
Iain Buclaw
March 29, 2016
https://issues.dlang.org/show_bug.cgi?id=15848

lasssafin@gmail.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |lasssafin@gmail.com

--- Comment #1 from lasssafin@gmail.com ---
I'm not sure I follow: Why should opAssign be called?

--
March 29, 2016
https://issues.dlang.org/show_bug.cgi?id=15848

ag0aep6g@gmail.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ag0aep6g@gmail.com

--- Comment #2 from ag0aep6g@gmail.com ---
(In reply to lasssafin from comment #1)
> I'm not sure I follow: Why should opAssign be called?

Either a new Test is constructed by foo, but then the destructor should be called on the old Test. Or the existing Test is used, but then opAssign should be called instead of just writing Test.init over the old value.

I think the spec [1] is rather clear about which one should happen:

> out	parameter is initialized upon function entry with the default value for its type

So I think the destructor should be called.


1 http://dlang.org/spec/function.html#parameters

--
March 31, 2016
https://issues.dlang.org/show_bug.cgi?id=15848

Alex Parrill <initrd.gz@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |initrd.gz@gmail.com

--- Comment #3 from Alex Parrill <initrd.gz@gmail.com> ---
I don't think opAssign should be called here. Default initialization is not assignment; declaring a variable does not call opAssign with T.init, it just copies over T.init.

So the real issue is that `t`'s destructor is not being called when `foo(t)` is
ran.

--
April 03, 2016
https://issues.dlang.org/show_bug.cgi?id=15848

--- Comment #4 from Marc Schütz <schuetzm@gmx.net> ---
I don't feel strongly either way. But the specification is not clear IMO, "initialized" could be understood as construction as well as a "first assignment".

--
June 15, 2016
https://issues.dlang.org/show_bug.cgi?id=15848

Mathias Lang <mathias.lang@sociomantic.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |mathias.lang@sociomantic.co
                   |                            |m
           Hardware|x86_64                      |All
            Summary|out doesn't call opAssign() |Identity opAssign not
                   |                            |called on out parameters
                 OS|Linux                       |All

--- Comment #5 from Mathias Lang <mathias.lang@sociomantic.com> ---
Note: Your `opAssign` is not an identity `opAssign` (http://dlang.org/spec/struct.html#assign-overload), so it wouldn't be called in any copying situation.

Correct test code:

```
import std.stdio;

void foo(out Test x) {
    writeln("x.n = ", x.n);
}

struct Test {
    int n;
    ~this() {
        writeln("~this()");
    }
    ref Test opAssign(Test val) {
        writefln("opAssign(%s)", val);
        return this;
    }
}

void main() {
    Test t;
    foo(t);
    writeln("done");
}
```

And this doesn't call `opAssign` either (same output).

What should happen here:
- The destructor should be called if no `opAssign` is defined, because the
compiler provides an elaborate `opAssign` when it sees a struct with a dtor or
postblit being copied.
- If an identity `opAssign` is defined, it should be called.

I'll change the title, because `out` can be contract as well.

--
June 15, 2016
https://issues.dlang.org/show_bug.cgi?id=15848

Mathias Lang <mathias.lang@sociomantic.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Assignee|nobody@puremagic.com        |mathias.lang@sociomantic.co
                   |                            |m

--- Comment #6 from Mathias Lang <mathias.lang@sociomantic.com> ---
Note: Your `opAssign` is not an identity `opAssign` (http://dlang.org/spec/struct.html#assign-overload), so it wouldn't be called in any copying situation.

Correct test code:

```
import std.stdio;

void foo(out Test x) {
    writeln("x.n = ", x.n);
}

struct Test {
    int n;
    ~this() {
        writeln("~this()");
    }
    ref Test opAssign(Test val) {
        writefln("opAssign(%s)", val);
        return this;
    }
}

void main() {
    Test t;
    foo(t);
    writeln("done");
}
```

And this doesn't call `opAssign` either (same output).

What should happen here:
- The destructor should be called if no `opAssign` is defined, because the
compiler provides an elaborate `opAssign` when it sees a struct with a dtor or
postblit being copied.
- If an identity `opAssign` is defined, it should be called.

I'll change the title, because `out` can be contract as well.

--
July 05, 2017
https://issues.dlang.org/show_bug.cgi?id=15848

Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |wrong-code

--
November 07, 2022
https://issues.dlang.org/show_bug.cgi?id=15848

RazvanN <razvan.nitu1305@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
                 CC|                            |razvan.nitu1305@gmail.com
         Resolution|---                         |WORKSFORME

--- Comment #7 from RazvanN <razvan.nitu1305@gmail.com> ---
I think that the behavior exhibited here is correct.

The spec for out parameters says: "The argument must be an lvalue, which will be passed by reference and initialized upon function entry with the default value (T.init) of its type."

Although it is not clearly stated, the way I read it is that the compiler simply blits T.init over the argument and then it passes a reference to it. No copy constructor and no assignment operator are called. Therefore, the destructor is called only for `t` to destroy the object in the main function.

This is correct behavior and much more efficient as it avoids a copy constructor/assignment call and a destructor call.

I'm going to tentatively close this as WORKSFORME. But please reopen if I am missing something.

--
November 07, 2022
https://issues.dlang.org/show_bug.cgi?id=15848

ag0aep6g <ag0aep6g@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|WORKSFORME                  |---

--- Comment #8 from ag0aep6g <ag0aep6g@gmail.com> ---
(In reply to RazvanN from comment #7)
> Although it is not clearly stated, the way I read it is that the compiler simply blits T.init over the argument and then it passes a reference to it. No copy constructor and no assignment operator are called. Therefore, the destructor is called only for `t` to destroy the object in the main function.
> 
> This is correct behavior and much more efficient as it avoids a copy constructor/assignment call and a destructor call.
> 
> I'm going to tentatively close this as WORKSFORME. But please reopen if I am missing something.

Reopening. It's *because* no copy constructor and no assignment operator are being called that the destructor must  be called.

You can't just blit T.init over an existing value without calling its destructor first.

By the way, if the observed behavior were correct, the proper status to close this would be INVALID. WORKSFORME is when you cannot reproduce the described behavior.

--
December 17, 2022
https://issues.dlang.org/show_bug.cgi?id=15848

Iain Buclaw <ibuclaw@gdcproject.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Priority|P1                          |P3

--
« First   ‹ Prev
1 2