~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to contrib/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
ļ»æ// Copyright (c) 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
 
 
19
 
using System;
20
 
using System.Collections.Generic;
21
 
using System.Diagnostics;
22
 
using System.Linq;
23
 
using System.Text;
24
 
 
25
 
using ICSharpCode.NRefactory.Semantics;
26
 
using ICSharpCode.NRefactory.TypeSystem;
27
 
using ICSharpCode.NRefactory.TypeSystem.Implementation;
28
 
 
29
 
namespace 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
 
                                IMethod method = member as IMethod;
105
 
                                if (method != null && method.TypeParameters.Count > 0) {
106
 
                                        // For generic methods, go back to the original parameters
107
 
                                        // (without any type parameter substitution, not even class type parameters)
108
 
                                        // We'll re-substitute them as part of RunTypeInference().
109
 
                                        method = (IMethod)method.MemberDefinition;
110
 
                                        this.Parameters = method.Parameters;
111
 
                                        this.TypeParameters = method.TypeParameters;
112
 
                                } else {
113
 
                                        this.Parameters = member.Parameters;
114
 
                                }
115
 
                                this.ParameterTypes = new IType[this.Parameters.Count];
116
 
                        }
117
 
                        
118
 
                        public void AddError(OverloadResolutionErrors newError)
119
 
                        {
120
 
                                this.Errors |= newError;
121
 
                                this.ErrorCount++;
122
 
                        }
123
 
                }
124
 
                
125
 
                readonly ICompilation compilation;
126
 
                readonly ResolveResult[] arguments;
127
 
                readonly string[] argumentNames;
128
 
                readonly CSharpConversions conversions;
129
 
                //List<Candidate> candidates = new List<Candidate>();
130
 
                Candidate bestCandidate;
131
 
                Candidate bestCandidateAmbiguousWith;
132
 
                IType[] explicitlyGivenTypeArguments;
133
 
                
134
 
                #region Constructor
135
 
                public OverloadResolution(ICompilation compilation, ResolveResult[] arguments, string[] argumentNames = null, IType[] typeArguments = null, CSharpConversions conversions = null)
136
 
                {
137
 
                        if (compilation == null)
138
 
                                throw new ArgumentNullException("compilation");
139
 
                        if (arguments == null)
140
 
                                throw new ArgumentNullException("arguments");
141
 
                        if (argumentNames == null)
142
 
                                argumentNames = new string[arguments.Length];
143
 
                        else if (argumentNames.Length != arguments.Length)
144
 
                                throw new ArgumentException("argumentsNames.Length must be equal to arguments.Length");
145
 
                        this.compilation = compilation;
146
 
                        this.arguments = arguments;
147
 
                        this.argumentNames = argumentNames;
148
 
                        
149
 
                        // keep explicitlyGivenTypeArguments==null when no type arguments were specified
150
 
                        if (typeArguments != null && typeArguments.Length > 0)
151
 
                                this.explicitlyGivenTypeArguments = typeArguments;
152
 
                        
153
 
                        this.conversions = conversions ?? CSharpConversions.Get(compilation);
154
 
                        this.AllowExpandingParams = true;
155
 
                }
156
 
                #endregion
157
 
                
158
 
                /// <summary>
159
 
                /// Gets/Sets whether the methods are extension methods that are being called using extension method syntax.
160
 
                /// </summary>
161
 
                /// <remarks>
162
 
                /// Setting this property to true restricts the possible conversions on the first argument to
163
 
                /// implicit identity, reference, or boxing conversions.
164
 
                /// </remarks>
165
 
                public bool IsExtensionMethodInvocation { get; set; }
166
 
                
167
 
                /// <summary>
168
 
                /// Gets/Sets whether expanding 'params' into individual elements is allowed.
169
 
                /// The default value is true.
170
 
                /// </summary>
171
 
                public bool AllowExpandingParams { get; set; }
172
 
                
173
 
                /// <summary>
174
 
                /// Gets the arguments for which this OverloadResolution instance was created.
175
 
                /// </summary>
176
 
                public IList<ResolveResult> Arguments {
177
 
                        get { return arguments; }
178
 
                }
179
 
                
180
 
                #region AddCandidate
181
 
                public OverloadResolutionErrors AddCandidate(IParameterizedMember member)
182
 
                {
183
 
                        return AddCandidate(member, OverloadResolutionErrors.None);
184
 
                }
185
 
                
186
 
                public OverloadResolutionErrors AddCandidate(IParameterizedMember member, OverloadResolutionErrors additionalErrors)
