August 02

On Friday, 1 August 2025 at 10:36:37 UTC, Nick Treleaven wrote:

>

On Friday, 1 August 2025 at 10:08:09 UTC, Nick Treleaven wrote:
Sorry, wrong code above.

    T a, b;
    alias seq = AliasSeq!(a, b);
    // auto (x, y) = seq;
    auto x = seq[0];
    auto y = seq[1];
>

a[0] and a[1] are not moved by the lowered code.

So obviously a and b are lvalues and shouldn't be moved, sorry. Other cases:

import std;

struct S
{
    int i;
    this(ref S s) { writeln("copy", s.i); }
}

void main()
{
    writeln("rvalue");
    auto a = tuple(S(1))[0]; // copy, not move!
    writeln("lvalue");
    auto t = tuple(S(2));
    writeln("index");
    auto b = t[0]; // copy, OK
    writeln("seq");
    auto c = AliasSeq!(S(3))[0]; // move, OK
}

So the DIP should move when unpacking into c. For a, I think we would need tuple literals to avoid a copy.

3 days ago
On 8/2/25 00:06, Richard (Rikki) Andrew Cattermole wrote:
> On 01/08/2025 10:08 PM, Nick Treleaven wrote:
>> On Sunday, 27 July 2025 at 21:52:15 UTC, Richard (Rikki) Andrew Cattermole wrote:
>>> 2. Moving elements should be in DIP even if implementation doesn't support it. I don't think anything special needs to be done here. My understanding is the compiler should be seeing the VarDeclaration -> VarDeclaration assignment and handle it normally.
>>
>> I think you are asking for something special:
>>
>> ```d
>>      T a, b;
>>      alias seq = AliasSeq!(a, b);
>>      // auto (x, y) = a;
>>      auto x = a[0];
>>      auto y = a[1];
>> ```
>>
>> `a[0]` and `a[1]` are not moved by the lowered code.
>>
>> Do you have an example where there should be a move?
> 
> You are getting to what I was thinking, there is no reason to mention copying or moving. Its defined behavior elsewhere.
> 

The _limitations_ section mentions moves because there is no move-unpack.

`auto (x, y) = move(a);` moves `a` into a temporary, copies `a[0]` and `a[1]`, then destroys the temporary:

```d
import std.stdio;
import core.lifetime;

struct S{
    this(this){
        writeln("S copied");
    }
    ~this(){
        writeln("S destroyed");
    }
}

struct Tuple(T...) {
    T expand;
    alias expand this;
    this(this){
        writeln("Tuple copied");
    }
    ~this(){
        writeln("Tuple destroyed");
    }
}

void main(){
    Tuple!(S, S) t;
    auto (a, b) = move(t);
}
```

```
$ dmd -run test.d
S copied
S copied
S destroyed
S destroyed
Tuple destroyed
S destroyed
S destroyed
Tuple destroyed
S destroyed
S destroyed
```

I.e, it copies the two fields into a temporary, then it destroys the temporary.


What it would look like without the limitation:

```
$ dmd -run test.d
S destroyed
S destroyed
Tuple destroyed
S destroyed
S destroyed
Tuple destroyed
S destroyed
S destroyed
```

The pie-in-the-sky ideal output:

```
S destroyed
S destroyed
```

I also noticed that this causes an ICE in the DMD backend atm, will have to fix it before upstreaming the implementation:

```d
import std.stdio;
import core.lifetime;

struct S{
    this(ref S){
        writeln("S copied");
    }
    this(S){
        writeln("S moved");
    }
    ~this(){
        writeln("S destroyed");
    }
}

struct Tuple(T...) {
    T expand;
    alias expand this;
    this(ref Tuple rhs){
        this.expand=rhs.expand;
        writeln("Tuple copied");
    }
    this(Tuple rhs){
        static foreach(i;0..expand.length)
            this.expand[i]=move(rhs.expand[i]);
        writeln("S moved");
    }
    ~this(){
        writeln("Tuple destroyed");
    }
}

void main(){
    Tuple!(S, S) t;
    auto (a, b) = move(t);
}
```

Once this works though it will still have copies in it, as you can verify by annotating the copy constructor of `S` with `@disable`.
3 days ago
On 7/31/25 13:46, IchorDev wrote:
> I really don't understand why it wouldn't be supported

Because it is not implemented and there is not yet a spec for it. Also, move semantics is still somewhat in flux.

I guess one way to do it for tuples would be to move each field on its own if the unpack rhs is an rvalue. Not sure how to implement that, but I can try during the dconf hackathon. I guess would need to call the move constructor and then blit `.init` on the source field.

Ideally though it would not even run the destructor on the moved fields anymore. Eliding all the destructor calls would need some cooperation from the tuple type though.

I had hoped there would be some way to move on last use and avoid any further destructor calls, but it's not really where we have been moving so far and therefore there has been no progress on interations of unpacking with moves either.
2 hours ago

On Wednesday, 20 August 2025 at 16:11:42 UTC, Timon Gehr wrote:

>

On 7/31/25 13:46, IchorDev wrote:

>

I really don't understand why it wouldn't be supported

Because it is not implemented and there is not yet a spec for it.

That's the case of the whole DIP. It sounds like they're proposing tuple unpacking whereby it always requires a copy; which would defeat some of the point of having functions return tuples in the future, because it'd be no better (from a code optimisation standpoint) than just placing the return values in a struct & unpacking manually.

>

I guess one way to do it for tuples would be to move each field on its own if the unpack rhs is an rvalue. Not sure how to implement that, but I can try during the dconf hackathon. I guess would need to call the move constructor and then blit .init on the source field.

Ideally though it would not even run the destructor on the moved fields anymore. Eliding all the destructor calls would need some cooperation from the tuple type though.

Yes. That sounds amazing. Please, please do all of that.

>

I had hoped there would be some way to move on last use and avoid any further destructor calls, but it's not really where we have been moving so far

Alas...

1 2
Next ›   Last »