[runtime] Implement lazy interface implementation for arrays.
commit1b9659a1d2c26c2be7714c946e3a45a350905945
authorRodrigo Kumpera <kumpera@gmail.com>
Mon, 21 Nov 2016 23:22:08 +0000 (21 15:22 -0800)
committerRodrigo Kumpera <kumpera@gmail.com>
Mon, 28 Nov 2016 23:10:15 +0000 (28 15:10 -0800)
treef1a43a513e113b19d04b900ae2487b860a02fec3
parent5e75d4dcab7f8387b73c10f1a0786a9030f91e83
[runtime] Implement lazy interface implementation for arrays.

We have the following set of problems:

Arrays implement interfaces that are invariant but behave like covariant.
Those are IList`1, ICollection`1, IEnumerable`1

Arrays of primitive types are type equivalent based on their underlying type.
This means int[], uint[] and IntEnum[] are all interchangeable. And so is casting among their interfaces.
Meaning this is valid: `(IList<IntEnum>)(object)new int[1]`.

Finally, the way we implement IEnumerator`1 for arrays forces it to behave just like the other 3 interfaces.

Implementation:

Introduce MonoClass::is_array_special_interface, set for all the above interfaces. This allow fast checking of the interfaces in question.

mono_class_is_assignable_from now includes the array casting rules.

Change mono_class_interface_offset_with_variance to take arrays into account when looking for a candidate iface.

The JIT implements casting of those interfaces using the cast with cache wrapper.

Finally, to handle the weird primitive rules, set the cast_class of sbyte, ushort, uint and ulong to their same-size buddies.
This makes it easier for the casting code to resolve this problem.

Problems:

Casting performance is terrible, anywhere from 60% to 1000% slower on a micro-benchmark.
Can be improved by extending our casting wrapper to handle those interfaces (probably would have to give up inlining as they would be huge)

The code in domain.c is icky, but not sure where to put it otherwise.

IEnumerator could be solved in Array.cs by not forcing it to be magic as well. Meaning the folowing

```
object array = new int[1];
var a = array as IList<int>;
var b = array as IList<uint>;

a.GetEnumerator().GetType () != b.GetEnumerator().GetType ();
```

The last line is false today but it's harmless to change it to avoid the casting penalty for such central type.
mono/metadata/class-internals.h
mono/metadata/class.c
mono/metadata/domain.c
mono/metadata/object.c
mono/mini/method-to-ir.c