187
 
                {
188
 
                        if (member == null)
189
 
                                throw new ArgumentNullException("member");
190
 
                        
191
 
                        Candidate c = new Candidate(member, false);
192
 
                        if (additionalErrors != OverloadResolutionErrors.None)
193
 
                                c.AddError(additionalErrors);
194
 
                        if (CalculateCandidate(c)) {
195
 
                                //candidates.Add(c);
196
 
                        }
197
 
                        
198
 
                        if (this.AllowExpandingParams && member.Parameters.Count > 0
199
 
                            && member.Parameters[member.Parameters.Count - 1].IsParams)
200
 
                        {
201
 
                                Candidate expandedCandidate = new Candidate(member, true);
202
 
                                if (additionalErrors != OverloadResolutionErrors.None)
203
 
                                        expandedCandidate.AddError(additionalErrors);
204
 
                                // consider expanded form only if it isn't obviously wrong
205
 
                                if (CalculateCandidate(expandedCandidate)) {
206
 
                                        //candidates.Add(expandedCandidate);
207
 
                                        
208
 
                                        if (expandedCandidate.ErrorCount < c.ErrorCount)
209
 
                                                return expandedCandidate.Errors;
210
 
                                }
211
 
                        }
212
 
                        return c.Errors;
213
 
                }
214
 
                
215
 
                public static bool IsApplicable(OverloadResolutionErrors errors)
216
 
                {
217
 
                        return (errors & ~OverloadResolutionErrors.AmbiguousMatch) == OverloadResolutionErrors.None;
218
 
                }
219
 
                
220
 
                /// <summary>
221
 
                /// Calculates applicability etc. for the candidate.
222
 
                /// </summary>
223
 
                /// <returns>True if the calculation was successful, false if the candidate should be removed without reporting an error</returns>
224
 
                bool CalculateCandidate(Candidate candidate)
225
 
                {
226
 
                        if (!ResolveParameterTypes(candidate))
227
 
                                return false;
228
 
                        MapCorrespondingParameters(candidate);
229
 
                        RunTypeInference(candidate);
230
 
                        CheckApplicability(candidate);
231
 
                        ConsiderIfNewCandidateIsBest(candidate);
232
 
                        return true;
233
 
                }
234
 
                
235
 
                bool ResolveParameterTypes(Candidate candidate)
236
 
                {
237
 
                        for (int i = 0; i < candidate.Parameters.Count; i++) {
238
 
                                IType type = candidate.Parameters[i].Type;
239
 
                                if (candidate.IsExpandedForm && i == candidate.Parameters.Count - 1) {
240
 
                                        ArrayType arrayType = type as ArrayType;
241
 
                                        if (arrayType != null && arrayType.Dimensions == 1)
242
 
                                                type = arrayType.ElementType;
243
 
                                        else
244
 
                                                return false; // error: cannot unpack params-array. abort considering the expanded form for this candidate
245
 
                                }
246
 
                                candidate.ParameterTypes[i] = type;
247
 
                        }
248
 
                        return true;
249
 
                }
250
 
                #endregion
251
 
                
252
 
                #region AddMethodLists
253
 
                /// <summary>
254
 
                /// Adds all candidates from the method lists.
255
 
                /// 
256
 
                /// This method implements the logic that causes applicable methods in derived types to hide
257
 
                /// all methods in base types.
258
 
                /// </summary>
259
 
                /// <param name="methodLists">The methods, grouped by declaring type. Base types must come first in the list.</param>
260
 
                public void AddMethodLists(IList<MethodListWithDeclaringType> methodLists)
261
 
                {
262
 
                        if (methodLists == null)
263
 
                                throw new ArgumentNullException("methodLists");
264
 
                        // Base types come first, so go through the list backwards (derived types first)
265
 
                        bool[] isHiddenByDerivedType;
266
 
                        if (methodLists.Count > 1)
267
 
                                isHiddenByDerivedType = new bool[methodLists.Count];
268
 
                        else
269
 
                                isHiddenByDerivedType = null;
270
 
                        for (int i = methodLists.Count - 1; i >= 0; i--) {
271
 
                                if (isHiddenByDerivedType != null && isHiddenByDerivedType[i]) {
272
 
                                        Log.WriteLine("  Skipping methods in {0} because they are hidden by an applicable method in a derived type", methodLists[i].DeclaringType);
273
 
                                        continue;
274
 
                                }
275
 
                                
276
 
                                MethodListWithDeclaringType methodList = methodLists[i];
277
 
                                bool foundApplicableCandidateInCurrentList = false;
278
 
                                
279
 
                                for (int j = 0; j < methodList.Count; j++) {
280
 
                                        IParameterizedMember method = methodList[j];
281
 
                                        Log.Indent();
282
 
                                        OverloadResolutionErrors errors = AddCandidate(method);
283
 
                                        Log.Unindent();
284
 
                                        LogCandidateAddingResult("  Candidate", method, errors);
285
 
                                        
286
 
                                        foundApplicableCandidateInCurrentList |= IsApplicable(errors);
287
 
                                }
288
 
                                
289
 
                                if (foundApplicableCandidateInCurrentList && i > 0) {
290
 
                                        foreach (IType baseType in methodList.DeclaringType.GetAllBaseTypes()) {
291
 
                                                for (int j = 0; j < i; j++) {
292
 
                                                        if (!isHiddenByDerivedType[j] && baseType.Equals(methodLists[j].DeclaringType))
293
 
                                                                isHiddenByDerivedType[j] = true;
294
 
                                                }
295
 
                                        }
296
 
                                }
297
 
                        }
298
 
                }
