1
ļ»æ// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
3
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4
// software and associated documentation files (the "Software"), to deal in the Software
5
// without restriction, including without limitation the rights to use, copy, modify, merge,
6
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7
// to whom the Software is furnished to do so, subject to the following conditions:
9
// The above copyright notice and this permission notice shall be included in all copies or
10
// substantial portions of the Software.
12
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17
// DEALINGS IN THE SOFTWARE.
20
using System.Collections.Generic;
21
using System.Diagnostics;
25
using ICSharpCode.NRefactory.Semantics;
26
using ICSharpCode.NRefactory.TypeSystem;
27
using ICSharpCode.NRefactory.TypeSystem.Implementation;
29
namespace ICSharpCode.NRefactory.CSharp.Resolver
32
/// C# overload resolution (C# 4.0 spec: Ā§7.5).
34
public class OverloadResolution
36
sealed class Candidate
38
public readonly IParameterizedMember Member;
41
/// Returns the normal form candidate, if this is an expanded candidate.
43
public readonly bool IsExpandedForm;
46
/// Gets the parameter types. In the first step, these are the types without any substition.
47
/// After type inference, substitutions will be performed.
49
public readonly IType[] ParameterTypes;
52
/// argument index -> parameter index; -1 for arguments that could not be mapped
54
public int[] ArgumentToParameterMap;
56
public OverloadResolutionErrors Errors;
57
public int ErrorCount;
59
public bool HasUnmappedOptionalParameters;
61
public IType[] InferredTypes;
64
/// Gets the original member parameters (before any substitution!)
66
public readonly IList<IParameter> Parameters;
69
/// Gets the original method type parameters (before any substitution!)
71
public readonly IList<ITypeParameter> TypeParameters;
74
/// Conversions applied to the arguments.
75
/// This field is set by the CheckApplicability step.
77
public Conversion[] ArgumentConversions;
79
public bool IsGenericMethod {
81
IMethod method = Member as IMethod;
82
return method != null && method.TypeParameters.Count > 0;
86
public int ArgumentsPassedToParamsArray {
90
int paramsParameterIndex = this.Parameters.Count - 1;
91
foreach (int parameterIndex in ArgumentToParameterMap) {
92
if (parameterIndex == paramsParameterIndex)
100
public Candidate(IParameterizedMember member, bool isExpanded)
102
this.Member = member;
103
this.IsExpandedForm = isExpanded;
104
IParameterizedMember memberDefinition = (IParameterizedMember)member.MemberDefinition;
105
// For specificialized methods, go back to the original parameters:
106
// (without any type parameter substitution, not even class type parameters)
107
// We'll re-substitute them as part of RunTypeInference().
108
this.Parameters = memberDefinition.Parameters;
109
IMethod methodDefinition = memberDefinition as IMethod;
110
if (methodDefinition != null && methodDefinition.TypeParameters.Count > 0) {
111
this.TypeParameters = methodDefinition.TypeParameters;
113
this.ParameterTypes = new IType[this.Parameters.Count];
116
public void AddError(OverloadResolutionErrors newError)
118
this.Errors |= newError;
119
if (!IsApplicable(newError))
124
readonly ICompilation compilation;
125
readonly ResolveResult[] arguments;
126
readonly string[] argumentNames;
127
readonly CSharpConversions conversions;
128
//List<Candidate> candidates = new List<Candidate>();
129
Candidate bestCandidate;
130
Candidate bestCandidateAmbiguousWith;
131
IType[] explicitlyGivenTypeArguments;
132
bool bestCandidateWasValidated;
133
OverloadResolutionErrors bestCandidateValidationResult;
136
public OverloadResolution(ICompilation compilation, ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null, CSharpConversions conversions = null)
138
if (compilation == null)
139
throw new ArgumentNullException("compilation");
140
if (arguments == null)
141
throw new ArgumentNullException("arguments");
142
if (argumentNames == null)
143
argumentNames = new string[arguments.Length];
144
else if (argumentNames.Length != arguments.Length)
145
throw new ArgumentException("argumentsNames.Length must be equal to arguments.Length");
146
this.compilation = compilation;
147
this.arguments = arguments;
148
this.argumentNames = argumentNames;
150
// keep explicitlyGivenTypeArguments==null when no type arguments were specified
151
if (typeArguments != null && typeArguments.Length > 0)
152
this.explicitlyGivenTypeArguments = typeArguments;
154
this.conversions = conversions ?? CSharpConversions.Get(compilation);
155
this.AllowExpandingParams = true;
156
this.AllowOptionalParameters = true;
160
#region Input Properties
162
/// Gets/Sets whether the methods are extension methods that are being called using extension method syntax.
165
/// Setting this property to true restricts the possible conversions on the first argument to
166
/// implicit identity, reference, or boxing conversions.
168
public bool IsExtensionMethodInvocation { get; set; }
171
/// Gets/Sets whether expanding 'params' into individual elements is allowed.
172
/// The default value is true.
174
public bool AllowExpandingParams { get; set; }
177
/// Gets/Sets whether optional parameters may be left at their default value.
178
/// The default value is true.
179
/// If this property is set to false, optional parameters will be treated like regular parameters.
181
public bool AllowOptionalParameters { get; set; }
184
/// Gets/Sets whether ConversionResolveResults created by this OverloadResolution
185
/// instance apply overflow checking.
186
/// The default value is false.
188
public bool CheckForOverflow { get; set; }
191
/// Gets the arguments for which this OverloadResolution instance was created.
193
public IList<ResolveResult> Arguments {
194
get { return arguments; }
200
/// Adds a candidate to overload resolution.
202
/// <param name="member">The candidate member to add.</param>
203
/// <returns>The errors that prevent the member from being applicable, if any.
204
/// Note: this method does not return errors that do not affect applicability.</returns>
205
public OverloadResolutionErrors AddCandidate(IParameterizedMember member)
207
return AddCandidate(member, OverloadResolutionErrors.None);
211
/// Adds a candidate to overload resolution.
213
/// <param name="member">The candidate member to add.</param>
214
/// <param name="additionalErrors">Additional errors that apply to the candidate.
215
/// This is used to represent errors during member lookup (e.g. OverloadResolutionErrors.Inaccessible)
216
/// in overload resolution.</param>
217
/// <returns>The errors that prevent the member from being applicable, if any.
218
/// Note: this method does not return errors that do not affect applicability.</returns>
219
public OverloadResolutionErrors AddCandidate(IParameterizedMember member, OverloadResolutionErrors additionalErrors)
222
throw new ArgumentNullException("member");
224
Candidate c = new Candidate(member, false);
225
c.AddError(additionalErrors);
226
if (CalculateCandidate(c)) {
230
if (this.AllowExpandingParams && member.Parameters.Count > 0
231
&& member.Parameters[member.Parameters.Count - 1].IsParams)
233
Candidate expandedCandidate = new Candidate(member, true);
234
expandedCandidate.AddError(additionalErrors);
235
// consider expanded form only if it isn't obviously wrong
236
if (CalculateCandidate(expandedCandidate)) {
237
//candidates.Add(expandedCandidate);
239
if (expandedCandidate.ErrorCount < c.ErrorCount)
240
return expandedCandidate.Errors;
247
/// Calculates applicability etc. for the candidate.
249
/// <returns>True if the calculation was successful, false if the candidate should be removed without reporting an error</returns>
250
bool CalculateCandidate(Candidate candidate)
252
if (!ResolveParameterTypes(candidate, false))
254
MapCorrespondingParameters(candidate);
255
RunTypeInference(candidate);
256
CheckApplicability(candidate);
257
ConsiderIfNewCandidateIsBest(candidate);
261
bool ResolveParameterTypes(Candidate candidate, bool useSpecializedParameters)
263
for (int i = 0; i < candidate.Parameters.Count; i++) {
265
if (useSpecializedParameters) {
266
// Use the parameter type of the specialized non-generic method or indexer
267
Debug.Assert(!candidate.IsGenericMethod);
268
type = candidate.Member.Parameters[i].Type;
270
// Use the type of the original formal parameter
271
type = candidate.Parameters[i].Type;
273
if (candidate.IsExpandedForm && i == candidate.Parameters.Count - 1) {
274
ArrayType arrayType = type as ArrayType;
275
if (arrayType != null && arrayType.Dimensions == 1)
276
type = arrayType.ElementType;
278
return false; // error: cannot unpack params-array. abort considering the expanded form for this candidate
280
candidate.ParameterTypes[i] = type;
286
#region AddMethodLists
288
/// Adds all candidates from the method lists.
290
/// This method implements the logic that causes applicable methods in derived types to hide
291
/// all methods in base types.
293
/// <param name="methodLists">The methods, grouped by declaring type. Base types must come first in the list.</param>
294
public void AddMethodLists(IList<MethodListWithDeclaringType> methodLists)
296
if (methodLists == null)
297
throw new ArgumentNullException("methodLists");
298
// Base types come first, so go through the list backwards (derived types first)
299
bool[] isHiddenByDerivedType;
300
if (methodLists.Count > 1)
301
isHiddenByDerivedType = new bool[methodLists.Count];
303
isHiddenByDerivedType = null;
304
for (int i = methodLists.Count - 1; i >= 0; i--) {
305
if (isHiddenByDerivedType != null && isHiddenByDerivedType[i]) {
306
Log.WriteLine(" Skipping methods in {0} because they are hidden by an applicable method in a derived type", methodLists[i].DeclaringType);
310
MethodListWithDeclaringType methodList = methodLists[i];
311
bool foundApplicableCandidateInCurrentList = false;
313
for (int j = 0; j < methodList.Count; j++) {
314
IParameterizedMember method = methodList[j];
316
OverloadResolutionErrors errors = AddCandidate(method);
318
LogCandidateAddingResult(" Candidate", method, errors);
320
foundApplicableCandidateInCurrentList |= IsApplicable(errors);
323
if (foundApplicableCandidateInCurrentList && i > 0) {
324
foreach (IType baseType in methodList.DeclaringType.GetAllBaseTypes()) {
325
for (int j = 0; j < i; j++) {
326
if (!isHiddenByDerivedType[j] && baseType.Equals(methodLists[j].DeclaringType))
327
isHiddenByDerivedType[j] = true;
334
[Conditional("DEBUG")]
335
internal void LogCandidateAddingResult(string text, IParameterizedMember method, OverloadResolutionErrors errors)
338
Log.WriteLine(string.Format("{0} {1} = {2}{3}",
340
errors == OverloadResolutionErrors.None ? "Success" : errors.ToString(),
341
this.BestCandidate == method ? " (best candidate so far)" :
342
this.BestCandidateAmbiguousWith == method ? " (ambiguous)" : ""
348
#region MapCorrespondingParameters
349
void MapCorrespondingParameters(Candidate candidate)
351
// C# 4.0 spec: Ā§7.5.1.1 Corresponding parameters
352
candidate.ArgumentToParameterMap = new int[arguments.Length];
353
for (int i = 0; i < arguments.Length; i++) {
354
candidate.ArgumentToParameterMap[i] = -1;
355
if (argumentNames[i] == null) {
356
// positional argument
357
if (i < candidate.ParameterTypes.Length) {
358
candidate.ArgumentToParameterMap[i] = i;
359
} else if (candidate.IsExpandedForm) {
360
candidate.ArgumentToParameterMap[i] = candidate.ParameterTypes.Length - 1;
362
candidate.AddError(OverloadResolutionErrors.TooManyPositionalArguments);
366
for (int j = 0; j < candidate.Parameters.Count; j++) {
367
if (argumentNames[i] == candidate.Parameters[j].Name) {
368
candidate.ArgumentToParameterMap[i] = j;
371
if (candidate.ArgumentToParameterMap[i] < 0)
372
candidate.AddError(OverloadResolutionErrors.NoParameterFoundForNamedArgument);
378
#region RunTypeInference
379
void RunTypeInference(Candidate candidate)
381
if (candidate.TypeParameters == null) {
382
if (explicitlyGivenTypeArguments != null) {
383
// method does not expect type arguments, but was given some
384
candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments);
386
// Grab new parameter types:
387
ResolveParameterTypes(candidate, true);
390
ParameterizedType parameterizedDeclaringType = candidate.Member.DeclaringType as ParameterizedType;
391
IList<IType> classTypeArguments;
392
if (parameterizedDeclaringType != null) {
393
classTypeArguments = parameterizedDeclaringType.TypeArguments;
395
classTypeArguments = null;
397
// The method is generic:
398
if (explicitlyGivenTypeArguments != null) {
399
if (explicitlyGivenTypeArguments.Length == candidate.TypeParameters.Count) {
400
candidate.InferredTypes = explicitlyGivenTypeArguments;
402
candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments);
403
// wrong number of type arguments given, so truncate the list or pad with UnknownType
404
candidate.InferredTypes = new IType[candidate.TypeParameters.Count];
405
for (int i = 0; i < candidate.InferredTypes.Length; i++) {
406
if (i < explicitlyGivenTypeArguments.Length)
407
candidate.InferredTypes[i] = explicitlyGivenTypeArguments[i];
409
candidate.InferredTypes[i] = SpecialType.UnknownType;
413
TypeInference ti = new TypeInference(compilation, conversions);
415
candidate.InferredTypes = ti.InferTypeArguments(candidate.TypeParameters, arguments, candidate.ParameterTypes, out success, classTypeArguments);
417
candidate.AddError(OverloadResolutionErrors.TypeInferenceFailed);
419
// Now substitute in the formal parameters:
420
var substitution = new ConstraintValidatingSubstitution(classTypeArguments, candidate.InferredTypes, this);
421
for (int i = 0; i < candidate.ParameterTypes.Length; i++) {
422
candidate.ParameterTypes[i] = candidate.ParameterTypes[i].AcceptVisitor(substitution);
424
if (!substitution.ConstraintsValid)
425
candidate.AddError(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint);
428
sealed class ConstraintValidatingSubstitution : TypeParameterSubstitution
430
readonly CSharpConversions conversions;
431
public bool ConstraintsValid = true;
433
public ConstraintValidatingSubstitution(IList<IType> classTypeArguments, IList<IType> methodTypeArguments, OverloadResolution overloadResolution)
434
: base(classTypeArguments, methodTypeArguments)
436
this.conversions = overloadResolution.conversions;
439
public override IType VisitParameterizedType(ParameterizedType type)
441
IType newType = base.VisitParameterizedType(type);
442
if (newType != type && ConstraintsValid) {
443
// something was changed, so we need to validate the constraints
444
ParameterizedType newParameterizedType = newType as ParameterizedType;
445
if (newParameterizedType != null) {
446
// C# 4.0 spec: Ā§4.4.4 Satisfying constraints
447
var typeParameters = newParameterizedType.GetDefinition().TypeParameters;
448
var substitution = newParameterizedType.GetSubstitution();
449
for (int i = 0; i < typeParameters.Count; i++) {
450
if (!ValidateConstraints(typeParameters[i], newParameterizedType.GetTypeArgument(i), substitution, conversions)) {
451
ConstraintsValid = false;
462
#region Validate Constraints
463
OverloadResolutionErrors ValidateMethodConstraints(Candidate candidate)
465
// If type inference already failed, we won't check the constraints:
466
if ((candidate.Errors & OverloadResolutionErrors.TypeInferenceFailed) != 0)
467
return OverloadResolutionErrors.None;
469
IMethod method = candidate.Member as IMethod;
470
if (method == null || method.TypeParameters.Count == 0)
471
return OverloadResolutionErrors.None; // the method isn't generic
472
var substitution = GetSubstitution(candidate);
473
for (int i = 0; i < method.TypeParameters.Count; i++) {
474
if (!ValidateConstraints(method.TypeParameters[i], substitution.MethodTypeArguments[i], substitution))
475
return OverloadResolutionErrors.MethodConstraintsNotSatisfied;
477
return OverloadResolutionErrors.None;
481
/// Validates whether the given type argument satisfies the constraints for the given type parameter.
483
/// <param name="typeParameter">The type parameter.</param>
484
/// <param name="typeArgument">The type argument.</param>
485
/// <param name="substitution">The substitution that defines how type parameters are replaced with type arguments.
486
/// The substitution is used to check constraints that depend on other type parameters (or recursively on the same type parameter).
487
/// May be null if no substitution should be used.</param>
488
/// <returns>True if the constraints are satisfied; false otherwise.</returns>
489
public static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution = null)
491
if (typeParameter == null)
492
throw new ArgumentNullException("typeParameter");
493
if (typeArgument == null)
494
throw new ArgumentNullException("typeArgument");
495
return ValidateConstraints(typeParameter, typeArgument, substitution, CSharpConversions.Get(typeParameter.Owner.Compilation));
498
internal static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution, CSharpConversions conversions)
500
switch (typeArgument.Kind) { // void, null, and pointers cannot be used as type arguments
503
case TypeKind.Pointer:
506
if (typeParameter.HasReferenceTypeConstraint) {
507
if (typeArgument.IsReferenceType != true)
510
if (typeParameter.HasValueTypeConstraint) {
511
if (!NullableType.IsNonNullableValueType(typeArgument))
514
if (typeParameter.HasDefaultConstructorConstraint) {
515
ITypeDefinition def = typeArgument.GetDefinition();
516
if (def != null && def.IsAbstract)
518
var ctors = typeArgument.GetConstructors(
519
m => m.Parameters.Count == 0 && m.Accessibility == Accessibility.Public,
520
GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions
525
foreach (IType constraintType in typeParameter.DirectBaseTypes) {
526
IType c = constraintType;
527
if (substitution != null)
528
c = c.AcceptVisitor(substitution);
529
if (!conversions.IsConstraintConvertible(typeArgument, c))
536
#region CheckApplicability
538
/// Returns whether a candidate with the given errors is still considered to be applicable.
540
public static bool IsApplicable(OverloadResolutionErrors errors)
542
const OverloadResolutionErrors errorsThatDoNotMatterForApplicability =
543
OverloadResolutionErrors.AmbiguousMatch | OverloadResolutionErrors.MethodConstraintsNotSatisfied;
544
return (errors & ~errorsThatDoNotMatterForApplicability) == OverloadResolutionErrors.None;
547
void CheckApplicability(Candidate candidate)
549
// C# 4.0 spec: Ā§7.5.3.1 Applicable function member
551
// Test whether parameters were mapped the correct number of arguments:
552
int[] argumentCountPerParameter = new int[candidate.ParameterTypes.Length];
553
foreach (int parameterIndex in candidate.ArgumentToParameterMap) {
554
if (parameterIndex >= 0)
555
argumentCountPerParameter[parameterIndex]++;
557
for (int i = 0; i < argumentCountPerParameter.Length; i++) {
558
if (candidate.IsExpandedForm && i == argumentCountPerParameter.Length - 1)
559
continue; // any number of arguments is fine for the params-array
560
if (argumentCountPerParameter[i] == 0) {
561
if (this.AllowOptionalParameters && candidate.Parameters[i].IsOptional)
562
candidate.HasUnmappedOptionalParameters = true;
564
candidate.AddError(OverloadResolutionErrors.MissingArgumentForRequiredParameter);
565
} else if (argumentCountPerParameter[i] > 1) {
566
candidate.AddError(OverloadResolutionErrors.MultipleArgumentsForSingleParameter);
570
candidate.ArgumentConversions = new Conversion[arguments.Length];
571
// Test whether argument passing mode matches the parameter passing mode
572
for (int i = 0; i < arguments.Length; i++) {
573
int parameterIndex = candidate.ArgumentToParameterMap[i];
574
if (parameterIndex < 0) {
575
candidate.ArgumentConversions[i] = Conversion.None;
579
ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult;
581
if ((brrr.IsOut && !candidate.Parameters[parameterIndex].IsOut) || (brrr.IsRef && !candidate.Parameters[parameterIndex].IsRef))
582
candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch);
584
if (candidate.Parameters[parameterIndex].IsOut || candidate.Parameters[parameterIndex].IsRef)
585
candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch);
587
IType parameterType = candidate.ParameterTypes[parameterIndex];
588
Conversion c = conversions.ImplicitConversion(arguments[i], parameterType);
589
candidate.ArgumentConversions[i] = c;
590
if (IsExtensionMethodInvocation && parameterIndex == 0) {
591
// First parameter to extension method must be an identity, reference or boxing conversion
592
if (!(c == Conversion.IdentityConversion || c == Conversion.ImplicitReferenceConversion || c == Conversion.BoxingConversion))
593
candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch);
595
if ((!c.IsValid && !c.IsUserDefined && !c.IsMethodGroupConversion) && parameterType.Kind != TypeKind.Unknown)
596
candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch);
602
#region BetterFunctionMember
604
/// Returns 1 if c1 is better than c2; 2 if c2 is better than c1; or 0 if neither is better.
606
int BetterFunctionMember(Candidate c1, Candidate c2)
608
// prefer applicable members (part of heuristic that produces a best candidate even if none is applicable)
609
if (c1.ErrorCount == 0 && c2.ErrorCount > 0)
611
if (c1.ErrorCount > 0 && c2.ErrorCount == 0)
614
// C# 4.0 spec: Ā§7.5.3.2 Better function member
615
bool c1IsBetter = false;
616
bool c2IsBetter = false;
617
for (int i = 0; i < arguments.Length; i++) {
618
int p1 = c1.ArgumentToParameterMap[i];
619
int p2 = c2.ArgumentToParameterMap[i];
620
if (p1 >= 0 && p2 < 0) {
622
} else if (p1 < 0 && p2 >= 0) {
624
} else if (p1 >= 0 && p2 >= 0) {
625
switch (conversions.BetterConversion(arguments[i], c1.ParameterTypes[p1], c2.ParameterTypes[p2])) {
635
if (c1IsBetter && !c2IsBetter)
637
if (!c1IsBetter && c2IsBetter)
640
// prefer members with less errors (part of heuristic that produces a best candidate even if none is applicable)
641
if (c1.ErrorCount < c2.ErrorCount) return 1;
642
if (c1.ErrorCount > c2.ErrorCount) return 2;
644
if (!c1IsBetter && !c2IsBetter) {
645
// we need the tie-breaking rules
647
// non-generic methods are better
648
if (!c1.IsGenericMethod && c2.IsGenericMethod)
650
else if (c1.IsGenericMethod && !c2.IsGenericMethod)
653
// non-expanded members are better
654
if (!c1.IsExpandedForm && c2.IsExpandedForm)
656
else if (c1.IsExpandedForm && !c2.IsExpandedForm)
659
// prefer the member with less arguments mapped to the params-array
660
int r = c1.ArgumentsPassedToParamsArray.CompareTo(c2.ArgumentsPassedToParamsArray);
662
else if (r > 0) return 2;
664
// prefer the member where no default values need to be substituted
665
if (!c1.HasUnmappedOptionalParameters && c2.HasUnmappedOptionalParameters)
667
else if (c1.HasUnmappedOptionalParameters && !c2.HasUnmappedOptionalParameters)
670
// compare the formal parameters
671
r = MoreSpecificFormalParameters(c1, c2);
675
// prefer non-lifted operators
676
ILiftedOperator lift1 = c1.Member as ILiftedOperator;
677
ILiftedOperator lift2 = c2.Member as ILiftedOperator;
678
if (lift1 == null && lift2 != null)
680
if (lift1 != null && lift2 == null)
687
/// Implement this interface to give overload resolution a hint that the member represents a lifted operator,
688
/// which is used in the tie-breaking rules.
690
public interface ILiftedOperator : IParameterizedMember
692
IList<IParameter> NonLiftedParameters { get; }
695
int MoreSpecificFormalParameters(Candidate c1, Candidate c2)
697
// prefer the member with more formal parmeters (in case both have different number of optional parameters)
698
int r = c1.Parameters.Count.CompareTo(c2.Parameters.Count);
700
else if (r < 0) return 2;
702
return MoreSpecificFormalParameters(c1.Parameters.Select(p => p.Type), c2.Parameters.Select(p => p.Type));
705
static int MoreSpecificFormalParameters(IEnumerable<IType> t1, IEnumerable<IType> t2)
707
bool c1IsBetter = false;
708
bool c2IsBetter = false;
709
foreach (var pair in t1.Zip(t2, (a,b) => new { Item1 = a, Item2 = b })) {
710
switch (MoreSpecificFormalParameter(pair.Item1, pair.Item2)) {
719
if (c1IsBetter && !c2IsBetter)
721
if (!c1IsBetter && c2IsBetter)
726
static int MoreSpecificFormalParameter(IType t1, IType t2)
728
if ((t1 is ITypeParameter) && !(t2 is ITypeParameter))
730
if ((t2 is ITypeParameter) && !(t1 is ITypeParameter))
733
ParameterizedType p1 = t1 as ParameterizedType;
734
ParameterizedType p2 = t2 as ParameterizedType;
735
if (p1 != null && p2 != null && p1.TypeParameterCount == p2.TypeParameterCount) {
736
int r = MoreSpecificFormalParameters(p1.TypeArguments, p2.TypeArguments);
740
TypeWithElementType tew1 = t1 as TypeWithElementType;
741
TypeWithElementType tew2 = t2 as TypeWithElementType;
742
if (tew1 != null && tew2 != null) {
743
return MoreSpecificFormalParameter(tew1.ElementType, tew2.ElementType);
749
#region ConsiderIfNewCandidateIsBest
750
void ConsiderIfNewCandidateIsBest(Candidate candidate)
752
if (bestCandidate == null) {
753
bestCandidate = candidate;
754
bestCandidateWasValidated = false;
756
switch (BetterFunctionMember(candidate, bestCandidate)) {
758
// Overwrite 'bestCandidateAmbiguousWith' so that API users can
759
// detect the set of all ambiguous methods if they look at
760
// bestCandidateAmbiguousWith after each step.
761
bestCandidateAmbiguousWith = candidate;
764
bestCandidate = candidate;
765
bestCandidateWasValidated = false;
766
bestCandidateAmbiguousWith = null;
768
// case 2: best candidate stays best
774
#region Output Properties
775
public IParameterizedMember BestCandidate {
776
get { return bestCandidate != null ? bestCandidate.Member : null; }
780
/// Returns the errors that apply to the best candidate.
781
/// This includes additional errors that do not affect applicability (e.g. AmbiguousMatch, MethodConstraintsNotSatisfied)
783
public OverloadResolutionErrors BestCandidateErrors {
785
if (bestCandidate == null)
786
return OverloadResolutionErrors.None;
787
if (!bestCandidateWasValidated) {
788
bestCandidateValidationResult = ValidateMethodConstraints(bestCandidate);
789
bestCandidateWasValidated = true;
791
OverloadResolutionErrors err = bestCandidate.Errors | bestCandidateValidationResult;
792
if (bestCandidateAmbiguousWith != null)
793
err |= OverloadResolutionErrors.AmbiguousMatch;
798
public bool FoundApplicableCandidate {
799
get { return bestCandidate != null && IsApplicable(bestCandidate.Errors); }
802
public IParameterizedMember BestCandidateAmbiguousWith {
803
get { return bestCandidateAmbiguousWith != null ? bestCandidateAmbiguousWith.Member : null; }
806
public bool BestCandidateIsExpandedForm {
807
get { return bestCandidate != null ? bestCandidate.IsExpandedForm : false; }
810
public bool IsAmbiguous {
811
get { return bestCandidateAmbiguousWith != null; }
814
public IList<IType> InferredTypeArguments {
816
if (bestCandidate != null && bestCandidate.InferredTypes != null)
817
return bestCandidate.InferredTypes;
819
return EmptyList<IType>.Instance;
824
/// Gets the implicit conversions that are being applied to the arguments.
826
public IList<Conversion> ArgumentConversions {
828
if (bestCandidate != null && bestCandidate.ArgumentConversions != null)
829
return bestCandidate.ArgumentConversions;
831
return Enumerable.Repeat(Conversion.None, arguments.Length).ToList();
836
/// Gets an array that maps argument indices to parameter indices.
837
/// For arguments that could not be mapped to any parameter, the value will be -1.
839
/// parameterIndex = GetArgumentToParameterMap()[argumentIndex]
841
public IList<int> GetArgumentToParameterMap()
843
if (bestCandidate != null)
844
return bestCandidate.ArgumentToParameterMap;
850
/// Returns the arguments for the method call in the order they were provided (not in the order of the parameters).
851
/// Arguments are wrapped in a <see cref="ConversionResolveResult"/> if an implicit conversion is being applied
852
/// to them when calling the method.
854
public IList<ResolveResult> GetArgumentsWithConversions()
856
if (bestCandidate == null)
859
return GetArgumentsWithConversions(null, null);
863
/// Returns the arguments for the method call in the order they were provided (not in the order of the parameters).
864
/// Arguments are wrapped in a <see cref="ConversionResolveResult"/> if an implicit conversion is being applied
865
/// to them when calling the method.
866
/// For arguments where an explicit argument name was provided, the argument will
867
/// be wrapped in a <see cref="NamedArgumentResolveResult"/>.
869
public IList<ResolveResult> GetArgumentsWithConversionsAndNames()
871
if (bestCandidate == null)
874
return GetArgumentsWithConversions(null, GetBestCandidateWithSubstitutedTypeArguments());
877
IList<ResolveResult> GetArgumentsWithConversions(ResolveResult targetResolveResult, IParameterizedMember bestCandidateForNamedArguments)
879
var conversions = this.ArgumentConversions;
880
ResolveResult[] args = new ResolveResult[arguments.Length];
881
for (int i = 0; i < args.Length; i++) {
882
var argument = arguments[i];
883
if (this.IsExtensionMethodInvocation && i == 0 && targetResolveResult != null)
884
argument = targetResolveResult;
885
int parameterIndex = bestCandidate.ArgumentToParameterMap[i];
886
if (parameterIndex >= 0 && conversions[i] != Conversion.IdentityConversion) {
887
// Wrap argument in ConversionResolveResult
888
IType parameterType = bestCandidate.ParameterTypes[parameterIndex];
889
if (parameterType.Kind != TypeKind.Unknown) {
890
if (arguments[i].IsCompileTimeConstant && conversions[i] != Conversion.None) {
891
argument = new CSharpResolver(compilation).WithCheckForOverflow(CheckForOverflow).ResolveCast(parameterType, argument);
893
argument = new ConversionResolveResult(parameterType, argument, conversions[i], CheckForOverflow);
897
if (bestCandidateForNamedArguments != null && argumentNames[i] != null) {
898
// Wrap argument in NamedArgumentResolveResult
899
if (parameterIndex >= 0) {
900
argument = new NamedArgumentResolveResult(bestCandidateForNamedArguments.Parameters[parameterIndex], argument, bestCandidateForNamedArguments);
902
argument = new NamedArgumentResolveResult(argumentNames[i], argument);
910
public IParameterizedMember GetBestCandidateWithSubstitutedTypeArguments()
912
if (bestCandidate == null)
914
IMethod method = bestCandidate.Member as IMethod;
915
if (method != null && method.TypeParameters.Count > 0) {
916
return ((IMethod)method.MemberDefinition).Specialize(GetSubstitution(bestCandidate));
918
return bestCandidate.Member;
922
TypeParameterSubstitution GetSubstitution(Candidate candidate)
924
// Do not compose the substitutions, but merge them.
925
// This is required for InvocationTests.SubstituteClassAndMethodTypeParametersAtOnce
926
return new TypeParameterSubstitution(candidate.Member.Substitution.ClassTypeArguments, candidate.InferredTypes);
930
/// Creates a ResolveResult representing the result of overload resolution.
932
/// <param name="targetResolveResult">
933
/// The target expression of the call. May be <c>null</c> for static methods/constructors.
935
/// <param name="initializerStatements">
936
/// Statements for Objects/Collections initializer.
937
/// <see cref="InvocationResolveResult.InitializerStatements"/>
938
/// <param name="returnTypeOverride">
939
/// If not null, use this instead of the ReturnType of the member as the type of the created resolve result.
942
public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetResolveResult, IList<ResolveResult> initializerStatements = null, IType returnTypeOverride = null)
944
IParameterizedMember member = GetBestCandidateWithSubstitutedTypeArguments();
946
throw new InvalidOperationException();
948
return new CSharpInvocationResolveResult(
949
this.IsExtensionMethodInvocation ? new TypeResolveResult(member.DeclaringType) : targetResolveResult,
951
GetArgumentsWithConversions(targetResolveResult, member),
952
this.BestCandidateErrors,
953
this.IsExtensionMethodInvocation,
954
this.BestCandidateIsExpandedForm,
955
isDelegateInvocation: false,
956
argumentToParameterMap: this.GetArgumentToParameterMap(),
957
initializerStatements: initializerStatements,
958
returnTypeOverride: returnTypeOverride);