[mcs] Explicit type parameter conversion to generic parameter
authorMarek Safar <marek.safar@gmail.com>
Wed, 21 May 2014 08:19:25 +0000 (10:19 +0200)
committerMarek Safar <marek.safar@gmail.com>
Wed, 21 May 2014 08:19:25 +0000 (10:19 +0200)
mcs/mcs/convert.cs
mcs/tests/gtest-610.cs [new file with mode: 0644]
mcs/tests/ver-il-net_4_5.xml

index 3fcffa8d7b8912e3ee4e029e3514e75bf8c41d13..05709f6ae471068a6d25302c3bfc4ec284ef93d5 100644 (file)
@@ -126,34 +126,39 @@ namespace Mono.CSharp {
                        return null;
                }
 
-               static Expression ExplicitTypeParameterConversion (Expression source, TypeSpec source_type, TypeSpec target_type)
+               static Expression ExplicitTypeParameterConversionFromT (Expression source, TypeSpec source_type, TypeSpec target_type)
                {
                        var target_tp = target_type as TypeParameterSpec;
                        if (target_tp != null) {
+                               //
+                               // From a type parameter U to T, provided T depends on U
+                               //
                                if (target_tp.TypeArguments != null && target_tp.HasDependencyOn (source_type)) {
                                        return source == null ? EmptyExpression.Null : new ClassCast (source, target_type);
                                }
-
-/*
-                               if (target_tp.Interfaces != null) {
-                                       foreach (TypeSpec iface in target_tp.Interfaces) {
-                                               if (!TypeManager.IsGenericParameter (iface))
-                                                       continue;
-
-                                               if (TypeManager.IsSubclassOf (source_type, iface))
-                                                       return source == null ? EmptyExpression.Null : new ClassCast (source, target_type, true);
-                                       }
-                               }
-*/
-                               return null;
                        }
 
+                       //
+                       // From T to any interface-type I provided there is not already an implicit conversion from T to I
+                       //
                        if (target_type.IsInterface)
                                return source == null ? EmptyExpression.Null : new ClassCast (source, target_type, true);
 
                        return null;
                }
 
+               static Expression ExplicitTypeParameterConversionToT (Expression source, TypeSpec source_type, TypeParameterSpec target_type)
+               {
+                       //
+                       // From the effective base class C of T to T and from any base class of C to T
+                       //
+                       var effective = target_type.GetEffectiveBase ();
+                       if (TypeSpecComparer.IsEqual (effective, source_type) || TypeSpec.IsBaseClass (effective, source_type, false))
+                               return source == null ? EmptyExpression.Null : new ClassCast (source, target_type);
+
+                       return null;
+               }
+
                public static Expression ImplicitReferenceConversion (Expression expr, TypeSpec target_type, bool explicit_cast)
                {
                        TypeSpec expr_type = expr.Type;
@@ -1812,10 +1817,10 @@ namespace Mono.CSharp {
                                return source == null ? EmptyExpression.Null : new UnboxCast (source, target_type);
 
                        //
-                       // Explicit type parameter conversion.
+                       // Explicit type parameter conversion from T
                        //
                        if (source_type.Kind == MemberKind.TypeParameter)
-                               return ExplicitTypeParameterConversion (source, source_type, target_type);
+                               return ExplicitTypeParameterConversionFromT (source, source_type, target_type);
 
                        bool target_is_value_type = target_type.Kind == MemberKind.Struct || target_type.Kind == MemberKind.Enum;
 
@@ -1849,6 +1854,9 @@ namespace Mono.CSharp {
                        // From any interface-type S to to any class type T, provided T is not
                        // sealed, or provided T implements S.
                        //
+                       // This also covers Explicit conversions involving type parameters
+                       // section From any interface type to T
+                       //
                        if (source_type.Kind == MemberKind.Interface) {
                                if (!target_type.IsSealed || target_type.ImplementsInterface (source_type, true)) {
                                        if (source == null)
@@ -1971,6 +1979,10 @@ namespace Mono.CSharp {
                                        return source == null ? EmptyExpression.Null : new ClassCast (source, target_type);
                        }
 
+                       var tps = target_type as TypeParameterSpec;
+                       if (tps != null)
+                               return ExplicitTypeParameterConversionToT (source, source_type, tps);
+
                        return null;
                }
 
diff --git a/mcs/tests/gtest-610.cs b/mcs/tests/gtest-610.cs
new file mode 100644 (file)
index 0000000..5299a57
--- /dev/null
@@ -0,0 +1,35 @@
+using System;
+
+class G1<T1, T2>
+       where T1 : B
+       where T2 : T1
+{
+       public static T2 Test1 (B b)
+       {
+               return (T2)b;
+       }
+
+       public static T2 Test2 (A a)
+       {
+               return (T2)a;
+       }
+
+       public static T2 Test3 (dynamic a)
+       {
+               return (T2)a;
+       }
+}
+
+class B : A
+{
+}
+
+class A
+{
+       static void Main ()
+       {
+               G1<B, B>.Test1 (new B ());
+               G1<B, B>.Test2 (new B ());
+               G1<B, B>.Test3 (null);
+       }
+}
\ No newline at end of file
index 0eeb6c747b98d2370946230034a1392873b30fc1..84a0189a8879c04f450e87f925e144ab49ecdabc 100644 (file)
       </method>\r
     </type>\r
   </test>\r
+  <test name="gtest-610.cs">\r
+    <type name="G1`2[T1,T2]">\r
+      <method name="T2 Test1(B)" attrs="150">\r
+        <size>15</size>\r
+      </method>\r
+      <method name="T2 Test2(A)" attrs="150">\r
+        <size>15</size>\r
+      </method>\r
+      <method name="T2 Test3(System.Object)" attrs="150">\r
+        <size>77</size>\r
+      </method>\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+    <type name="B">\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+    <type name="A">\r
+      <method name="Void Main()" attrs="145">\r
+        <size>31</size>\r
+      </method>\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+  </test>\r
   <test name="gtest-anontype-01.cs">\r
     <type name="Test">\r
       <method name="Int32 Main()" attrs="150">\r