299
 
                
300
 
                [Conditional("DEBUG")]
301
 
                internal void LogCandidateAddingResult(string text, IParameterizedMember method, OverloadResolutionErrors errors)
302
 
                {
303
 
                        #if DEBUG
304
 
                        Log.WriteLine(string.Format("{0} {1} = {2}{3}",
305
 
                                                    text, method,
306
 
                                                    errors == OverloadResolutionErrors.None ? "Success" : errors.ToString(),
307
 
                                                    this.BestCandidate == method ? " (best candidate so far)" :
308
 
                                                    this.BestCandidateAmbiguousWith == method ? " (ambiguous)" : ""
309
 
                                                   ));
310
 
                        #endif
311
 
                }
312
 
                #endregion
313
 
                
314
 
                #region MapCorrespondingParameters
315
 
                void MapCorrespondingParameters(Candidate candidate)
316
 
                {
317
 
                        // C# 4.0 spec: Ā§7.5.1.1 Corresponding parameters
318
 
                        candidate.ArgumentToParameterMap = new int[arguments.Length];
319
 
                        for (int i = 0; i < arguments.Length; i++) {
320
 
                                candidate.ArgumentToParameterMap[i] = -1;
321
 
                                if (argumentNames[i] == null) {
322
 
                                        // positional argument
323
 
                                        if (i < candidate.ParameterTypes.Length) {
324
 
                                                candidate.ArgumentToParameterMap[i] = i;
325
 
                                        } else if (candidate.IsExpandedForm) {
326
 
                                                candidate.ArgumentToParameterMap[i] = candidate.ParameterTypes.Length - 1;
327
 
                                        } else {
328
 
                                                candidate.AddError(OverloadResolutionErrors.TooManyPositionalArguments);
329
 
                                        }
330
 
                                } else {
331
 
                                        // named argument
332
 
                                        for (int j = 0; j < candidate.Parameters.Count; j++) {
333
 
                                                if (argumentNames[i] == candidate.Parameters[j].Name) {
334
 
                                                        candidate.ArgumentToParameterMap[i] = j;
335
 
                                                }
336
 
                                        }
337
 
                                        if (candidate.ArgumentToParameterMap[i] < 0)
338
 
                                                candidate.AddError(OverloadResolutionErrors.NoParameterFoundForNamedArgument);
339
 
                                }
340
 
                        }
341
 
                }
342
 
                #endregion
343
 
                
344
 
                #region RunTypeInference
345
 
                void RunTypeInference(Candidate candidate)
346
 
                {
347
 
                        IMethod method = candidate.Member as IMethod;
348
 
                        if (method == null || method.TypeParameters.Count == 0) {
349
 
                                if (explicitlyGivenTypeArguments != null) {
350
 
                                        // method does not expect type arguments, but was given some
351
 
                                        candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments);
352
 
                                }
353
 
                                return;
354
 
                        }
355
 
                        ParameterizedType parameterizedDeclaringType = candidate.Member.DeclaringType as ParameterizedType;
356
 
                        IList<IType> classTypeArguments;
357
 
                        if (parameterizedDeclaringType != null) {
358
 
                                classTypeArguments = parameterizedDeclaringType.TypeArguments;
359
 
                        } else {
360
 
                                classTypeArguments = null;
361
 
                        }
362
 
                        // The method is generic:
363
 
                        if (explicitlyGivenTypeArguments != null) {
364
 
                                if (explicitlyGivenTypeArguments.Length == method.TypeParameters.Count) {
365
 
                                        candidate.InferredTypes = explicitlyGivenTypeArguments;
366
 
                                } else {
367
 
                                        candidate.AddError(OverloadResolutionErrors.WrongNumberOfTypeArguments);
368
 
                                        // wrong number of type arguments given, so truncate the list or pad with UnknownType
369
 
                                        candidate.InferredTypes = new IType[method.TypeParameters.Count];
370
 
                                        for (int i = 0; i < candidate.InferredTypes.Length; i++) {
371
 
                                                if (i < explicitlyGivenTypeArguments.Length)
372
 
                                                        candidate.InferredTypes[i] = explicitlyGivenTypeArguments[i];
373
 
                                                else
374
 
                                                        candidate.InferredTypes[i] = SpecialType.UnknownType;
375
 
                                        }
376
 
                                }
377
 
                        } else {
378
 
                                TypeInference ti = new TypeInference(compilation, conversions);
379
 
                                bool success;
380
 
                                candidate.InferredTypes = ti.InferTypeArguments(candidate.TypeParameters, arguments, candidate.ParameterTypes, out success, classTypeArguments);
381
 
                                if (!success)
382
 
                                        candidate.AddError(OverloadResolutionErrors.TypeInferenceFailed);
383
 
                        }
