Browse Source

Add support for user-defined conversions starting with a constant expression conversion.

newNRvisualizers
Daniel Grunwald 13 years ago
parent
commit
902f00ee7a
  1. 56
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs
  2. 9
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs
  3. 2
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs

56
ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs

@ -109,9 +109,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (c.IsValid) return c; if (c.IsValid) return c;
if (ImplicitConstantExpressionConversion(resolveResult, toType)) if (ImplicitConstantExpressionConversion(resolveResult, toType))
return Conversion.ImplicitConstantExpressionConversion; 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) if (resolveResult.Type.Kind == TypeKind.Dynamic)
return Conversion.ImplicitDynamicConversion; return Conversion.ImplicitDynamicConversion;
c = AnonymousFunctionConversion(resolveResult, toType); c = AnonymousFunctionConversion(resolveResult, toType);
@ -135,7 +140,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// C# 4.0 spec: §6.1 // C# 4.0 spec: §6.1
c = StandardImplicitConversion(fromType, toType); c = StandardImplicitConversion(fromType, toType);
if (!c.IsValid) { if (!c.IsValid) {
c = UserDefinedImplicitConversion(fromType, toType); c = UserDefinedImplicitConversion(null, fromType, toType);
} }
implicitConversionCache[pair] = c; implicitConversionCache[pair] = c;
return c; return c;
@ -214,8 +219,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Conversion c = ImplicitConversion(resolveResult, toType); Conversion c = ImplicitConversion(resolveResult, toType);
if (c.IsValid) if (c.IsValid)
return c; return c;
else c = ExplicitConversionImpl(resolveResult.Type, toType);
return ExplicitConversionImpl(resolveResult.Type, toType); if (c.IsValid)
return c;
return UserDefinedExplicitConversion(resolveResult, resolveResult.Type, toType);
} }
public Conversion ExplicitConversion(IType fromType, IType toType) public Conversion ExplicitConversion(IType fromType, IType toType)
@ -228,8 +235,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
Conversion c = ImplicitConversion(fromType, toType); Conversion c = ImplicitConversion(fromType, toType);
if (c.IsValid) if (c.IsValid)
return c; return c;
else c = ExplicitConversionImpl(fromType, toType);
return ExplicitConversionImpl(fromType, toType); if (c.IsValid)
return c;
return UserDefinedExplicitConversion(null, fromType, toType);
} }
Conversion ExplicitConversionImpl(IType fromType, IType toType) Conversion ExplicitConversionImpl(IType fromType, IType toType)
@ -252,7 +261,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return c; return c;
if (ExplicitPointerConversion(fromType, toType)) if (ExplicitPointerConversion(fromType, toType))
return Conversion.ExplicitPointerConversion; return Conversion.ExplicitPointerConversion;
return UserDefinedExplicitConversion(fromType, toType); return Conversion.None;
} }
#endregion #endregion
@ -643,8 +652,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#region Implicit Constant-Expression Conversion #region Implicit Constant-Expression Conversion
bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType) bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType)
{ {
if (rr == null || !rr.IsCompileTimeConstant)
return false;
// C# 4.0 spec: §6.1.9 // C# 4.0 spec: §6.1.9
Debug.Assert(rr.IsCompileTimeConstant);
TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type); TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type);
TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType)); TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType));
if (fromTypeCode == TypeCode.Int64) { 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. // "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 // 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) { if (operators.Count > 0) {
IType S0 = NullableType.GetUnderlyingType(fromType); 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 // 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) { if (operators.Count > 0) {
IType S0 = NullableType.GetUnderlyingType(fromType); IType S0 = NullableType.GetUnderlyingType(fromType);
IType T0 = NullableType.GetUnderlyingType(toType); IType T0 = NullableType.GetUnderlyingType(toType);
IType mostSpecificSource; IType mostSpecificSource;
if (operators.Any(op => op.SourceType.Equals(S0))) if (operators.Any(op => op.SourceType.Equals(S0))) {
mostSpecificSource = S0; mostSpecificSource = S0;
else if (operators.Any(op => IsEncompassedBy(S0, op.SourceType))) } else {
mostSpecificSource = FindMostEncompassedType(operators.Where(op => IsEncompassedBy(S0, op.SourceType)).Select(op => op.SourceType)); var operatorsWithSourceEncompassingFromType = operators.Where(op => IsEncompassedBy(S0, op.SourceType) || ImplicitConstantExpressionConversion(fromResult, op.SourceType));
else if (operatorsWithSourceEncompassingFromType.Any())
mostSpecificSource = FindMostEncompassingType(operators.Select(op => op.SourceType)); mostSpecificSource = FindMostEncompassedType(operatorsWithSourceEncompassingFromType.Select(op => op.SourceType));
else
mostSpecificSource = FindMostEncompassingType(operators.Select(op => op.SourceType));
}
if (mostSpecificSource == null) if (mostSpecificSource == null)
return Conversion.None; return Conversion.None;
@ -860,7 +873,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
} }
List<OperatorInfo> GetApplicableConversionOperators(IType fromType, IType toType, bool isExplicit) List<OperatorInfo> GetApplicableConversionOperators(ResolveResult fromResult, IType fromType, IType toType, bool isExplicit)
{ {
// Find the candidate operators: // Find the candidate operators:
Predicate<IUnresolvedMethod> opFilter; Predicate<IUnresolvedMethod> opFilter;
@ -879,10 +892,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// Try if the operator is applicable: // Try if the operator is applicable:
bool isApplicable; bool isApplicable;
if (isExplicit) { if (isExplicit) {
isApplicable = IsEncompassingOrEncompassedBy(fromType, sourceType) isApplicable = (IsEncompassingOrEncompassedBy(fromType, sourceType) || ImplicitConstantExpressionConversion(fromResult, sourceType))
&& IsEncompassingOrEncompassedBy(targetType, toType); && IsEncompassingOrEncompassedBy(targetType, toType);
} else { } 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: // Try if the operator is applicable in lifted form:
bool isApplicableInLiftedForm = false; bool isApplicableInLiftedForm = false;

9
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs

@ -832,7 +832,7 @@ class Test {
Assert.AreEqual("System.Object", c.Method.Parameters.Single().Type.FullName); 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() public void UserDefined_IntLiteral_ViaUInt_ToCustomStruct()
{ {
string program = @"using System; string program = @"using System;
@ -843,10 +843,6 @@ class Test {
static void M() { static void M() {
T t = $1$; T t = $1$;
} }
}"; }";
var c = GetConversion(program); var c = GetConversion(program);
Assert.IsTrue(c.IsValid); Assert.IsTrue(c.IsValid);
@ -861,7 +857,6 @@ struct T {
public static implicit operator T(string a) { return new T(); } public static implicit operator T(string a) { return new T(); }
} }
class Test { class Test {
static void M() { static void M() {
T t = $null$; T t = $null$;
@ -1033,7 +1028,7 @@ class Test {
Assert.AreEqual("ni", c.Method.Parameters[0].Name); 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() { public void UserDefinedImplicitConversion_UIntConstant() {
string program = @"using System; string program = @"using System;
class Convertible { class Convertible {

2
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExplicitConversionsTest.cs

@ -711,7 +711,7 @@ class Test {
Assert.AreEqual("ni", rr.Conversion.Method.Parameters[0].Name); 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() { public void UserDefinedExplicitConversion_UIntConstant() {
string program = @"using System; string program = @"using System;
class Convertible { class Convertible {

Loading…
Cancel
Save