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