384
 
                        // Now substitute in the formal parameters:
385
 
                        var substitution = new ConstraintValidatingSubstitution(classTypeArguments, candidate.InferredTypes, this);
386
 
                        for (int i = 0; i < candidate.ParameterTypes.Length; i++) {
387
 
                                candidate.ParameterTypes[i] = candidate.ParameterTypes[i].AcceptVisitor(substitution);
388
 
                        }
389
 
                        if (!substitution.ConstraintsValid)
390
 
                                candidate.AddError(OverloadResolutionErrors.ConstructedTypeDoesNotSatisfyConstraint);
391
 
                }
392
 
                
393
 
                sealed class ConstraintValidatingSubstitution : TypeParameterSubstitution
394
 
                {
395
 
                        readonly CSharpConversions conversions;
396
 
                        public bool ConstraintsValid = true;
397
 
                        
398
 
                        public ConstraintValidatingSubstitution(IList<IType> classTypeArguments, IList<IType> methodTypeArguments, OverloadResolution overloadResolution)
399
 
                                : base(classTypeArguments, methodTypeArguments)
400
 
                        {
401
 
                                this.conversions = overloadResolution.conversions;
402
 
                        }
403
 
                        
404
 
                        public override IType VisitParameterizedType(ParameterizedType type)
405
 
                        {
406
 
                                IType newType = base.VisitParameterizedType(type);
407
 
                                if (newType != type && ConstraintsValid) {
408
 
                                        // something was changed, so we need to validate the constraints
409
 
                                        ParameterizedType newParameterizedType = newType as ParameterizedType;
410
 
                                        if (newParameterizedType != null) {
411
 
                                                // C# 4.0 spec: Ā§4.4.4 Satisfying constraints
412
 
                                                var typeParameters = newParameterizedType.GetDefinition().TypeParameters;
413
 
                                                var substitution = newParameterizedType.GetSubstitution();
414
 
                                                for (int i = 0; i < typeParameters.Count; i++) {
415
 
                                                        if (!ValidateConstraints(typeParameters[i], newParameterizedType.GetTypeArgument(i), substitution, conversions)) {
416
 
                                                                ConstraintsValid = false;
417
 
                                                                break;
418
 
                                                        }
419
 
                                                }
420
 
                                        }
421
 
                                }
422
 
                                return newType;
423
 
                        }
424
 
                }
425
 
                #endregion
426
 
                
427
 
                #region Validate Constraints
428
 
                /// <summary>
429
 
                /// Validates whether the given type argument satisfies the constraints for the given type parameter.
430
 
                /// </summary>
431
 
                /// <param name="typeParameter">The type parameter.</param>
432
 
                /// <param name="typeArgument">The type argument.</param>
433
 
                /// <param name="substitution">The substitution that defines how type parameters are replaced with type arguments.
434
 
                /// The substitution is used to check constraints that depend on other type parameters (or recursively on the same type parameter).</param>
435
 
                /// <returns>True if the constraints are satisfied; false otherwise.</returns>
436
 
                public static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution)
437
 
                {
438
 
                        if (typeParameter == null)
439
 
                                throw new ArgumentNullException("typeParameter");
440
 
                        if (typeParameter.Owner == null)
441
 
                                throw new ArgumentNullException("typeParameter.Owner");
442
 
                        if (typeArgument == null)
443
 
                                throw new ArgumentNullException("typeArgument");
444
 
                        return ValidateConstraints(typeParameter, typeArgument, substitution, CSharpConversions.Get(typeParameter.Owner.Compilation));
445
 
                }
446
 
                
447
 
                internal static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution, CSharpConversions conversions)
448
 
                {
449
 
                        switch (typeArgument.Kind) { // void, null, and pointers cannot be used as type arguments
450
 
                                case TypeKind.Void:
451
 
                                case TypeKind.Null:
452
 
                                case TypeKind.Pointer:
453
 
                                        return false;
454
 
                        }
455
 
                        if (typeParameter.HasReferenceTypeConstraint) {
456
 
                                if (typeArgument.IsReferenceType != true)
457
 
                                        return false;
458
 
                        }
459
 
                        if (typeParameter.HasValueTypeConstraint) {
460
 
                                if (!NullableType.IsNonNullableValueType(typeArgument))
461
 
                                        return false;
462
 
                        }
463
 
                        if (typeParameter.HasDefaultConstructorConstraint) {
464
 
                                ITypeDefinition def = typeArgument.GetDefinition();
465
 
                                if (def != null && def.IsAbstract)
466
 
                                        return false;
467
 
                                var ctors = typeArgument.GetConstructors(
468
 
                                        m => m.Parameters.Count == 0 && m.Accessibility == Accessibility.Public,
469
 
                                        GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions
470
 
                                );
471
 
                                if (!ctors.Any())
472
 
                                        return false;
473
 
                        }
474
 
                        foreach (IType constraintType in typeParameter.DirectBaseTypes) {
475
 
                                IType c = constraintType;
476
 
                                if (substitution != null)
477
 
                                        c = c.AcceptVisitor(substitution);
478
 
                                if (!conversions.IsConstraintConvertible(typeArgument, c))
479
 
                                        return false;
480
 
                        }
481
 
                        return true;
482
 
                }
