Bug 58809 - Return result of Type.IsAssignableFrom with the generic parameters differs from MS.NET one
Summary: Return result of Type.IsAssignableFrom with the generic parameters differs fr...
Alias: None
Product: Runtime
Classification: Mono
Component: General (show other bugs)
Version: 5.4 (2017-06)
Hardware: PC Linux
: --- normal
Target Milestone: ---
Assignee: Aleksey Kliger
Depends on:
Reported: 2017-08-16 12:13 UTC by misonijnik
Modified: 2017-10-19 19:36 UTC (History)
4 users (show)

See Also:
Tags: bugpool-archive
Is this bug a regression?: ---
Last known good build:

Plain text (674 bytes, text/x-csharp)
2017-08-16 12:13 UTC, misonijnik

Description misonijnik 2017-08-16 12:13:12 UTC
Created attachment 24232 [details]
Plain text

Description of Problem:
In the constraint of the parameter T2 it is written that it must be subclass of Foo<Τ3>. Therefore, the Type.IsAssignableFrom must return true for T2 and Foo<T3>. But it returns false. But if you comment out the tenth line ("where T1 : T2"), then the function returns true. In MS.NET Type.IsAssignableFrom always returns true for T2 and Foo<T3>.

Steps to reproduce the problem:
1. Compile "test.cs"
2. Run "test.exe"

Actual Results:
"False" in console.

Expected Results:
"True" in console.

How often does this happen? 
Each run.
Comment 1 Ludovic Henry 2017-08-16 20:20:33 UTC
Thank you very much for the very reproductible test case. I could reproduce as expected on Mono (2017-04/ef39d08) as well as on .NET.
Comment 2 Ludovic Henry 2017-09-06 19:45:28 UTC
I can reproduce with Mono (2017-06/6425f06)
Comment 3 Aleksey Kliger 2017-09-14 19:02:47 UTC
I think this is happening because make_generic_param_class () calls mono_class_setup_supertypes ().  make_generic_param_class () is ultimately called from mono_class_from_mono_type () which is, in turn, called on "T2" when loading the constraint "where T1 : T2".  We load constraints in the order of the generic parameter to which they apply (ie "where T1 : C1" before "where T2 : C2").   For example if the reproduction example is modified to

    class GenericClass <T1, T2, T3, T4> where T4 : T2 where T2 : Foo<T3>

then typeof(Foo<T3>).IsAssignableFrom(typeof(T4)) does return True as expected.

It's not totally clear to me yet if I can just delay setting up the supertypes array in make_generic_param_class or if we need to be a lot more careful about creating a generic param MonoClass from a MonoType as a side-effect of loading the constraints.
Comment 4 Aleksey Kliger 2017-09-18 14:59:43 UTC
The simple fix doesn't work: just processing the constraints in metadata.c get_constraints() is enough to cause the later type variables to be created before they've had their constraints attached.

I'm now working on two-phase creation of MonoClass-es for type variables: (1) create barebones typevariables; (2) create the constraints; (3) attach the constraints to the type variables and finish initialization.
Comment 5 Aleksey Kliger 2017-09-21 17:56:12 UTC
For the record, we also get typeof (Foo<T3>).IsAssignableFrom(typeof(T1)) wrong, and for a new (but related) reason:

 - our code that computes the parent class of a generic param (in mono_class_from_generic_parameter_internal ()) just checks whether the first constraint is not an interface and makes that the parent object, otherwise System.Object is the parent.  (This is already wrong an naive, if a gparam has multiple constraints, but even so...)

  - MONO_CLASS_IS_INTERFACE() assumes that any type variable is an interface.

  - T2 cannot be an interface because it derives from a class (Foo<T3>) that is definitely not an interface.

To get this right we need to set MonoClass:parent to the (at most 1) MonoClass that *must* be a non-interface class.  And we must know that gparams like T2 that derive from a class can never be interfaces.
Comment 6 Ludovic Henry 2017-09-22 15:45:43 UTC

Note You need to log in before you can comment on or make changes to this bug.