Jump to page: 1 2
Thread overview
[Issue 22916] [dip1000] copy of ref return still treated as scope variable
Aug 15, 2022
Walter Bright
Aug 15, 2022
Walter Bright
Aug 15, 2022
Dennis
Aug 15, 2022
Dennis
Aug 26, 2022
Walter Bright
Aug 26, 2022
Dennis
Aug 29, 2022
Walter Bright
Aug 29, 2022
Walter Bright
Aug 29, 2022
Walter Bright
Aug 29, 2022
Dennis
Aug 29, 2022
Dennis
Oct 10, 2022
Dennis
Dec 17, 2022
Iain Buclaw
Feb 09, 2023
Dennis
Feb 10, 2023
Walter Bright
Feb 11, 2023
Dlang Bot
Feb 20, 2023
Dlang Bot
August 15, 2022
https://issues.dlang.org/show_bug.cgi?id=22916

Walter Bright <bugzilla@digitalmars.com> changed:

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

--- Comment #1 from Walter Bright <bugzilla@digitalmars.com> ---
Here's the problem:

    ref int* index() return scope {
        return *ptr;
    }

The `scope` applies to the `this` reference, in this case, `ptr`. Although the *ptr is not `scope`, because `scope` is not transitive, the compiler transfers the `scope` to the return value because it is told to. (This is a feature, not a bug.)

--
August 15, 2022
https://issues.dlang.org/show_bug.cgi?id=22916

--- Comment #2 from Walter Bright <bugzilla@digitalmars.com> ---
I think the basic problem here is the code is trying to make scope transitive, and it just doesn't work. At some point the code will have to use @trusted code to make it work.

--
August 15, 2022
https://issues.dlang.org/show_bug.cgi?id=22916

--- Comment #3 from Dennis <dkorpel@live.nl> ---
(In reply to Walter Bright from comment #1)
> Although
> the *ptr is not `scope`, because `scope` is not transitive, the compiler
> transfers the `scope` to the return value because it is told to.

No, my `return scope` annotation is correct. *ptr becomes `scope` again because I return it by `ref`, which is effectively taking the address which cancels out the dereference. If you mark `index` just `scope`, the compiler correctly complains about `return *ptr`:

> Error: scope variable `this` may not be returned

--
August 15, 2022
https://issues.dlang.org/show_bug.cgi?id=22916

--- Comment #4 from Dennis <dkorpel@live.nl> ---
(In reply to Walter Bright from comment #2)
> I think the basic problem here is the code is trying to make scope transitive, and it just doesn't work.

No, my array struct works exactly like a dynamic array. It does not try to have scope pointers as array elements.

--
August 26, 2022
https://issues.dlang.org/show_bug.cgi?id=22916

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|---                         |INVALID

--- Comment #5 from Walter Bright <bugzilla@digitalmars.com> ---
Looking at:

    ref int* index() return scope {
        return *ptr;
    }

the compiler sees it as:

    ref return scope T index();

which is interpreted as `ref` and `return scope` attached to `this`. `a` is `this`. Since `a` is `scope`, the return value of `a.index()` is also `scope`.

This `scope` return value is then passed to `assign(int*)`, where the `int*` parameter is not `scope`.

Assigning `scope` to `not scope` is an error and is correctly diagnosed by the compiler.

--
August 26, 2022
https://issues.dlang.org/show_bug.cgi?id=22916

Dennis <dkorpel@live.nl> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|INVALID                     |---

--- Comment #6 from Dennis <dkorpel@live.nl> ---
(In reply to Walter Bright from comment #5)
> which is interpreted as `ref` and `return scope` attached to `this`. `a` is `this`. Since `a` is `scope`, the return value of `a.index()` is also `scope`.

And that's what the compiler does wrong. The `return scope` applies to the `ref` return, not the returned `int*` value. This can be showcased by this example:

```
ref int f(ref return scope int* x) @safe
{
    return *x;
}
```
Notice how the return value has no pointers, and yet the compiler will raise an
error if you don't annotate if `return scope`. dmd will also infer this as
`return scope` itself if you make it a template.

The job of `return scope` here is to prevent returning `f(stackPointer)` by ref
or escaping `&f(stackPointer)`, but this works:

```
int g() @safe
{
    int x;
    int y = f(&x); // `y` is not `scope`, how could it be?
    return y; // 'escaping' the result of f()
}
```

The only thing my original example does differently is giving the pointer payload a different  type (`int*` instead of `int`), which shouldn't affect this example since it's the second layer of indirection, it shouldn't be affected by `scope` or `return scope`.

By the way, I reduced the original example to `int**` because you usually request that, but it might be actually illuminating to see what the original code looked like:

```
void scoot(scope Array!string a) @safe
{
    a[0] = a[1];
}
```

This obviously doesn't violate `scope` and it works with `string[]`, so it should also work with library array types, or it's poor design of dip1000.

--
August 29, 2022
https://issues.dlang.org/show_bug.cgi?id=22916

--- Comment #7 from Walter Bright <bugzilla@digitalmars.com> ---
I should have looked at the cheat sheet, which has:

    ref X foo(ref return scope P p)
        ReturnRef-Scope

which concurs with what you wrote.

--
August 29, 2022
https://issues.dlang.org/show_bug.cgi?id=22916

--- Comment #8 from Walter Bright <bugzilla@digitalmars.com> ---
(The cheat sheet was out of date. My bad. Remember when we changed `return scope` to always mean ReturnScope, and `ref scope return` never means `Ref-ReturnScope`?)

Let's change x to p for clarity:

  ref int f(ref return scope int* p) @safe
  {
    return *p;
  }

This compiles as `ref` `return scope`. This protects the value of p. Not the address of p. This compiles successfully, as the value of `p` is what is returned.

Now let's try not having `return scope` next to each other:

  ref int f(ref5 scope return int* p) @safe
  {
    return *p;
  }

This compiles as `return ref` `scope`. It fails to compile with:

    Error: scope variable `p` may not be returned.

Both are correct behavior. Note that `return scope` is *always* interpreted as returnScope.

Also, the behavior is *always* determined by the function signature, not whatever the return expression is.

--
August 29, 2022
https://issues.dlang.org/show_bug.cgi?id=22916

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|REOPENED                    |RESOLVED
         Resolution|---                         |INVALID

--- Comment #9 from Walter Bright <bugzilla@digitalmars.com> ---
> The only thing my original example does differently is giving the pointer payload a different  type (`int*` instead of `int`), which shouldn't affect this example since it's the second layer of indirection, it shouldn't be affected by `scope` or `return scope`.

That difference makes all the difference.

The trouble is the code is trying to store a scope protected value `int* p` into the unprotected payload pointer `*ptr`. There's no way dip1000 can do that. You'll have to use @trusted code.

--
August 29, 2022
https://issues.dlang.org/show_bug.cgi?id=22916

--- Comment #10 from Dennis <dkorpel@live.nl> ---
(In reply to Walter Bright from comment #8)
> Note that `return scope` is *always* interpreted as returnScope.
> 
> Also, the behavior is *always* determined by the function signature, not whatever the return expression is.

This issue is not about whether my `Arr.index` function is ReturnRef-Scope or Ref-ReturnScope, it's always been clear from the start it's Ref-ReturnScope.

--
« First   ‹ Prev
1 2