483
 
                #endregion
484
 
                
485
 
                #region CheckApplicability
486
 
                void CheckApplicability(Candidate candidate)
487
 
                {
488
 
                        // C# 4.0 spec: Ā§7.5.3.1 Applicable function member
489
 
                        
490
 
                        // Test whether parameters were mapped the correct number of arguments:
491
 
                        int[] argumentCountPerParameter = new int[candidate.ParameterTypes.Length];
492
 
                        foreach (int parameterIndex in candidate.ArgumentToParameterMap) {
493
 
                                if (parameterIndex >= 0)
494
 
                                        argumentCountPerParameter[parameterIndex]++;
495
 
                        }
496
 
                        for (int i = 0; i < argumentCountPerParameter.Length; i++) {
497
 
                                if (candidate.IsExpandedForm && i == argumentCountPerParameter.Length - 1)
498
 
                                        continue; // any number of arguments is fine for the params-array
499
 
                                if (argumentCountPerParameter[i] == 0) {
500
 
                                        if (candidate.Parameters[i].IsOptional)
501
 
                                                candidate.HasUnmappedOptionalParameters = true;
502
 
                                        else
503
 
                                                candidate.AddError(OverloadResolutionErrors.MissingArgumentForRequiredParameter);
504
 
                                } else if (argumentCountPerParameter[i] > 1) {
505
 
                                        candidate.AddError(OverloadResolutionErrors.MultipleArgumentsForSingleParameter);
506
 
                                }
507
 
                        }
508
 
                        
509
 
                        candidate.ArgumentConversions = new Conversion[arguments.Length];
510
 
                        // Test whether argument passing mode matches the parameter passing mode
511
 
                        for (int i = 0; i < arguments.Length; i++) {
512
 
                                int parameterIndex = candidate.ArgumentToParameterMap[i];
513
 
                                if (parameterIndex < 0) {
514
 
                                        candidate.ArgumentConversions[i] = Conversion.None;
515
 
                                        continue;
516
 
                                }
517
 
                                
518
 
                                ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult;
519
 
                                if (brrr != null) {
520
 
                                        if ((brrr.IsOut && !candidate.Parameters[parameterIndex].IsOut) || (brrr.IsRef && !candidate.Parameters[parameterIndex].IsRef))
521
 
                                                candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch);
522
 
                                } else {
523
 
                                        if (candidate.Parameters[parameterIndex].IsOut || candidate.Parameters[parameterIndex].IsRef)
524
 
                                                candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch);
525
 
                                }
526
 
                                IType parameterType = candidate.ParameterTypes[parameterIndex];
527
 
                                Conversion c = conversions.ImplicitConversion(arguments[i], parameterType);
528
 
                                candidate.ArgumentConversions[i] = c;
529
 
                                if (IsExtensionMethodInvocation && parameterIndex == 0) {
530
 
                                        // First parameter to extension method must be an identity, reference or boxing conversion
531
 
                                        if (!(c == Conversion.IdentityConversion || c == Conversion.ImplicitReferenceConversion || c == Conversion.BoxingConversion))
532
 
                                                candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch);
533
 
                                } else {
534
 
                                        if (!c.IsValid && parameterType.Kind != TypeKind.Unknown)
535
 
                                                candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch);
536
 
                                }
537
 
                        }
538
 
                }
539
 
                #endregion
540
 
                
541
 
                #region BetterFunctionMember
542
 
                /// <summary>
543
 
                /// Returns 1 if c1 is better than c2; 2 if c2 is better than c1; or 0 if neither is better.
544
 
                /// </summary>
545
 
                int BetterFunctionMember(Candidate c1, Candidate c2)
