Tell us your DIP1000 woes

Richard (Rikki) Andrew Cattermole richard at cattermole.co.nz
Thu Aug 29 08:53:09 UTC 2024


I've been thinking about swap/move functions some more for my own proposal.

The following applies to swap just as much as move, but I'll use move as 
its simpler to understand here.

Lets say you don't have owner escape analysis (so just DIP1000).
And you have some stack memory that then gets borrowed from.
You can corrupt the borrowed memory, by calling a function like move.

Simply because a dependency exists of the thing being moved/swapped.

```d
int* value = ...;
int** ptr = &value; // ptr depends upon value

int* another = move(value);
// ugh oh, ptr is now corrupted
```

Now lets say you are able to annotate the escape sets fully (DIP1000 can 
do move).

```d
T move(T)(ref return T input);
```

Thing is, this signature just says the parameter contributes towards the 
return, it doesn't consume and invalidate the original input variable.

So a solution is to throw a new attribute into the mix like 
``@escapeas(return)``. To show that it'll consume the input and it will 
go to a specific variable.

```d
void swap(T)(ref @escapeas(input2) T input1, ref @escapeas(input1) T 
input2);
```

Ok great, you modeled the escapes, congratulations.

Or you could use owner escape analysis to say "if have borrow, make 
effectively const" there cannot be assigned to.

Boom, neither swap nor move could modify the original and it all works.

So there are three scenarios here that I can think of:

1. Attribute the exact variable it'll escape to, to know that it will 
invalidate the input, preventing any relationships from it from being 
corrupted
2. Make these functions ``@system`` and say its a rare enough thing that 
it doesn't need to be made ``@safe``
3. Use owner escape analysis to provide the effectively const guarantees 
to prevent the mutation

Full example of a move in ``@safe`` code, that can corrupt memory (null, 
wrong length, wrong value ext.):

```d
@safe:

void main() {
     int*[1] value;
     int** ptr = &value[0];

     int* another = move(value[0]);
}

T move(T)(scope ref return T input) {
     T temp = input;
     input = T.init;
     return temp;
}
```


More information about the Digitalmars-d mailing list