diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs index eb6b010d13..22c5d2b128 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs @@ -109,9 +109,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (c.IsValid) return c; if (ImplicitConstantExpressionConversion(resolveResult, toType)) return Conversion.ImplicitConstantExpressionConversion; + c = StandardImplicitConversion(resolveResult.Type, toType); + if (c.IsValid) return c; + c = UserDefinedImplicitConversion(resolveResult, resolveResult.Type, toType); + if (c.IsValid) return c; + } else { + c = ImplicitConversion(resolveResult.Type, toType); + if (c.IsValid) return c; } - c = ImplicitConversion(resolveResult.Type, toType); - if (c.IsValid) return c; if (resolveResult.Type.Kind == TypeKind.Dynamic) return Conversion.ImplicitDynamicConversion; c = AnonymousFunctionConversion(resolveResult, toType); @@ -135,7 +140,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver // C# 4.0 spec: §6.1 c = StandardImplicitConversion(fromType, toType); if (!c.IsValid) { - c = UserDefinedImplicitConversion(fromType, toType); + c = UserDefinedImplicitConversion(null, fromType, toType); } implicitConversionCache[pair] = c; return c; @@ -214,8 +219,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Conversion c = ImplicitConversion(resolveResult, toType); if (c.IsValid) return c; - else - return ExplicitConversionImpl(resolveResult.Type, toType); + c = ExplicitConversionImpl(resolveResult.Type, toType); + if (c.IsValid) + return c; + return UserDefinedExplicitConversion(resolveResult, resolveResult.Type, toType); } public Conversion ExplicitConversion(IType fromType, IType toType) @@ -228,8 +235,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Conversion c = ImplicitConversion(fromType, toType); if (c.IsValid) return c; - else - return ExplicitConversionImpl(fromType, toType); + c = ExplicitConversionImpl(fromType, toType); + if (c.IsValid) + return c; + return UserDefinedExplicitConversion(null, fromType, toType); } Conversion ExplicitConversionImpl(IType fromType, IType toType) @@ -252,7 +261,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return c; if (ExplicitPointerConversion(fromType, toType)) return Conversion.ExplicitPointerConversion; - return UserDefinedExplicitConversion(fromType, toType); + return Conversion.None; } #endregion @@ -643,8 +652,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver #region Implicit Constant-Expression Conversion bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType) { + if (rr == null || !rr.IsCompileTimeConstant) + return false; // C# 4.0 spec: §6.1.9 - Debug.Assert(rr.IsCompileTimeConstant); TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type); TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType)); if (fromTypeCode == TypeCode.Int64) { @@ -784,10 +794,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver // "selected.Count == 1" check above should have found the unique lifted operator. } - Conversion UserDefinedImplicitConversion(IType fromType, IType toType) + Conversion UserDefinedImplicitConversion(ResolveResult fromResult, IType fromType, IType toType) { // C# 4.0 spec §6.4.4 User-defined implicit conversions - var operators = GetApplicableConversionOperators(fromType, toType, false); + var operators = GetApplicableConversionOperators(fromResult, fromType, toType, false); if (operators.Count > 0) { IType S0 = NullableType.GetUnderlyingType(fromType); @@ -808,21 +818,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - Conversion UserDefinedExplicitConversion(IType fromType, IType toType) + Conversion UserDefinedExplicitConversion(ResolveResult fromResult, IType fromType, IType toType) { // C# 4.0 spec §6.4.5 User-defined implicit conversions - var operators = GetApplicableConversionOperators(fromType, toType, true); + var operators = GetApplicableConversionOperators(fromResult, fromType, toType, true); if (operators.Count > 0) { IType S0 = NullableType.GetUnderlyingType(fromType); IType T0 = NullableType.GetUnderlyingType(toType); IType mostSpecificSource; - if (operators.Any(op => op.SourceType.Equals(S0))) + if (operators.Any(op => op.SourceType.Equals(S0))) { mostSpecificSource = S0; - else if (operators.Any(op => IsEncompassedBy(S0, op.SourceType))) - mostSpecificSource = FindMostEncompassedType(operators.Where(op => IsEncompassedBy(S0, op.SourceType)).Select(op => op.SourceType)); - else - mostSpecificSource = FindMostEncompassingType(operators.Select(op => op.SourceType)); + } else { + var operatorsWithSourceEncompassingFromType = operators.Where(op => IsEncompassedBy(S0, op.SourceType) || ImplicitConstantExpressionConversion(fromResult, op.SourceType)); + if (operatorsWithSourceEncompassingFromType.Any()) + mostSpecificSource = FindMostEncompassedType(operatorsWithSourceEncompassingFromType.Select(op => op.SourceType)); + else + mostSpecificSource = FindMostEncompassingType(operators.Select(op => op.SourceType)); + } if (mostSpecificSource == null) return Conversion.None; @@ -860,7 +873,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - List GetApplicableConversionOperators(IType fromType, IType toType, bool isExplicit) + List GetApplicableConversionOperators(ResolveResult fromResult, IType fromType, IType toType, bool isExplicit) { // Find the candidate operators: Predicate opFilter; @@ -879,10 +892,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver // Try if the operator is applicable: bool isApplicable; if (isExplicit) { - isApplicable = IsEncompassingOrEncompassedBy(fromType, sourceType) + isApplicable = (IsEncompassingOrEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType)) && IsEncompassingOrEncompassedBy(targetType, toType); } else { - isApplicable = IsEncompassedBy(fromType, sourceType) && IsEncompassedBy(targetType, toType); + isApplicable = (IsEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType)) + && IsEncompassedBy(targetType, toType); } // Try if the operator is applicable in lifted form: bool isApplicableInLiftedForm = false; diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs index 9f330beff3..3179de03fe 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs @@ -832,7 +832,7 @@ class Test { Assert.AreEqual("System.Object", c.Method.Parameters.Single().Type.FullName); } - [Test, Ignore("expression-based user-defined conversions not implemented")] + [Test] public void UserDefined_IntLiteral_ViaUInt_ToCustomStruct() { string program = @"using System; @@ -843,10 +843,6 @@ class Test { static void M() { T t = $1$; } - - - - }"; var c = GetConversion(program); Assert.IsTrue(c.IsValid); @@ -861,7 +857,6 @@ struct T { public static implicit operator T(string a) { return new T(); } } - class Test { static void M() { T t = $null$; @@ -1033,7 +1028,7 @@ class Test { Assert.AreEqual("ni", c.Method.Parameters[0].Name); } - [Test, Ignore("TODO: The 'most encompassing' algorithm should pick the long overload, but csc picks the uint one.")] + [Test] public void UserDefinedImplicitConversion_UIntConstant() { string program = @"using System; class Convertible { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs index 644c897322..6c52f662b0 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs @@ -711,7 +711,7 @@ class Test { Assert.AreEqual("ni", rr.Conversion.Method.Parameters[0].Name); } - [Test, Ignore("TODO: The 'most encompassing' algorithm should pick the long overload, but csc picks the uint one.")] + [Test] public void UserDefinedExplicitConversion_UIntConstant() { string program = @"using System; class Convertible {