546
 
                {
547
 
                        // prefer applicable members (part of heuristic that produces a best candidate even if none is applicable)
548
 
                        if (c1.ErrorCount == 0 && c2.ErrorCount > 0)
549
 
                                return 1;
550
 
                        if (c1.ErrorCount > 0 && c2.ErrorCount == 0)
551
 
                                return 2;
552
 
                        
553
 
                        // C# 4.0 spec: Ā§7.5.3.2 Better function member
554
 
                        bool c1IsBetter = false;
555
 
                        bool c2IsBetter = false;
556
 
                        for (int i = 0; i < arguments.Length; i++) {
557
 
                                int p1 = c1.ArgumentToParameterMap[i];
558
 
                                int p2 = c2.ArgumentToParameterMap[i];
559
 
                                if (p1 >= 0 && p2 < 0) {
560
 
                                        c1IsBetter = true;
561
 
                                } else if (p1 < 0 && p2 >= 0) {
562
 
                                        c2IsBetter = true;
563
 
                                } else if (p1 >= 0 && p2 >= 0) {
564
 
                                        switch (conversions.BetterConversion(arguments[i], c1.ParameterTypes[p1], c2.ParameterTypes[p2])) {
565
 
                                                case 1:
566
 
                                                        c1IsBetter = true;
567
 
                                                        break;
568
 
                                                case 2:
569
 
                                                        c2IsBetter = true;
570
 
                                                        break;
571
 
                                        }
572
 
                                }
573
 
                        }
574
 
                        if (c1IsBetter && !c2IsBetter)
575
 
                                return 1;
576
 
                        if (!c1IsBetter && c2IsBetter)
577
 
                                return 2;
578
 
                        
579
 
                        // prefer members with less errors (part of heuristic that produces a best candidate even if none is applicable)
580
 
                        if (c1.ErrorCount < c2.ErrorCount) return 1;
581
 
                        if (c1.ErrorCount > c2.ErrorCount) return 2;
582
 
                        
583
 
                        if (!c1IsBetter && !c2IsBetter) {
584
 
                                // we need the tie-breaking rules
585
 
                                
586
 
                                // non-generic methods are better
587
 
                                if (!c1.IsGenericMethod && c2.IsGenericMethod)
588
 
                                        return 1;
589
 
                                else if (c1.IsGenericMethod && !c2.IsGenericMethod)
590
 
                                        return 2;
591
 
                                
592
 
                                // non-expanded members are better
593
 
                                if (!c1.IsExpandedForm && c2.IsExpandedForm)
594
 
                                        return 1;
595
 
                                else if (c1.IsExpandedForm && !c2.IsExpandedForm)
596
 
                                        return 2;
597
 
                                
598
 
                                // prefer the member with less arguments mapped to the params-array
599
 
                                int r = c1.ArgumentsPassedToParamsArray.CompareTo(c2.ArgumentsPassedToParamsArray);
600
 
                                if (r < 0) return 1;
601
 
                                else if (r > 0) return 2;
602
 
                                
603
 
                                // prefer the member where no default values need to be substituted
604
 
                                if (!c1.HasUnmappedOptionalParameters && c2.HasUnmappedOptionalParameters)
605
 
                                        return 1;
606
 
                                else if (c1.HasUnmappedOptionalParameters && !c2.HasUnmappedOptionalParameters)
607
 
                                        return 2;
608
 
                                
609
 
                                // compare the formal parameters
610
 
                                r = MoreSpecificFormalParameters(c1, c2);
611
 
                                if (r != 0)
612
 
                                        return r;
613
 
                                
614
 
                                // prefer non-lifted operators
615
 
                                ILiftedOperator lift1 = c1.Member as ILiftedOperator;
616
 
                                ILiftedOperator lift2 = c2.Member as ILiftedOperator;
617
 
                                if (lift1 == null && lift2 != null)
618
 
                                        return 1;
619
 
                                if (lift1 != null && lift2 == null)
620
 
                                        return 2;
621
 
                        }
622
 
                        return 0;
623
 
                }
624
 
                
625
 
                /// <summary>
626
 
                /// Implement this interface to give overload resolution a hint that the member represents a lifted operator,
627
 
                /// which is used in the tie-breaking rules.
628
 
                /// </summary>
629
 
                public interface ILiftedOperator : IParameterizedMember
630
 
                {
631
 
                        IList<IParameter> NonLiftedParameters { get; }
632
 
                }
633
 
                
634
 
                int MoreSpecificFormalParameters(Candidate c1, Candidate c2)
635
 
                {
636
 
                        // prefer the member with more formal parmeters (in case both have different number of optional parameters)
637
 
                        int r = c1.Parameters.Count.CompareTo(c2.Parameters.Count);
638
 
                        if (r > 0) return 1;
639
 
                        else if (r < 0) return 2;
640
 
                        
641
 
                        return MoreSpecificFormalParameters(c1.Parameters.Select(p => p.Type), c2.Parameters.Select(p => p.Type));
642
 
                }
643
 
                
644
 
                static int MoreSpecificFormalParameters(IEnumerable<IType> t1, IEnumerable<IType> t2)
645
 
                {
646
 
                        bool c1IsBetter = false;
647
 
                        bool c2IsBetter = false;
648
 
                        foreach (var pair in t1.Zip(t2, (a,b) => new { Item1 = a, Item2 = b })) {
649
 
                                switch (MoreSpecificFormalParameter(pair.Item1, pair.Item2)) {
650
 
                                        case 1:
651
 
                                                c1IsBetter = true;
652
 
                                                break;
653
 
                                        case 2:
654
 
                                                c2IsBetter = true;
655
 
                                                break;
656
 
                                }
657
 
                        }
658
 
                        if (c1IsBetter && !c2IsBetter)
659
 
                                return 1;
660
 
                        if (!c1IsBetter && c2IsBetter)
661
 
                                return 2;
662
 
                        return 0;
663
 
                }
