Because there is no transitive scope, taking the address of a scope variable is not allowed. There are 4 cases: - ref param &pointer: rejected with `checkAddresVar` - ref param slice[]: rejected with `checkAddresVar` - ref return &pointer: too restrictive (issue 22519) - ref return slice[]: too lenient (this issue) This allows you to escape scope pointers: ``` // REQUIRED_ARGS -preview=dip1000 @safe: ref int*[1] identity(ref return scope int*[1] x) { return x; } int* escape() { int stackVar = 0xFF; scope int*[1] x = [&stackVar]; int*[] y = identity(x)[]; return y[0]; } void main() { int* dangling = escape(); } ```
@dkorpel created dlang/dmd pull request #13362 "Fix issue 22539 - slicing of returned ref scope static array should not be allowed" fixing this issue: - Fix issue 22539 - slicing of returned ref scope static array should not be allowed https://github.com/dlang/dmd/pull/13362
I suspect the problem is actually this line: scope int*[1] x = [&stackVar]; Since scope is not transitive, allowing this causes the compiler to lose track of stack addresses, and will allow escapes.
(In reply to Walter Bright from comment #2) > I suspect the problem is actually this line: > > scope int*[1] x = [&stackVar]; > > Since scope is not transitive, allowing this causes the compiler to lose > track of stack addresses, and will allow escapes. Only if x were a slice, but since it's a static array with dimension 1 it is identical to: scope int* x = &stackVar; So it is correctly permitted.
Add @safe to escape() and it will fail to compile.
P.S. many times I've chased rabbits only to discover I forgot to add @safe. @safe really should be the default.
(In reply to Walter Bright from comment #4) > Add @safe to escape() and it will fail to compile. `escape` is already under `@safe:`. I added `@safe` to escape explicitly to double check, it still compiles on my end. What error do you get?
(In reply to Walter Bright from comment #5) > P.S. many times I've chased rabbits only to discover I forgot to add @safe. > @safe really should be the default. I didn't forget @safe, I added @safe: on top (as I usually do).
My bad, adding -dip1000 removes the error message. I had neglected to do that.
(In reply to Walter Bright from comment #8) > My bad, adding -dip1000 removes the error message. I had neglected to do > that. I guess -dip1000 really should be the default :)
Moving on from my previous mistakes: @safe: ref int* identity(ref return scope int* x) { return x; } int* escape() { int stackVar; scope int* x = &stackVar; int* y = identity(x); return y; // error: scope variable `y` may not be returned } Good. Next, @safe: ref int*[1] identity(ref return scope int*[1] x) { return x; } int*[1] escape() { int stackVar; scope int*[1] x = [&stackVar]; int*[1] y = identity(x); return y; // error: scope variable `y` may not be returned } Good. int* escape() { int stackVar; scope int*[1] x = [&stackVar]; int*[1] y = identity(x); return y[0]; // error: scope variable `y` may not be returned } Good. int* escape() { int stackVar; scope int*[1] x = [&stackVar]; int*[1] y = identity(x); int*[] z = y[]; // error: cannot take address of `scope` local `y` return z[0]; } Good. Here, the slicing of `y` is properly detected. But if we put the [] after the identity call: int* escape() { int stackVar; scope int*[1] x = [&stackVar]; int*[] y = identity(x)[]; return y[0]; } No error; bad. The slicing of the return value of identity() is not properly propagating the scope-ness of its argument x. This would be a problem in escape.d.
(In reply to Walter Bright from comment #10) > No error; bad. The slicing of the return value of identity() is not properly > propagating the scope-ness of its argument x. This would be a problem in > escape.d. Yes, the scope-ness is lost because there is no transitive scope, so the slicing shouldn't be allowed in @safe code. It's essentially the same as: https://issues.dlang.org/show_bug.cgi?id=20691 Which you fixed with: https://github.com/dlang/dmd/pull/10951 But that fix is limited to variables, not return values, so my pull request extends it by also considering slicing a CallExp: https://github.com/dlang/dmd/pull/13362 I don't understand what your comment is getting at, do you have any objections to this?
Objections? Not at the moment, I'm just trying to thoroughly understand the problem.
(In reply to Walter Bright from comment #12) > Objections? Not at the moment, I'm just trying to thoroughly understand the > problem. See the PR for my remaining objection. Thanks for your patience with this.
dlang/dmd pull request #13362 "Fix issue 22539 - slicing of returned ref scope static array should not be allowed" was merged into master: - 8f3f72f93a6b6241342c495c569f294c4ea0238f by dkorpel: Fix issue 22539 - slicing of returned ref scope static array should not be allowed https://github.com/dlang/dmd/pull/13362