Constness and delegates
Mafi
mafi at example.org
Fri Jan 10 13:54:00 UTC 2020
On Friday, 10 January 2020 at 02:50:16 UTC, Timon Gehr wrote:
> On 10.01.20 00:11, Mafi wrote:
>> Regarding the work and comments on a pull request about
>> delegate constness (https://github.com/dlang/dmd/pull/10644) I
>> think there is some things we need to discuss.
>>
>> I think the only sane way to analyze the soundness of
>> delegates is to equate:
>>
>> R delegate(P) qualifier f;
>>
>> with:
>>
>> interface I { R f(P) qualifier; }
>> I f;
>> ...
>
> No, this is not exactly the right way to think about this. A
> delegate is not an interface, because it *includes* an _opaque_
> context pointer. The qualifier on a delegate qualifies _both_
> the function and the context pointer and the reason why
> qualifiers can be dropped anyway is that `qualified(void*)` can
> implicitly convert to `void*` (even though DMD does not know it
> yet) and the delegate maintains an invariant that says that the
> function pointer and the context pointer are compatible.
I see. So the additional opacity is that an interface type I,
that is qualified mutable, could be upcast to some class C and
you would expect C to mutable as well. Therefore you may not
convert an interface consisting of only const methods from const
to mutable. But a delegate is different, it can never be
inspected. Correct? That's interesting!
> ...
>> Therefore the qualifier should be handled in a contravariant
>> manner (and not covariant) because it describes the implicit
>> this-Parameter of the referenced method and not the stored
>> this-Pointer. The const-ness of the this-Pointer is the one
>> "outside" the delegate.
>> ...
>
> Yes, exactly.
>
> ...
>
> Yes. And it's not only `const`. You can lose *all* qualifiers.
>
So the qualifier on the right of the delegate is not actually
contravariant in the type-way but rather the inside-out direction
of transitive constness (mutable data can reference const data
can reference immutable data). Thus delegate() immutable ->
delegate() const -> delegate(). This is also a nice symmetry
between constness and other qualifiers.
Additionally because of the simple opaque nature of delegates
'qualifierA R delegate(P) qualifer1' should be implicitely
convertible to 'qualifierB R delegate(P) qualifier1' as long as
qualifierA and qualifierB are "weaker" than qualifier1. That is
mutable/const/immtutable R delegate() immutable all convert to
one another. And mutable/const R delegate(P) const convert
between each other.
Which I think gives this graph (qualifier1/2 => qualifier1 R
delegate(P) qualifier2:
m/m <- m/c <- m/i
| ^ ^
v v v
c/m <- c/c <- c/i
^ ^ ^
| | v
i/m <- i/c <- i/i
Where a delegate is callable iff 'qualifier1 qualifier2' is
convertible to 'qualifier2'. Therefore only i/m and c/m are not
callable (and they don't convert to a callable one). Is this
correct?
More information about the Digitalmars-d
mailing list