DIP1000: The return of 'Extend Return Scope Semantics'

Dennis dkorpel at gmail.com
Tue May 25 11:24:59 UTC 2021


## Background

The `return` attribute allows you to return a scope variable.
The compiler knows that the returned value has the same lifetime 
as the argument passed to the function.
```D
// ┌───────────<──────────┐
int* identity(return int* x) @safe {
     return x;
}

void main() @safe {
     int x;
     //   ┌──────<──────┐
     int* y = identity(&x);
     static int* z; // global variable
     z = y;         // error! scope variable `y` assigned to 
non-scope `z`
}
```

What if you are not using a return value, but setting it to 
another parameter, e.g. using `ref` or `out`?

```D
//                           ┌─────────<─────────┐
void assign(ref scope int* target, return int* source) @safe {
     target = source;
}
```
The compiler knows that `source` cannot be returned since it's a 
`void` function, so it makes the first parameter, _but only the 
first parameter_, the destination of the `return` parameter 
`source`.
```D
void main() @safe {
     int x;
     int* y;
     //     ┌─<─┐
     assign(y, &x); // allowed
}
```

This feature has been proposed and implemented by Walter Bright: 
[Issue 19097](https://issues.dlang.org/show_bug.cgi?id=19097), 
[fix Issue 19097 - Extend Return Scope 
Semantics](https://github.com/dlang/dmd/pull/8504).
It was merged on January 2019, but in August 2018 there was a 
discussion about it:
[Re: Is @safe still a 
work-in-progress?](https://forum.dlang.org/post/plja2k$28r0$1@digitalmars.com)

Mike Franklin:
> Why not the first `ref` parameter regardless of whether it's 
> the absolute first in the list.  Why not the last `ref` 
> parameter?  Why not all `ref` parameters?

Walter Bright:
> Good question. If this fairly restricted form solves the 
> problems, then there is no need for the more flexible form. 
> Things can always be made more flexible in the future, but 
> tightening things can be pretty disruptive. Hence, unless there 
> is an obvious and fairly strong case case for the flexibility, 
> then it should be avoided for now.

So did the restricted form turn out to be good enough? Well, on 
March 23 2019, [Build entire Phobos with -preview=dip1000 
#6931](https://github.com/dlang/phobos/pull/6931) was merged, so 
it seems to be. Hurray!

Except... It turns out we're not quite done yet, because Druntime 
and Phobos still rely on a pretty major accepts-invalid bug to 
compile with -dip1000, see:
[dip1000 + pure is a DEADLY 
COMBO](https://forum.dlang.org/thread/jnkdcngzytgtobihzggj@forum.dlang.org)

The good news is, Per Nordlöw and I have been working on [fixing 
Phobos](https://github.com/dlang/phobos/pull/8076), and Druntime 
[[1]](https://github.com/dlang/druntime/pull/3471), 
[[2]](https://github.com/dlang/druntime/pull/3472), 
[[3]](https://github.com/dlang/druntime/pull/3474), 
[[4]](https://github.com/dlang/druntime/pull/3480) (also thanks 
to [aG0aep6G](https://github.com/dlang/phobos/pull/8109), [Walter 
Bright](https://github.com/dlang/phobos/pull/8104), [Iain 
Buclaw](https://github.com/dlang/phobos/pull/8103), and 
reviewers). During this, we did stumble on cases where the 
restriction came up, such as 
[BigUint.divMod](https://github.com/dlang/phobos/pull/8081#issuecomment-842976462) having `out` parameters after the input parameters. More importantly:

## The issue

[It turns 
out](https://github.com/dlang/druntime/pull/3480#issuecomment-845480887) that `core.lifetime: move` (which is [also in Phobos](https://github.com/dlang/phobos/blob/460ed9c6198b1c84d54ef98b4d0240bc672b89b5/std/algorithm/mutation.d#L1075)), has a signature that's incompatible with -dip1000 in its current form:

```D
void move(T)(ref T source, ref T target)
```

We want to express:
```D
//                       ┌─────────>─────────┐
void move(T)(return T source, ref scope T target)
```

But there's currently now way of making `target` the return scope 
destination when it's not the first parameter. So unless we want 
to leave `move` `@system` or create a `move2` function with 
parameters reversed, we're back at the drawing board for "Extend 
Return Scope Parameters". Two earlier proposals were:

```D
// Mike Franklin's proposal:
void move(T)(return(target) T source, ref scope T target)

// Steven Schveighoffer's proposal:
void move(T)(return T source, @__sink ref scope T target)
```

What do you think?


More information about the Digitalmars-d mailing list