The following compiles:
int x;
void delegate() d = delegate() { x = 1; };
immutable(void delegate()) bad1 = d;
shared(void delegate()) bad2 = d;
And the following, as a result, does too:
void delegate() shared {
bad1();
bad2();
}
This allows you to transfer bad1 or bad2 to another thread, to call it, and modify x without synchronization.
* * *
Theoretically, a type like void delegate() immutable could be seen as the following:
struct dgt_ {
int* x;
void opCall() immutable;
}
Whereas a type like immutable(void delegate()) could be seen as the following:
struct dgt_ {
int* x;
void opCall();
};
alias dgt = immutable(dgt_);
In the latter case, immutable should apply to x too (similar for shared) but it seemingly doesn’t for actual delegate types.
* * *
Verified with:
- DMD64 D Compiler v2.085.1
- LDC 1.16.0 based on DMD v2.086.1 and LLVM 8.0.1
Comment #1 by zopsicle — 2019-12-07T18:57:18Z
To clarify, the proposed solution is to disallow the conversion of:
T delegate()
into:
immutable(T delegate())
shared(T delegate())
Just like it is disallowed to convert T[] to immutable(T[]), or T* to immutable(T*). Similar for shared.
Comment #2 by dlang-bot — 2019-12-07T20:39:00Z
@chloekek updated dlang/dmd pull request #10644 "Fix issue 20437 - Transitive immutable/shared does not apply to variables captured by delegates" fixing this issue:
- Fix issue 20437 - Transitive immutable/shared does not apply to variables captured by delegates
https://github.com/dlang/dmd/pull/10644
Comment #3 by pro.mathias.lang — 2019-12-09T00:23:34Z