Merge pull request #4723 from lambdageek/bug-54485
authorAleksey Kliger (λgeek) <akliger@gmail.com>
Fri, 21 Apr 2017 20:26:39 +0000 (16:26 -0400)
committerGitHub <noreply@github.com>
Fri, 21 Apr 2017 20:26:39 +0000 (16:26 -0400)
[metadata] expand uninstantiated generic type definitions used as generic type args

mcs/class/corlib/Test/System/TypeTest.cs
mono/metadata/class-accessors.c
mono/metadata/class-internals.h
mono/metadata/class.c
mono/metadata/metadata.c
mono/metadata/sre.c

index d1765680ff4f2066005bdee207ec5ccec672f5ff..9cc8ca79a413f707d27ad210899583c3fdb03456 100644 (file)
@@ -3271,6 +3271,38 @@ namespace MonoTests.System
                        Assert.AreSame (expectedType, r, "#2");
                }
 
+               public class BConstrained<Y> where Y : BConstrained<Y> {
+               }
+
+               public class AConstrained<X> : BConstrained<AConstrained<X>> {
+               }
+
+               [Test] // Bug https://bugzilla.xamarin.com/show_bug.cgi?id=54485
+               public void MakeGenericType_GTD_Constraint ()
+               {
+                       // This is pretty weird, but match .NET behavior (note
+                       // that typeof(BConstrained<AConstrained<>>) is a
+                       // compile-time error with roslyn, but it's apparently
+                       // an ok thing to make with reflection.
+                       var tb = typeof (BConstrained<>);
+                       var ta = typeof (AConstrained<>);
+                       var result = tb.MakeGenericType (ta);
+                       Assert.IsNotNull (result, "#1");
+                       // lock down the answer to match what .NET makes
+                       Assert.IsTrue (result.IsGenericType, "#2");
+                       Assert.AreEqual (tb, result.GetGenericTypeDefinition (), "#3");
+                       var bargs = result.GetGenericArguments ();
+                       Assert.AreEqual (1, bargs.Length, "#4");
+                       var arg = bargs [0];
+                       Assert.IsTrue (arg.IsGenericType, "#5");
+                       // N.B. evidently AConstrained`1 and AConstrained`1<!0> are the same type
+                       Assert.IsTrue (arg.IsGenericTypeDefinition, "#6");
+                       Assert.AreEqual (ta, arg.GetGenericTypeDefinition (), "#7");
+                       var aargs = arg.GetGenericArguments ();
+                       Assert.AreEqual (1, aargs.Length, "#8");
+                       Assert.AreEqual (ta.GetGenericArguments () [0], aargs [0], "#9");
+               }
+
        [Test]
        public void EqualsUserType () {
                UserType2 t1 = new UserType2(null);
index 327a1c1cfe79bbf76ed0e5f8507a13bf0200c122..ef1bd78da1c006140f6628102bd753954f94cb57 100644 (file)
@@ -382,3 +382,10 @@ mono_class_set_is_com_object (MonoClass *klass)
        mono_loader_unlock ();
 #endif
 }
+
+MonoType*
+mono_class_gtd_get_canonical_inst (MonoClass *klass)
+{
+       g_assert (mono_class_is_gtd (klass));
+       return &((MonoClassGtd*)klass)->canonical_inst;
+}
index 7d85d8ce84f9010140021b975b495d572b119b81..39061bd142a1ec1e5411aaee98f7848babe99d1b 100644 (file)
@@ -390,6 +390,9 @@ typedef struct {
 typedef struct {
        MonoClassDef class;
        MonoGenericContainer *generic_container;
+       /* The canonical GENERICINST where we instantiate a generic type definition with its own generic parameters.*/
+       /* Suppose we have class T`2<A,B> {...}.  canonical_inst is the GTD T`2 applied to A and B. */
+       MonoType canonical_inst;
 } MonoClassGtd;
 
 typedef struct {
@@ -1444,6 +1447,9 @@ mono_class_try_get_generic_container (MonoClass *klass);
 void
 mono_class_set_generic_container (MonoClass *klass, MonoGenericContainer *container);
 
+MonoType*
+mono_class_gtd_get_canonical_inst (MonoClass *klass);
+
 guint32
 mono_class_get_first_method_idx (MonoClass *klass);
 
index cd15ef8ea77dda8f112f841eab872d4b241e10f5..ac7ea15d7b0e53ac90bb2f8544196d868ec12757 100644 (file)
@@ -5574,6 +5574,9 @@ mono_class_create_from_typedef (MonoImage *image, guint32 type_token, MonoError
                generic_container->is_anonymous = FALSE; // Owner class is now known, container is no longer anonymous
                context = &generic_container->context;
                mono_class_set_generic_container (klass, generic_container);
+               MonoType *canonical_inst = &((MonoClassGtd*)klass)->canonical_inst;
+               canonical_inst->type = MONO_TYPE_GENERICINST;
+               canonical_inst->data.generic_class = mono_metadata_lookup_generic_class (klass, context->class_inst, FALSE);
                enable_gclass_recording ();
        }
 
index 9b1c17772b0e72ee2c7795a9cfce1917ee642f74..54699c48ed7d833a7622464169ef23d2eb29489f 100644 (file)
@@ -3073,6 +3073,18 @@ mono_metadata_get_image_set_for_method (MonoMethodInflated *method)
        return set;
 }
 
+static gboolean
+type_is_gtd (MonoType *type)
+{
+       switch (type->type) {
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_VALUETYPE:
+               return mono_class_is_gtd (type->data.klass);
+       default:
+               return FALSE;
+       }
+}
+
 /*
  * mono_metadata_get_generic_inst:
  *
@@ -3100,6 +3112,13 @@ mono_metadata_get_generic_inst (int type_argc, MonoType **type_argv)
        ginst->type_argc = type_argc;
        memcpy (ginst->type_argv, type_argv, type_argc * sizeof (MonoType *));
 
+       for (i = 0; i < type_argc; ++i) {
+               MonoType *t = ginst->type_argv [i];
+               if (type_is_gtd (t)) {
+                       ginst->type_argv [i] = mono_class_gtd_get_canonical_inst (t->data.klass);
+               }
+       }
+
        return mono_metadata_get_canonical_generic_inst (ginst);
 }
 
index 9e4f12c75f6ddb7c229d751b0fa4c1ac0c8b8fb7..96ff937e0d1a6048376cffc928462b0f766136f3 100644 (file)
@@ -2581,6 +2581,11 @@ reflection_init_generic_class (MonoReflectionTypeBuilderHandle ref_tb, MonoError
        }
 
        generic_container->context.class_inst = mono_get_shared_generic_inst (generic_container);
+       MonoGenericContext* context = &generic_container->context;
+       MonoType *canonical_inst = &((MonoClassGtd*)klass)->canonical_inst;
+       canonical_inst->type = MONO_TYPE_GENERICINST;
+       canonical_inst->data.generic_class = mono_metadata_lookup_generic_class (klass, context->class_inst, FALSE);
+
 leave:
        HANDLE_FUNCTION_RETURN_VAL (is_ok (error));
 }