Browse Source

Add support for user-defined operators.

newNRvisualizers
Daniel Grunwald 15 years ago
parent
commit
43b5897b21
  1. 35
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs
  2. 77
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConditionalOperatorTests.cs
  3. 3
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs
  4. 147
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  5. 4
      ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs
  6. 34
      ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs
  7. 33
      ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs
  8. 5
      ICSharpCode.NRefactory/TypeSystem/NullableType.cs

35
ICSharpCode.NRefactory.Tests/CSharp/Resolver/BinaryOperatorTests.cs

@ -270,7 +270,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -270,7 +270,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public void Equality()
{
TestOperator(MakeResult(typeof(int*)), BinaryOperatorType.Equality, MakeResult(typeof(uint*)),
Conversion.ImplicitPointerConversion, Conversion.ImplicitPointerConversion, typeof(bool));
Conversion.IdentityConversion, Conversion.IdentityConversion, typeof(bool));
TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Equality, MakeResult(typeof(int?)),
Conversion.ImplicitNullableConversion, Conversion.IdentityConversion, typeof(bool));
TestOperator(MakeResult(typeof(int)), BinaryOperatorType.Equality, MakeResult(typeof(float)),
Conversion.ExplicitNumericConversion, Conversion.IdentityConversion, typeof(bool));
}
[Test]
@ -322,8 +328,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -322,8 +328,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
AssertConstant(false, resolver.ResolveBinaryOperator(
BinaryOperatorType.Equality, MakeConstant(0), MakeConstant(StringComparison.Ordinal)));
AssertConstant(false, resolver.ResolveBinaryOperator(
BinaryOperatorType.Equality, MakeConstant(0), MakeConstant(StringComparison.Ordinal)));
AssertConstant(true, resolver.ResolveBinaryOperator(
BinaryOperatorType.Equality, MakeConstant(0), MakeConstant(StringComparison.CurrentCulture)));
Assert.IsFalse(resolver.ResolveBinaryOperator(
BinaryOperatorType.Equality, MakeConstant(StringComparison.Ordinal), MakeConstant(1)).IsCompileTimeConstant);
@ -454,7 +460,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -454,7 +460,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
BinaryOperatorType.NullCoalescing, MakeResult(typeof(string)), MakeResult(typeof(dynamic))));
}
[Test, Ignore("user-defined operators not yet implemented")]
[Test]
public void LiftedUserDefined()
{
AssertType(typeof(TimeSpan), resolver.ResolveBinaryOperator(
@ -467,7 +473,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -467,7 +473,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
BinaryOperatorType.Subtract, MakeResult(typeof(DateTime?)), MakeResult(typeof(DateTime?))));
}
[Test, Ignore("user-defined operators not yet implemented")]
[Test]
public void UserDefinedNeedsLiftingDueToImplicitConversion()
{
string program = @"struct S {}
@ -482,15 +488,22 @@ class Test { @@ -482,15 +488,22 @@ class Test {
}
}
";
MemberResolveResult trr = Resolve<MemberResolveResult>(program);
Assert.IsFalse(trr.IsError);
Assert.AreEqual("A.op_Addition", trr.Member.FullName);
InvocationResolveResult irr = Resolve<InvocationResolveResult>(program);
Assert.IsFalse(irr.IsError);
Assert.IsTrue(irr.IsLiftedOperatorInvocation);
Assert.AreEqual("A.op_Addition", irr.Member.FullName);
// even though we're calling the lifted operator, trr.Member should be the original operator method
Assert.AreEqual("S", trr.Member.ReturnType.Resolve(context).ReflectionName);
Assert.AreEqual("System.Nullable`1[[S]]", trr.Type.ReflectionName);
Assert.AreEqual("S", irr.Member.ReturnType.Resolve(context).ReflectionName);
Assert.AreEqual("System.Nullable`1[[S]]", irr.Type.ReflectionName);
Conversion lhsConv = ((ConversionResolveResult)irr.Arguments[0]).Conversion;
Conversion rhsConv = ((ConversionResolveResult)irr.Arguments[1]).Conversion;
Assert.AreEqual(Conversion.ImplicitNullableConversion, lhsConv);
Assert.IsTrue(rhsConv.IsUserDefined);
Assert.AreEqual("A.op_Implicit", rhsConv.Method.FullName);
}
[Test, Ignore("user-defined operators not yet implemented")]
[Test]
public void ThereIsNoLiftedOperatorsForClasses()
{
string program = @"struct S {}

77
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConditionalOperatorTests.cs

@ -13,55 +13,94 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -13,55 +13,94 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
[TestFixture]
public class ConditionalOperatorTests : ResolverTestBase
{
void TestOperator(ResolveResult condition, ResolveResult trueExpr, ResolveResult falseExpr,
Conversion conditionConv, Conversion trueConv, Conversion falseConv,
Type expectedResultType)
{
var corr = (ConditionalOperatorResolveResult)resolver.ResolveConditional(condition, trueExpr, falseExpr);
AssertType(expectedResultType, corr);
AssertConversion(corr.Condition, condition, conditionConv, "Condition Conversion");
AssertConversion(corr.True, trueExpr, trueConv, "True Conversion");
AssertConversion(corr.False, falseExpr, falseConv, "False Conversion");
}
[Test]
public void PickMoreGeneralOfTheTypes()
{
AssertType(typeof(object), resolver.ResolveConditional(
MakeResult(typeof(bool)), MakeResult(typeof(string)), MakeResult(typeof(object))));
AssertType(typeof(long), resolver.ResolveConditional(
MakeResult(typeof(bool)), MakeResult(typeof(int)), MakeResult(typeof(long))));
TestOperator(MakeResult(typeof(bool)), MakeResult(typeof(string)), MakeResult(typeof(object)),
Conversion.IdentityConversion, Conversion.ImplicitReferenceConversion, Conversion.IdentityConversion,
typeof(object));
TestOperator(MakeResult(typeof(bool)), MakeResult(typeof(int)), MakeResult(typeof(long)),
Conversion.IdentityConversion, Conversion.ImplicitNumericConversion, Conversion.IdentityConversion,
typeof(long));
}
[Test]
public void Null()
public void StringAndNull()
{
AssertType(typeof(string), resolver.ResolveConditional(
MakeResult(typeof(bool)), MakeResult(typeof(string)), MakeConstant(null)));
AssertType(typeof(string), resolver.ResolveConditional(
MakeResult(typeof(bool)), MakeConstant(null), MakeResult(typeof(string))));
ResolveResult condition = MakeResult(typeof(bool));
ResolveResult trueExpr = MakeResult(typeof(string));
var result = (ConditionalOperatorResolveResult)resolver.ResolveConditional(
condition, trueExpr, MakeConstant(null));
AssertType(typeof(string), result);
AssertConversion(result.Condition, condition, Conversion.IdentityConversion, "Condition Conversion");
AssertConversion(result.True, trueExpr, Conversion.IdentityConversion, "True Conversion");
Assert.IsTrue(result.False.IsCompileTimeConstant);
Assert.IsNull(result.False.ConstantValue);
Assert.AreEqual("System.String", result.False.Type.FullName);
}
[Test]
public void NullAndString()
{
ResolveResult condition = MakeResult(typeof(bool));
ResolveResult falseExpr = MakeResult(typeof(string));
var result = (ConditionalOperatorResolveResult)resolver.ResolveConditional(
condition, MakeConstant(null), falseExpr);
AssertType(typeof(string), result);
AssertConversion(result.Condition, condition, Conversion.IdentityConversion, "Condition Conversion");
Assert.IsTrue(result.True.IsCompileTimeConstant);
Assert.IsNull(result.True.ConstantValue);
Assert.AreEqual("System.String", result.True.Type.FullName);
AssertConversion(result.False, falseExpr, Conversion.IdentityConversion, "False Conversion");
}
[Test]
public void DynamicInArguments()
{
AssertType(typeof(dynamic), resolver.ResolveConditional(
MakeResult(typeof(bool)), MakeResult(typeof(dynamic)), MakeResult(typeof(double))));
TestOperator(MakeResult(typeof(bool)), MakeResult(typeof(dynamic)), MakeResult(typeof(double)),
Conversion.IdentityConversion, Conversion.IdentityConversion, Conversion.BoxingConversion,
typeof(dynamic));
AssertType(typeof(dynamic), resolver.ResolveConditional(
MakeResult(typeof(bool)), MakeResult(typeof(double)), MakeResult(typeof(dynamic))));
TestOperator(MakeResult(typeof(bool)), MakeResult(typeof(double)), MakeResult(typeof(dynamic)),
Conversion.IdentityConversion, Conversion.BoxingConversion, Conversion.IdentityConversion,
typeof(dynamic));
}
[Test]
public void DynamicInCondition()
{
AssertType(typeof(double), resolver.ResolveConditional(
MakeResult(typeof(dynamic)), MakeResult(typeof(float)), MakeResult(typeof(double))));
TestOperator(MakeResult(typeof(dynamic)), MakeResult(typeof(float)), MakeResult(typeof(double)),
Conversion.ImplicitDynamicConversion, Conversion.ImplicitNumericConversion, Conversion.IdentityConversion,
typeof(double));
}
[Test]
public void AllDynamic()
{
AssertType(typeof(dynamic), resolver.ResolveConditional(
MakeResult(typeof(dynamic)), MakeResult(typeof(dynamic)), MakeResult(typeof(dynamic))));
TestOperator(MakeResult(typeof(dynamic)), MakeResult(typeof(dynamic)), MakeResult(typeof(dynamic)),
Conversion.ImplicitDynamicConversion, Conversion.IdentityConversion, Conversion.IdentityConversion,
typeof(dynamic));
}
[Test]
public void ListOfDynamicAndListOfObject()
{
AssertError(typeof(List<dynamic>), resolver.ResolveConditional(
AssertError(typeof(List<object>), resolver.ResolveConditional(
MakeResult(typeof(bool)), MakeResult(typeof(List<object>)), MakeResult(typeof(List<dynamic>))));
AssertError(typeof(List<object>), resolver.ResolveConditional(
AssertError(typeof(List<dynamic>), resolver.ResolveConditional(
MakeResult(typeof(bool)), MakeResult(typeof(List<dynamic>)), MakeResult(typeof(List<object>))));
}

3
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs

@ -147,7 +147,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -147,7 +147,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (expectedConversion == Conversion.IdentityConversion) {
Assert.AreSame(expectedRR, conversionResult, "Expected no " + text);
} else {
ConversionResolveResult crr = (ConversionResolveResult)conversionResult;
ConversionResolveResult crr = conversionResult as ConversionResolveResult;
Assert.IsNotNull(crr, "Could not find ConversionResolveResult for " + text);
Assert.AreEqual(expectedConversion, crr.Conversion, text);
Assert.AreSame(expectedRR, crr.Input, "Input of " + text);
}

147
ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs

@ -475,8 +475,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -475,8 +475,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
bool isNullable = NullableType.IsNullable(expression.Type);
// the operator is overloadable:
// TODO: implicit support for user operators
//var candidateSet = GetUnaryOperatorCandidates();
OverloadResolution r = new OverloadResolution(context, new[] { expression });
foreach (var candidate in GetUserDefinedOperatorCandidates(type, overloadableOperatorName)) {
r.AddCandidate(candidate);
}
if (r.FoundApplicableCandidate) {
return CreateResolveResultForUserDefinedOperator(r);
}
expression = UnaryNumericPromotion(op, ref type, isNullable, expression);
OperatorMethod[] methodGroup;
@ -518,7 +523,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -518,7 +523,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
default:
throw new InvalidOperationException();
}
OverloadResolution r = new OverloadResolution(context, new[] { expression });
foreach (var candidate in methodGroup) {
r.AddCandidate(candidate);
}
@ -686,13 +690,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -686,13 +690,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
new LambdaUnaryOperatorMethod<ulong>(i => ~i)
);
#endregion
object GetUserUnaryOperatorCandidates()
{
// C# 4.0 spec: §7.3.5 Candidate user-defined operators
// TODO: implement user-defined operators
throw new NotImplementedException();
}
#endregion
#region ResolveBinaryOperator
@ -732,7 +729,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -732,7 +729,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
IType lhsType = NullableType.GetUnderlyingType(lhs.Type);
IType rhsType = NullableType.GetUnderlyingType(rhs.Type);
// TODO: find user-defined operators
// the operator is overloadable:
OverloadResolution r = new OverloadResolution(context, new[] { lhs, rhs });
HashSet<IParameterizedMember> userOperatorCandidates = new HashSet<IParameterizedMember>();
userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(lhsType, overloadableOperatorName));
userOperatorCandidates.UnionWith(GetUserDefinedOperatorCandidates(rhsType, overloadableOperatorName));
foreach (var candidate in userOperatorCandidates) {
r.AddCandidate(candidate);
}
if (r.FoundApplicableCandidate) {
return CreateResolveResultForUserDefinedOperator(r);
}
if (SharedTypes.Null.Equals(lhsType) && rhsType.IsReferenceType(context) == false
|| lhsType.IsReferenceType(context) == false && SharedTypes.Null.Equals(rhsType))
@ -877,7 +884,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -877,7 +884,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
// bool operator op(E x, E y);
return HandleEnumComparison(op, rhsType, isNullable, lhs, rhs);
} else if (lhsType is PointerType && rhsType is PointerType) {
return new ResolveResult(KnownTypeReference.Boolean.Resolve(context));
return new BinaryOperatorResolveResult(KnownTypeReference.Boolean.Resolve(context), lhs, op, rhs);
}
switch (op) {
case BinaryOperatorType.Equality:
@ -907,7 +914,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -907,7 +914,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
case BinaryOperatorType.BitwiseOr:
case BinaryOperatorType.ExclusiveOr:
{
Conversion c;
if (lhsType.Kind == TypeKind.Enum && TryConvert(ref rhs, lhs.Type)) {
// bool operator op(E x, E y);
return HandleEnumOperator(isNullable, lhsType, op, lhs, rhs);
@ -940,7 +946,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -940,7 +946,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
default:
throw new InvalidOperationException();
}
OverloadResolution r = new OverloadResolution(context, new[] { lhs, rhs });
foreach (var candidate in methodGroup) {
r.AddCandidate(candidate);
}
@ -1605,12 +1610,87 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1605,12 +1610,87 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
}
#endregion
#endregion
object GetUserBinaryOperatorCandidates()
#region Get user-defined operator candidates
IEnumerable<IParameterizedMember> GetUserDefinedOperatorCandidates(IType type, string operatorName)
{
if (operatorName == null)
return EmptyList<IMethod>.Instance;
// C# 4.0 spec: §7.3.5 Candidate user-defined operators
// TODO: implement user-defined operators
throw new NotImplementedException();
var operators = type.GetMethods(context, m => m.IsOperator && m.Name == operatorName).ToList<IParameterizedMember>();
LiftUserDefinedOperators(operators);
return operators;
}
void LiftUserDefinedOperators(List<IParameterizedMember> operators)
{
int nonLiftedMethodCount = operators.Count;
// Construct lifted operators
for (int i = 0; i < nonLiftedMethodCount; i++) {
var liftedMethod = LiftUserDefinedOperator(operators[i]);
if (liftedMethod != null)
operators.Add(liftedMethod);
}
}
LiftedUserDefinedOperator LiftUserDefinedOperator(IParameterizedMember m)
{
IType returnType = m.ReturnType.Resolve(context);
if (!NullableType.IsNonNullableValueType(returnType, context))
return null; // cannot lift this operator
LiftedUserDefinedOperator liftedOperator = new LiftedUserDefinedOperator(m);
for (int i = 0; i < m.Parameters.Count; i++) {
IType parameterType = m.Parameters[i].Type.Resolve(context);
if (!NullableType.IsNonNullableValueType(parameterType, context))
return null; // cannot lift this operator
var p = new DefaultParameter(m.Parameters[i]);
p.Type = NullableType.Create(parameterType, context);
liftedOperator.Parameters.Add(p);
}
liftedOperator.ReturnType = NullableType.Create(returnType, context);
return liftedOperator;
}
sealed class LiftedUserDefinedOperator : OperatorMethod, OverloadResolution.ILiftedOperator
{
internal readonly IParameterizedMember nonLiftedOperator;
public LiftedUserDefinedOperator(IParameterizedMember nonLiftedMethod)
{
this.nonLiftedOperator = nonLiftedMethod;
}
public IList<IParameter> NonLiftedParameters {
get { return nonLiftedOperator.Parameters; }
}
public override bool Equals(object obj)
{
LiftedUserDefinedOperator op = obj as LiftedUserDefinedOperator;
return op != null && this.nonLiftedOperator.Equals(op.nonLiftedOperator);
}
public override int GetHashCode()
{
return nonLiftedOperator.GetHashCode() ^ 0x7191254;
}
}
ResolveResult CreateResolveResultForUserDefinedOperator(OverloadResolution r)
{
LiftedUserDefinedOperator lifted = r.BestCandidate as LiftedUserDefinedOperator;
if (lifted != null) {
return new InvocationResolveResult(
null, lifted.nonLiftedOperator, lifted.ReturnType.Resolve(context),
r.GetArgumentsWithConversions(), r.BestCandidateErrors,
typeArguments: r.InferredTypeArguments,
isLiftedOperatorInvocation: true,
argumentToParameterMap: r.GetArgumentToParameterMap()
);
} else {
return new InvocationResolveResult(null, r, context);
}
}
#endregion
@ -2177,7 +2257,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -2177,7 +2257,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
or.AddCandidate(ctor);
}
if (or.BestCandidate != null) {
return new InvocationResolveResult(new TypeResolveResult(type), or, context);
return new InvocationResolveResult(null, or, context);
} else {
return new ErrorResolveResult(type);
}
@ -2261,33 +2341,44 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -2261,33 +2341,44 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
IType resultType;
if (SharedTypes.Dynamic.Equals(trueExpression.Type) || SharedTypes.Dynamic.Equals(falseExpression.Type)) {
resultType = SharedTypes.Dynamic;
isValid = true;
isValid = TryConvert(ref trueExpression, resultType) & TryConvert(ref falseExpression, resultType);
} else if (HasType(trueExpression) && HasType(falseExpression)) {
bool t2f = conversions.ImplicitConversion(trueExpression.Type, falseExpression.Type);
bool f2t = conversions.ImplicitConversion(falseExpression.Type, trueExpression.Type);
resultType = (f2t && !t2f) ? trueExpression.Type : falseExpression.Type;
Conversion t2f = conversions.ImplicitConversion(trueExpression.Type, falseExpression.Type);
Conversion f2t = conversions.ImplicitConversion(falseExpression.Type, trueExpression.Type);
// The operator is valid:
// a) if there's a conversion in one direction but not the other
// b) if there are conversions in both directions, and the types are equivalent
isValid = (t2f != f2t) || (t2f && f2t && trueExpression.Type.Equals(falseExpression.Type));
if (t2f && !f2t) {
resultType = falseExpression.Type;
isValid = true;
trueExpression = Convert(trueExpression, resultType, t2f);
} else if (f2t && !t2f) {
resultType = trueExpression.Type;
isValid = true;
falseExpression = Convert(falseExpression, resultType, f2t);
} else {
resultType = trueExpression.Type;
isValid = trueExpression.Type.Equals(falseExpression.Type);
}
} else if (HasType(trueExpression)) {
resultType = trueExpression.Type;
isValid = conversions.ImplicitConversion(falseExpression, resultType);
isValid = TryConvert(ref falseExpression, resultType);
} else if (HasType(falseExpression)) {
resultType = falseExpression.Type;
isValid = conversions.ImplicitConversion(trueExpression, resultType);
isValid = TryConvert(ref trueExpression, resultType);
} else {
return ErrorResult;
}
isValid &= TryConvert(ref condition, KnownTypeReference.Boolean.Resolve(context));
if (isValid) {
if (condition.IsCompileTimeConstant && trueExpression.IsCompileTimeConstant && falseExpression.IsCompileTimeConstant) {
bool? val = condition.ConstantValue as bool?;
if (val == true)
return ResolveCast(resultType, trueExpression);
return trueExpression;
else if (val == false)
return ResolveCast(resultType, falseExpression);
return falseExpression;
}
return new ResolveResult(resultType);
return new ConditionalOperatorResolveResult(resultType, condition, trueExpression, falseExpression);
} else {
return new ErrorResolveResult(resultType);
}

4
ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs

@ -773,7 +773,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -773,7 +773,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
result.Add(new OperatorInfo(op, sourceType, targetType, false));
}
// Try if the operator is applicable in lifted form:
if (sourceType.IsReferenceType(context) == false && targetType.IsReferenceType(context) == false) {
if (NullableType.IsNonNullableValueType(sourceType, context)
&& NullableType.IsNonNullableValueType(targetType, context))
{
IType liftedSourceType = NullableType.Create(sourceType, context);
IType liftedTargetType = NullableType.Create(targetType, context);
if (isExplicit) {

34
ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs

@ -34,7 +34,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -34,7 +34,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
public readonly IList<IType> TypeArguments;
public readonly IList<ResolveResult> Arguments;
public readonly IList<Conversion> ArgumentConversions;
/// <summary>
/// Gets whether this invocation is calling an extension method using extension method syntax.
@ -46,21 +45,46 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -46,21 +45,46 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// </summary>
public readonly bool IsExpandedForm;
/// <summary>
/// Gets whether this is a lifted operator invocation.
/// </summary>
public readonly bool IsLiftedOperatorInvocation;
readonly IList<int> argumentToParameterMap;
public InvocationResolveResult(ResolveResult targetResult, OverloadResolution or, ITypeResolveContext context)
: base(
or.IsExtensionMethodInvocation ? new TypeResolveResult(or.BestCandidate.DeclaringType) : targetResult,
or.IsExtensionMethodInvocation ? null : targetResult,
or.BestCandidate,
GetReturnType(or, context))
{
this.OverloadResolutionErrors = or.BestCandidateErrors;
this.TypeArguments = or.InferredTypeArguments;
this.Arguments = or.Arguments;
this.ArgumentConversions = or.ArgumentConversions;
this.argumentToParameterMap = or.GetArgumentToParameterMap();
this.Arguments = or.GetArgumentsWithConversions();
this.IsExtensionMethodInvocation = or.IsExtensionMethodInvocation;
this.IsExpandedForm = or.BestCandidateIsExpandedForm;
this.argumentToParameterMap = or.GetArgumentToParameterMap();
this.IsLiftedOperatorInvocation = or.BestCandidate is OverloadResolution.ILiftedOperator;
}
public InvocationResolveResult(
ResolveResult targetResult, IParameterizedMember member, IType returnType,
IList<ResolveResult> arguments,
OverloadResolutionErrors overloadResolutionErrors = OverloadResolutionErrors.None,
IList<IType> typeArguments = null,
bool isExtensionMethodInvocation = false, bool isExpandedForm = false,
bool isLiftedOperatorInvocation = false,
IList<int> argumentToParameterMap = null)
: base(targetResult, member, returnType)
{
this.OverloadResolutionErrors = overloadResolutionErrors;
this.TypeArguments = typeArguments ?? EmptyList<IType>.Instance;
this.Arguments = arguments ?? EmptyList<ResolveResult>.Instance;
this.IsExtensionMethodInvocation = isExtensionMethodInvocation;
this.IsExpandedForm = isExpandedForm;
this.IsLiftedOperatorInvocation = isLiftedOperatorInvocation;
this.argumentToParameterMap = argumentToParameterMap;
}
static IType GetReturnType(OverloadResolution or, ITypeResolveContext context)

33
ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs

@ -304,14 +304,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -304,14 +304,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
for (int i = 0; i < typeParameters.Count; i++) {
ITypeParameter tp = typeParameters[i];
IType typeArg = newParameterizedType.TypeArguments[i];
switch (typeArg.Kind) { // void, null, and pointers cannot be used as type arguments
case TypeKind.Void:
case TypeKind.Null:
case TypeKind.Pointer:
ConstraintsValid = false;
break;
}
if (tp.HasReferenceTypeConstraint) {
if (typeArg.IsReferenceType(overloadResolution.context) != true)
ConstraintsValid = false;
}
if (tp.HasValueTypeConstraint) {
if (typeArg.IsReferenceType(overloadResolution.context) != false)
ConstraintsValid = false;
if (NullableType.IsNullable(typeArg))
if (!NullableType.IsNonNullableValueType(typeArg, overloadResolution.context))
ConstraintsValid = false;
}
if (tp.HasDefaultConstructorConstraint) {
@ -628,5 +633,27 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -628,5 +633,27 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
else
return null;
}
public IList<ResolveResult> GetArgumentsWithConversions()
{
if (bestCandidate == null)
return arguments;
var conversions = this.ArgumentConversions;
ResolveResult[] args = new ResolveResult[arguments.Length];
for (int i = 0; i < args.Length; i++) {
if (conversions[i] == Conversion.IdentityConversion || conversions[i] == Conversion.None) {
args[i] = arguments[i];
} else {
int parameterIndex = bestCandidate.ArgumentToParameterMap[i];
IType parameterType;
if (parameterIndex >= 0)
parameterType = bestCandidate.ParameterTypes[parameterIndex];
else
parameterType = SharedTypes.UnknownType;
args[i] = new ConversionResolveResult(parameterType, args[i], conversions[i]);
}
}
return args;
}
}
}

5
ICSharpCode.NRefactory/TypeSystem/NullableType.cs

@ -37,6 +37,11 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -37,6 +37,11 @@ namespace ICSharpCode.NRefactory.TypeSystem
return pt != null && pt.TypeArguments.Count == 1 && pt.FullName == "System.Nullable";
}
public static bool IsNonNullableValueType(IType type, ITypeResolveContext context)
{
return type.IsReferenceType(context) == false && !IsNullable(type);
}
/// <summary>
/// Returns the element type, if <paramref name="type"/> is a nullable type.
/// Otherwise, returns the type itself.

Loading…
Cancel
Save