Thread overview
[Issue 21286] [DIP1000] Can't return scope reference from a function
Jul 27, 2021
Walter Bright
Dec 17, 2022
Iain Buclaw
Oct 26, 2023
timon.gehr@gmx.ch
Nov 17, 2023
Paul Backus
Nov 17, 2023
Dennis
Nov 18, 2023
Paul Backus
July 27, 2021
https://issues.dlang.org/show_bug.cgi?id=21286

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bugzilla@digitalmars.com

--- Comment #1 from Walter Bright <bugzilla@digitalmars.com> ---
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;
  }

--
December 17, 2022
https://issues.dlang.org/show_bug.cgi?id=21286

Iain Buclaw <ibuclaw@gdcproject.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Priority|P1                          |P4

--
October 26, 2023
https://issues.dlang.org/show_bug.cgi?id=21286

timon.gehr@gmx.ch changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |timon.gehr@gmx.ch

--- Comment #2 from timon.gehr@gmx.ch ---
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.

--
November 17, 2023
https://issues.dlang.org/show_bug.cgi?id=21286

--- Comment #3 from Paul Backus <snarwin+bugzilla@gmail.com> ---
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.

--
November 17, 2023
https://issues.dlang.org/show_bug.cgi?id=21286

Dennis <dkorpel@live.nl> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |dkorpel@live.nl
           Hardware|x86_64                      |All
                 OS|Linux                       |All

--- Comment #4 from Dennis <dkorpel@live.nl> ---
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.

--
November 18, 2023
https://issues.dlang.org/show_bug.cgi?id=21286

--- Comment #5 from Paul Backus <snarwin+bugzilla@gmail.com> ---
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.

--