Add more type parameter checks for inherited constraints.
authorMarek Safar <marek.safar@gmail.com>
Thu, 28 Oct 2010 12:43:17 +0000 (13:43 +0100)
committerMarek Safar <marek.safar@gmail.com>
Thu, 28 Oct 2010 12:44:32 +0000 (13:44 +0100)
mcs/errors/gcs0315-3.cs [new file with mode: 0644]
mcs/errors/gcs0455-4.cs [new file with mode: 0644]
mcs/errors/gcs0455-5.cs [new file with mode: 0644]
mcs/mcs/convert.cs
mcs/mcs/expression.cs
mcs/mcs/generic.cs
mcs/mcs/method.cs
mcs/tests/gtest-544.cs [new file with mode: 0644]

diff --git a/mcs/errors/gcs0315-3.cs b/mcs/errors/gcs0315-3.cs
new file mode 100644 (file)
index 0000000..80ab3db
--- /dev/null
@@ -0,0 +1,21 @@
+// CS0315: The type `int' cannot be used as type parameter `U' in the generic type or method `A<int?>.Test<U>()'. There is no boxing conversion from `int' to `int?'
+// Line: 19
+
+class A<T>
+{
+       public static void Test<U> () where U : T
+       {
+       }
+}
+
+class B : A<int?>
+{
+}
+
+class Program
+{
+       public static void Main ()
+       {
+               B.Test<int> ();
+       }
+}
diff --git a/mcs/errors/gcs0455-4.cs b/mcs/errors/gcs0455-4.cs
new file mode 100644 (file)
index 0000000..b27656d
--- /dev/null
@@ -0,0 +1,14 @@
+// CS0455: Type parameter `Y' inherits conflicting constraints `long' and `long?'
+// Line: 11
+
+abstract class A<T1, T2>
+{
+       public abstract void Foo<U> () where U : T1, T2;
+}
+
+class B : A<long, long?>
+{
+       public override void Foo<Y> ()
+       {
+       }
+}
diff --git a/mcs/errors/gcs0455-5.cs b/mcs/errors/gcs0455-5.cs
new file mode 100644 (file)
index 0000000..3844f9d
--- /dev/null
@@ -0,0 +1,14 @@
+// CS0455: Type parameter `Y' inherits conflicting constraints `class' and `long'
+// Line: 11
+
+abstract class A<T>
+{
+       public abstract void Foo<U> () where U : class, T;
+}
+
+class B : A<long>
+{
+       public override void Foo<Y> ()
+       {
+       }
+}
index e40de33856d5921976cfcf3d1e5d06cf2b952af8..0f549ec51169aed830cc82fbaa2dc7e6934cddda 100644 (file)
@@ -89,7 +89,7 @@ namespace Mono.CSharp {
                        return ImplicitReferenceConversionExists (MyEmptyExpr, arg_type) || ExplicitReferenceConversionExists (array.Element, arg_type);
                }
 
