__rvalue and Move Semantics first draft
Richard (Rikki) Andrew Cattermole
richard at cattermole.co.nz
Sun Nov 10 02:31:42 UTC 2024
On 10/11/2024 11:59 AM, Walter Bright wrote:
> On 11/9/2024 8:15 AM, Richard (Rikki) Andrew Cattermole wrote:
>> 1. We'll need to introduce a swap builtin, since we have no way to say
>> describe moves between parameters. This can come later, as it is an
>> addition.
>
> Doesn't a swap function get arguments passed by `ref`?
Yes, but for lifetime tracking, we need to be able to say the original
value isn't here anymore.
```d
int* a, b;
int* c = a, d = b;
swap(a, b);
// c has same variable state as a
// d has same variable state as b
```
In general moving is easy:
```d
int* move(?initialized,reachable ref int* input) {
return input;
}
```
But swap isn't.
```d
void swap(
?initialized,initialized @escape(b) ref int* a,
?initialized,initialized @escape(a) ref int* b);
```
>> 2. I have the concern that existing code that is not designed to
>> accept a move, will have a move into it. White listing via an
>> attribute ``@move`` to say that this constructor/opAssign is designed
>> to handle a move in would be valuable.
>
> This can work, but if the users have to proactively add this attribute,
> I'm afraid we've failed.
The alternative is to disallow constructor/opAssign that is in a D2
module and not by-ref to have __rvalue passed to it.
Tie it to a new edition.
Any function being called that is by-ref will work the same.
```d
module thing 2025;
struct Foo {
this(Foo input);
}
void main() {
Foo f;
Foo t = __rvalue(f); // move constructor call
}
```
```d
module thing 2;
struct Foo {
this(Foo input);
this(ref Foo input);
}
void main() {
Foo f;
Foo t = __rvalue(f); // copy constructor call
}
```
>> 3. Optimizing of eliding of destructors should be done with type state
>> analysis, it does not need its own dedicated DFA.
>
> The two are the same, aren't they?
Yes exactly.
When you converge (or other known points), you'd look to see what the
last destructor is, and if appropriete ``var.lastDestroy.disabled = true;``.
Type state analysis has the absolutely beautiful property that the
builtin states are 100% correct even in ``@system`` code.
It is _always_ an error to dereference a null pointer.
It is _always_ a logic error to read from uninitialized memory.
So it'll be run on all code, which means we can rely on it to do eliding
for stuff like this.
Same situation with RC.
```d
rc.opAddRef();
rc.opSubRef();
```
Same object, pair can be elided.
It is why the add needs to happen in the called function, because then
it can be elided without cross-function analysis.
More information about the dip.development
mailing list