Bug 21511 – Uniqueness detection failed in non-template function with delegate parameter

Status
RESOLVED
Resolution
INVALID
Severity
critical
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2020-12-29T00:16:46Z
Last change time
2021-01-02T02:59:19Z
Keywords
rejects-valid, safe
Assigned to
No Owner
Creator
Bolpat

Comments

Comment #0 by qs.il.paperinik — 2020-12-29T00:16:46Z
immutable(int)* f(const scope int* function() pure @safe fp) @safe { return fp(); // okay } immutable(int)* g(const scope int* delegate() pure @safe dg) @safe { return dg(); // error } immutable(int)* h(DG)(const scope DG dg) @safe // just to be sure nothing surprising happens: if (is(DG == int* delegate() pure @safe)) { return dg(); // okay } The reason why the result of calling the function pointer or delegate are valid, is due to uniqueness.[1] Especially weird is the fact that h lowers to g after the template parameter is determined (only one option). Whether the template parameter is given explicitly or is inferred from the argument is irrelevant. struct Test { int* pureFunc() @safe pure; int* impureFunc() @safe; } @safe unittest { Test test; auto p = h(&test.pureFunc); // okay auto q = h!(int* delegate() pure @safe)(&test.pureFunc); // also okay } [1] https://dlang.org/spec/const3.html#creating_immutable_data
Comment #1 by ag0aep6g — 2020-12-29T15:11:04Z
(In reply to Bolpat from comment #0) > immutable(int)* g(const scope int* delegate() pure @safe dg) @safe > { > return dg(); // error > } > > immutable(int)* h(DG)(const scope DG dg) @safe > // just to be sure nothing surprising happens: > if (is(DG == int* delegate() pure @safe)) > { > return dg(); // okay > } > > The reason why the result of calling the function pointer or delegate are > valid, is due to uniqueness.[1] Especially weird is the fact that h lowers > to g after the template parameter is determined (only one option). [...] > [1] https://dlang.org/spec/const3.html#creating_immutable_data There is a significant difference between g and h: g isn't `pure`. h is. The issue can be summarized like this: immutable(int)* foo(const scope int* delegate() pure @safe dg) pure @safe { immutable int* i = dg(); /* error (line A) */ return dg(); /* no error (line B) */ } The heart of the problem is that delegates offer a head const mechanism. `dg` can return a pre-existing mutable int* from its context. It's not guaranteed to be unique. Example: class C { int x; int* method() pure @safe { return &x; } } void main() { auto c = new C; const int* delegate() dg = &c.method; int* p = dg(); /* no error; not a unique reference (line C) */ } Such a `dg()` cannot be allowed to implicitly convert to immutable. The compiler recognizes that when it rejects line A, but it misses line B. We can either: 1) disallow line B, or 2) make `const` on delegates transitive, consequently allowing line A and disallowing line C.