Bug 22539 – [dip1000] slicing of returned ref scope static array should not be allowed
Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2021-11-23T19:38:16Z
Last change time
2022-03-10T08:34:11Z
Keywords
pull
Assigned to
No Owner
Creator
Dennis
Comments
Comment #0 by dkorpel — 2021-11-23T19:38:16Z
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();
}
```
Comment #1 by dlang-bot — 2021-11-26T17:17:00Z
@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
Comment #2 by bugzilla — 2021-11-27T19:57:20Z
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.
Comment #3 by dkorpel — 2021-11-27T20:08:37Z
(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.
Comment #4 by bugzilla — 2021-11-27T20:15:07Z
Add @safe to escape() and it will fail to compile.
Comment #5 by bugzilla — 2021-11-27T20:19:19Z
P.S. many times I've chased rabbits only to discover I forgot to add @safe. @safe really should be the default.
Comment #6 by dkorpel — 2021-11-27T20:19:36Z
(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?
Comment #7 by dkorpel — 2021-11-27T20:23:14Z
(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).
Comment #8 by bugzilla — 2021-11-27T20:26:43Z
My bad, adding -dip1000 removes the error message. I had neglected to do that.
Comment #9 by dkorpel — 2021-11-27T20:30:24Z
(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 :)
Comment #10 by bugzilla — 2021-11-27T21:29:00Z
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.
Comment #11 by dkorpel — 2021-11-27T22:41:19Z
(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?
Comment #12 by bugzilla — 2021-11-28T02:41:21Z
Objections? Not at the moment, I'm just trying to thoroughly understand the problem.
Comment #13 by bugzilla — 2021-11-29T00:07:54Z
(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.
Comment #14 by dlang-bot — 2022-03-10T08:34:11Z
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