664
 
                
665
 
                static int MoreSpecificFormalParameter(IType t1, IType t2)
666
 
                {
667
 
                        if ((t1 is ITypeParameter) && !(t2 is ITypeParameter))
668
 
                                return 2;
669
 
                        if ((t2 is ITypeParameter) && !(t1 is ITypeParameter))
670
 
                                return 1;
671
 
                        
672
 
                        ParameterizedType p1 = t1 as ParameterizedType;
673
 
                        ParameterizedType p2 = t2 as ParameterizedType;
674
 
                        if (p1 != null && p2 != null && p1.TypeParameterCount == p2.TypeParameterCount) {
675
 
                                int r = MoreSpecificFormalParameters(p1.TypeArguments, p2.TypeArguments);
676
 
                                if (r > 0)
677
 
                                        return r;
678
 
                        }
679
 
                        TypeWithElementType tew1 = t1 as TypeWithElementType;
680
 
                        TypeWithElementType tew2 = t2 as TypeWithElementType;
681
 
                        if (tew1 != null && tew2 != null) {
682
 
                                return MoreSpecificFormalParameter(tew1.ElementType, tew2.ElementType);
683
 
                        }
684
 
                        return 0;
685
 
                }
686
 
                #endregion
687
 
                
688
 
                #region ConsiderIfNewCandidateIsBest
689
 
                void ConsiderIfNewCandidateIsBest(Candidate candidate)
690
 
                {
691
 
                        if (bestCandidate == null) {
692
 
                                bestCandidate = candidate;
693
 
                        } else {
694
 
                                switch (BetterFunctionMember(candidate, bestCandidate)) {
695
 
                                        case 0:
696
 
                                                // Overwrite 'bestCandidateAmbiguousWith' so that API users can
697
 
                                                // detect the set of all ambiguous methods if they look at
698
 
                                                // bestCandidateAmbiguousWith after each step.
699
 
                                                bestCandidateAmbiguousWith = candidate;
700
 
                                                break;
701
 
                                        case 1:
702
 
                                                bestCandidate = candidate;
703
 
                                                bestCandidateAmbiguousWith = null;
704
 
                                                break;
705
 
                                                // case 2: best candidate stays best
706
 
                                }
707
 
                        }
708
 
                }
709
 
                #endregion
710
 
                
711
 
                public IParameterizedMember BestCandidate {
712
 
                        get { return bestCandidate != null ? bestCandidate.Member : null; }
713
 
                }
714
 
                
715
 
                public OverloadResolutionErrors BestCandidateErrors {
716
 
                        get {
717
 
                                if (bestCandidate == null)
718
 
                                        return OverloadResolutionErrors.None;
719
 
                                OverloadResolutionErrors err = bestCandidate.Errors;
720
 
                                if (bestCandidateAmbiguousWith != null)
721
 
                                        err |= OverloadResolutionErrors.AmbiguousMatch;
722
 
                                return err;
723
 
                        }
724
 
                }
725
 
                
726
 
                public bool FoundApplicableCandidate {
727
 
                        get { return bestCandidate != null && bestCandidate.Errors == OverloadResolutionErrors.None; }
728
 
                }
729
 
                
730
 
                public IParameterizedMember BestCandidateAmbiguousWith {
731
 
                        get { return bestCandidateAmbiguousWith != null ? bestCandidateAmbiguousWith.Member : null; }
732
 
                }
733
 
                
734
 
                public bool BestCandidateIsExpandedForm {
735
 
                        get { return bestCandidate != null ? bestCandidate.IsExpandedForm : false; }
736
 
                }
737
 
                
738
 
                public bool IsAmbiguous {
739
 
                        get { return bestCandidateAmbiguousWith != null; }
740
 
                }
741
 
                
742
 
                public IList<IType> InferredTypeArguments {
743
 
                        get {
744
 
                                if (bestCandidate != null && bestCandidate.InferredTypes != null)
745
 
                                        return bestCandidate.InferredTypes;
746
 
                                else
747
 
                                        return EmptyList<IType>.Instance;
748
 
                        }
749
 
                }
750
 
                
751
 
                /// <summary>
752
 
                /// Gets the implicit conversions that are being applied to the arguments.
753
 
                /// </summary>
754
 
                public IList<Conversion> ArgumentConversions {
755
 
                        get {
756
 
                                if (bestCandidate != null && bestCandidate.ArgumentConversions != null)
757
 
                                        return bestCandidate.ArgumentConversions;
758
 
                                else
759
 
                                        return Enumerable.Repeat(Conversion.None, arguments.Length).ToList();
760
 
                        }
761
 
                }
762
 
                
763
 
                /// <summary>
764
 
                /// Gets an array that maps argument indices to parameter indices.
765
 
                /// For arguments that could not be mapped to any parameter, the value will be -1.
