Free cookie consent management tool by TermsFeed Policy Generator

source: branches/RemoveBackwardsCompatibility/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Resolver/OverloadResolution.cs @ 13401

Last change on this file since 13401 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 36.1 KB
Line 
1// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
2//
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:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
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.
18
19using System;
20using System.Collections.Generic;
21using System.Diagnostics;
22using System.Linq;
23using System.Text;
24
25using ICSharpCode.NRefactory.Semantics;
26using ICSharpCode.NRefactory.TypeSystem;
27using ICSharpCode.NRefactory.TypeSystem.Implementation;
28
29namespace ICSharpCode.NRefactory.CSharp.Resolver
30{
31  /// <summary>
32  /// C# overload resolution (C# 4.0 spec: §7.5).
33  /// </summary>
34  public class OverloadResolution
35  {
36    sealed class Candidate
37    {
38      public readonly IParameterizedMember Member;
39     
40      /// <summary>
41      /// Returns the normal form candidate, if this is an expanded candidate.
42      /// </summary>
43      public readonly bool IsExpandedForm;
44     
45      /// <summary>
46      /// Gets the parameter types. In the first step, these are the types without any substition.
47      /// After type inference, substitutions will be performed.
48      /// </summary>
49      public readonly IType[] ParameterTypes;
50     
51      /// <summary>
52      /// argument index -> parameter index; -1 for arguments that could not be mapped
53      /// </summary>
54      public int[] ArgumentToParameterMap;
55     
56      public OverloadResolutionErrors Errors;
57      public int ErrorCount;
58     
59      public bool HasUnmappedOptionalParameters;
60     
61      public IType[] InferredTypes;
62     
63      /// <summary>
64      /// Gets the original member parameters (before any substitution!)
65      /// </summary>
66      public readonly IList<IParameter> Parameters;
67     
68      /// <summary>
69      /// Gets the original method type parameters (before any substitution!)
70      /// </summary>
71      public readonly IList<ITypeParameter> TypeParameters;
72     
73      /// <summary>
74      /// Conversions applied to the arguments.
75      /// This field is set by the CheckApplicability step.
76      /// </summary>
77      public Conversion[] ArgumentConversions;
78     
79      public bool IsGenericMethod {
80        get {
81          IMethod method = Member as IMethod;
82          return method != null && method.TypeParameters.Count > 0;
83        }
84      }
85     
86      public int ArgumentsPassedToParamsArray {
87        get {
88          int count = 0;
89          if (IsExpandedForm) {
90            int paramsParameterIndex = this.Parameters.Count - 1;
91            foreach (int parameterIndex in ArgumentToParameterMap) {
92              if (parameterIndex == paramsParameterIndex)
93                count++;
94            }
95          }
96          return count;
97        }
98      }
99     
100      public Candidate(IParameterizedMember member, bool isExpanded)
101      {
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;
112        }
113        this.ParameterTypes = new IType[this.Parameters.Count];
114      }
115     
116      public void AddError(OverloadResolutionErrors newError)
117      {
118        this.Errors |= newError;
119        if (!IsApplicable(newError))
120          this.ErrorCount++;
121      }
122    }
123   
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;
134   
135    #region Constructor
136    public OverloadResolution(ICompilation compilation, ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null, CSharpConversions conversions = null)
137    {
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;
149     
150      // keep explicitlyGivenTypeArguments==null when no type arguments were specified
151      if (typeArguments != null && typeArguments.Length > 0)
152        this.explicitlyGivenTypeArguments = typeArguments;
153     
154      this.conversions = conversions ?? CSharpConversions.Get(compilation);
155      this.AllowExpandingParams = true;
156      this.AllowOptionalParameters = true;
157    }
158    #endregion
159   
160    #region Input Properties
161    /// <summary>
162    /// Gets/Sets whether the methods are extension methods that are being called using extension method syntax.
163    /// </summary>
164    /// <remarks>
165    /// Setting this property to true restricts the possible conversions on the first argument to
166    /// implicit identity, reference, or boxing conversions.
167    /// </remarks>
168    public bool IsExtensionMethodInvocation { get; set; }
169   
170    /// <summary>
171    /// Gets/Sets whether expanding 'params' into individual elements is allowed.
172    /// The default value is true.
173    /// </summary>
174    public bool AllowExpandingParams { get; set; }
175   
176    /// <summary>
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.
180    /// </summary>
181    public bool AllowOptionalParameters { get; set; }
182   
183    /// <summary>
184    /// Gets/Sets whether ConversionResolveResults created by this OverloadResolution
185    /// instance apply overflow checking.
186    /// The default value is false.
187    /// </summary>
188    public bool CheckForOverflow { get; set; }
189   
190    /// <summary>
191    /// Gets the arguments for which this OverloadResolution instance was created.
192    /// </summary>
193    public IList<ResolveResult> Arguments {
194      get { return arguments; }
195    }
196    #endregion
197   
198    #region AddCandidate
199    /// <summary>
200    /// Adds a candidate to overload resolution.
201    /// </summary>
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)
206    {
207      return AddCandidate(member, OverloadResolutionErrors.None);
208    }
209   
210    /// <summary>
211    /// Adds a candidate to overload resolution.
212    /// </summary>
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)
220    {
221      if (member == null)
222        throw new ArgumentNullException("member");
223     
224      Candidate c = new Candidate(member, false);
225      c.AddError(additionalErrors);
226      if (CalculateCandidate(c)) {
227        //candidates.Add(c);
228      }
229     
230      if (this.AllowExpandingParams && member.Parameters.Count > 0
231          && member.Parameters[member.Parameters.Count - 1].IsParams)
232      {
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);
238         
239          if (expandedCandidate.ErrorCount < c.ErrorCount)
240            return expandedCandidate.Errors;
241        }
242      }
243      return c.Errors;
244    }
245   
246    /// <summary>
247    /// Calculates applicability etc. for the candidate.
248    /// </summary>
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)
251    {
252      if (!ResolveParameterTypes(candidate, false))
253        return false;
254      MapCorrespondingParameters(candidate);
255      RunTypeInference(candidate);
256      CheckApplicability(candidate);
257      ConsiderIfNewCandidateIsBest(candidate);
258      return true;
259    }
260   
261    bool ResolveParameterTypes(Candidate candidate, bool useSpecializedParameters)
262    {
263      for (int i = 0; i < candidate.Parameters.Count; i++) {
264        IType type;
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;
269        } else {
270          // Use the type of the original formal parameter
271          type = candidate.Parameters[i].Type;
272        }
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;
277          else
278            return false; // error: cannot unpack params-array. abort considering the expanded form for this candidate
279        }
280        candidate.ParameterTypes[i] = type;
281      }
282      return true;
283    }
284    #endregion
285   
286    #region AddMethodLists
287    /// <summary>
288    /// Adds all candidates from the method lists.
289    ///
290    /// This method implements the logic that causes applicable methods in derived types to hide
291    /// all methods in base types.
292    /// </summary>
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)
295    {
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];
302      else
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);
307          continue;
308        }
309       
310        MethodListWithDeclaringType methodList = methodLists[i];
311        bool foundApplicableCandidateInCurrentList = false;
312       
313        for (int j = 0; j < methodList.Count; j++) {
314          IParameterizedMember method = methodList[j];
315          Log.Indent();
316          OverloadResolutionErrors errors = AddCandidate(method);
317          Log.Unindent();
318          LogCandidateAddingResult("  Candidate", method, errors);
319         
320          foundApplicableCandidateInCurrentList |= IsApplicable(errors);
321        }
322       
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;
328            }
329          }
330        }
331      }
332    }
333   
334    [Conditional("DEBUG")]
335    internal void LogCandidateAddingResult(string text, IParameterizedMember method, OverloadResolutionErrors errors)
336    {
337      #if DEBUG
338      Log.WriteLine(string.Format("{0} {1} = {2}{3}",
339                                  text, method,
340                                  errors == OverloadResolutionErrors.None ? "Success" : errors.ToString(),
341                                  this.BestCandidate == method ? " (best candidate so far)" :
342                                  this.BestCandidateAmbiguousWith == method ? " (ambiguous)" : ""
343                                 ));
344      #endif
345    }
346    #endregion
347   
348    #region MapCorrespondingParameters
349    void MapCorrespondingParameters(Candidate candidate)
350    {
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;
361          } else {
362            candidate.AddError(OverloadResolutionErrors.TooManyPositionalArguments);
363          }
364        } else {
365          // named argument
366          for (int j = 0; j < candidate.Parameters.Count; j++) {
367            if (argumentNames[i] == candidate.Parameters[j].Name) {
368              candidate.ArgumentToParameterMap[i] = j;
369            }
370          }
371          if (candidate.ArgumentToParameterMap[i] < 0)
372            candidate.AddError(OverloadResolutionErrors.NoParameterFoundForNamedArgument);
373        }
374      }
375    }
376    #endregion
377   
378    #region RunTypeInference
379    void RunTypeInference(Candidate candidate)
380    {
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);
385        }
386        // Grab new parameter types:
387        ResolveParameterTypes(candidate, true);
388        return;
389      }
390      ParameterizedType parameterizedDeclaringType = candidate.Member.DeclaringType as ParameterizedType;
391      IList<IType> classTypeArguments;
392      if (parameterizedDeclaringType != null) {
393        classTypeArguments = parameterizedDeclaringType.TypeArguments;
394      } else {
395        classTypeArguments = null;
396      }
397      // The method is generic:
398      if (explicitlyGivenTypeArguments != null) {
399        if (explicitlyGivenTypeArguments.Length == candidate.TypeParameters.Count) {
400          candidate.InferredTypes = explicitlyGivenTypeArguments;
401        } else {
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];
408            else
409              candidate.InferredTypes[i] = SpecialType.UnknownType;
410          }
411        }
412      } else {
413        TypeInference ti = new TypeInference(compilation, conversions);
414        bool success;
415        candidate.InferredTypes = ti.InferTypeArguments(candidate.TypeParameters, arguments, candidate.ParameterTypes, out success, classTypeArguments);
416        if (!success)
417          candidate.AddError(OverloadResolutionErrors.TypeInferenceFailed);
418      }
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);
423      }
424      if (!substitution.ConstraintsValid)
425        candidate.AddError(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint);
426    }
427   
428    sealed class ConstraintValidatingSubstitution : TypeParameterSubstitution
429    {
430      readonly CSharpConversions conversions;
431      public bool ConstraintsValid = true;
432     
433      public ConstraintValidatingSubstitution(IList<IType> classTypeArguments, IList<IType> methodTypeArguments, OverloadResolution overloadResolution)
434        : base(classTypeArguments, methodTypeArguments)
435      {
436        this.conversions = overloadResolution.conversions;
437      }
438     
439      public override IType VisitParameterizedType(ParameterizedType type)
440      {
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;
452                break;
453              }
454            }
455          }
456        }
457        return newType;
458      }
459    }
460    #endregion
461   
462    #region Validate Constraints
463    OverloadResolutionErrors ValidateMethodConstraints(Candidate candidate)
464    {
465      // If type inference already failed, we won't check the constraints:
466      if ((candidate.Errors & OverloadResolutionErrors.TypeInferenceFailed) != 0)
467        return OverloadResolutionErrors.None;
468     
469      if (candidate.TypeParameters == null || candidate.TypeParameters.Count == 0)
470        return OverloadResolutionErrors.None; // the method isn't generic
471      var substitution = GetSubstitution(candidate);
472      for (int i = 0; i < candidate.TypeParameters.Count; i++) {
473        if (!ValidateConstraints(candidate.TypeParameters[i], substitution.MethodTypeArguments[i], substitution))
474          return OverloadResolutionErrors.MethodConstraintsNotSatisfied;
475      }
476      return OverloadResolutionErrors.None;
477    }
478   
479    /// <summary>
480    /// Validates whether the given type argument satisfies the constraints for the given type parameter.
481    /// </summary>
482    /// <param name="typeParameter">The type parameter.</param>
483    /// <param name="typeArgument">The type argument.</param>
484    /// <param name="substitution">The substitution that defines how type parameters are replaced with type arguments.
485    /// The substitution is used to check constraints that depend on other type parameters (or recursively on the same type parameter).
486    /// May be null if no substitution should be used.</param>
487    /// <returns>True if the constraints are satisfied; false otherwise.</returns>
488    public static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution = null)
489    {
490      if (typeParameter == null)
491        throw new ArgumentNullException("typeParameter");
492      if (typeArgument == null)
493        throw new ArgumentNullException("typeArgument");
494      return ValidateConstraints(typeParameter, typeArgument, substitution, CSharpConversions.Get(typeParameter.Owner.Compilation));
495    }
496   
497    internal static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution, CSharpConversions conversions)
498    {
499      switch (typeArgument.Kind) { // void, null, and pointers cannot be used as type arguments
500        case TypeKind.Void:
501        case TypeKind.Null:
502        case TypeKind.Pointer:
503          return false;
504      }
505      if (typeParameter.HasReferenceTypeConstraint) {
506        if (typeArgument.IsReferenceType != true)
507          return false;
508      }
509      if (typeParameter.HasValueTypeConstraint) {
510        if (!NullableType.IsNonNullableValueType(typeArgument))
511          return false;
512      }
513      if (typeParameter.HasDefaultConstructorConstraint) {
514        ITypeDefinition def = typeArgument.GetDefinition();
515        if (def != null && def.IsAbstract)
516          return false;
517        var ctors = typeArgument.GetConstructors(
518          m => m.Parameters.Count == 0 && m.Accessibility == Accessibility.Public,
519          GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions
520        );
521        if (!ctors.Any())
522          return false;
523      }
524      foreach (IType constraintType in typeParameter.DirectBaseTypes) {
525        IType c = constraintType;
526        if (substitution != null)
527          c = c.AcceptVisitor(substitution);
528        if (!conversions.IsConstraintConvertible(typeArgument, c))
529          return false;
530      }
531      return true;
532    }
533    #endregion
534   
535    #region CheckApplicability
536    /// <summary>
537    /// Returns whether a candidate with the given errors is still considered to be applicable.
538    /// </summary>
539    public static bool IsApplicable(OverloadResolutionErrors errors)
540    {
541      const OverloadResolutionErrors errorsThatDoNotMatterForApplicability =
542        OverloadResolutionErrors.AmbiguousMatch | OverloadResolutionErrors.MethodConstraintsNotSatisfied;
543      return (errors & ~errorsThatDoNotMatterForApplicability) == OverloadResolutionErrors.None;
544    }
545   
546    void CheckApplicability(Candidate candidate)
547    {
548      // C# 4.0 spec: §7.5.3.1 Applicable function member
549     
550      // Test whether parameters were mapped the correct number of arguments:
551      int[] argumentCountPerParameter = new int[candidate.ParameterTypes.Length];
552      foreach (int parameterIndex in candidate.ArgumentToParameterMap) {
553        if (parameterIndex >= 0)
554          argumentCountPerParameter[parameterIndex]++;
555      }
556      for (int i = 0; i < argumentCountPerParameter.Length; i++) {
557        if (candidate.IsExpandedForm && i == argumentCountPerParameter.Length - 1)
558          continue; // any number of arguments is fine for the params-array
559        if (argumentCountPerParameter[i] == 0) {
560          if (this.AllowOptionalParameters && candidate.Parameters[i].IsOptional)
561            candidate.HasUnmappedOptionalParameters = true;
562          else
563            candidate.AddError(OverloadResolutionErrors.MissingArgumentForRequiredParameter);
564        } else if (argumentCountPerParameter[i] > 1) {
565          candidate.AddError(OverloadResolutionErrors.MultipleArgumentsForSingleParameter);
566        }
567      }
568     
569      candidate.ArgumentConversions = new Conversion[arguments.Length];
570      // Test whether argument passing mode matches the parameter passing mode
571      for (int i = 0; i < arguments.Length; i++) {
572        int parameterIndex = candidate.ArgumentToParameterMap[i];
573        if (parameterIndex < 0) {
574          candidate.ArgumentConversions[i] = Conversion.None;
575          continue;
576        }
577       
578        ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult;
579        if (brrr != null) {
580          if ((brrr.IsOut && !candidate.Parameters[parameterIndex].IsOut) || (brrr.IsRef && !candidate.Parameters[parameterIndex].IsRef))
581            candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch);
582        } else {
583          if (candidate.Parameters[parameterIndex].IsOut || candidate.Parameters[parameterIndex].IsRef)
584            candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch);
585        }
586        IType parameterType = candidate.ParameterTypes[parameterIndex];
587        Conversion c = conversions.ImplicitConversion(arguments[i], parameterType);
588        candidate.ArgumentConversions[i] = c;
589        if (IsExtensionMethodInvocation && parameterIndex == 0) {
590          // First parameter to extension method must be an identity, reference or boxing conversion
591          if (!(c == Conversion.IdentityConversion || c == Conversion.ImplicitReferenceConversion || c == Conversion.BoxingConversion))
592            candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch);
593        } else {
594          if ((!c.IsValid && !c.IsUserDefined && !c.IsMethodGroupConversion) && parameterType.Kind != TypeKind.Unknown)
595            candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch);
596        }
597      }
598    }
599    #endregion
600   
601    #region BetterFunctionMember
602    /// <summary>
603    /// Returns 1 if c1 is better than c2; 2 if c2 is better than c1; or 0 if neither is better.
604    /// </summary>
605    int BetterFunctionMember(Candidate c1, Candidate c2)
606    {
607      // prefer applicable members (part of heuristic that produces a best candidate even if none is applicable)
608      if (c1.ErrorCount == 0 && c2.ErrorCount > 0)
609        return 1;
610      if (c1.ErrorCount > 0 && c2.ErrorCount == 0)
611        return 2;
612     
613      // C# 4.0 spec: §7.5.3.2 Better function member
614      bool c1IsBetter = false;
615      bool c2IsBetter = false;
616      for (int i = 0; i < arguments.Length; i++) {
617        int p1 = c1.ArgumentToParameterMap[i];
618        int p2 = c2.ArgumentToParameterMap[i];
619        if (p1 >= 0 && p2 < 0) {
620          c1IsBetter = true;
621        } else if (p1 < 0 && p2 >= 0) {
622          c2IsBetter = true;
623        } else if (p1 >= 0 && p2 >= 0) {
624          switch (conversions.BetterConversion(arguments[i], c1.ParameterTypes[p1], c2.ParameterTypes[p2])) {
625            case 1:
626              c1IsBetter = true;
627              break;
628            case 2:
629              c2IsBetter = true;
630              break;
631          }
632        }
633      }
634      if (c1IsBetter && !c2IsBetter)
635        return 1;
636      if (!c1IsBetter && c2IsBetter)
637        return 2;
638     
639      // prefer members with less errors (part of heuristic that produces a best candidate even if none is applicable)
640      if (c1.ErrorCount < c2.ErrorCount) return 1;
641      if (c1.ErrorCount > c2.ErrorCount) return 2;
642     
643      if (!c1IsBetter && !c2IsBetter) {
644        // we need the tie-breaking rules
645       
646        // non-generic methods are better
647        if (!c1.IsGenericMethod && c2.IsGenericMethod)
648          return 1;
649        else if (c1.IsGenericMethod && !c2.IsGenericMethod)
650          return 2;
651       
652        // non-expanded members are better
653        if (!c1.IsExpandedForm && c2.IsExpandedForm)
654          return 1;
655        else if (c1.IsExpandedForm && !c2.IsExpandedForm)
656          return 2;
657       
658        // prefer the member with less arguments mapped to the params-array
659        int r = c1.ArgumentsPassedToParamsArray.CompareTo(c2.ArgumentsPassedToParamsArray);
660        if (r < 0) return 1;
661        else if (r > 0) return 2;
662       
663        // prefer the member where no default values need to be substituted
664        if (!c1.HasUnmappedOptionalParameters && c2.HasUnmappedOptionalParameters)
665          return 1;
666        else if (c1.HasUnmappedOptionalParameters && !c2.HasUnmappedOptionalParameters)
667          return 2;
668       
669        // compare the formal parameters
670        r = MoreSpecificFormalParameters(c1, c2);
671        if (r != 0)
672          return r;
673       
674        // prefer non-lifted operators
675        ILiftedOperator lift1 = c1.Member as ILiftedOperator;
676        ILiftedOperator lift2 = c2.Member as ILiftedOperator;
677        if (lift1 == null && lift2 != null)
678          return 1;
679        if (lift1 != null && lift2 == null)
680          return 2;
681      }
682      return 0;
683    }
684   
685    /// <summary>
686    /// Implement this interface to give overload resolution a hint that the member represents a lifted operator,
687    /// which is used in the tie-breaking rules.
688    /// </summary>
689    public interface ILiftedOperator : IParameterizedMember
690    {
691      IList<IParameter> NonLiftedParameters { get; }
692    }
693   
694    int MoreSpecificFormalParameters(Candidate c1, Candidate c2)
695    {
696      // prefer the member with more formal parmeters (in case both have different number of optional parameters)
697      int r = c1.Parameters.Count.CompareTo(c2.Parameters.Count);
698      if (r > 0) return 1;
699      else if (r < 0) return 2;
700     
701      return MoreSpecificFormalParameters(c1.Parameters.Select(p => p.Type), c2.Parameters.Select(p => p.Type));
702    }
703   
704    static int MoreSpecificFormalParameters(IEnumerable<IType> t1, IEnumerable<IType> t2)
705    {
706      bool c1IsBetter = false;
707      bool c2IsBetter = false;
708      foreach (var pair in t1.Zip(t2, (a,b) => new { Item1 = a, Item2 = b })) {
709        switch (MoreSpecificFormalParameter(pair.Item1, pair.Item2)) {
710          case 1:
711            c1IsBetter = true;
712            break;
713          case 2:
714            c2IsBetter = true;
715            break;
716        }
717      }
718      if (c1IsBetter && !c2IsBetter)
719        return 1;
720      if (!c1IsBetter && c2IsBetter)
721        return 2;
722      return 0;
723    }
724   
725    static int MoreSpecificFormalParameter(IType t1, IType t2)
726    {
727      if ((t1 is ITypeParameter) && !(t2 is ITypeParameter))
728        return 2;
729      if ((t2 is ITypeParameter) && !(t1 is ITypeParameter))
730        return 1;
731     
732      ParameterizedType p1 = t1 as ParameterizedType;
733      ParameterizedType p2 = t2 as ParameterizedType;
734      if (p1 != null && p2 != null && p1.TypeParameterCount == p2.TypeParameterCount) {
735        int r = MoreSpecificFormalParameters(p1.TypeArguments, p2.TypeArguments);
736        if (r > 0)
737          return r;
738      }
739      TypeWithElementType tew1 = t1 as TypeWithElementType;
740      TypeWithElementType tew2 = t2 as TypeWithElementType;
741      if (tew1 != null && tew2 != null) {
742        return MoreSpecificFormalParameter(tew1.ElementType, tew2.ElementType);
743      }
744      return 0;
745    }
746    #endregion
747   
748    #region ConsiderIfNewCandidateIsBest
749    void ConsiderIfNewCandidateIsBest(Candidate candidate)
750    {
751      if (bestCandidate == null) {
752        bestCandidate = candidate;
753        bestCandidateWasValidated = false;
754      } else {
755        switch (BetterFunctionMember(candidate, bestCandidate)) {
756          case 0:
757            // Overwrite 'bestCandidateAmbiguousWith' so that API users can
758            // detect the set of all ambiguous methods if they look at
759            // bestCandidateAmbiguousWith after each step.
760            bestCandidateAmbiguousWith = candidate;
761            break;
762          case 1:
763            bestCandidate = candidate;
764            bestCandidateWasValidated = false;
765            bestCandidateAmbiguousWith = null;
766            break;
767            // case 2: best candidate stays best
768        }
769      }
770    }
771    #endregion
772   
773    #region Output Properties
774    public IParameterizedMember BestCandidate {
775      get { return bestCandidate != null ? bestCandidate.Member : null; }
776    }
777   
778    /// <summary>
779    /// Returns the errors that apply to the best candidate.
780    /// This includes additional errors that do not affect applicability (e.g. AmbiguousMatch, MethodConstraintsNotSatisfied)
781    /// </summary>
782    public OverloadResolutionErrors BestCandidateErrors {
783      get {
784        if (bestCandidate == null)
785          return OverloadResolutionErrors.None;
786        if (!bestCandidateWasValidated) {
787          bestCandidateValidationResult = ValidateMethodConstraints(bestCandidate);
788          bestCandidateWasValidated = true;
789        }
790        OverloadResolutionErrors err = bestCandidate.Errors | bestCandidateValidationResult;
791        if (bestCandidateAmbiguousWith != null)
792          err |= OverloadResolutionErrors.AmbiguousMatch;
793        return err;
794      }
795    }
796   
797    public bool FoundApplicableCandidate {
798      get { return bestCandidate != null && IsApplicable(bestCandidate.Errors); }
799    }
800   
801    public IParameterizedMember BestCandidateAmbiguousWith {
802      get { return bestCandidateAmbiguousWith != null ? bestCandidateAmbiguousWith.Member : null; }
803    }
804   
805    public bool BestCandidateIsExpandedForm {
806      get { return bestCandidate != null ? bestCandidate.IsExpandedForm : false; }
807    }
808   
809    public bool IsAmbiguous {
810      get { return bestCandidateAmbiguousWith != null; }
811    }
812   
813    public IList<IType> InferredTypeArguments {
814      get {
815        if (bestCandidate != null && bestCandidate.InferredTypes != null)
816          return bestCandidate.InferredTypes;
817        else
818          return EmptyList<IType>.Instance;
819      }
820    }
821   
822    /// <summary>
823    /// Gets the implicit conversions that are being applied to the arguments.
824    /// </summary>
825    public IList<Conversion> ArgumentConversions {
826      get {
827        if (bestCandidate != null && bestCandidate.ArgumentConversions != null)
828          return bestCandidate.ArgumentConversions;
829        else
830          return Enumerable.Repeat(Conversion.None, arguments.Length).ToList();
831      }
832    }
833   
834    /// <summary>
835    /// Gets an array that maps argument indices to parameter indices.
836    /// For arguments that could not be mapped to any parameter, the value will be -1.
837    ///
838    /// parameterIndex = GetArgumentToParameterMap()[argumentIndex]
839    /// </summary>
840    public IList<int> GetArgumentToParameterMap()
841    {
842      if (bestCandidate != null)
843        return bestCandidate.ArgumentToParameterMap;
844      else
845        return null;
846    }
847   
848    /// <summary>
849    /// Returns the arguments for the method call in the order they were provided (not in the order of the parameters).
850    /// Arguments are wrapped in a <see cref="ConversionResolveResult"/> if an implicit conversion is being applied
851    /// to them when calling the method.
852    /// </summary>
853    public IList<ResolveResult> GetArgumentsWithConversions()
854    {
855      if (bestCandidate == null)
856        return arguments;
857      else
858        return GetArgumentsWithConversions(null, null);
859    }
860   
861    /// <summary>
862    /// Returns the arguments for the method call in the order they were provided (not in the order of the parameters).
863    /// Arguments are wrapped in a <see cref="ConversionResolveResult"/> if an implicit conversion is being applied
864    /// to them when calling the method.
865    /// For arguments where an explicit argument name was provided, the argument will
866    /// be wrapped in a <see cref="NamedArgumentResolveResult"/>.
867    /// </summary>
868    public IList<ResolveResult> GetArgumentsWithConversionsAndNames()
869    {
870      if (bestCandidate == null)
871        return arguments;
872      else
873        return GetArgumentsWithConversions(null, GetBestCandidateWithSubstitutedTypeArguments());
874    }
875   
876    IList<ResolveResult> GetArgumentsWithConversions(ResolveResult targetResolveResult, IParameterizedMember bestCandidateForNamedArguments)
877    {
878      var conversions = this.ArgumentConversions;
879      ResolveResult[] args = new ResolveResult[arguments.Length];
880      for (int i = 0; i < args.Length; i++) {
881        var argument = arguments[i];
882        if (this.IsExtensionMethodInvocation && i == 0 && targetResolveResult != null)
883          argument = targetResolveResult;
884        int parameterIndex = bestCandidate.ArgumentToParameterMap[i];
885        if (parameterIndex >= 0 && conversions[i] != Conversion.IdentityConversion) {
886          // Wrap argument in ConversionResolveResult
887          IType parameterType = bestCandidate.ParameterTypes[parameterIndex];
888          if (parameterType.Kind != TypeKind.Unknown) {
889            if (arguments[i].IsCompileTimeConstant && conversions[i].IsValid && !conversions[i].IsUserDefined) {
890              argument = new CSharpResolver(compilation).WithCheckForOverflow(CheckForOverflow).ResolveCast(parameterType, argument);
891            } else {
892              argument = new ConversionResolveResult(parameterType, argument, conversions[i], CheckForOverflow);
893            }
894          }
895        }
896        if (bestCandidateForNamedArguments != null && argumentNames[i] != null) {
897          // Wrap argument in NamedArgumentResolveResult
898          if (parameterIndex >= 0) {
899            argument = new NamedArgumentResolveResult(bestCandidateForNamedArguments.Parameters[parameterIndex], argument, bestCandidateForNamedArguments);
900          } else {
901            argument = new NamedArgumentResolveResult(argumentNames[i], argument);
902          }
903        }
904        args[i] = argument;
905      }
906      return args;
907    }
908   
909    public IParameterizedMember GetBestCandidateWithSubstitutedTypeArguments()
910    {
911      if (bestCandidate == null)
912        return null;
913      IMethod method = bestCandidate.Member as IMethod;
914      if (method != null && method.TypeParameters.Count > 0) {
915        return ((IMethod)method.MemberDefinition).Specialize(GetSubstitution(bestCandidate));
916      } else {
917        return bestCandidate.Member;
918      }
919    }
920   
921    TypeParameterSubstitution GetSubstitution(Candidate candidate)
922    {
923      // Do not compose the substitutions, but merge them.
924      // This is required for InvocationTests.SubstituteClassAndMethodTypeParametersAtOnce
925      return new TypeParameterSubstitution(candidate.Member.Substitution.ClassTypeArguments, candidate.InferredTypes);
926    }
927   
928    /// <summary>
929    /// Creates a ResolveResult representing the result of overload resolution.
930    /// </summary>
931    /// <param name="targetResolveResult">
932    /// The target expression of the call. May be <c>null</c> for static methods/constructors.
933    /// </param>
934    /// <param name="initializerStatements">
935    /// Statements for Objects/Collections initializer.
936    /// <see cref="InvocationResolveResult.InitializerStatements"/>
937    /// <param name="returnTypeOverride">
938    /// If not null, use this instead of the ReturnType of the member as the type of the created resolve result.
939    /// </param>
940    public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetResolveResult, IList<ResolveResult> initializerStatements = null, IType returnTypeOverride = null)
941    {
942      IParameterizedMember member = GetBestCandidateWithSubstitutedTypeArguments();
943      if (member == null)
944        throw new InvalidOperationException();
945
946      return new CSharpInvocationResolveResult(
947        this.IsExtensionMethodInvocation ? new TypeResolveResult(member.DeclaringType) : targetResolveResult,
948        member,
949        GetArgumentsWithConversions(targetResolveResult, member),
950        this.BestCandidateErrors,
951        this.IsExtensionMethodInvocation,
952        this.BestCandidateIsExpandedForm,
953        isDelegateInvocation: false,
954        argumentToParameterMap: this.GetArgumentToParameterMap(),
955        initializerStatements: initializerStatements,
956        returnTypeOverride: returnTypeOverride);
957    }
958    #endregion
959  }
960}
Note: See TracBrowser for help on using the repository browser.