Thread overview
[Issue 19721] Cannot take address of scope local variable even with dip1000 if a member variable is a delegate
Mar 06, 2019
Atila Neves
Feb 09, 2020
ag0aep6g
Mar 04, 2020
Walter Bright
Mar 04, 2020
Walter Bright
March 06, 2019
https://issues.dlang.org/show_bug.cgi?id=19721

Atila Neves <atila.neves@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |rejects-valid, safe

--
February 09, 2020
https://issues.dlang.org/show_bug.cgi?id=19721

ag0aep6g <ag0aep6g@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ag0aep6g@gmail.com

--- Comment #1 from ag0aep6g <ag0aep6g@gmail.com> ---
As far as I understand, it's correct that the code is rejected. `scope` only provides one level of protection. That means, you can't return a `scope` pointer, but you can dereference it, make a copy of the pointee, and return that.

In the example, `func` is allowed to return `*clientData`, regardless of what happens anywhere else in the code. And since `s` is `scope`, `s.dg` might rely on references to the stack. So to avoid a leak, you can't be allowed to call `func` on `s`.

In code:

----
Struct g;

void main() @safe {
    main2();
    () { int[10] stompy = 13; } ();
    g.dg(0); /* Prints "13". We've got memory corruption. */
}

void main2() @safe {
    int stack;
    scope s = Struct();
    s.dg = (int x) @safe { import std.stdio; writeln(stack); };
    () @trusted { func(&s); } (); /* Let's pretend this works in @safe. */
}

private struct Struct {
    void delegate(int) @safe dg;
}

void func(scope Struct* clientData) @safe nothrow {
    g = *clientData;
}
----

(In reply to Atila Neves from comment #0)
> It compiles if the `dg` member variable is removed or replaced with, say, an int.

An int doesn't have any indirections, unlike a delegate.

--
March 04, 2020
https://issues.dlang.org/show_bug.cgi?id=19721

Walter Bright <bugzilla@digitalmars.com> changed:

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

--- Comment #2 from Walter Bright <bugzilla@digitalmars.com> ---
It also fails to compile if dg is defined as `int* dg;`:

---------------
void main() @safe {
    scope s = Struct();
    func(&s);
}

private struct Struct { int* dg; }

void func(scope Struct* clientData) @safe nothrow { }
-----------------------

In fact, it has nothing to do with structs, as this fails to compile the same way:

-----
void main() @safe {
    scope int* s = null;
    func(&s);
}

void func(scope int** clientData) @safe nothrow { }
------

--
March 04, 2020
https://issues.dlang.org/show_bug.cgi?id=19721

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
           Hardware|x86_64                      |All
         Resolution|---                         |INVALID
                 OS|Linux                       |All

--- Comment #3 from Walter Bright <bugzilla@digitalmars.com> ---
Here's what's happening. `scope int* s;` declares `s` as a pointer that must not be allowed to escape `main()`. The `func(&s);` passes the address of `s` to `func`. `func` declares its parameter as `scope int**`. This ensures that the address of `s` does not escape, but says nothing about `s`'s pointer value, which must not be allowed to escape. I.e. the value of `s` is not protected from escaping `func`, so the call causes a compile error.

If `s` is simply declared as an `int`, the `scope` annotation for `s` is meaningless, as there is no pointer value to protect, and it compiles successfully.

----

Generally, rewriting perplexing examples as simple pointers tends to make what is happening much easier to determine. A delegate is regarded as a pointer. A struct that contains pointer values is itself regarded as a pointer. Hence the simplification of the example.

--