766
 
                /// 
767
 
                /// parameterIndex = GetArgumentToParameterMap()[argumentIndex]
768
 
                /// </summary>
769
 
                public IList<int> GetArgumentToParameterMap()
770
 
                {
771
 
                        if (bestCandidate != null)
772
 
                                return bestCandidate.ArgumentToParameterMap;
773
 
                        else
774
 
                                return null;
775
 
                }
776
 
                
777
 
                public IList<ResolveResult> GetArgumentsWithConversions()
778
 
                {
779
 
                        if (bestCandidate == null)
780
 
                                return arguments;
781
 
                        else
782
 
                                return GetArgumentsWithConversions(null);
783
 
                }
784
 
                
785
 
                IList<ResolveResult> GetArgumentsWithConversions(ResolveResult targetResolveResult)
786
 
                {
787
 
                        var conversions = this.ArgumentConversions;
788
 
                        ResolveResult[] args = new ResolveResult[arguments.Length];
789
 
                        for (int i = 0; i < args.Length; i++) {
790
 
                                var argument = arguments[i];
791
 
                                if (this.IsExtensionMethodInvocation && i == 0 && targetResolveResult != null)
792
 
                                        argument = targetResolveResult;
793
 
                                if (conversions[i] == Conversion.IdentityConversion) {
794
 
                                        args[i] = argument;
795
 
                                } else {
796
 
                                        int parameterIndex = bestCandidate.ArgumentToParameterMap[i];
797
 
                                        IType parameterType;
798
 
                                        if (parameterIndex >= 0) {
799
 
                                                parameterType = bestCandidate.ParameterTypes[parameterIndex];
800
 
                                        } else {
801
 
                                                parameterType = SpecialType.UnknownType;
802
 
                                        }
803
 
                                        if (arguments[i].IsCompileTimeConstant && conversions[i] != Conversion.None) {
804
 
                                                args[i] = new CSharpResolver(compilation).ResolveCast(parameterType, argument);
805
 
                                        } else {
806
 
                                                args[i] = new ConversionResolveResult(parameterType, argument, conversions[i]);
807
 
                                        }
808
 
                                }
809
 
                        }
810
 
                        return args;
811
 
                }
812
 
                
813
 
                public IParameterizedMember GetBestCandidateWithSubstitutedTypeArguments()
814
 
                {
815
 
                        if (bestCandidate == null)
816
 
                                return null;
817
 
                        IMethod method = bestCandidate.Member as IMethod;
818
 
                        if (method != null && method.TypeParameters.Count > 0) {
819
 
                                SpecializedMethod sm = method as SpecializedMethod;
820
 
                                if (sm != null) {
821
 
                                        // Do not compose the substitutions, but merge them.
822
 
                                        // This is required for InvocationTests.SubstituteClassAndMethodTypeParametersAtOnce
823
 
                                        return new SpecializedMethod(
824
 
                                                (IMethod)method.MemberDefinition,
825
 
                                                new TypeParameterSubstitution(sm.Substitution.ClassTypeArguments, bestCandidate.InferredTypes));
826
 
                                } else {
827
 
                                        return new SpecializedMethod(method, new TypeParameterSubstitution(null, bestCandidate.InferredTypes));
828
 
                                }
829
 
                        } else {
830
 
                                return bestCandidate.Member;
831
 
                        }
832
 
                }
833
 
                
834
 
                /// <summary>
835
 
                /// Creates a ResolveResult representing the result of overload resolution.
836
 
                /// </summary>
837
 
                /// <param name="targetResolveResult">
838
 
                /// The target expression of the call. May be <c>null</c> for static methods/constructors.
839
 
                /// </param>
840
 
                /// <param name="initializerStatements">
841
 
                /// Statements for Objects/Collections initializer.
842
 
                /// <see cref="InvocationResolveResult.InitializerStatements"/>
843
 
                /// </param>
844
 
                public CSharpInvocationResolveResult CreateResolveResult(ResolveResult targetResolveResult, IList<ResolveResult> initializerStatements = null)
845
 
                {
846
 
                        IParameterizedMember member = GetBestCandidateWithSubstitutedTypeArguments();
847
 
                        if (member == null)
848
 
                                throw new InvalidOperationException();
849
 
                        
850
 
                        return new CSharpInvocationResolveResult(
851
 
                                this.IsExtensionMethodInvocation ? new TypeResolveResult(member.DeclaringType) : targetResolveResult,
852
 
                                member,
853
 
                                GetArgumentsWithConversions(targetResolveResult),
854
 
                                this.BestCandidateErrors,
855
 
                                this.IsExtensionMethodInvocation,
856
 
                                this.BestCandidateIsExpandedForm,
857
 
                                isDelegateInvocation: false,
858
 
                                argumentToParameterMap: this.GetArgumentToParameterMap(),
859
 
                                initializerStatements: initializerStatements);
860
 
                }
861
 
        }
862
 
}