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 ()
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)

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

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

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