Spin-off from issue 16056. dmd considers the two types to be equal: ---- alias A = immutable int* delegate(); alias B = immutable int* delegate() immutable; static assert(is(A == B)); /* passes */ ---- That's ok. But it treats them differently. This is accepted with `-version=V1`, but it's rejected with `-version=V2`: ---- version (V1) alias T = immutable void delegate(); version (V2) alias T = immutable void delegate() immutable; void main() { int x = 1; T dg = { ++x; }; } ---- Both V1 and V2 should be rejected. Furthermore, when you use both types, the first one determines how the second is treated. This is accepted: ---- void main() { int x = 1; immutable void delegate() dg1 = { ++x; }; immutable void delegate() immutable dg2 = { ++x; }; } ---- Swap the two delegates lines and both are rejected. Again, both variants should be rejected. All this applies to const as well, of course.
I think it makes sense for the types not to be considered equal. (Head-mutable ptrs to immutable data exist, so why should delegates be inconsistent?)
(In reply to Eyal Lotem from comment #1) > I think it makes sense for the types not to be considered equal. > (Head-mutable ptrs to immutable data exist, so why should delegates be > inconsistent?) A head mutable delegate would be `void delegate() immutable`, no? That is considered different from the two types this is about.
From the type qualifier transitivity, immutable(int* delegate()) should be same with immutable(int* delegate() immutable). The root problem is in dmd implementation, TypeNext.makeXXX functions. Fixing those implementation bugs is not difficult.
(In reply to Kenji Hara from comment #3) > From the type qualifier transitivity, immutable(int* delegate()) should be > same with immutable(int* delegate() immutable). > ... This does not follow from transitivity because the postfix `immutable` also annotates the implicit context parameter of the function pointer while the `immutable` qualifier on the delegate a priori does not. If those two types are conflated this actually leads to type system unsoundness: ---- auto foo()pure{ int x; return ()pure{ return x++; }; } void main(){ immutable dg=foo(); // can convert to immutable as it's a strongly pure call import std.stdio; writeln(dg()," ",dg()); // 0 1, immutable context is modified } ----