Thread overview
[Issue 21175] opAssign should be allowed to return void and let the compiler take care of chained assignments
Aug 19, 2020
Paul Backus
Mar 24, 2021
Bolpat
Nov 18, 2021
Dennis
Dec 17, 2022
Iain Buclaw
Feb 22, 2023
Bolpat
August 18, 2020
https://issues.dlang.org/show_bug.cgi?id=21175

hsteoh@quickfur.ath.cx changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |hsteoh@quickfur.ath.cx

--
August 19, 2020
https://issues.dlang.org/show_bug.cgi?id=21175

Paul Backus <snarwin+bugzilla@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |snarwin+bugzilla@gmail.com

--- Comment #1 from Paul Backus <snarwin+bugzilla@gmail.com> ---
I think a lowering that works for all cases is

    e1 = e2

to

    auto ref typeof(e1) (auto ref typeof(e1) v1) {
        v1.opAssign(e2);
        return v1;
    }(e1)

This can be expanded recursively to handle any number of chained assignments (e.g., replace e2 with `e2a = e2b`).

The only catch is that D doesn't currently allow function literals that return `auto ref`.

--
March 24, 2021
https://issues.dlang.org/show_bug.cgi?id=21175

Bolpat <qs.il.paperinik@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |qs.il.paperinik@gmail.com

--
November 18, 2021
https://issues.dlang.org/show_bug.cgi?id=21175

Dennis <dkorpel@live.nl> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |dkorpel@live.nl

--- Comment #2 from Dennis <dkorpel@live.nl> ---
N.b. this is relevant to dip1000, since this works:

```
@safe:
struct S {
        int* x;
        void opAssign(return scope S other) scope {
                this.x = other.x;
        }
}

void main() {
        scope S b;
        scope S a;
        a = b;
}
```

But this doesn't:
```
@safe:
struct S {
        int* x;
        ref S opAssign(return scope S other) return scope {
                this.x = other.x;
                return this;
        }
}

void main() {
        scope S b;
        scope S a;
        a = b;
}
```

source/apps/retscope.d(7): Error: scope variable `other` assigned to `this`
with longer lifetime

The return destination of `return scope S other` was `this` when `opAssign` returned `void`, but now the return destination is the return value. Example in Phobos:

https://github.com/dlang/phobos/blob/62780daf85c4c57bbc805e1e6499a33a852a2802/std/datetime/systime.d#L696

The only reason this compiles currently is because of issue 20149

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

Iain Buclaw <ibuclaw@gdcproject.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Priority|P1                          |P4

--
February 22, 2023
https://issues.dlang.org/show_bug.cgi?id=21175

--- Comment #3 from Bolpat <qs.il.paperinik@gmail.com> ---
(In reply to Andrei Alexandrescu from comment #0)
> The return value of opAssign should always be `this` of type `ref T`. Literally anything else is a bug, and that makes for a poor convention. The compiler should take care of all that. Means user code is free of the convention and can return void.
> 
> Chained assignments should be lowered as follows. Currently e1 = e2 = e3 is lowered as:
> 
> e1.opAssign(e2.opAssign(e3))
> 
> It should be lowered as:
> 
> (ref T x) { x.opAssign(e3); e1.opAssign(x); }(e2)
> 
> meaning e2 is evaluated first, then e3 is evaluated and assigned to (the
> result of evaluating) e2, then then x is assigned to (the result of
> evaluating) e1.

The idea is right in principle, but what comes to my mind is C++ valarray. A
lot of operator= in there return void. I guess that’s because an assignment
operator should not return something else than a reference to the assigned
object. If that’s not possible, return `void`. D has better indexing operators
than C++:
`obj[index] = rhs` can lower to one function call of `opIndexAssign`, meaning
that the example is invalid in D; but that is due to index+assignment being a
special case in D. There are similar scenarios imaginable in which the issue is
relevant.

--