Browse Source

Improve overload resolution in HandleCallInstruction

pull/847/head
Siegfried Pammer 8 years ago
parent
commit
2177f39341
  1. 53
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

53
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1079,11 +1079,8 @@ namespace ICSharpCode.Decompiler.CSharp
Debug.Assert(inst.Arguments.Count == firstParamIndex + inst.Method.Parameters.Count); Debug.Assert(inst.Arguments.Count == firstParamIndex + inst.Method.Parameters.Count);
for (int i = 0; i < arguments.Length; i++) { for (int i = 0; i < arguments.Length; i++) {
var parameter = method.Parameters[i]; var parameter = method.Parameters[i];
arguments[i] = Translate(inst.Arguments[firstParamIndex + i]); arguments[i] = Translate(inst.Arguments[firstParamIndex + i])
// HACK : do not add casts to anonymous types, in order to avoid compile errors. .ConvertTo(parameter.Type, this, allowImplicitConversion: true);
// Ideally we should be able to remove casts to any type, if not explicitly required.
if (!parameter.Type.ContainsAnonymousType())
arguments[i] = arguments[i].ConvertTo(parameter.Type, this);
if (parameter.IsOut && arguments[i].Expression is DirectionExpression dirExpr) { if (parameter.IsOut && arguments[i].Expression is DirectionExpression dirExpr) {
dirExpr.FieldDirection = FieldDirection.Out; dirExpr.FieldDirection = FieldDirection.Out;
@ -1094,7 +1091,8 @@ namespace ICSharpCode.Decompiler.CSharp
int regularParameterCount = ((VarArgInstanceMethod)method).RegularParameterCount; int regularParameterCount = ((VarArgInstanceMethod)method).RegularParameterCount;
var argListArg = new UndocumentedExpression(); var argListArg = new UndocumentedExpression();
argListArg.UndocumentedExpressionType = UndocumentedExpressionType.ArgList; argListArg.UndocumentedExpressionType = UndocumentedExpressionType.ArgList;
argListArg.Arguments.AddRange(arguments.Skip(regularParameterCount).Select(arg => arg.Expression)); int paramIndex = regularParameterCount;
argListArg.Arguments.AddRange(arguments.Skip(regularParameterCount).Select(arg => arg.ConvertTo(method.Parameters[paramIndex++].Type, this).Expression));
var argListRR = new ResolveResult(SpecialType.ArgList); var argListRR = new ResolveResult(SpecialType.ArgList);
arguments = arguments.Take(regularParameterCount) arguments = arguments.Take(regularParameterCount)
.Concat(new[] { argListArg.WithoutILInstruction().WithRR(argListRR) }).ToArray(); .Concat(new[] { argListArg.WithoutILInstruction().WithRR(argListRR) }).ToArray();
@ -1139,17 +1137,54 @@ namespace ICSharpCode.Decompiler.CSharp
expr = HandleAccessorCall(inst, target, method, arguments.ToList()); expr = HandleAccessorCall(inst, target, method, arguments.ToList());
} else { } else {
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly); var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentAssembly);
var result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
// 1.
// Try overload resolution and check if the correct call is selected with the given casts.
// If anything goes wrong, apply explicit casts to all arguments.
if (result == null) {
for (int i = 0; i < arguments.Length; i++) {
arguments[i] = arguments[i].ConvertTo(method.Parameters[i].Type, this);
}
} else {
var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray()); var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray());
var result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, true) as MethodGroupResolveResult; or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, inst.OpCode == OpCode.CallVirt)) {
for (int i = 0; i < arguments.Length; i++) {
arguments[i] = arguments[i].ConvertTo(method.Parameters[i].Type, this);
}
}
}
result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
// 2.
// Try overload resolution again and if anything goes wrong,
// fix the call by explicitly casting to method.DeclaringType.
if (result == null) { if (result == null) {
target = target.ConvertTo(method.DeclaringType, this); target = target.ConvertTo(method.DeclaringType, this);
} else { } else {
var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray());
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray()); or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, inst.OpCode == OpCode.CallVirt)) if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, inst.OpCode == OpCode.CallVirt))
target = target.ConvertTo(method.DeclaringType, this); target = target.ConvertTo(method.DeclaringType, this);
} }
bool requireTypeArguments = false;
result = lookup.Lookup(target.ResolveResult, method.Name, EmptyList<IType>.Instance, true) as MethodGroupResolveResult;
// 3.
// Try overload resolution again and if anything goes wrong,
// fix the call by explicitly stating the type arguments.
if (result == null) {
requireTypeArguments = true;
} else {
var or = new OverloadResolution(resolver.Compilation, arguments.Select(a => a.ResolveResult).ToArray());
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
if (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(method, or.BestCandidate, inst.OpCode == OpCode.CallVirt))
requireTypeArguments = true;
}
Expression targetExpr = target.Expression; Expression targetExpr = target.Expression;
string methodName = method.Name; string methodName = method.Name;
// HACK : convert this.Dispose() to ((IDisposable)this).Dispose(), if Dispose is an explicitly implemented interface method. // HACK : convert this.Dispose() to ((IDisposable)this).Dispose(), if Dispose is an explicitly implemented interface method.
@ -1158,8 +1193,8 @@ namespace ICSharpCode.Decompiler.CSharp
methodName = method.ImplementedInterfaceMembers[0].Name; methodName = method.ImplementedInterfaceMembers[0].Name;
} }
var mre = new MemberReferenceExpression(targetExpr, methodName); var mre = new MemberReferenceExpression(targetExpr, methodName);
if (!method.TypeArguments.Any(t => t.ContainsAnonymousType())) if (requireTypeArguments)
mre.TypeArguments.AddRange(method.TypeArguments.Select(a => ConvertType(a))); mre.TypeArguments.AddRange(method.TypeArguments.Select(ConvertType));
var argumentExpressions = arguments.Select(arg => arg.Expression); var argumentExpressions = arguments.Select(arg => arg.Expression);
expr = new InvocationExpression(mre, argumentExpressions); expr = new InvocationExpression(mre, argumentExpressions);
} }

Loading…
Cancel
Save