-               static Expression ImplicitTypeParameterConversion (Expression expr, TypeSpec target_type)
+               public static Expression ImplicitTypeParameterConversion (Expression expr, TypeSpec target_type)
                {
                        var expr_type = (TypeParameterSpec) expr.Type;
 
index 3fd641f6aafc301c72722b3bc46729611385aa8a..e8d5538dc0047974d93560b7af9df6eb687f09b3 100644 (file)
@@ -5505,7 +5505,11 @@ namespace Mono.CSharp {
 
                        var tparam = type as TypeParameterSpec;
                        if (tparam != null) {
-                               if (!tparam.HasSpecialConstructor && !tparam.HasSpecialStruct) {
+                               //
+                               // Check whether the type of type parameter can be constructed. BaseType can be a struct for method overrides
+                               // where type parameter constraint is inflated to struct
+                               //
+                               if ((tparam.SpecialConstraint & (SpecialConstraint.Struct | SpecialConstraint.Constructor)) == 0 && !tparam.BaseType.IsStruct) {
                                        ec.Report.Error (304, loc,
                                                "Cannot create an instance of the variable type `{0}' because it does not have the new() constraint",
                                                TypeManager.CSharpName (type));
index d59cfcdccc0d74ee52e3a743b7fed5b03196d873..4c5a94593273ec6672e9cf943001e2006b7781b7 100644 (file)
@@ -88,17 +88,31 @@ namespace Mono.CSharp {
 
                #endregion
 
-               bool CheckConflictingInheritedConstraint (TypeSpec ba, TypeSpec bb, IMemberContext context, Location loc)
+               public static bool CheckConflictingInheritedConstraint (TypeParameterSpec spec, TypeSpec bb, IMemberContext context, Location loc)
                {
-                       if (!TypeSpec.IsBaseClass (ba, bb, false) && !TypeSpec.IsBaseClass (bb, ba, false)) {
+                       if (spec.HasSpecialClass && bb.IsStruct) {
                                context.Compiler.Report.Error (455, loc,
                                        "Type parameter `{0}' inherits conflicting constraints `{1}' and `{2}'",
-                                       tparam.Value,
-                                       ba.GetSignatureForError (), bb.GetSignatureForError ());
+                                       spec.Name, "class", bb.GetSignatureForError ());
+
                                return false;
                        }
 
-                       return true;
+                       return CheckConflictingInheritedConstraint (spec, spec.BaseType, bb, context, loc);
+               }
+
+               static bool CheckConflictingInheritedConstraint (TypeParameterSpec spec, TypeSpec ba, TypeSpec bb, IMemberContext context, Location loc)
+               {
+                       if (ba == bb)
+                               return true;
+
+                       if (TypeSpec.IsBaseClass (ba, bb, false) || TypeSpec.IsBaseClass (bb, ba, false))
+                               return true;
+
+                       context.Compiler.Report.Error (455, loc,
+                               "Type parameter `{0}' inherits conflicting constraints `{1}' and `{2}'",
+                               spec.Name, ba.GetSignatureForError (), bb.GetSignatureForError ());
+                       return false;
                }
 
                public void CheckGenericConstraints (IMemberContext context)
@@ -210,14 +224,14 @@ namespace Mono.CSharp {
                                        //
                                        if (constraint_tp.HasTypeConstraint) {
                                                if (spec.HasTypeConstraint || spec.HasSpecialStruct) {
-                                                       if (!CheckConflictingInheritedConstraint (spec.BaseType, constraint_tp.BaseType, context, constraint.Location))
+                                                       if (!CheckConflictingInheritedConstraint (spec, constraint_tp.BaseType, context, constraint.Location))
                                                                continue;
                                                } else {
                                                        for (int ii = 0; ii < tparam_types.Count; ++ii) {
                                                                if (!tparam_types[ii].HasTypeConstraint)
                                                                        continue;
 
-                                                               if (!CheckConflictingInheritedConstraint (tparam_types[ii].BaseType, constraint_tp.BaseType, context, constraint.Location))
+                                                               if (!CheckConflictingInheritedConstraint (spec, tparam_types[ii].BaseType, constraint_tp.BaseType, context, constraint.Location))
                                                                        break;
                                                        }
                                                }
@@ -743,6 +757,21 @@ namespace Mono.CSharp {
 
                #endregion
 
+               public void ChangeTypeArgumentToBaseType (int index)
+               {
+                       BaseType = targs [index];
+                       if (targs.Length == 1) {
+                               targs = null;
+                       } else {
+                               var copy = new TypeSpec[targs.Length - 1];
+                               if (index > 0)
+                                       Array.Copy (targs, copy, index);
+
+                               Array.Copy (targs, index + 1, copy, index, targs.Length - index - 1);
+                               targs = copy;
+                       }
+               }
+
                public string DisplayDebugInfo ()
                {
                        var s = GetSignatureForError ();
@@ -967,6 +996,7 @@ namespace Mono.CSharp {
                                for (int i = 0; i < ifaces.Count; ++i)
                                        tps.ifaces.Add (inflator.Inflate (ifaces[i]));
                        }
+
                        if (targs != null) {
                                tps.targs = new TypeSpec[targs.Length];
                                for (int i = 0; i < targs.Length; ++i)
@@ -2018,9 +2048,20 @@ namespace Mono.CSharp {
 
                bool CheckConversion (IMemberContext mc, MemberSpec context, TypeSpec atype, TypeParameterSpec tparam, TypeSpec ttype, Location loc)
                {
-                       var expr = new EmptyExpression (atype);
-                       if (Convert.ImplicitStandardConversionExists (expr, ttype))
-                               return true;
+                       if (TypeManager.IsValueType (atype)) {
+                               if (atype == ttype || Convert.ImplicitBoxingConversion (null, atype, ttype) != null)
+                                       return true;
+                       } else {
+                               var expr = new EmptyExpression (atype);
+
+                               if (atype.IsGenericParameter) {
+                                       if (Convert.ImplicitTypeParameterConversion (expr, ttype) != null)
+                                               return true;
+                               }
+
+                               if (Convert.ImplicitStandardConversionExists (expr, ttype))
+                                       return true;
+                       }
 
                        //
                        // When partial/full type inference finds a dynamic type argument delay
index b35b798dc11cee9e6ac59e7e12e7c7fa6d01b2ae..1cd5bf85aa688610b0173fe9796d736dc08d338f 100644 (file)
@@ -957,11 +957,38 @@ namespace Mono.CSharp {
                                //
                                if (base_tparams != null) {
                                        var base_tparam = base_tparams[i];
-                                       tp.Type.SpecialConstraint = base_tparam.SpecialConstraint;
+                                       var local_tparam = tp.Type;
+                                       local_tparam.SpecialConstraint = base_tparam.SpecialConstraint;
 
                                        var inflator = new TypeParameterInflator (CurrentType, base_decl_tparams, base_targs);
-                                       base_tparam.InflateConstraints (inflator, tp.Type);
-                               } else if (MethodData.implementing != null) {
+                                       base_tparam.InflateConstraints (inflator, local_tparam);
+
+                                       //
+                                       // Check all type argument constraints for possible collision
+                                       // introduced by inflating inherited constraints in this context
+                                       //
+                                       // Conflict example:
+                                       //
+                                       // class A<T> { virtual void Foo<U> () where U : class, T {} }
+                                       // class B : A<int> { override void Foo<U> {} }
+                                       //
+                                       var local_tparam_targs = local_tparam.TypeArguments;
+                                       if (local_tparam_targs != null) {                                       
+                                               for (int ii = 0; ii < local_tparam_targs.Length; ++ii) {
+                                                       var ta = local_tparam_targs [ii];
+                                                       if (!ta.IsClass && !ta.IsStruct)
+                                                               continue;
+
+                                                       if (Constraints.CheckConflictingInheritedConstraint (local_tparam, ta, this, Location)) {
+                                                               local_tparam.ChangeTypeArgumentToBaseType (ii);
+                                                       }
+                                               }
+                                       }
+
+                                       continue;
+                               }
+                               
+                               if (MethodData.implementing != null) {
                                        var base_tp = MethodData.implementing.Constraints[i];
                                        if (!tp.Type.HasSameConstraintsImplementation (base_tp)) {
                                                Report.SymbolRelatedToPreviousError (MethodData.implementing);
diff --git a/mcs/tests/gtest-544.cs b/mcs/tests/gtest-544.cs
new file mode 100644 (file)
index 0000000..98c2c85
--- /dev/null
@@ -0,0 +1,53 @@
+using System;
+
+public abstract class A<T>
+{
+       public abstract G Foo<G> () where G : T;
+       
+       public virtual G Foo2<G> () where G : T
+       {
+               return default (G);
+       }
+}
+
+public class B : A<int?>
+{
+       public override G Foo<G> ()
+       {
+               return new G ();
+       }
+       
+       public override G Foo2<G> ()
+       {
+               return base.Foo2<G> ();
+       }
+}
+
+abstract class A2<T>
+{
+       public abstract void Foo<U> () where U : struct, T;
+}
+
+class B2 : A2<System.ValueType>
+{
+       public override void Foo<Y> ()
+       {
+       }
+}
+
+class Program
+{
+       public static int Main ()
+       {
+               var b = new B ();
+               if (b.Foo<int?> () == null)
+                       return 0;
+               
+               b.Foo2<int?> ();
+               
+               var b2 = new B2 ();
+               b2.Foo<byte> ();
+               
+               return 1;
+       }
+}