OT: Tuple Syntax and Placeholders (Re: First Draft: Tuple Unpacking Syntax)

Timon Gehr timon.gehr at gmx.ch
Wed Aug 20 15:58:30 UTC 2025


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`.


More information about the dip.development mailing list