// Copyright (c) AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections; using System.Collections.Generic; using System.Linq; using ICSharpCode.NRefactory.CSharp.TypeSystem; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; using NUnit.Framework; namespace ICSharpCode.NRefactory.CSharp.Resolver { // assign short names to the fake reflection types using Null = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.Null; using dynamic = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.Dynamic; using C = Conversion; [TestFixture] public unsafe class ConversionsTest : ResolverTestBase { CSharpConversions conversions; public override void SetUp() { base.SetUp(); conversions = new CSharpConversions(compilation); } Conversion ImplicitConversion(Type from, Type to) { IType from2 = compilation.FindType(from); IType to2 = compilation.FindType(to); return conversions.ImplicitConversion(from2, to2); } [Test] public void IdentityConversions() { Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(char), typeof(char))); Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(string), typeof(string))); Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(object), typeof(object))); Assert.AreEqual(C.None, ImplicitConversion(typeof(bool), typeof(char))); Assert.AreEqual(C.IdentityConversion, conversions.ImplicitConversion(SpecialType.Dynamic, SpecialType.Dynamic)); Assert.AreEqual(C.IdentityConversion, conversions.ImplicitConversion(SpecialType.UnknownType, SpecialType.UnknownType)); Assert.AreEqual(C.IdentityConversion, conversions.ImplicitConversion(SpecialType.NullType, SpecialType.NullType)); } [Test] public void DynamicIdentityConversions() { Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(object), typeof(dynamic))); Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(dynamic), typeof(object))); } [Test] public void ComplexDynamicIdentityConversions() { Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(List), typeof(List))); Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(List), typeof(List))); Assert.AreEqual(C.None, ImplicitConversion(typeof(List), typeof(List))); Assert.AreEqual(C.None, ImplicitConversion(typeof(List), typeof(List))); Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(List[]>), typeof(List[]>))); Assert.AreEqual(C.IdentityConversion, ImplicitConversion(typeof(List[]>), typeof(List[]>))); Assert.AreEqual(C.None, ImplicitConversion(typeof(List[,]>), typeof(List[]>))); } [Test] public void PrimitiveConversions() { Assert.AreEqual(C.ImplicitNumericConversion, ImplicitConversion(typeof(char), typeof(ushort))); Assert.AreEqual(C.None, ImplicitConversion(typeof(byte), typeof(char))); Assert.AreEqual(C.ImplicitNumericConversion, ImplicitConversion(typeof(int), typeof(long))); Assert.AreEqual(C.None, ImplicitConversion(typeof(long), typeof(int))); Assert.AreEqual(C.ImplicitNumericConversion, ImplicitConversion(typeof(int), typeof(float))); Assert.AreEqual(C.None, ImplicitConversion(typeof(bool), typeof(float))); Assert.AreEqual(C.ImplicitNumericConversion, ImplicitConversion(typeof(float), typeof(double))); Assert.AreEqual(C.None, ImplicitConversion(typeof(float), typeof(decimal))); Assert.AreEqual(C.ImplicitNumericConversion, ImplicitConversion(typeof(char), typeof(long))); Assert.AreEqual(C.ImplicitNumericConversion, ImplicitConversion(typeof(uint), typeof(long))); } [Test] public void EnumerationConversion() { ResolveResult zero = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 0); ResolveResult one = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 1); C implicitEnumerationConversion = C.EnumerationConversion(true, false); Assert.AreEqual(implicitEnumerationConversion, conversions.ImplicitConversion(zero, compilation.FindType(typeof(StringComparison)))); Assert.AreEqual(C.None, conversions.ImplicitConversion(one, compilation.FindType(typeof(StringComparison)))); } [Test] public void NullableConversions() { Assert.AreEqual(C.ImplicitLiftedNumericConversion, ImplicitConversion(typeof(char), typeof(ushort?))); Assert.AreEqual(C.None, ImplicitConversion(typeof(byte), typeof(char?))); Assert.AreEqual(C.ImplicitLiftedNumericConversion, ImplicitConversion(typeof(int), typeof(long?))); Assert.AreEqual(C.None, ImplicitConversion(typeof(long), typeof(int?))); Assert.AreEqual(C.ImplicitLiftedNumericConversion, ImplicitConversion(typeof(int), typeof(float?))); Assert.AreEqual(C.None , ImplicitConversion(typeof(bool), typeof(float?))); Assert.AreEqual(C.ImplicitLiftedNumericConversion, ImplicitConversion(typeof(float), typeof(double?))); Assert.AreEqual(C.None, ImplicitConversion(typeof(float), typeof(decimal?))); } [Test] public void NullableConversions2() { Assert.AreEqual(C.ImplicitLiftedNumericConversion, ImplicitConversion(typeof(char?), typeof(ushort?))); Assert.AreEqual(C.None, ImplicitConversion(typeof(byte?), typeof(char?))); Assert.AreEqual(C.ImplicitLiftedNumericConversion, ImplicitConversion(typeof(int?), typeof(long?))); Assert.AreEqual(C.None, ImplicitConversion(typeof(long?), typeof(int?))); Assert.AreEqual(C.ImplicitLiftedNumericConversion, ImplicitConversion(typeof(int?), typeof(float?))); Assert.AreEqual(C.None, ImplicitConversion(typeof(bool?), typeof(float?))); Assert.AreEqual(C.ImplicitLiftedNumericConversion, ImplicitConversion(typeof(float?), typeof(double?))); Assert.AreEqual(C.None, ImplicitConversion(typeof(float?), typeof(decimal?))); } [Test] public void NullLiteralConversions() { Assert.AreEqual(C.NullLiteralConversion, ImplicitConversion(typeof(Null), typeof(int?))); Assert.AreEqual(C.NullLiteralConversion, ImplicitConversion(typeof(Null), typeof(char?))); Assert.AreEqual(C.None, ImplicitConversion(typeof(Null), typeof(int))); Assert.AreEqual(C.NullLiteralConversion, ImplicitConversion(typeof(Null), typeof(object))); Assert.AreEqual(C.NullLiteralConversion, ImplicitConversion(typeof(Null), typeof(dynamic))); Assert.AreEqual(C.NullLiteralConversion, ImplicitConversion(typeof(Null), typeof(string))); Assert.AreEqual(C.NullLiteralConversion, ImplicitConversion(typeof(Null), typeof(int[]))); } [Test] public void SimpleReferenceConversions() { Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(string), typeof(object))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(BitArray), typeof(ICollection))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(IList), typeof(IEnumerable))); Assert.AreEqual(C.None, ImplicitConversion(typeof(object), typeof(string))); Assert.AreEqual(C.None, ImplicitConversion(typeof(ICollection), typeof(BitArray))); Assert.AreEqual(C.None, ImplicitConversion(typeof(IEnumerable), typeof(IList))); } [Test] public void ConversionToDynamic() { Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(string), typeof(dynamic))); Assert.AreEqual(C.BoxingConversion, ImplicitConversion(typeof(int), typeof(dynamic))); } [Test] public void ConversionFromDynamic() { // There is no conversion from the type 'dynamic' to other types (except the identity conversion to object). // Such conversions only exists from dynamic expression. // This is an important distinction for type inference (see TypeInferenceTests.IEnumerableCovarianceWithDynamic) Assert.AreEqual(C.None, ImplicitConversion(typeof(dynamic), typeof(string))); Assert.AreEqual(C.None, ImplicitConversion(typeof(dynamic), typeof(int))); var dynamicRR = new ResolveResult(SpecialType.Dynamic); Assert.AreEqual(C.ImplicitDynamicConversion, conversions.ImplicitConversion(dynamicRR, compilation.FindType(typeof(string)))); Assert.AreEqual(C.ImplicitDynamicConversion, conversions.ImplicitConversion(dynamicRR, compilation.FindType(typeof(int)))); } [Test] public void ParameterizedTypeConversions() { Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(List), typeof(ICollection))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(IList), typeof(ICollection))); Assert.AreEqual(C.None, ImplicitConversion(typeof(List), typeof(ICollection))); Assert.AreEqual(C.None, ImplicitConversion(typeof(IList), typeof(ICollection))); } [Test] public void ArrayConversions() { Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(string[]), typeof(object[]))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(string[,]), typeof(object[,]))); Assert.AreEqual(C.None, ImplicitConversion(typeof(string[]), typeof(object[,]))); Assert.AreEqual(C.None, ImplicitConversion(typeof(object[]), typeof(string[]))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(string[]), typeof(IList))); Assert.AreEqual(C.None, ImplicitConversion(typeof(string[,]), typeof(IList))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(string[]), typeof(IList))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(string[]), typeof(Array))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(string[]), typeof(ICloneable))); Assert.AreEqual(C.None, ImplicitConversion(typeof(Array), typeof(string[]))); Assert.AreEqual(C.None, ImplicitConversion(typeof(object), typeof(object[]))); } [Test] public void VarianceConversions() { Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(List), typeof(IEnumerable))); Assert.AreEqual(C.None, ImplicitConversion(typeof(List), typeof(IEnumerable))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(IEnumerable), typeof(IEnumerable))); Assert.AreEqual(C.None, ImplicitConversion(typeof(ICollection), typeof(ICollection))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(Comparer), typeof(IComparer))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(Comparer), typeof(IComparer))); Assert.AreEqual(C.None, ImplicitConversion(typeof(Comparer), typeof(Comparer))); Assert.AreEqual(C.None, ImplicitConversion(typeof(List), typeof(IEnumerable))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(IEnumerable), typeof(IEnumerable))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(Func), typeof(Func))); Assert.AreEqual(C.ImplicitReferenceConversion, ImplicitConversion(typeof(Func), typeof(Func))); Assert.AreEqual(C.None, ImplicitConversion(typeof(Func), typeof(Func))); Assert.AreEqual(C.None, ImplicitConversion(typeof(Func), typeof(Func))); } [Test] public void ImplicitPointerConversion() { Assert.AreEqual(C.ImplicitPointerConversion, ImplicitConversion(typeof(Null), typeof(int*))); Assert.AreEqual(C.ImplicitPointerConversion, ImplicitConversion(typeof(int*), typeof(void*))); } [Test] public void NoConversionFromPointerTypeToObject() { Assert.AreEqual(C.None, ImplicitConversion(typeof(int*), typeof(object))); Assert.AreEqual(C.None, ImplicitConversion(typeof(int*), typeof(dynamic))); } [Test] public void UnconstrainedTypeParameter() { ITypeParameter t = new DefaultTypeParameter(compilation, EntityType.TypeDefinition, 0, "T"); ITypeParameter t2 = new DefaultTypeParameter(compilation, EntityType.TypeDefinition, 1, "T2"); ITypeParameter tm = new DefaultTypeParameter(compilation, EntityType.Method, 0, "TM"); Assert.AreEqual(C.None, conversions.ImplicitConversion(SpecialType.NullType, t)); Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object))); Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, SpecialType.Dynamic)); Assert.AreEqual(C.None, conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType)))); Assert.AreEqual(C.IdentityConversion, conversions.ImplicitConversion(t, t)); Assert.AreEqual(C.None, conversions.ImplicitConversion(t2, t)); Assert.AreEqual(C.None, conversions.ImplicitConversion(t, t2)); Assert.AreEqual(C.None, conversions.ImplicitConversion(t, tm)); Assert.AreEqual(C.None, conversions.ImplicitConversion(tm, t)); } [Test] public void TypeParameterWithReferenceTypeConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, EntityType.TypeDefinition, 0, "T", hasReferenceTypeConstraint: true); Assert.AreEqual(C.NullLiteralConversion, conversions.ImplicitConversion(SpecialType.NullType, t)); Assert.AreEqual(C.ImplicitReferenceConversion, conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object))); Assert.AreEqual(C.ImplicitReferenceConversion, conversions.ImplicitConversion(t, SpecialType.Dynamic)); Assert.AreEqual(C.None, conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType)))); } [Test] public void TypeParameterWithValueTypeConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, EntityType.TypeDefinition, 0, "T", hasValueTypeConstraint: true); Assert.AreEqual(C.None, conversions.ImplicitConversion(SpecialType.NullType, t)); Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object))); Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, SpecialType.Dynamic)); Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType)))); } [Test] public void TypeParameterWithClassConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, EntityType.TypeDefinition, 0, "T", constraints: new[] { compilation.FindType(typeof(StringComparer)) }); Assert.AreEqual(C.NullLiteralConversion, conversions.ImplicitConversion(SpecialType.NullType, t)); Assert.AreEqual(C.ImplicitReferenceConversion, conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object))); Assert.AreEqual(C.ImplicitReferenceConversion, conversions.ImplicitConversion(t, SpecialType.Dynamic)); Assert.AreEqual(C.None, conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType)))); Assert.AreEqual(C.ImplicitReferenceConversion, conversions.ImplicitConversion(t, compilation.FindType(typeof(StringComparer)))); Assert.AreEqual(C.ImplicitReferenceConversion, conversions.ImplicitConversion(t, compilation.FindType(typeof(IComparer)))); Assert.AreEqual(C.None, conversions.ImplicitConversion(t, compilation.FindType(typeof(IComparer)))); Assert.AreEqual(C.ImplicitReferenceConversion, conversions.ImplicitConversion(t, compilation.FindType(typeof(IComparer)))); } [Test] public void TypeParameterWithInterfaceConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, EntityType.TypeDefinition, 0, "T", constraints: new [] { compilation.FindType(typeof(IList)) }); Assert.AreEqual(C.None, conversions.ImplicitConversion(SpecialType.NullType, t)); Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object))); Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, SpecialType.Dynamic)); Assert.AreEqual(C.None, conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType)))); Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, compilation.FindType(typeof(IList)))); Assert.AreEqual(C.BoxingConversion, conversions.ImplicitConversion(t, compilation.FindType(typeof(IEnumerable)))); } [Test] public void UserDefinedImplicitConversion() { Conversion c = ImplicitConversion(typeof(DateTime), typeof(DateTimeOffset)); Assert.IsTrue(c.IsImplicit && c.IsUserDefined); Assert.AreEqual("System.DateTimeOffset.op_Implicit", c.Method.FullName); Assert.AreEqual(C.None, ImplicitConversion(typeof(DateTimeOffset), typeof(DateTime))); } [Test] public void UserDefinedImplicitNullableConversion() { // User-defined conversion followed by nullable conversion Conversion c = ImplicitConversion(typeof(DateTime), typeof(DateTimeOffset?)); Assert.IsTrue(c.IsValid && c.IsUserDefined); Assert.IsFalse(c.IsLifted); // Lifted user-defined conversion c = ImplicitConversion(typeof(DateTime?), typeof(DateTimeOffset?)); Assert.IsTrue(c.IsValid && c.IsUserDefined && c.IsLifted); // User-defined conversion doesn't drop the nullability c = ImplicitConversion(typeof(DateTime?), typeof(DateTimeOffset)); Assert.IsFalse(c.IsValid); } bool IntegerLiteralConversion(object value, Type to) { IType fromType = compilation.FindType(value.GetType()); ConstantResolveResult crr = new ConstantResolveResult(fromType, value); IType to2 = compilation.FindType(to); return conversions.ImplicitConversion(crr, to2).IsValid; } [Test] public void IntegerLiteralToEnumConversions() { Assert.IsTrue(IntegerLiteralConversion(0, typeof(LoaderOptimization))); Assert.IsTrue(IntegerLiteralConversion(0L, typeof(LoaderOptimization))); Assert.IsTrue(IntegerLiteralConversion(0, typeof(LoaderOptimization?))); Assert.IsFalse(IntegerLiteralConversion(0, typeof(string))); Assert.IsFalse(IntegerLiteralConversion(1, typeof(LoaderOptimization))); } [Test] public void ImplicitConstantExpressionConversion() { Assert.IsTrue(IntegerLiteralConversion(0, typeof(int))); Assert.IsTrue(IntegerLiteralConversion(0, typeof(ushort))); Assert.IsTrue(IntegerLiteralConversion(0, typeof(sbyte))); Assert.IsTrue (IntegerLiteralConversion(-1, typeof(int))); Assert.IsFalse(IntegerLiteralConversion(-1, typeof(ushort))); Assert.IsTrue (IntegerLiteralConversion(-1, typeof(sbyte))); Assert.IsTrue (IntegerLiteralConversion(200, typeof(int))); Assert.IsTrue (IntegerLiteralConversion(200, typeof(ushort))); Assert.IsFalse(IntegerLiteralConversion(200, typeof(sbyte))); } [Test] public void ImplicitLongConstantExpressionConversion() { Assert.IsFalse(IntegerLiteralConversion(0L, typeof(int))); Assert.IsFalse(IntegerLiteralConversion(0L, typeof(short))); Assert.IsTrue(IntegerLiteralConversion(0L, typeof(long))); Assert.IsTrue(IntegerLiteralConversion(0L, typeof(ulong))); Assert.IsTrue(IntegerLiteralConversion(-1L, typeof(long))); Assert.IsFalse(IntegerLiteralConversion(-1L, typeof(ulong))); } [Test] public void ImplicitConstantExpressionConversionToNullable() { Assert.IsTrue(IntegerLiteralConversion(0, typeof(uint?))); Assert.IsTrue(IntegerLiteralConversion(0, typeof(short?))); Assert.IsTrue(IntegerLiteralConversion(0, typeof(byte?))); Assert.IsFalse(IntegerLiteralConversion(-1, typeof(uint?))); Assert.IsTrue (IntegerLiteralConversion(-1, typeof(short?))); Assert.IsFalse(IntegerLiteralConversion(-1, typeof(byte?))); Assert.IsTrue(IntegerLiteralConversion(200, typeof(uint?))); Assert.IsTrue(IntegerLiteralConversion(200, typeof(short?))); Assert.IsTrue(IntegerLiteralConversion(200, typeof(byte?))); Assert.IsFalse(IntegerLiteralConversion(0L, typeof(uint?))); Assert.IsTrue (IntegerLiteralConversion(0L, typeof(long?))); Assert.IsTrue (IntegerLiteralConversion(0L, typeof(ulong?))); Assert.IsTrue(IntegerLiteralConversion(-1L, typeof(long?))); Assert.IsFalse(IntegerLiteralConversion(-1L, typeof(ulong?))); } [Test] public void ImplicitConstantExpressionConversionNumberInterfaces() { Assert.IsTrue(IntegerLiteralConversion(0, typeof(IFormattable))); Assert.IsTrue(IntegerLiteralConversion(0, typeof(IComparable))); Assert.IsFalse(IntegerLiteralConversion(0, typeof(IComparable))); Assert.IsFalse(IntegerLiteralConversion(0, typeof(IComparable))); } int BetterConversion(Type s, Type t1, Type t2) { IType sType = compilation.FindType(s); IType t1Type = compilation.FindType(t1); IType t2Type = compilation.FindType(t2); return conversions.BetterConversion(sType, t1Type, t2Type); } int BetterConversion(object value, Type t1, Type t2) { IType fromType = compilation.FindType(value.GetType()); ConstantResolveResult crr = new ConstantResolveResult(fromType, value); IType t1Type = compilation.FindType(t1); IType t2Type = compilation.FindType(t2); return conversions.BetterConversion(crr, t1Type, t2Type); } [Test] public void BetterConversion() { Assert.AreEqual(1, BetterConversion(typeof(string), typeof(string), typeof(object))); Assert.AreEqual(2, BetterConversion(typeof(string), typeof(object), typeof(IComparable))); Assert.AreEqual(0, BetterConversion(typeof(string), typeof(IEnumerable), typeof(IComparable))); } [Test] public void BetterPrimitiveConversion() { Assert.AreEqual(1, BetterConversion(typeof(short), typeof(int), typeof(long))); Assert.AreEqual(1, BetterConversion(typeof(short), typeof(int), typeof(uint))); Assert.AreEqual(2, BetterConversion(typeof(ushort), typeof(uint), typeof(int))); Assert.AreEqual(1, BetterConversion(typeof(char), typeof(short), typeof(int))); Assert.AreEqual(1, BetterConversion(typeof(char), typeof(ushort), typeof(int))); Assert.AreEqual(1, BetterConversion(typeof(sbyte), typeof(long), typeof(ulong))); Assert.AreEqual(2, BetterConversion(typeof(byte), typeof(ushort), typeof(short))); Assert.AreEqual(1, BetterConversion(1, typeof(sbyte), typeof(byte))); Assert.AreEqual(2, BetterConversion(1, typeof(ushort), typeof(sbyte))); } [Test] public void BetterNullableConversion() { Assert.AreEqual(0, BetterConversion(typeof(byte), typeof(int), typeof(uint?))); Assert.AreEqual(0, BetterConversion(typeof(byte?), typeof(int?), typeof(uint?))); Assert.AreEqual(1, BetterConversion(typeof(byte), typeof(ushort?), typeof(uint?))); Assert.AreEqual(2, BetterConversion(typeof(byte?), typeof(ulong?), typeof(uint?))); Assert.AreEqual(0, BetterConversion(typeof(byte), typeof(ushort?), typeof(uint))); Assert.AreEqual(0, BetterConversion(typeof(byte), typeof(ushort?), typeof(int))); Assert.AreEqual(2, BetterConversion(typeof(byte), typeof(ulong?), typeof(uint))); Assert.AreEqual(0, BetterConversion(typeof(byte), typeof(ulong?), typeof(int))); Assert.AreEqual(2, BetterConversion(typeof(ushort?), typeof(long?), typeof(int?))); Assert.AreEqual(0, BetterConversion(typeof(sbyte), typeof(int?), typeof(uint?))); } [Test] public void ExpansiveInheritance() { var a = new DefaultUnresolvedTypeDefinition(string.Empty, "A"); var b = new DefaultUnresolvedTypeDefinition(string.Empty, "B"); // interface A a.Kind = TypeKind.Interface; a.TypeParameters.Add(new DefaultUnresolvedTypeParameter(EntityType.TypeDefinition, 0, "U") { Variance = VarianceModifier.Contravariant }); // interface B : A>> { } b.TypeParameters.Add(new DefaultUnresolvedTypeParameter(EntityType.TypeDefinition, 0, "X")); b.BaseTypes.Add(new ParameterizedTypeReference( a, new[] { new ParameterizedTypeReference( a, new [] { new ParameterizedTypeReference( b, new [] { new TypeParameterReference(EntityType.TypeDefinition, 0) } ) } ) })); ICompilation compilation = TypeSystemHelper.CreateCompilation(a, b); ITypeDefinition resolvedA = compilation.MainAssembly.GetTypeDefinition(a.FullTypeName); ITypeDefinition resolvedB = compilation.MainAssembly.GetTypeDefinition(b.FullTypeName); IType type1 = new ParameterizedType(resolvedB, new [] { compilation.FindType(KnownTypeCode.Double) }); IType type2 = new ParameterizedType(resolvedA, new [] { new ParameterizedType(resolvedB, new[] { compilation.FindType(KnownTypeCode.String) }) }); Assert.IsFalse(conversions.ImplicitConversion(type1, type2).IsValid); } [Test] public void ImplicitTypeParameterConversion() { string program = @"using System; class Test { public void M(T t) where T : U { U u = $t$; } }"; Assert.AreEqual(C.BoxingConversion, GetConversion(program)); } [Test] public void InvalidImplicitTypeParameterConversion() { string program = @"using System; class Test { public void M(T t) where U : T { U u = $t$; } }"; Assert.AreEqual(C.None, GetConversion(program)); } [Test] public void ImplicitTypeParameterArrayConversion() { string program = @"using System; class Test { public void M(T[] t) where T : U { U[] u = $t$; } }"; // invalid, e.g. T=int[], U=object[] Assert.AreEqual(C.None, GetConversion(program)); } [Test] public void ImplicitTypeParameterConversionWithClassConstraint() { string program = @"using System; class Test { public void M(T t) where T : class, U where U : class { U u = $t$; } }"; Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program)); } [Test] public void ImplicitTypeParameterArrayConversionWithClassConstraint() { string program = @"using System; class Test { public void M(T[] t) where T : class, U where U : class { U[] u = $t$; } }"; Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program)); } [Test] public void ImplicitTypeParameterConversionWithClassConstraintOnlyOnT() { string program = @"using System; class Test { public void M(T t) where T : class, U { U u = $t$; } }"; Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program)); } [Test] public void ImplicitTypeParameterArrayConversionWithClassConstraintOnlyOnT() { string program = @"using System; class Test { public void M(T[] t) where T : class, U { U[] u = $t$; } }"; Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program)); } [Test] public void MethodGroupConversion_Void() { string program = @"using System; delegate void D(); class Test { D d = $M$; public static void M() {} }"; var c = GetConversion(program); Assert.IsTrue(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); Assert.IsNotNull(c.Method); } [Test] public void MethodGroupConversion_MatchingSignature() { string program = @"using System; delegate object D(int argument); class Test { D d = $M$; public static object M(int argument) {} }"; var c = GetConversion(program); Assert.IsTrue(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_InvalidReturnType() { string program = @"using System; delegate object D(int argument); class Test { D d = $M$; public static int M(int argument) {} }"; var c = GetConversion(program); Assert.IsFalse(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_CovariantReturnType() { string program = @"using System; delegate object D(int argument); class Test { D d = $M$; public static string M(int argument) {} }"; var c = GetConversion(program); Assert.IsTrue(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_RefArgumentTypesEqual() { string program = @"using System; delegate void D(ref object o); class Test { D d = $M$; public static void M(ref object o) {} }"; var c = GetConversion(program); Assert.IsTrue(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_RefArgumentObjectVsDynamic() { string program = @"using System; delegate void D(ref object o); class Test { D d = $M$; public static void M(ref dynamic o) {} }"; var c = GetConversion(program); Assert.IsFalse(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_RefVsOut() { string program = @"using System; delegate void D(ref object o); class Test { D d = $M$; public static void M(out object o) {} }"; var c = GetConversion(program); Assert.IsFalse(c.IsValid); } [Test] public void MethodGroupConversion_RefVsNormal() { string program = @"using System; delegate void D(ref object o); class Test { D d = $M$; public static void M(object o) {} }"; var c = GetConversion(program); Assert.IsFalse(c.IsValid); } [Test] public void MethodGroupConversion_NormalVsOut() { string program = @"using System; delegate void D(object o); class Test { D d = $M$; public static void M(out object o) {} }"; var c = GetConversion(program); Assert.IsFalse(c.IsValid); } [Test] public void MethodGroupConversion_MatchingNormalParameter() { string program = @"using System; delegate void D(object o); class Test { D d = $M$; public static void M(object o) {} }"; var c = GetConversion(program); Assert.IsTrue(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_IdentityConversion() { string program = @"using System; delegate void D(object o); class Test { D d = $M$; public static void M(dynamic o) {} }"; var c = GetConversion(program); Assert.IsTrue(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_Contravariance() { string program = @"using System; delegate void D(string o); class Test { D d = $M$; public static void M(object o) {} }"; var c = GetConversion(program); Assert.IsTrue(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); } [Test, Ignore("Not sure if this conversion should be valid or not... NR and mcs both accept it as valid, csc treats it as invalid")] public void MethodGroupConversion_NoContravarianceDynamic() { string program = @"using System; delegate void D(string o); class Test { D d = $M$; public static void M(dynamic o) {} }"; var c = GetConversion(program); //Assert.IsFrue(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_ExactMatchIsBetter() { string program = @"using System; class Test { delegate void D(string a); D d = $M$; static void M(object x) {} static void M(string x = null) {} }"; var c = GetConversion(program); Assert.IsTrue(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); Assert.AreEqual("System.String", c.Method.Parameters.Single().Type.FullName); } [Test] public void MethodGroupConversion_CannotLeaveOutOptionalParameters() { string program = @"using System; class Test { delegate void D(string a); D d = $M$; static void M(object x) {} static void M(string x, string y = null) {} }"; var c = GetConversion(program); Assert.IsTrue(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); Assert.AreEqual("System.Object", c.Method.Parameters.Single().Type.FullName); } [Test] public void MethodGroupConversion_CannotUseExpandedParams() { string program = @"using System; class Test { delegate void D(string a); D d = $M$; static void M(object x) {} static void M(params string[] x) {} }"; var c = GetConversion(program); Assert.IsTrue(c.IsValid); Assert.IsTrue(c.IsMethodGroupConversion); Assert.AreEqual("System.Object", c.Method.Parameters.Single().Type.FullName); } } }