I've looked at invariant a bit, and found a few things.

    void func(const(int)[] answers) {
        invariant(int)[][] invar = [[]];
        const(int)[][] unsafe = invar;
        unsafe[0] = answers;
    }

This is unsafe, since answers, which is merely a read-only view, has been sneaked into invar, thereby disguising it as something that will never change. So invariant is just like plain mutable in this instance. OTOH, it would be OK assigned to a const(int[])[], or even a const(invariant(int)[])[]. (My experiment shows that the transitivity of invariant overrides that of const, though this doesn't seem to be in the spec.)


OTOH, an invariant class array can be upcast no problem

    invariant(DerivedClass)[]
to
    invariant(BaseClass)[]
or to either of these replacing invariant with const. But we still need const on levels that are further out:
    invariant(DerivedClass)[][]
to
    const(invariant(DerivedClass)[])[]
    const(invariant(BaseClass)[])[]
    const(BaseClass[])[]


So we can rewrite my proposed set of rules:

1. Generalise the definition of an upcast to be any of the following:
(a) conversion of a class type to a class type further up the hierarchy
(b) conversion of a mutable or invariant type to a const version of that type
(c) an implicit conversion permitted by rule 2

2. If U is an upcast of T, then a legal implicit conversion is any of:
(a) T[] to const(U)[]
(b) T* to const(U)*
(c) invariant(T)[] to invariant(U)[]
(d) invariant(T)* to invariant(U)*
Any other conversion from T[] to U[] or T* to U* is illegal.


Notice that:

- the case of const(T)[] to const(U)[] is covered, since const(const(U)) is the same as const(U)

- given invariant(int)[][], both possible conversions are covered
-- const(int[])[] by first applying 1(b) to invariant(int) and then by applying 2(a) with T = const(int)[]
-- const(invariant(int)[])[] by just applying 1(b) to invariant(int)[]


That leaves AAs to consider....

Stewart.

Reply via email to