[metadata] Fields whose types are gparams with a reference type constraint aren't...
commit04517d3a3955c5a4a899d88c4af7987b2b277101
authorAleksey Kliger (λgeek) <alklig@microsoft.com>
Thu, 25 Jul 2019 21:31:48 +0000 (25 17:31 -0400)
committerGitHub <noreply@github.com>
Thu, 25 Jul 2019 21:31:48 +0000 (25 17:31 -0400)
treeed9b6f9c6a508483c56f81ede334c9db9f942bf0
parent090dffb9a09f26dc250d8d09a46819d192290b0b
[metadata] Fields whose types are gparams with a reference type constraint aren't blittlable. (#15761)

* [metadata] Fields whose types are gparams with a reference type constraint
aren't blittlable.
Don't try to layout the field to find out if it's blittable.
For gshared gparams, follow the blittability of the constraint.

Fixes certain recursive examples.

```
using System;

namespace TestRecursiveType
{
    class Program
    {
        static void Main(string[] args)
        {
            SubClass subC = new SubClass();
            Console.WriteLine(subC.GetTest());
        }
    }

    public struct ValueTest<U>
    {
        // When U is instantiated with T, from BaseClass, we know it'll be a
// reference field, so we know the instantiation ValueTest<T> won't
// be blittable.
        public readonly U value;
    }

    public abstract class BaseClass<T> where T : BaseClass<T>
    {
        public ValueTest<T> valueTest = default(ValueTest<T>);
    }

    public class SubClass : BaseClass<SubClass>
    {
        private String test = "test";

        public string GetTest()
        {
            return test;
        }
    }
}
```

Fixes https://github.com/mono/mono/issues/15760

---

The failure is happening when we are doing mono_class_setup_fields ("BaseClass<T>") which needs to decide for each field whether it is blittable or not. So therefore we are trying to decide if ValueTest<T> (that is: the ValueTest<U> field inside BaseClass<T>) is blittable or not.

So we instantiate U with T.
Now to decide whether VaueTest<T> is blittable or not, we look at every field.
So then we look at T value.
To decide if T is blittable we first check if it's a reference type.

That check is currently inadequate for generic parameters - what the PR adds is the ability to see if theres a T : class constraint or a T : C constraint - where C is some class. As soon as we know that T's constraint will force it to be a reference type we can definitely say that T won't be blittable without having to initialize C, at all.

Previously, Mono would see that T is some kind of type for which it couldn't definitively decide that it's a reference type and it would call: mono_class_setup_fields (field_class) which would then try to setup the fields of the parent class BaseClass<T>. And that would hit the recursion check.
mono/metadata/class-init.c
mono/metadata/class.c
mono/metadata/metadata.c
mono/tests/Makefile.am
mono/tests/recursive-generics.3.cs [new file with mode: 0644]