Bug 21286 – [DIP1000] Can't return scope reference from a function

Status
NEW
Severity
enhancement
Priority
P4
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2020-09-30T17:36:06Z
Last change time
2024-12-13T19:11:52Z
Assigned to
No Owner
Creator
Paul Backus
Moved to GitHub: dmd#19797 →

Comments

Comment #0 by snarwin+bugzilla — 2020-09-30T17:36:06Z
The following program fails to compile with -preview=dip1000: --- void main() @safe { int[3] a = [1, 2, 3]; int[] slice; // ok slice = a[]; scope ref getSlice() { return slice; } // Error: reference to local variable a assigned to non-scope getSlice() getSlice() = a[]; } --- It should be possible for getSlice to return a reference that `a[]` can be safely assigned to.
Comment #1 by bugzilla — 2021-07-27T07:19:31Z
As always, it's clearer what's happening when we peel back to abstractions to mere pointers: @safe void test() { int i; scope int* p = &i; scope ref get() { return p; } get() = &i; }
Comment #2 by timon.gehr — 2023-10-26T14:35:47Z
It is not so clear to me whether `scope` on a local function is supposed to qualify the context pointer or the return value. Delegate contexts in general are a big source of DIP1000 unsoundness.
Comment #3 by snarwin+bugzilla — 2023-11-17T20:28:14Z
This also affects non-nested functions. For example: --- ref int* getRef(return ref scope int* p) @safe { return p; } void main() @safe { int n; scope int* p; getRef(p) = &n; } --- I'm not sure if it's feasible to accept code like this without a full-blown lifetime system like Rust's, but it would certainly be convenient if we could.
Comment #4 by dkorpel — 2023-11-17T21:57:17Z
I think the problem there is that it doesn't consider `getRef(p)` to be variable `p`. While the right hand side of an = uses `escapeByValue/escapeByRef` logic, the left hand side uses `expToVariable`, which can only return one variable, so it gives up on potential multiple-variable expressions. For example: ```D void main() @safe { int n; scope int* p; (n ? p : p) = &n; } ``` Error: reference to local variable `n` assigned to non-scope `*(n ? & p : & p)` I think this could be fixed by using the same escapeByValue logic for the lhs and repeating the rest of the checkAssignEscape logic for all possible lhs variables. It won't have the best time complexity, but complex lhs expressions should be rare.
Comment #5 by snarwin+bugzilla — 2023-11-18T15:07:14Z
Perhaps more to the point, the bigger problem is that from the outside, the compiler has no way to distinguish between these two implementations of getRef: --- @safe: int global; ref int* getRef1(return ref int* p) => p; ref int* getRef2(return ref int* p) => global; --- Allowing a scope pointer to be assigned to the return value of getRef2 would clearly be a mistake, so it has to be forbidden in general. You could maybe justify allowing the assignment if getRef were a pure function, but giving pure functions special exemptions from the normal safety rules has bitten us in the past, and it's still just a band-aid over the real problem (lack of explicit lifetimes), so I wouldn't recommend it.
Comment #6 by robert.schadek — 2024-12-13T19:11:52Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19797 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB