Bug 19721 – Cannot take address of scope local variable even with dip1000 if a member variable is a delegate

Status
RESOLVED
Resolution
INVALID
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2019-03-06T11:06:05Z
Last change time
2020-03-04T08:01:30Z
Keywords
rejects-valid, safe
Assigned to
No Owner
Creator
Atila Neves

Comments

Comment #0 by atila.neves — 2019-03-06T11:06:05Z
This code doesn't compile even with -dip1000 on dmd 2.085.0: ----------------------------------- void main() @safe { scope s = Struct(); func(&s); } private struct Struct { void delegate(int) dg; } void func(scope Struct* clientData) @safe nothrow { } ----------------------------------- It compiles if the `dg` member variable is removed or replaced with, say, an int.
Comment #1 by ag0aep6g — 2020-02-09T21:38:10Z
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.
Comment #2 by bugzilla — 2020-03-04T05:14:47Z
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 { } ------
Comment #3 by bugzilla — 2020-03-04T08:01:30Z
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.