Bug 19752 – dip1000 isn't @safe if struct contains a slice

Status
RESOLVED
Resolution
INVALID
Severity
critical
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
Linux
Creation time
2019-03-19T11:26:45Z
Last change time
2020-03-04T08:37:04Z
Keywords
safe
Assigned to
No Owner
Creator
Atila Neves

Comments

Comment #0 by atila.neves — 2019-03-19T11:26:45Z
The code below compiles as expected with dip100. It's @safe because `range`'s return value has the lifetime extended from the `this` parameter: -------------------------------- struct Container { auto range() @safe return scope { static struct Range { Container *self; } return Range(&this); } } -------------------------------- Trying to escape the result of `range` then fails to compile, as expected. However, adding a slice member variable to the struct above causes this: ---------------------------------- struct Container { // as before int[] ints; ---------------------------------- $ dmd -preview=dip1000 bug2.d bug2.d(10): Error: cannot take address of scope parameter this in @safe function range From the original DIP, it's to be expected that `ints` would have the same lifetime as `this` and things should work. This is particularly bad because if the programmer attempts a workaround the code below compiles and shouldn't: https://github.com/atilaneves/automem/issues/26
Comment #1 by ag0aep6g — 2019-03-19T18:38:42Z
This seems to come down to `ref` parameters. `this` is a `ref` parameter, right? This works: struct Container1 { int* ints; } Container1* range1(return scope Container1* that) @safe { return that; /* accepted */ } This doesn't: struct Container2 { int* ints; } Container2* range2(return scope ref Container2 that) @safe { return &that; /* Error: cannot take address of scope parameter that */ } As far as I see, the two should be same to DIP 1000.
Comment #2 by monkeyworks12 — 2019-03-19T20:52:26Z
I believe this is because adding a slice member to Container makes it a type with indirections, whereas it was not before. DIP1000 only applies to types with indirections. If you were to add an int member to Container instead, the code would still compile as adding an int member to Container does not make it a type with indirections.
Comment #3 by atila.neves — 2019-03-20T09:35:31Z
> I believe this is because adding a slice member to Container makes it a type with indirections, whereas it was not before. DIP1000 only applies to types with indirections. In either case, `Range` is a type with an indirection, and `this` is an indirection itself, so the error message that "cannot take the address of a scope parameter this is @safe function range" makes no sense. The function is deliberately `return scope` so that the lifetime of the return value is tied to `this` and shouldn't be able to outlive it.
Comment #4 by bugzilla — 2020-03-04T08:35:04Z
Let's do a little rewriting: ---- struct Range { Container *self; } struct Container { int* p; static Range range(return scope ref Container c) @safe { return Range(&c); } } ---- which produces the same error. More rewrites: ---- struct Range { Container *self; } struct Container { int* p; } Range range(return scope ref Container c) @safe { return Range(&c); } ---- produces the same error. More: ---- struct Container { int* p; } Container* range(return scope ref Container c) @safe { return &c; } ---- produces the same error. More: ---- int** range(return scope ref int* c) @safe { return &c; } ---- produces the same error: Error: cannot take address of ref parameter c in @safe function range Now, the return value is not `ref`, so the `return` applies to the `int*`, not the `ref`. But we're not returning the `int*`, we're returning the address of the `int*` and recall the `return` doesn't apply to that, hence the error.
Comment #5 by bugzilla — 2020-03-04T08:37:04Z
The key to understanding perplexing examples is to ruthlessly rewrite them in terms of `int*` and plain functions. Get rid of slices, this references, structs, member functions, etc. which do nothing but obfuscate what is happening, as they are just fancier versions of int* and functions.