OverloadResolution.cs 135.9 KB
Newer Older
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
P
Pilchie 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{
    internal enum BetterResult
    {
        Left,
        Right,
        Neither,
        Equal
    }

    internal sealed partial class OverloadResolution
    {
22
        private readonly Binder _binder;
P
Pilchie 已提交
23 24 25

        public OverloadResolution(Binder binder)
        {
26
            _binder = binder;
P
Pilchie 已提交
27 28
        }

29
        private CSharpCompilation Compilation
P
Pilchie 已提交
30
        {
31
            get { return _binder.Compilation; }
P
Pilchie 已提交
32 33
        }

34
        private Conversions Conversions
P
Pilchie 已提交
35
        {
36
            get { return _binder.Conversions; }
P
Pilchie 已提交
37 38
        }

39
        // lazily compute if the compiler is in "strict" mode (rather than duplicating bugs for compatibility)
40
        private bool? _strict;
41
        private bool Strict
42 43 44
        {
            get
            {
45
                if (_strict.HasValue) return _strict.Value;
46
                bool value = _binder.Compilation.FeatureStrictEnabled;
47
                _strict = value;
48 49 50 51
                return value;
            }
        }

52
        // UNDONE: This List<MethodResolutionResult> deal should probably be its own data structure.
P
Pilchie 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
        // We need an indexable collection of mappings from method candidates to their up-to-date
        // overload resolution status. It must be fast and memory efficient, but it will very often
        // contain just 1 candidate.      
        private static int RemainingCandidatesCount<TMember>(ArrayBuilder<MemberResolutionResult<TMember>> list)
            where TMember : Symbol
        {
            var count = 0;
            for (int i = 0; i < list.Count; i++)
            {
                if (list[i].Result.IsValid)
                {
                    count += 1;
                }
            }

            return count;
        }

        // Perform overload resolution on the given method group, with the given arguments and
        // names. The names can be null if no names were supplied to any arguments.
        public void ObjectCreationOverloadResolution(ImmutableArray<MethodSymbol> constructors, AnalyzedArguments arguments, OverloadResolutionResult<MethodSymbol> result, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            var results = result.ResultsBuilder;

            // First, attempt overload resolution not getting complete results.
            PerformObjectCreationOverloadResolution(results, constructors, arguments, false, ref useSiteDiagnostics);

            if (!OverloadResolutionResultIsValid(results, arguments.HasDynamicArgument))
            {
                // We didn't get a single good result. Get full results of overload resolution and return those.
                result.Clear();
                PerformObjectCreationOverloadResolution(results, constructors, arguments, true, ref useSiteDiagnostics);
            }
        }

        // Perform overload resolution on the given method group, with the given arguments and
        // names. The names can be null if no names were supplied to any arguments.
        public void MethodInvocationOverloadResolution(
            ArrayBuilder<MethodSymbol> methods,
            ArrayBuilder<TypeSymbol> typeArguments,
            AnalyzedArguments arguments,
            OverloadResolutionResult<MethodSymbol> result,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics,
            bool isMethodGroupConversion = false,
            bool allowRefOmittedArguments = false,
98 99
            bool inferWithDynamic = false,
            bool allowUnexpandedForm = true)
P
Pilchie 已提交
100 101 102
        {
            MethodOrPropertyOverloadResolution(
                methods, typeArguments, arguments, result, isMethodGroupConversion,
103 104
                allowRefOmittedArguments, ref useSiteDiagnostics, inferWithDynamic: inferWithDynamic,
                allowUnexpandedForm: allowUnexpandedForm);
P
Pilchie 已提交
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
        }

        // Perform overload resolution on the given property group, with the given arguments and
        // names. The names can be null if no names were supplied to any arguments.
        public void PropertyOverloadResolution(
            ArrayBuilder<PropertySymbol> indexers,
            AnalyzedArguments arguments,
            OverloadResolutionResult<PropertySymbol> result,
            bool allowRefOmittedArguments,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            ArrayBuilder<TypeSymbol> typeArguments = ArrayBuilder<TypeSymbol>.GetInstance();
            MethodOrPropertyOverloadResolution(indexers, typeArguments, arguments, result, isMethodGroupConversion: false, allowRefOmittedArguments: allowRefOmittedArguments, useSiteDiagnostics: ref useSiteDiagnostics);
            typeArguments.Free();
        }

        internal void MethodOrPropertyOverloadResolution<TMember>(
            ArrayBuilder<TMember> members,
            ArrayBuilder<TypeSymbol> typeArguments,
            AnalyzedArguments arguments,
            OverloadResolutionResult<TMember> result,
            bool isMethodGroupConversion,
            bool allowRefOmittedArguments,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics,
129 130
            bool inferWithDynamic = false,
            bool allowUnexpandedForm = true)
P
Pilchie 已提交
131 132 133 134 135 136 137
            where TMember : Symbol
        {
            var results = result.ResultsBuilder;

            // First, attempt overload resolution not getting complete results.
            PerformMemberOverloadResolution(
                results, members, typeArguments, arguments, false, isMethodGroupConversion,
138 139
                allowRefOmittedArguments, ref useSiteDiagnostics, inferWithDynamic: inferWithDynamic,
                allowUnexpandedForm: allowUnexpandedForm);
P
Pilchie 已提交
140 141 142 143 144

            if (!OverloadResolutionResultIsValid(results, arguments.HasDynamicArgument))
            {
                // We didn't get a single good result. Get full results of overload resolution and return those.
                result.Clear();
145 146
                PerformMemberOverloadResolution(results, members, typeArguments, arguments, true, isMethodGroupConversion,
                    allowRefOmittedArguments, ref useSiteDiagnostics, allowUnexpandedForm: allowUnexpandedForm);
P
Pilchie 已提交
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
            }
        }

        private static bool OverloadResolutionResultIsValid<TMember>(ArrayBuilder<MemberResolutionResult<TMember>> results, bool hasDynamicArgument)
            where TMember : Symbol
        {
            // If there were no dynamic arguments then overload resolution succeeds if there is exactly one method
            // that is applicable and not worse than another method.
            //
            // If there were dynamic arguments then overload resolution succeeds if there were one or more applicable
            // methods; which applicable method that will be invoked, if any, will be worked out at runtime.
            //
            // Note that we could in theory do a better job of detecting situations that we know will fail. We do not
            // treat methods that violate generic type constraints as inapplicable; rather, if such a method is chosen
            // as the best method we give an error during the "final validation" phase. In the dynamic argument
            // scenario there could be two methods, both applicable, ambiguous as to which is better, and neither
            // would pass final validation. In that case we could give the error at compile time, but we do not.

            if (hasDynamicArgument)
            {
                foreach (var curResult in results)
                {
                    if (curResult.Result.IsApplicable)
                    {
                        return true;
                    }
                }

                return false;
            }

            bool oneValid = false;

            foreach (var curResult in results)
            {
                if (curResult.Result.IsValid)
                {
                    if (oneValid)
                    {
                        // We somehow have two valid results.
                        return false;
                    }
                    oneValid = true;
                }
            }

            return oneValid;
        }

        // Perform method/indexer overload resolution, storing the results into "results". If
        // completeResults is false, then invalid results don't have to be stored. The results will
        // still contain all possible successful resolution.
        private void PerformMemberOverloadResolution<TMember>(
            ArrayBuilder<MemberResolutionResult<TMember>> results,
            ArrayBuilder<TMember> members,
            ArrayBuilder<TypeSymbol> typeArguments,
            AnalyzedArguments arguments,
            bool completeResults,
            bool isMethodGroupConversion,
            bool allowRefOmittedArguments,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics,
208 209
            bool inferWithDynamic = false,
            bool allowUnexpandedForm = true)
P
Pilchie 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
            where TMember : Symbol
        {
            // SPEC: The binding-time processing of a method invocation of the form M(A), where M is a 
            // SPEC: method group (possibly including a type-argument-list), and A is an optional 
            // SPEC: argument-list, consists of the following steps:

            // NOTE: We use a quadratic algorithm to determine which members override/hide
            // each other (i.e. we compare them pairwise).  We could move to a linear
            // algorithm that builds the closure set of overridden/hidden members and then
            // uses that set to filter the candidate, but that would still involve realizing
            // a lot of PE symbols.  Instead, we partition the candidates by containing type.
            // With this information, we can efficiently skip checks where the (potentially)
            // overriding or hiding member is not in a subtype of the type containing the
            // (potentially) overridden or hidden member.
            Dictionary<NamedTypeSymbol, ArrayBuilder<TMember>> containingTypeMapOpt = null;
            if (members.Count > 50) // TODO: fine-tune this value
            {
                containingTypeMapOpt = PartitionMembersByContainingType(members);
            }

            // SPEC: The set of candidate methods for the method invocation is constructed.
            for (int i = 0; i < members.Count; i++)
            {
                AddMemberToCandidateSet(
                    members[i], results, members, typeArguments, arguments, completeResults,
235 236
                    isMethodGroupConversion, allowRefOmittedArguments, containingTypeMapOpt, inferWithDynamic: inferWithDynamic,
                    useSiteDiagnostics: ref useSiteDiagnostics, allowUnexpandedForm: allowUnexpandedForm);
P
Pilchie 已提交
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
            }

            // CONSIDER: use containingTypeMapOpt for RemoveLessDerivedMembers?

            ClearContainingTypeMap(ref containingTypeMapOpt);

            // Remove methods that are inaccessible because their inferred type arguments are inaccessible.
            // It is not clear from the spec how or where this is supposed to occur.
            RemoveInaccessibleTypeArguments(results, ref useSiteDiagnostics);

            // SPEC: The set of candidate methods is reduced to contain only methods from the most derived types.
            RemoveLessDerivedMembers(results, ref useSiteDiagnostics);

            // NB: As in dev12, we do this AFTER removing less derived members.
            // Also note that less derived members are not actually removed - they are simply flagged.
            ReportUseSiteDiagnostics(results, ref useSiteDiagnostics);

            // SPEC: If the resulting set of candidate methods is empty, then further processing along the following steps are abandoned, 
            // SPEC: and instead an attempt is made to process the invocation as an extension method invocation. If this fails, then no 
            // SPEC: applicable methods exist, and a binding-time error occurs. 
            if (RemainingCandidatesCount(results) == 0)
            {
                // UNDONE: Extension methods!
                return;
            }

            // SPEC: The best method of the set of candidate methods is identified. If a single best method cannot be identified, 
            // SPEC: the method invocation is ambiguous, and a binding-time error occurs. 

            RemoveWorseMembers(results, arguments, ref useSiteDiagnostics);

            // Note, the caller is responsible for "final validation",
            // as that is not part of overload resolution.
        }

        private static Dictionary<NamedTypeSymbol, ArrayBuilder<TMember>> PartitionMembersByContainingType<TMember>(ArrayBuilder<TMember> members) where TMember : Symbol
        {
            Dictionary<NamedTypeSymbol, ArrayBuilder<TMember>> containingTypeMap = new Dictionary<NamedTypeSymbol, ArrayBuilder<TMember>>();
            for (int i = 0; i < members.Count; i++)
            {
                TMember member = members[i];
                NamedTypeSymbol containingType = member.ContainingType;
                ArrayBuilder<TMember> builder;
                if (!containingTypeMap.TryGetValue(containingType, out builder))
                {
                    builder = ArrayBuilder<TMember>.GetInstance();
                    containingTypeMap[containingType] = builder;
                }
                builder.Add(member);
            }
            return containingTypeMap;
        }

        private static void ClearContainingTypeMap<TMember>(ref Dictionary<NamedTypeSymbol, ArrayBuilder<TMember>> containingTypeMapOpt) where TMember : Symbol
        {
            if ((object)containingTypeMapOpt != null)
            {
                foreach (ArrayBuilder<TMember> builder in containingTypeMapOpt.Values)
                {
                    builder.Free();
                }
                containingTypeMapOpt = null;
            }
        }

        private void AddConstructorToCandidateSet(MethodSymbol constructor, ArrayBuilder<MemberResolutionResult<MethodSymbol>> results,
            AnalyzedArguments arguments, bool completeResults, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            // Filter out constructors with unsupported metadata.
            if (constructor.HasUnsupportedMetadata)
            {
                Debug.Assert(!MemberAnalysisResult.UnsupportedMetadata().HasUseSiteDiagnosticToReportFor(constructor));
                if (completeResults)
                {
                    results.Add(new MemberResolutionResult<MethodSymbol>(constructor, constructor, MemberAnalysisResult.UnsupportedMetadata()));
                }
                return;
            }

            var normalResult = IsConstructorApplicableInNormalForm(constructor, arguments, ref useSiteDiagnostics);
            var result = normalResult;
            if (!normalResult.IsValid)
            {
                if (IsValidParams(constructor))
                {
                    var expandedResult = IsConstructorApplicableInExpandedForm(constructor, arguments, ref useSiteDiagnostics);
                    if (expandedResult.IsValid || completeResults)
                    {
                        result = expandedResult;
                    }
                }
            }

            // If the constructor has a use site diagnostic, we don't want to discard it because we'll have to report the diagnostic later.
            if (result.IsValid || completeResults || result.HasUseSiteDiagnosticToReportFor(constructor))
            {
                results.Add(new MemberResolutionResult<MethodSymbol>(constructor, constructor, result));
            }
        }

        private MemberAnalysisResult IsConstructorApplicableInNormalForm(MethodSymbol constructor, AnalyzedArguments arguments, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            var argumentAnalysis = AnalyzeArguments(constructor, arguments, isMethodGroupConversion: false, expanded: false); // Constructors are never involved in method group conversion.
            if (!argumentAnalysis.IsValid)
            {
                return MemberAnalysisResult.ArgumentParameterMismatch(argumentAnalysis);
            }

            // Check after argument analysis, but before more complicated type inference and argument type validation.
            if (constructor.HasUseSiteError)
            {
                return MemberAnalysisResult.UseSiteError();
            }

            var effectiveParameters = GetEffectiveParametersInNormalForm(constructor, arguments.Arguments.Count, argumentAnalysis.ArgsToParamsOpt, arguments.RefKinds, allowRefOmittedArguments: false);

            return IsApplicable(constructor, effectiveParameters, arguments, argumentAnalysis.ArgsToParamsOpt, isVararg: constructor.IsVararg, hasAnyRefOmittedArgument: false, ignoreOpenTypes: false, useSiteDiagnostics: ref useSiteDiagnostics);
        }

        private MemberAnalysisResult IsConstructorApplicableInExpandedForm(MethodSymbol constructor, AnalyzedArguments arguments, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            var argumentAnalysis = AnalyzeArguments(constructor, arguments, isMethodGroupConversion: false, expanded: true);
            if (!argumentAnalysis.IsValid)
            {
                return MemberAnalysisResult.ArgumentParameterMismatch(argumentAnalysis);
            }

            // Check after argument analysis, but before more complicated type inference and argument type validation.
            if (constructor.HasUseSiteError)
            {
                return MemberAnalysisResult.UseSiteError();
            }

            var effectiveParameters = GetEffectiveParametersInExpandedForm(constructor, arguments.Arguments.Count, argumentAnalysis.ArgsToParamsOpt, arguments.RefKinds, allowRefOmittedArguments: false);

            // A vararg ctor is never applicable in its expanded form because
            // it is never a params method.
            Debug.Assert(!constructor.IsVararg);
            var result = IsApplicable(constructor, effectiveParameters, arguments, argumentAnalysis.ArgsToParamsOpt, isVararg: false, hasAnyRefOmittedArgument: false, ignoreOpenTypes: false, useSiteDiagnostics: ref useSiteDiagnostics);

            return result.IsValid ? MemberAnalysisResult.ExpandedForm(result.ArgsToParamsOpt, result.ConversionsOpt, hasAnyRefOmittedArgument: false) : result;
        }

        private void AddMemberToCandidateSet<TMember>(
            TMember member, // method or property
            ArrayBuilder<MemberResolutionResult<TMember>> results,
            ArrayBuilder<TMember> members,
            ArrayBuilder<TypeSymbol> typeArguments,
            AnalyzedArguments arguments,
            bool completeResults,
            bool isMethodGroupConversion,
            bool allowRefOmittedArguments,
            Dictionary<NamedTypeSymbol, ArrayBuilder<TMember>> containingTypeMapOpt,
            bool inferWithDynamic,
391 392
            ref HashSet<DiagnosticInfo> useSiteDiagnostics,
            bool allowUnexpandedForm)
P
Pilchie 已提交
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
            where TMember : Symbol
        {
            // SPEC VIOLATION:
            //
            // The specification states that the method group that resulted from member lookup has
            // already had all the "override" methods removed; according to the spec, only the
            // original declaring type declarations remain. 
            //
            // However, for IDE purposes ("go to definition") we *want* member lookup and overload
            // resolution to identify the overriding method. And the same for the purposes of code
            // generation. (For example, if you have 123.ToString() then we want to make a call to
            // Int32.ToString() directly, passing the int, rather than boxing and calling
            // Object.ToString() on the boxed object.)
            // 
            // Therefore, in member lookup we do *not* eliminate the "override" methods, even though
            // the spec says to. When overload resolution is handed a method group, it contains both
            // the overriding methods and the overridden methods.
            //
            // This is bad; it means that we're going to be doing a lot of extra work. We don't need
            // to analyze every overload of every method to determine if it is applicable; we
            // already know that if one of them is applicable then they all will be. And we don't
            // want to be in a situation where we're comparing two identical methods for which is
            // "better" either.
            //
            // What we'll do here is first eliminate all the "duplicate" overriding methods.
            // However, because we want to give the result as the more derived method, we'll do the
            // opposite of what the member lookup spec says; we'll eliminate the less-derived
            // methods, not the more-derived overrides. This means that we'll have to be a bit more
            // clever in filtering out methods from less-derived classes later, but we'll cross that
            // bridge when we come to it.

            if (members.Count < 2)
            {
                // No hiding or overriding possible.
            }
            else if (containingTypeMapOpt == null)
            {
                if (MemberGroupContainsOverride(members, member))
                {
                    // Don't even add it to the result set.  We'll add only the most-overriding members.
                    return;
                }

                if (MemberGroupHidesByName(members, member, ref useSiteDiagnostics))
                {
                    return;
                }
            }
            else if (containingTypeMapOpt.Count == 1)
            {
                // No hiding or overriding since all members are in the same type.
            }
            else
            {
                // NOTE: only check for overriding/hiding in subtypes of f.ContainingType.
                NamedTypeSymbol memberContainingType = member.ContainingType;
                foreach (var pair in containingTypeMapOpt)
                {
                    NamedTypeSymbol otherType = pair.Key;
                    if (otherType.IsDerivedFrom(memberContainingType, ignoreDynamic: false, useSiteDiagnostics: ref useSiteDiagnostics))
                    {
                        ArrayBuilder<TMember> others = pair.Value;

                        if (MemberGroupContainsOverride(others, member))
                        {
                            // Don't even add it to the result set.  We'll add only the most-overriding members.
                            return;
                        }

                        if (MemberGroupHidesByName(others, member, ref useSiteDiagnostics))
                        {
                            return;
                        }
                    }
                }
            }

470
            var leastOverriddenMember = (TMember)member.GetLeastOverriddenMember(_binder.ContainingType);
P
Pilchie 已提交
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487

            // Filter out members with unsupported metadata.
            if (member.HasUnsupportedMetadata)
            {
                Debug.Assert(!MemberAnalysisResult.UnsupportedMetadata().HasUseSiteDiagnosticToReportFor(member));
                if (completeResults)
                {
                    results.Add(new MemberResolutionResult<TMember>(member, leastOverriddenMember, MemberAnalysisResult.UnsupportedMetadata()));
                }
                return;
            }

            // First deal with eliminating generic-arity mismatches.

            // SPEC: If F is generic and M includes a type argument list, F is a candidate when:
            // SPEC: * F has the same number of method type parameters as were supplied in the type argument list, and
            //
488 489
            // This is specifying an impossible condition; the member lookup algorithm has already filtered
            // out methods from the method group that have the wrong generic arity.
P
Pilchie 已提交
490

491
            Debug.Assert(typeArguments.Count == 0 || typeArguments.Count == member.GetMemberArity());
P
Pilchie 已提交
492 493 494

            // Second, we need to determine if the method is applicable in its normal form or its expanded form.

495 496 497
            var normalResult = (allowUnexpandedForm || !IsValidParams(leastOverriddenMember))
                ? IsMemberApplicableInNormalForm(member, leastOverriddenMember, typeArguments, arguments, isMethodGroupConversion, allowRefOmittedArguments, inferWithDynamic, ref useSiteDiagnostics)
                : default(MemberResolutionResult<TMember>);
P
Pilchie 已提交
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549

            var result = normalResult;
            if (!normalResult.Result.IsValid)
            {
                // Whether a virtual method [indexer] is a "params" method [indexer] or not depends solely on how the
                // *original* declaration was declared. There are a variety of C# or MSIL
                // tricks you can pull to make overriding methods [indexers] inconsistent with overridden
                // methods [indexers] (or implementing methods [indexers] inconsistent with interfaces). 

                if (!isMethodGroupConversion && IsValidParams(leastOverriddenMember))
                {
                    var expandedResult = IsMemberApplicableInExpandedForm(member, leastOverriddenMember, typeArguments, arguments, allowRefOmittedArguments, ref useSiteDiagnostics);
                    if (PreferExpandedFormOverNormalForm(normalResult.Result, expandedResult.Result))
                    {
                        result = expandedResult;
                    }
                }
            }

            // Retain candidates with use site diagnostics for later reporting.
            if (result.Result.IsValid || completeResults || result.HasUseSiteDiagnosticToReport)
            {
                results.Add(result);
            }
        }

        // If the normal form is invalid and the expanded form is valid then obviously we prefer
        // the expanded form. However, there may be error-reporting situations where we
        // prefer to report the error on the expanded form rather than the normal form. 
        // For example, if you have something like Foo<T>(params T[]) and a call
        // Foo(1, "") then the error for the normal form is "too many arguments"
        // and the error for the expanded form is "failed to infer T". Clearly the
        // expanded form error is better.
        private static bool PreferExpandedFormOverNormalForm(MemberAnalysisResult normalResult, MemberAnalysisResult expandedResult)
        {
            Debug.Assert(!normalResult.IsValid);
            if (expandedResult.IsValid)
            {
                return true;
            }
            switch (normalResult.Kind)
            {
                case MemberResolutionKind.RequiredParameterMissing:
                case MemberResolutionKind.NoCorrespondingParameter:
                    switch (expandedResult.Kind)
                    {
                        case MemberResolutionKind.BadArguments:
                        case MemberResolutionKind.NameUsedForPositional:
                        case MemberResolutionKind.TypeInferenceFailed:
                        case MemberResolutionKind.TypeInferenceExtensionInstanceArgument:
                        case MemberResolutionKind.ConstructedParameterFailedConstraintCheck:
                        case MemberResolutionKind.NoCorrespondingNamedParameter:
550
                        case MemberResolutionKind.UseSiteError:
P
Pilchie 已提交
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
                            return true;
                    }
                    break;
            }
            return false;
        }

        // We need to know if this is a valid formal parameter list with a parameter array
        // as the final formal parameter. We might be in an error recovery scenario
        // where the params array is not an array type.
        public static bool IsValidParams(Symbol member)
        {
            // A varargs method is never a valid params method.
            if (member.GetIsVararg())
            {
                return false;
            }

            int paramCount = member.GetParameterCount();
            if (paramCount == 0)
            {
                return false;
            }

            // Note: we need to confirm the "arrayness" on the original definition because
            // it's possible that the type becomes an array as a result of substitution.
            ParameterSymbol final = member.GetParameters().Last();
578
            return final.IsParams && ((ParameterSymbol)final.OriginalDefinition).Type.IsSZArray();
P
Pilchie 已提交
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
        }

        private static bool IsOverride(Symbol overridden, Symbol overrider)
        {
            if (overridden.ContainingType == overrider.ContainingType ||
                !MemberSignatureComparer.SloppyOverrideComparer.Equals(overridden, overrider))
            {
                // Easy out.
                return false;
            }

            // Does overrider override overridden?
            var current = overrider;
            while (true)
            {
                if (!current.IsOverride)
                {
                    return false;
                }
                current = current.GetOverriddenMember();

                // We could be in error recovery.
                if ((object)current == null)
                {
                    return false;
                }

                if (current == overridden)
                {
                    return true;
                }

                // Don't search beyond the overridden member.
                if (current.ContainingType == overridden.ContainingType)
                {
                    return false;
                }
            }
        }

        private static bool MemberGroupContainsOverride<TMember>(ArrayBuilder<TMember> members, TMember member)
            where TMember : Symbol
        {
            if (!member.IsVirtual && !member.IsAbstract && !member.IsOverride)
            {
                return false;
            }

            for (var i = 0; i < members.Count; ++i)
            {
                if (IsOverride(member, members[i]))
                {
                    return true;
                }
            }

            return false;
        }

        private static bool MemberGroupHidesByName<TMember>(ArrayBuilder<TMember> members, TMember member, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
            where TMember : Symbol
        {
            NamedTypeSymbol memberContainingType = member.ContainingType;
            foreach (var otherMember in members)
            {
                NamedTypeSymbol otherContainingType = otherMember.ContainingType;
                if (HidesByName(otherMember) && otherContainingType.IsDerivedFrom(memberContainingType, ignoreDynamic: false, useSiteDiagnostics: ref useSiteDiagnostics))
                {
                    return true;
                }
            }

            return false;
        }

        /// <remarks>
        /// This is specifically a private helper function (rather than a public property or extension method)
        /// because applying this predicate to a non-method member doesn't have a clear meaning.  The goal was
        /// simply to avoid repeating ad-hoc code in a group of related collections.
        /// </remarks>
        private static bool HidesByName(Symbol member)
        {
            switch (member.Kind)
            {
                case SymbolKind.Method:
                    return ((MethodSymbol)member).HidesBaseMethodsByName;
                case SymbolKind.Property:
                    return ((PropertySymbol)member).HidesBasePropertiesByName;
                default:
                    throw ExceptionUtilities.UnexpectedValue(member.Kind);
            }
        }

        private void RemoveInaccessibleTypeArguments<TMember>(ArrayBuilder<MemberResolutionResult<TMember>> results, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
            where TMember : Symbol
        {
            for (int f = 0; f < results.Count; ++f)
            {
                var result = results[f];
                if (result.Result.IsValid && !TypeArgumentsAccessible(result.Member.GetMemberTypeArgumentsNoUseSiteDiagnostics(), ref useSiteDiagnostics))
                {
                    results[f] = new MemberResolutionResult<TMember>(result.Member, result.LeastOverriddenMember, MemberAnalysisResult.InaccessibleTypeArgument());
                }
            }
        }

        private bool TypeArgumentsAccessible(ImmutableArray<TypeSymbol> typeArguments, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            foreach (TypeSymbol arg in typeArguments)
            {
689
                if (!_binder.IsAccessible(arg, ref useSiteDiagnostics)) return false;
P
Pilchie 已提交
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
            }
            return true;
        }

        private static void RemoveLessDerivedMembers<TMember>(ArrayBuilder<MemberResolutionResult<TMember>> results, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
            where TMember : Symbol
        {
            // 7.6.5.1 Method invocations
            // SPEC: For each method C.F in the set, where C is the type in which the method F is declared, 
            // SPEC: all methods declared in a base type of C are removed from the set. Furthermore, if C 
            // SPEC: is a class type other than object, all methods declared in an interface type are removed
            // SPEC: from the set. (This latter rule only has affect when the method group was the result of 
            // SPEC: a member lookup on a type parameter having an effective base class other than object 
            // SPEC: and a non-empty effective interface set.)

            // This is going to get a bit complicated.
            //
            // Call the "original declaring type" of a method the type which first declares the
            // method, rather than overriding it. 
            //
            // The specification states that the method group that resulted from member lookup has
            // already had all the "override" methods removed; according to the spec, only the
            // original declaring type declarations remain. This means that when we do this
            // filtering, we're not suppose to remove methods of a base class just because there was
            // some override in a more derived class. Whether there is an override or not is an
            // implementation detail of the derived class; it shouldn't affect overload resolution.
            // The point of overload resolution is to determine the *slot* that is going to be
            // invoked, not the specific overriding method body. 
            //
            // However, for IDE purposes ("go to definition") we *want* member lookup and overload
            // resolution to identify the overriding method. And the same for the purposes of code
            // generation. (For example, if you have 123.ToString() then we want to make a call to
            // Int32.ToString() directly, passing the int, rather than boxing and calling
            // Object.ToString() on the boxed object.)
            // 
            // Therefore, in member lookup we do *not* eliminate the "override" methods, even though
            // the spec says to. When overload resolution is handed a method group, it contains both
            // the overriding methods and the overridden methods.  We eliminate the *overridden*
            // methods during applicable candidate set construction.
            //
            // Let's look at an example. Suppose we have in the method group:
            //
            // virtual Animal.M(T1),
            // virtual Mammal.M(T2), 
            // virtual Mammal.M(T3), 
            // override Giraffe.M(T1),
            // override Giraffe.M(T2)
            //
            // According to the spec, the override methods should not even be there. But they are.
            //
            // When we constructed the applicable candidate set we already removed everything that
            // was less-overridden. So the applicable candidate set contains:
            //
            // virtual Mammal.M(T3), 
            // override Giraffe.M(T1),
            // override Giraffe.M(T2)
            //
            // Again, that is not what should be there; what should be there are the three non-
            // overriding methods. For the purposes of removing more stuff, we need to behave as
            // though that's what was there.
            //
751
            // The presence of Giraffe.M(T2) does *not* justify the removal of Mammal.M(T3); it is
P
Pilchie 已提交
752 753 754
            // not to be considered a method of Giraffe, but rather a method of Mammal for the
            // purposes of removing other methods. 
            //
755 756
            // However, the presence of Mammal.M(T3) does justify the removal of Giraffe.M(T1). Why?
            // Because the presence of Mammal.M(T3) justifies the removal of Animal.M(T1), and that
P
Pilchie 已提交
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
            // is what is supposed to be in the set instead of Giraffe.M(T1).
            //
            // The resulting candidate set after the filtering according to the spec should be:
            //
            // virtual Mammal.M(T3), virtual Mammal.M(T2)
            //
            // But what we actually want to be there is:
            //
            // virtual Mammal.M(T3), override Giraffe.M(T2)
            //
            // So that a "go to definition" (should the latter be chosen as best) goes to the override.
            //
            // OK, so what are we going to do here?
            //
            // First, deal with this business about object and interfaces.

            RemoveAllInterfaceMembers(results);

            // Second, apply the rule that we eliminate any method whose *original declaring type*
            // is a base type of the original declaring type of any other method.

            // Note that this (and several of the other algorithms in overload resolution) is
            // O(n^2). (We expect that n will be relatively small. Also, we're trying to do these
            // algorithms without allocating hardly any additional memory, which pushes us towards
            // walking data structures multiple times rather than caching information about them.)

            for (int f = 0; f < results.Count; ++f)
            {
                var result = results[f];

                // As in dev12, we want to drop use site errors from less-derived types.
                // NOTE: Because of use site warnings, a result with a diagnostic to report
                // might not have kind UseSiteError.  This could result in a kind being
                // switched to LessDerived (i.e. loss of information), but it is the most
                // straightforward way to suppress use site diagnostics from less-derived
                // members.
                if (!(result.Result.IsValid || result.HasUseSiteDiagnosticToReport))
                {
                    continue;
                }

                // Note that we are doing something which appears a bit dodgy here: we're modifying
                // the validity of elements of the set while inside an outer loop which is filtering
                // the set based on validity. This means that we could remove an item from the set
                // that we ought to be processing later. However, because the "is a base type of"
                // relationship is transitive, that's OK. For example, suppose we have members
                // Cat.M, Mammal.M and Animal.M in the set. The first time through the outer loop we
                // eliminate Mammal.M and Animal.M, and therefore we never process Mammal.M the
                // second time through the outer loop. That's OK, because we have already done the
                // work necessary to eliminate methods on base types of Mammal when we eliminated
                // methods on base types of Cat.

                if (IsLessDerivedThanAny(result.LeastOverriddenMember.ContainingType, results, ref useSiteDiagnostics))
                {
                    results[f] = new MemberResolutionResult<TMember>(result.Member, result.LeastOverriddenMember, MemberAnalysisResult.LessDerived());
                }
            }
        }

        // Is this type a base type of any valid method on the list?
        private static bool IsLessDerivedThanAny<TMember>(TypeSymbol type, ArrayBuilder<MemberResolutionResult<TMember>> results, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
            where TMember : Symbol
        {
            for (int f = 0; f < results.Count; ++f)
            {
                var result = results[f];

                if (!result.Result.IsValid)
                {
                    continue;
                }

                var currentType = result.LeastOverriddenMember.ContainingType;

                // For purposes of removing less-derived methods, object is considered to be a base
                // type of any type other than itself.

                // UNDONE: Do we also need to special-case System.Array being a base type of array,
                // and so on?

                if (type.SpecialType == SpecialType.System_Object && currentType.SpecialType != SpecialType.System_Object)
                {
                    return true;
                }

                if (currentType.IsInterfaceType() && type.IsInterfaceType() && currentType.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics).Contains((NamedTypeSymbol)type))
                {
                    return true;
                }

                else if (currentType.IsClassType() && type.IsClassType() && currentType.IsDerivedFrom(type, ignoreDynamic: false, useSiteDiagnostics: ref useSiteDiagnostics))
                {
                    return true;
                }
            }

            return false;
        }

        private static void RemoveAllInterfaceMembers<TMember>(ArrayBuilder<MemberResolutionResult<TMember>> results)
            where TMember : Symbol
        {
            // Consider the following case:
            // 
            // interface IFoo { string ToString(); }
            // class C { public override string ToString() { whatever } }
            // class D : C, IFoo 
            // { 
            //     public override string ToString() { whatever }
            //     string IFoo.ToString() { whatever }
            // }
            // ...
            // void M<U>(U u) where U : C, IFoo { u.ToString(); } // ???
            // ...
            // M(new D());
            //
            // What should overload resolution do on the call to u.ToString()?
            // 
            // We will have IFoo.ToString and C.ToString (which is an override of object.ToString)
            // in the candidate set. Does the rule apply to eliminate all interface methods?  NO.  The
            // rule only applies if the candidate set contains a method which originally came from a
            // class type other than object. The method C.ToString is the "slot" for
            // object.ToString, so this counts as coming from object.  M should call the explicit
            // interface implementation.
            //
            // If, by contrast, that said 
            //
            // class C { public new virtual string ToString() { whatever } }
            //
            // Then the candidate set contains a method ToString which comes from a class type other
            // than object. The interface method should be eliminated and M should call virtual
            // method C.ToString().

            bool anyClassOtherThanObject = false;
            for (int f = 0; f < results.Count; f++)
            {
                var result = results[f];
                if (!result.Result.IsValid)
                {
                    continue;
                }

                var type = result.LeastOverriddenMember.ContainingType;
                if (type.IsClassType() && type.GetSpecialTypeSafe() != SpecialType.System_Object)
                {
                    anyClassOtherThanObject = true;
                    break;
                }
            }

            if (!anyClassOtherThanObject)
            {
                return;
            }

            for (int f = 0; f < results.Count; f++)
            {
                var result = results[f];
                if (!result.Result.IsValid)
                {
                    continue;
                }

                var member = result.Member;
                if (member.ContainingType.IsInterfaceType())
                {
                    results[f] = new MemberResolutionResult<TMember>(member, result.LeastOverriddenMember, MemberAnalysisResult.LessDerived());
                }
            }
        }

        // Perform instance constructor overload resolution, storing the results into "results". If
        // completeResults is false, then invalid results don't have to be stored. The results will
        // still contain all possible successful resolution.
        private void PerformObjectCreationOverloadResolution(
            ArrayBuilder<MemberResolutionResult<MethodSymbol>> results,
            ImmutableArray<MethodSymbol> constructors,
            AnalyzedArguments arguments,
            bool completeResults,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            // SPEC: The instance constructor to invoke is determined using the overload resolution 
            // SPEC: rules of 7.5.3. The set of candidate instance constructors consists of all 
            // SPEC: accessible instance constructors declared in T which are applicable with respect 
            // SPEC: to A (7.5.3.1). If the set of candidate instance constructors is empty, or if a 
            // SPEC: single best instance constructor cannot be identified, a binding-time error occurs.

            foreach (MethodSymbol constructor in constructors)
            {
                AddConstructorToCandidateSet(constructor, results, arguments, completeResults, ref useSiteDiagnostics);
            }

            ReportUseSiteDiagnostics(results, ref useSiteDiagnostics);

            // The best method of the set of candidate methods is identified. If a single best
            // method cannot be identified, the method invocation is ambiguous, and a binding-time
            // error occurs. 
            RemoveWorseMembers(results, arguments, ref useSiteDiagnostics);

            return;
        }

        private static void ReportUseSiteDiagnostics<TMember>(ArrayBuilder<MemberResolutionResult<TMember>> results, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
            where TMember : Symbol
        {
            foreach (MemberResolutionResult<TMember> result in results)
            {
                if (result.HasUseSiteDiagnosticToReport)
                {
                    useSiteDiagnostics = useSiteDiagnostics ?? new HashSet<DiagnosticInfo>();
                    useSiteDiagnostics.Add(result.Member.GetUseSiteDiagnostic());
                }
            }
        }

        private void RemoveWorseMembers<TMember>(ArrayBuilder<MemberResolutionResult<TMember>> results, AnalyzedArguments arguments, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
            where TMember : Symbol
        {
            // SPEC: Given the set of applicable candidate function members, the best function member in
            // SPEC: that set is located. Otherwise, the best function member is the one function member
            // SPEC: that is better than all other function members with respect to the given argument
            // SPEC: list. 

            // Note that the above rules require that the best member be *better* than all other 
            // applicable candidates. Consider three overloads such that:
            //
            // 3 beats 2
            // 2 beats 1
            // 3 is neither better than nor worse than 1
            //
            // It is tempting to say that overload 3 is the winner because it is the one method
            // that beats something, and is beaten by nothing. But that would be incorrect;
            // method 3 needs to beat all other methods, including method 1.
            //
            // We work up a full analysis of every member of the set. If it is worse than anything
            // then we need to do no more work; we know it cannot win. But it is also possible that
            // it is not worse than anything but not better than everything. 

            const int unknown = 0;
            const int worseThanSomething = 1;
            const int notBetterThanEverything = 2;
            const int betterThanEverything = 3;

            var worse = ArrayBuilder<int>.GetInstance(results.Count, unknown);

            for (int c1Idx = 0; c1Idx < results.Count; c1Idx++)
            {
                var c1result = results[c1Idx];

                // If we already know this is worse than something else, no need to check again.
                if (!c1result.Result.IsValid || worse[c1Idx] == worseThanSomething)
                {
                    continue;
                }

                bool c1IsBetterThanEverything = true;
                for (int c2Idx = 0; c2Idx < results.Count; c2Idx++)
                {
                    var c2result = results[c2Idx];

                    if (!c2result.Result.IsValid)
                    {
                        continue;
                    }

                    if (c1result.Member == c2result.Member)
                    {
                        continue;
                    }

                    var better = BetterFunctionMember(c1result, c2result, arguments.Arguments, ref useSiteDiagnostics);
                    if (better == BetterResult.Left)
                    {
                        worse[c2Idx] = worseThanSomething;
                    }
                    else
                    {
                        c1IsBetterThanEverything = false;
                        if (better == BetterResult.Right)
                        {
                            worse[c1Idx] = worseThanSomething;
1038
                            break;
P
Pilchie 已提交
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
                        }
                    }
                }
                if (worse[c1Idx] == unknown)
                {
                    // c1 was not worse than anything. Was it better than everything?
                    worse[c1Idx] = c1IsBetterThanEverything ? betterThanEverything : notBetterThanEverything;
                }
            }

1049 1050 1051 1052 1053
            // See we have a winner, otherwise we might need to perform additional analysis
            // in order to improve diagnostics
            bool haveBestCandidate = false;
            int countOfNotBestCandidates = 0;
            int notBestIdx = -1;
P
Pilchie 已提交
1054 1055 1056
            for (int i = 0; i < worse.Count; ++i)
            {
                Debug.Assert(!results[i].Result.IsValid || worse[i] != unknown);
1057
                if (worse[i] == betterThanEverything)
P
Pilchie 已提交
1058
                {
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
                    haveBestCandidate = true;
                    break;
                }
                else if (worse[i] == notBetterThanEverything)
                {
                    countOfNotBestCandidates++;
                    notBestIdx = i;
                }
            }

            Debug.Assert(countOfNotBestCandidates == 0 || !haveBestCandidate);

            if (haveBestCandidate || countOfNotBestCandidates == 0)
            {
                for (int i = 0; i < worse.Count; ++i)
                {
                    Debug.Assert(!results[i].Result.IsValid || worse[i] != unknown);
                    if (worse[i] == worseThanSomething || worse[i] == notBetterThanEverything)
                    {
                        results[i] = new MemberResolutionResult<TMember>(results[i].Member, results[i].LeastOverriddenMember, MemberAnalysisResult.Worse());
                    }
                }
            }
            else if (countOfNotBestCandidates == 1)
            {
                for (int i = 0; i < worse.Count; ++i)
                {
                    Debug.Assert(!results[i].Result.IsValid || worse[i] != unknown);
                    if (worse[i] == worseThanSomething)
                    {
                        // Mark those candidates, that are worse than the single notBest candidate, as Worst in order to improve error reporting.
                        var analysisResult = BetterFunctionMember(results[notBestIdx], results[i], arguments.Arguments, ref useSiteDiagnostics) == BetterResult.Left ?
                                                MemberAnalysisResult.Worst() :
                                                MemberAnalysisResult.Worse();
                        results[i] = new MemberResolutionResult<TMember>(results[i].Member, results[i].LeastOverriddenMember, analysisResult);
                    }
J
Jared Parsons 已提交
1095
                    else
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
                    {
                        Debug.Assert(worse[i] != notBetterThanEverything || i == notBestIdx);
                    }
                }

                Debug.Assert(worse[notBestIdx] == notBetterThanEverything);
                results[notBestIdx] = new MemberResolutionResult<TMember>(results[notBestIdx].Member, results[notBestIdx].LeastOverriddenMember, MemberAnalysisResult.Worse());
            }
            else
            {
                Debug.Assert(countOfNotBestCandidates > 1);

                for (int i = 0; i < worse.Count; ++i)
                {
                    Debug.Assert(!results[i].Result.IsValid || worse[i] != unknown);
                    if (worse[i] == worseThanSomething)
                    {
                        // Mark those candidates, that are worse than something, as Worst in order to improve error reporting.
                        results[i] = new MemberResolutionResult<TMember>(results[i].Member, results[i].LeastOverriddenMember, MemberAnalysisResult.Worst());
                    }
                    else if (worse[i] == notBetterThanEverything)
                    {
                        results[i] = new MemberResolutionResult<TMember>(results[i].Member, results[i].LeastOverriddenMember, MemberAnalysisResult.Worse());
                    }
P
Pilchie 已提交
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
                }
            }

            worse.Free();
        }

        // Return the parameter type corresponding to the given argument index.
        private static TypeSymbol GetParameterType(int argIndex, MemberAnalysisResult result, ImmutableArray<ParameterSymbol> parameters)
        {
            RefKind discarded;
            return GetParameterType(argIndex, result, parameters, out discarded);
        }

        // Return the parameter type corresponding to the given argument index.
        private static TypeSymbol GetParameterType(int argIndex, MemberAnalysisResult result, ImmutableArray<ParameterSymbol> parameters, out RefKind refKind)
        {
            int paramIndex = result.ParameterFromArgument(argIndex);
            ParameterSymbol parameter = parameters[paramIndex];
            refKind = parameter.RefKind;

            if (result.Kind == MemberResolutionKind.ApplicableInExpandedForm &&
1141
                parameter.IsParams && parameter.Type.IsSZArray())
P
Pilchie 已提交
1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212
            {
                return ((ArrayTypeSymbol)parameter.Type).ElementType;
            }
            else
            {
                return parameter.Type;
            }
        }

        private BetterResult BetterFunctionMember<TMember>(
            MemberResolutionResult<TMember> m1,
            MemberResolutionResult<TMember> m2,
            ArrayBuilder<BoundExpression> arguments,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
            where TMember : Symbol
        {
            Debug.Assert(m1.Result.IsValid);
            Debug.Assert(m2.Result.IsValid);
            Debug.Assert(arguments != null);

            // Omit ref feature for COM interop: We can pass arguments by value for ref parameters if we are calling a method/property on an instance of a COM imported type.
            // We should have ignored the 'ref' on the parameter while determining the applicability of argument for the given method call.
            // As per Devdiv Bug #696573: '[Interop] Com omit ref overload resolution is incorrect', we must prefer non-ref omitted methods over ref omitted methods
            // when determining the BetterFunctionMember.
            // During argument rewriting, we will replace the argument value with a temporary local and pass that local by reference.

            bool hasAnyRefOmittedArgument1 = m1.Result.HasAnyRefOmittedArgument;
            bool hasAnyRefOmittedArgument2 = m2.Result.HasAnyRefOmittedArgument;
            if (hasAnyRefOmittedArgument1 != hasAnyRefOmittedArgument2)
            {
                return hasAnyRefOmittedArgument1 ? BetterResult.Right : BetterResult.Left;
            }
            else
            {
                return BetterFunctionMember(m1, m2, arguments, considerRefKinds: hasAnyRefOmittedArgument1, useSiteDiagnostics: ref useSiteDiagnostics);
            }
        }

        private BetterResult BetterFunctionMember<TMember>(
            MemberResolutionResult<TMember> m1,
            MemberResolutionResult<TMember> m2,
            ArrayBuilder<BoundExpression> arguments,
            bool considerRefKinds,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
            where TMember : Symbol
        {
            Debug.Assert(m1.Result.IsValid);
            Debug.Assert(m2.Result.IsValid);
            Debug.Assert(arguments != null);

            // SPEC:
            //   Parameter lists for each of the candidate function members are constructed in the following way: 
            //   The expanded form is used if the function member was applicable only in the expanded form.
            //   Optional parameters with no corresponding arguments are removed from the parameter list
            //   The parameters are reordered so that they occur at the same position as the corresponding argument in the argument list.
            // We don't actually create these lists, for efficiency reason. But we iterate over the arguments
            // and get the correspond parameter types.

            BetterResult result = BetterResult.Neither;
            bool okToDowngradeResultToNeither = false;
            bool ignoreDowngradableToNeither = false;

            // Given an argument list A with a set of argument expressions { E1, E2, ..., EN } and two 
            // applicable function members MP and MQ with parameter types { P1, P2, ..., PN } and { Q1, Q2, ..., QN }, 
            // MP is defined to be a better function member than MQ if

            // for each argument, the implicit conversion from EX to QX is not better than the
            // implicit conversion from EX to PX, and for at least one argument, the conversion from
            // EX to PX is better than the conversion from EX to QX.

            bool allSame = true; // Are all parameter types equivalent by identify conversions?
1213 1214
            int i;
            for (i = 0; i < arguments.Count; ++i)
P
Pilchie 已提交
1215
            {
1216
                var argumentKind = arguments[i].Kind;
P
Pilchie 已提交
1217 1218 1219

                // If these are both applicable varargs methods and we're looking at the __arglist argument
                // then clearly neither of them is going to be better in this argument.
1220
                if (argumentKind == BoundKind.ArgListOperator)
P
Pilchie 已提交
1221 1222 1223 1224 1225 1226 1227 1228 1229
                {
                    Debug.Assert(i == arguments.Count - 1);
                    Debug.Assert(m1.Member.GetIsVararg() && m2.Member.GetIsVararg());
                    continue;
                }

                RefKind refKind1, refKind2;
                var type1 = GetParameterType(i, m1.Result, m1.LeastOverriddenMember.GetParameters(), out refKind1);
                var type2 = GetParameterType(i, m2.Result, m2.LeastOverriddenMember.GetParameters(), out refKind2);
1230

P
Pilchie 已提交
1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253
                bool okToDowngradeToNeither;
                var r = BetterConversionFromExpression(arguments[i],
                                                       type1,
                                                       m1.Result.ConversionForArg(i),
                                                       refKind1,
                                                       type2,
                                                       m2.Result.ConversionForArg(i),
                                                       refKind2,
                                                       considerRefKinds,
                                                       ref useSiteDiagnostics,
                                                       out okToDowngradeToNeither);

                if (r == BetterResult.Neither)
                {
                    if (allSame && Conversions.ClassifyImplicitConversion(type1, type2, ref useSiteDiagnostics).Kind != ConversionKind.Identity)
                    {
                        allSame = false;
                    }

                    // We learned nothing from this one. Keep going.
                    continue;
                }

1254
                if (!considerRefKinds || Conversions.ClassifyImplicitConversion(type1, type2, ref useSiteDiagnostics).Kind != ConversionKind.Identity)
P
Pilchie 已提交
1255
                {
1256 1257 1258
                    // If considerRefKinds is false, conversion between parameter types isn't classified by the if condition.
                    // This assert is here to verify the assumption that the conversion is never an identity in that case and
                    // we can skip classification as an optimization.
1259
                    Debug.Assert(considerRefKinds || Conversions.ClassifyImplicitConversion(type1, type2, ref useSiteDiagnostics).Kind != ConversionKind.Identity);
P
Pilchie 已提交
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278
                    allSame = false;
                }

                // One of them was better. Does that contradict a previous result or add a new fact?
                if (result == BetterResult.Neither)
                {
                    if (!(ignoreDowngradableToNeither && okToDowngradeToNeither))
                    {
                        // Add a new fact; we know that one of them is better when we didn't know that before.
                        result = r;
                        okToDowngradeResultToNeither = okToDowngradeToNeither;
                    }
                }
                else if (result != r)
                {
                    // We previously got, say, Left is better in one place. Now we have that Right
                    // is better in one place. We know we can bail out at this point; neither is
                    // going to be better than the other.

1279
                    // But first, let's see if we can ignore the ambiguity due to an undocumented legacy behavior of the compiler.
P
Pilchie 已提交
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
                    // This is not part of the language spec.
                    if (okToDowngradeResultToNeither)
                    {
                        if (okToDowngradeToNeither)
                        {
                            // Ignore the new information and the current result. Going forward,
                            // continue ignoring any downgradable information.
                            result = BetterResult.Neither;
                            okToDowngradeResultToNeither = false;
                            ignoreDowngradableToNeither = true;
                            continue;
                        }
                        else
                        {
                            // Current result can be ignored, but the new information cannot be ignored.
                            // Let's ignore the current result.
                            result = r;
                            okToDowngradeResultToNeither = false;
                            continue;
                        }
                    }
                    else if (okToDowngradeToNeither)
                    {
                        // Current result cannot be ignored, but the new information can be ignored.
                        // Let's ignore it and continue with the current result.
                        continue;
                    }

1308 1309
                    result = BetterResult.Neither;
                    break;
P
Pilchie 已提交
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330
                }
                else
                {
                    Debug.Assert(result == r);
                    Debug.Assert(result == BetterResult.Left || result == BetterResult.Right);

                    okToDowngradeResultToNeither = (okToDowngradeResultToNeither && okToDowngradeToNeither);
                }
            }

            // Was one unambiguously better? Return it.
            if (result != BetterResult.Neither)
            {
                return result;
            }

            // In case the parameter type sequences {P1, P2, …, PN} and {Q1, Q2, …, QN} are
            // equivalent (i.e. each Pi has an identity conversion to the corresponding Qi), the
            // following tie-breaking rules are applied, in order, to determine the better function
            // member. 

1331 1332
            int m1ParameterCount;
            int m2ParameterCount;
1333 1334
            int m1ParametersUsedIncludingExpansionAndOptional;
            int m2ParametersUsedIncludingExpansionAndOptional;
1335

1336 1337 1338
            GetParameterCounts(m1, arguments, out m1ParameterCount, out m1ParametersUsedIncludingExpansionAndOptional);
            GetParameterCounts(m2, arguments, out m2ParameterCount, out m2ParametersUsedIncludingExpansionAndOptional);

1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368
            // We might have got out of the loop above early and allSame isn't completely calculated.
            // We need to ensure that we are not going to skip over the next 'if' because of that.
            if (allSame && m1ParametersUsedIncludingExpansionAndOptional == m2ParametersUsedIncludingExpansionAndOptional)
            {
                // Complete comparison for the remaining parameter types
                for (i = i + 1; i < arguments.Count; ++i)
                {
                    var argumentKind = arguments[i].Kind;

                    // If these are both applicable varargs methods and we're looking at the __arglist argument
                    // then clearly neither of them is going to be better in this argument.
                    if (argumentKind == BoundKind.ArgListOperator)
                    {
                        Debug.Assert(i == arguments.Count - 1);
                        Debug.Assert(m1.Member.GetIsVararg() && m2.Member.GetIsVararg());
                        continue;
                    }

                    RefKind refKind1, refKind2;
                    var type1 = GetParameterType(i, m1.Result, m1.LeastOverriddenMember.GetParameters(), out refKind1);
                    var type2 = GetParameterType(i, m2.Result, m2.LeastOverriddenMember.GetParameters(), out refKind2);

                    if (Conversions.ClassifyImplicitConversion(type1, type2, ref useSiteDiagnostics).Kind != ConversionKind.Identity)
                    {
                        allSame = false;
                        break;
                    }
                }
            }

1369
            // SPEC VIOLATION: When checking for matching parameter type sequences {P1, P2, …, PN} and {Q1, Q2, …, QN},
C
Charles Stoner 已提交
1370
            //                 native compiler includes types of optional parameters. We partially duplicate this behavior
1371 1372 1373
            //                 here by comparing the number of parameters used taking params expansion and 
            //                 optional parameters into account.
            if (!allSame || m1ParametersUsedIncludingExpansionAndOptional != m2ParametersUsedIncludingExpansionAndOptional)
P
Pilchie 已提交
1374
            {
1375
                // SPEC VIOLATION: Even when parameter type sequences {P1, P2, …, PN} and {Q1, Q2, …, QN} are
1376
                //                 not equivalent, we have tie-breaking rules.
A
AlekseyTs 已提交
1377
                //
1378
                // Relevant code in the native compiler is at the end of
1379 1380 1381 1382 1383
                //                       BetterTypeEnum ExpressionBinder::WhichMethodIsBetter(
                //                                           const CandidateFunctionMember &node1,
                //                                           const CandidateFunctionMember &node2,
                //                                           Type* pTypeThrough,
                //                                           ArgInfos*args)
A
AlekseyTs 已提交
1384
                //
1385

1386
                if (m1ParametersUsedIncludingExpansionAndOptional != m2ParametersUsedIncludingExpansionAndOptional)
1387
                {
1388
                    if (m1.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm)
1389
                    {
1390 1391 1392 1393
                        if (m2.Result.Kind != MemberResolutionKind.ApplicableInExpandedForm)
                        {
                            return BetterResult.Right;
                        }
1394
                    }
1395
                    else if (m2.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm)
1396
                    {
1397
                        Debug.Assert(m1.Result.Kind != MemberResolutionKind.ApplicableInExpandedForm);
1398 1399
                        return BetterResult.Left;
                    }
1400 1401 1402 1403 1404 1405

                    // Here, if both methods needed to use optionals to fill in the signatures,
                    // then we are ambiguous. Otherwise, take the one that didn't need any 
                    // optionals.

                    if (m1ParametersUsedIncludingExpansionAndOptional == arguments.Count)
1406 1407 1408
                    {
                        return BetterResult.Left;
                    }
1409 1410 1411 1412
                    else if (m2ParametersUsedIncludingExpansionAndOptional == arguments.Count)
                    {
                        return BetterResult.Right;
                    }
1413 1414
                }

P
Pilchie 已提交
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485
                return BetterResult.Neither;
            }

            // If MP is a non-generic method and MQ is a generic method, then MP is better than MQ.
            if (m1.Member.GetMemberArity() == 0)
            {
                if (m2.Member.GetMemberArity() > 0)
                {
                    return BetterResult.Left;
                }
            }
            else if (m2.Member.GetMemberArity() == 0)
            {
                return BetterResult.Right;
            }

            // Otherwise, if MP is applicable in its normal form and MQ has a params array and is
            // applicable only in its expanded form, then MP is better than MQ.

            if (m1.Result.Kind == MemberResolutionKind.ApplicableInNormalForm && m2.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm)
            {
                return BetterResult.Left;
            }

            if (m1.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm && m2.Result.Kind == MemberResolutionKind.ApplicableInNormalForm)
            {
                return BetterResult.Right;
            }

            // SPEC ERROR: The spec has a minor error in working here. It says:
            //
            // Otherwise, if MP has more declared parameters than MQ, then MP is better than MQ. 
            // This can occur if both methods have params arrays and are applicable only in their
            // expanded forms.
            //
            // The explanatory text actually should be normative. It should say:
            //
            // Otherwise, if both methods have params arrays and are applicable only in their
            // expanded forms, and if MP has more declared parameters than MQ, then MP is better than MQ. 

            if (m1.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm && m2.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm)
            {
                if (m1ParameterCount > m2ParameterCount)
                {
                    return BetterResult.Left;
                }

                if (m1ParameterCount < m2ParameterCount)
                {
                    return BetterResult.Right;
                }
            }

            // Otherwise if all parameters of MP have a corresponding argument whereas default
            // arguments need to be substituted for at least one optional parameter in MQ then MP is
            // better than MQ. 

            bool hasAll1 = m1.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm || m1ParameterCount == arguments.Count;
            bool hasAll2 = m2.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm || m2ParameterCount == arguments.Count;
            if (hasAll1 && !hasAll2)
            {
                return BetterResult.Left;
            }

            if (!hasAll1 && hasAll2)
            {
                return BetterResult.Right;
            }

            // Otherwise, if MP has more specific parameter types than MQ, then MP is better than
            // MQ. Let {R1, R2, …, RN} and {S1, S2, …, SN} represent the uninstantiated and
1486 1487
            // unexpanded parameter types of MP and MQ. MP's parameter types are more specific than
            // MQ's if, for each parameter, RX is not less specific than SX, and, for at least one
P
Pilchie 已提交
1488 1489 1490 1491 1492 1493 1494 1495 1496
            // parameter, RX is more specific than SX

            // NB: OriginalDefinition, not ConstructedFrom.  Substitutions into containing symbols
            // must also be ignored for this tie-breaker.

            var uninst1 = ArrayBuilder<TypeSymbol>.GetInstance();
            var uninst2 = ArrayBuilder<TypeSymbol>.GetInstance();
            var m1Original = m1.LeastOverriddenMember.OriginalDefinition.GetParameters();
            var m2Original = m2.LeastOverriddenMember.OriginalDefinition.GetParameters();
1497
            for (i = 0; i < arguments.Count; ++i)
P
Pilchie 已提交
1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546
            {
                uninst1.Add(GetParameterType(i, m1.Result, m1Original));
                uninst2.Add(GetParameterType(i, m2.Result, m2Original));
            }

            result = MoreSpecificType(uninst1, uninst2, ref useSiteDiagnostics);
            uninst1.Free();
            uninst2.Free();

            if (result != BetterResult.Neither)
            {
                return result;
            }

            // UNDONE: Otherwise if one member is a non-lifted operator and  the other is a lifted
            // operator, the non-lifted one is better.

            // The penultimate rule: Position in interactive submission chain. The last definition wins.
            if (m1.Member.ContainingType.TypeKind == TypeKind.Submission && m2.Member.ContainingType.TypeKind == TypeKind.Submission)
            {
                // script class is always defined in source:
                var compilation1 = m1.Member.DeclaringCompilation;
                var compilation2 = m2.Member.DeclaringCompilation;
                int submissionId1 = compilation1.GetSubmissionSlotIndex();
                int submissionId2 = compilation2.GetSubmissionSlotIndex();

                if (submissionId1 > submissionId2)
                {
                    return BetterResult.Left;
                }

                if (submissionId1 < submissionId2)
                {
                    return BetterResult.Right;
                }
            }

            // Finally, if one has fewer custom modifiers, that is better
            int m1ModifierCount = m1.LeastOverriddenMember.CustomModifierCount();
            int m2ModifierCount = m2.LeastOverriddenMember.CustomModifierCount();
            if (m1ModifierCount != m2ModifierCount)
            {
                return (m1ModifierCount < m2ModifierCount) ? BetterResult.Left : BetterResult.Right;
            }

            // Otherwise, neither function member is better.
            return BetterResult.Neither;
        }

1547 1548 1549 1550 1551 1552 1553 1554
        private static void GetParameterCounts<TMember>(MemberResolutionResult<TMember> m, ArrayBuilder<BoundExpression> arguments, out int declaredParameterCount, out int parametersUsedIncludingExpansionAndOptional) where TMember : Symbol
        {
            declaredParameterCount = m.Member.GetParameterCount();

            if (m.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm)
            {
                if (arguments.Count < declaredParameterCount)
                {
1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566
                    ImmutableArray<int> argsToParamsOpt = m.Result.ArgsToParamsOpt;

                    if (argsToParamsOpt.IsDefaultOrEmpty || !argsToParamsOpt.Contains(declaredParameterCount - 1))
                    {
                        // params parameter isn't used (see ExpressionBinder::TryGetExpandedParams in the native compiler)
                        parametersUsedIncludingExpansionAndOptional = declaredParameterCount - 1;
                    }
                    else
                    {
                        // params parameter is used by a named argument
                        parametersUsedIncludingExpansionAndOptional = declaredParameterCount;
                    }
1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578
                }
                else
                {
                    parametersUsedIncludingExpansionAndOptional = arguments.Count;
                }
            }
            else
            {
                parametersUsedIncludingExpansionAndOptional = declaredParameterCount;
            }
        }

P
Pilchie 已提交
1579 1580 1581 1582
        private static BetterResult MoreSpecificType(ArrayBuilder<TypeSymbol> t1, ArrayBuilder<TypeSymbol> t2, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(t1.Count == t2.Count);

C
ChuckStoner 已提交
1583
            // For t1 to be more specific than t2, it has to be not less specific in every member,
P
Pilchie 已提交
1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644
            // and more specific in at least one.

            var result = BetterResult.Neither;
            for (int i = 0; i < t1.Count; ++i)
            {
                var r = MoreSpecificType(t1[i], t2[i], ref useSiteDiagnostics);
                if (r == BetterResult.Neither)
                {
                    // We learned nothing. Do nothing.
                }
                else if (result == BetterResult.Neither)
                {
                    // We have found the first more specific type. See if
                    // all the rest on this side are not less specific.
                    result = r;
                }
                else if (result != r)
                {
                    // We have more specific types on both left and right, so we 
                    // cannot succeed in picking a better type list. Bail out now.
                    return BetterResult.Neither;
                }
            }

            return result;
        }

        private static BetterResult MoreSpecificType(TypeSymbol t1, TypeSymbol t2, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            // Spec 7.5.3.2:
            // - A type parameter is less specific than a non-type parameter. 

            var t1IsTypeParameter = t1.IsTypeParameter();
            var t2IsTypeParameter = t2.IsTypeParameter();

            if (t1IsTypeParameter && !t2IsTypeParameter)
            {
                return BetterResult.Right;
            }

            if (!t1IsTypeParameter && t2IsTypeParameter)
            {
                return BetterResult.Left;
            }

            if (t1IsTypeParameter && t2IsTypeParameter)
            {
                return BetterResult.Neither;
            }

            // Spec:
            // - An array type is more specific than another array type (with the same number of dimensions) 
            //   if the element type of the first is more specific than the element type of the second.

            if (t1.IsArray())
            {
                var arr1 = (ArrayTypeSymbol)t1;
                var arr2 = (ArrayTypeSymbol)t2;

                // We should not have gotten here unless there were identity conversions
                // between the two types.
1645
                Debug.Assert(arr1.HasSameShapeAs(arr2));
P
Pilchie 已提交
1646 1647 1648 1649 1650 1651

                return MoreSpecificType(arr1.ElementType, arr2.ElementType, ref useSiteDiagnostics);
            }

            // SPEC EXTENSION: We apply the same rule to pointer types. 

1652
            if (t1.TypeKind == TypeKind.Pointer)
P
Pilchie 已提交
1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809
            {
                var p1 = (PointerTypeSymbol)t1;
                var p2 = (PointerTypeSymbol)t2;
                return MoreSpecificType(p1.PointedAtType, p2.PointedAtType, ref useSiteDiagnostics);
            }

            if (t1.IsDynamic() || t2.IsDynamic())
            {
                Debug.Assert(t1.IsDynamic() && t2.IsDynamic() ||
                             t1.IsDynamic() && t2.SpecialType == SpecialType.System_Object ||
                             t2.IsDynamic() && t1.SpecialType == SpecialType.System_Object);

                return BetterResult.Neither;
            }

            // Spec:
            // - A constructed type is more specific than another
            //   constructed type (with the same number of type arguments) if at least one type
            //   argument is more specific and no type argument is less specific than the
            //   corresponding type argument in the other. 

            var n1 = t1 as NamedTypeSymbol;
            var n2 = t2 as NamedTypeSymbol;
            Debug.Assert(((object)n1 == null) == ((object)n2 == null));

            if ((object)n1 == null)
            {
                return BetterResult.Neither;
            }

            // We should not have gotten here unless there were identity conversions between the
            // two types.

            Debug.Assert(n1.OriginalDefinition == n2.OriginalDefinition);

            var allTypeArgs1 = ArrayBuilder<TypeSymbol>.GetInstance();
            var allTypeArgs2 = ArrayBuilder<TypeSymbol>.GetInstance();
            n1.GetAllTypeArguments(allTypeArgs1, ref useSiteDiagnostics);
            n2.GetAllTypeArguments(allTypeArgs2, ref useSiteDiagnostics);

            var result = MoreSpecificType(allTypeArgs1, allTypeArgs2, ref useSiteDiagnostics);

            allTypeArgs1.Free();
            allTypeArgs2.Free();
            return result;
        }

        // Determine whether t1 or t2 is a better conversion target from node.
        private BetterResult BetterConversionFromExpression(BoundExpression node, TypeSymbol t1, TypeSymbol t2, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(node.Kind != BoundKind.UnboundLambda);
            bool ignore;
            return BetterConversionFromExpression(
                node,
                t1,
                Conversions.ClassifyImplicitConversionFromExpression(node, t1, ref useSiteDiagnostics),
                t2,
                Conversions.ClassifyImplicitConversionFromExpression(node, t2, ref useSiteDiagnostics),
                ref useSiteDiagnostics,
                out ignore);
        }

        // Determine whether t1 or t2 is a better conversion target from node, possibly considering parameter ref kinds.
        private BetterResult BetterConversionFromExpression(
            BoundExpression node,
            TypeSymbol t1,
            Conversion conv1,
            RefKind refKind1,
            TypeSymbol t2,
            Conversion conv2,
            RefKind refKind2,
            bool considerRefKinds,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics,
            out bool okToDowngradeToNeither)
        {
            okToDowngradeToNeither = false;

            if (considerRefKinds)
            {
                // We may need to consider the ref kinds of the parameters while determining the better conversion from the given expression to the respective parameter types.
                // This is needed for the omit ref feature for COM interop: We can pass arguments by value for ref parameters if we are calling a method within a COM imported type.
                // We can reach here only if we had at least one ref omitted argument for the given call, which must be a call to a method within a COM imported type.

                // Algorithm for determining the better conversion from expression when ref kinds need to be considered is NOT provided in the C# language specification,
                // see section 7.5.3.3 'Better Conversion From Expression'.
                // We match native compiler's behavior for determining the better conversion as follows:
                //  1) If one of the contending parameters is a 'ref' parameter, say p1, and other is a non-ref parameter, say p2,
                //     then p2 is a better result if the argument has an identity conversion to p2's type. Otherwise, neither result is better.
                //  2) Otherwise, if both the contending parameters are 'ref' parameters, neither result is better.
                //  3) Otherwise, we use the algorithm in 7.5.3.3 for determining the better conversion without considering ref kinds.

                // NOTE:    Native compiler does not explicitly implement the above algorithm, but gets it by default. This is due to the fact that the RefKind of a parameter
                // NOTE:    gets considered while classifying conversions between parameter types when computing better conversion target in the native compiler.
                // NOTE:    Roslyn correctly follows the specification and ref kinds are not considered while classifying conversions between types, see method BetterConversionTarget.

                Debug.Assert(refKind1 == RefKind.None || refKind1 == RefKind.Ref);
                Debug.Assert(refKind2 == RefKind.None || refKind2 == RefKind.Ref);

                if (refKind1 != refKind2)
                {
                    if (refKind1 == RefKind.None)
                    {
                        return conv1.Kind == ConversionKind.Identity ? BetterResult.Left : BetterResult.Neither;
                    }
                    else
                    {
                        return conv2.Kind == ConversionKind.Identity ? BetterResult.Right : BetterResult.Neither;
                    }
                }
                else if (refKind1 == RefKind.Ref)
                {
                    return BetterResult.Neither;
                }
            }

            return BetterConversionFromExpression(node, t1, conv1, t2, conv2, ref useSiteDiagnostics, out okToDowngradeToNeither);
        }

        // Determine whether t1 or t2 is a better conversion target from node.
        private BetterResult BetterConversionFromExpression(BoundExpression node, TypeSymbol t1, Conversion conv1, TypeSymbol t2, Conversion conv2, ref HashSet<DiagnosticInfo> useSiteDiagnostics, out bool okToDowngradeToNeither)
        {
            okToDowngradeToNeither = false;

            if (Conversions.HasIdentityConversion(t1, t2))
            {
                // Both parameters have the same type.
                return BetterResult.Neither;
            }

            var lambdaOpt = node as UnboundLambda;

            // Given an implicit conversion C1 that converts from an expression E to a type T1, 
            // and an implicit conversion C2 that converts from an expression E to a type T2,
            // C1 is a better conversion than C2 if E does not exactly match T2 and one of the following holds:
            bool t1MatchesExactly = ExpressionMatchExactly(node, t1, ref useSiteDiagnostics);
            bool t2MatchesExactly = ExpressionMatchExactly(node, t2, ref useSiteDiagnostics);

            if (t1MatchesExactly)
            {
                if (t2MatchesExactly)
                {
                    // both exactly match expression
                    return BetterResult.Neither;
                }

                // - E exactly matches T1
                okToDowngradeToNeither = lambdaOpt != null && CanDowngradeConversionFromLambdaToNeither(BetterResult.Left, lambdaOpt, t1, t2, ref useSiteDiagnostics, false);
                return BetterResult.Left;
            }
            else if (t2MatchesExactly)
            {
                // - E exactly matches T1
                okToDowngradeToNeither = lambdaOpt != null && CanDowngradeConversionFromLambdaToNeither(BetterResult.Right, lambdaOpt, t1, t2, ref useSiteDiagnostics, false);
                return BetterResult.Right;
            }

            // - T1 is a better conversion target than T2
1810
            return BetterConversionTarget(node, t1, conv1, t2, conv2, ref useSiteDiagnostics, out okToDowngradeToNeither);
P
Pilchie 已提交
1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826
        }

        private bool ExpressionMatchExactly(BoundExpression node, TypeSymbol t, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            // Given an expression E and a type T, E exactly matches T if one of the following holds:

            // - E has a type S, and an identity conversion exists from S to T 
            if ((object)node.Type != null && Conversions.HasIdentityConversion(node.Type, t))
            {
                return true;
            }

            // - E is an anonymous function, T is either a delegate type D or an expression tree 
            //   type Expression<D>, D has a return type Y, and one of the following holds:
            NamedTypeSymbol d;
            MethodSymbol invoke;
1827 1828
            TypeSymbol y;

P
Pilchie 已提交
1829 1830
            if (node.Kind == BoundKind.UnboundLambda &&
                (object)(d = t.GetDelegateType()) != null &&
1831 1832
                (object)(invoke = d.DelegateInvokeMethod) != null &&
                (y = invoke.ReturnType).SpecialType != SpecialType.System_Void)
P
Pilchie 已提交
1833 1834 1835 1836 1837 1838 1839 1840 1841 1842
            {
                BoundLambda lambda = ((UnboundLambda)node).BindForReturnTypeInference(d);

                // - an inferred return type X exists for E in the context of the parameter list of D(§7.5.2.12), and an identity conversion exists from X to Y
                TypeSymbol x = lambda.InferredReturnType(ref useSiteDiagnostics);
                if ((object)x != null && Conversions.HasIdentityConversion(x, y))
                {
                    return true;
                }

1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854
                if (lambda.Symbol.IsAsync)
                {
                    // Dig through Task<...> for an async lambda.
                    if (y.OriginalDefinition == Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T))
                    {
                        y = ((NamedTypeSymbol)y).TypeArgumentsNoUseSiteDiagnostics[0];
                    }
                    else
                    {
                        y = null;
                    }
                }
1855

1856
                if ((object)y != null)
P
Pilchie 已提交
1857
                {
1858 1859 1860
                    // - The body of E is an expression that exactly matches Y, or
                    //   has a return statement with expression and all return statements have expression that 
                    //   exactly matches Y.
1861

1862 1863 1864 1865 1866 1867 1868 1869
                    // Handle trivial cases first
                    switch (lambda.Body.Statements.Length)
                    {
                        case 0:
                            break;

                        case 1:
                            if (lambda.Body.Statements[0].Kind == BoundKind.ReturnStatement)
1870
                            {
1871 1872 1873 1874 1875 1876 1877 1878 1879
                                var returnStmt = (BoundReturnStatement)lambda.Body.Statements[0];
                                if (returnStmt.ExpressionOpt != null && ExpressionMatchExactly(returnStmt.ExpressionOpt, y, ref useSiteDiagnostics))
                                {
                                    return true;
                                }
                            }
                            else
                            {
                                goto default;
1880 1881
                            }

1882
                            break;
1883

1884 1885 1886
                        default:
                            var returnStatements = ArrayBuilder<BoundReturnStatement>.GetInstance();
                            var walker = new ReturnStatements(returnStatements);
1887

1888
                            walker.Visit(lambda.Body);
1889

1890 1891
                            bool result = false;
                            foreach (BoundReturnStatement r in returnStatements)
1892
                            {
1893 1894 1895 1896 1897 1898 1899 1900 1901
                                if (r.ExpressionOpt == null || !ExpressionMatchExactly(r.ExpressionOpt, y, ref useSiteDiagnostics))
                                {
                                    result = false;
                                    break;
                                }
                                else
                                {
                                    result = true;
                                }
1902 1903
                            }

1904
                            returnStatements.Free();
1905

1906 1907 1908 1909 1910 1911
                            if (result)
                            {
                                return true;
                            }
                            break;
                    }
P
Pilchie 已提交
1912 1913 1914 1915 1916 1917
                }
            }

            return false;
        }

1918 1919
        private class ReturnStatements : BoundTreeWalker
        {
1920
            private readonly ArrayBuilder<BoundReturnStatement> _returns;
1921 1922 1923

            public ReturnStatements(ArrayBuilder<BoundReturnStatement> returns)
            {
1924
                _returns = returns;
1925 1926
            }

1927
            public override BoundNode Visit(BoundNode node)
1928
            {
1929 1930 1931 1932 1933
                if (!(node is BoundExpression))
                {
                    return base.Visit(node);
                }

1934 1935 1936
                return null;
            }

1937 1938 1939 1940 1941
            protected override BoundExpression VisitExpressionWithoutStackGuard(BoundExpression node)
            {
                throw ExceptionUtilities.Unreachable;
            }

1942 1943
            public override BoundNode VisitReturnStatement(BoundReturnStatement node)
            {
1944
                _returns.Add(node);
1945 1946 1947 1948
                return null;
            }
        }

1949 1950 1951 1952 1953 1954 1955
        private BetterResult BetterConversionTarget(TypeSymbol type1, TypeSymbol type2, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            bool okToDowngradeToNeither;
            return BetterConversionTarget(null, type1, default(Conversion), type2, default(Conversion), ref useSiteDiagnostics, out okToDowngradeToNeither);
        }

        private BetterResult BetterConversionTarget(BoundExpression node, TypeSymbol type1, Conversion conv1, TypeSymbol type2, Conversion conv2, ref HashSet<DiagnosticInfo> useSiteDiagnostics, out bool okToDowngradeToNeither)
P
Pilchie 已提交
1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968
        {
            okToDowngradeToNeither = false;

            if (Conversions.HasIdentityConversion(type1, type2))
            {
                // Both types are the same type.
                return BetterResult.Neither;
            }

            // Given two different types T1 and T2, T1 is a better conversion target than T2 if no implicit conversion from T2 to T1 exists, 
            // and at least one of the following holds:
            bool type1ToType2 = Conversions.ClassifyImplicitConversion(type1, type2, ref useSiteDiagnostics).IsImplicit;
            bool type2ToType1 = Conversions.ClassifyImplicitConversion(type2, type1, ref useSiteDiagnostics).IsImplicit;
1969
            UnboundLambda lambdaOpt = node as UnboundLambda;
P
Pilchie 已提交
1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998

            if (type1ToType2)
            {
                if (type2ToType1)
                {
                    // An implicit conversion both ways.
                    return BetterResult.Neither;
                }

                // - An implicit conversion from T1 to T2 exists 
                okToDowngradeToNeither = lambdaOpt != null && CanDowngradeConversionFromLambdaToNeither(BetterResult.Left, lambdaOpt, type1, type2, ref useSiteDiagnostics, true);
                return BetterResult.Left;
            }
            else if (type2ToType1)
            {
                // - An implicit conversion from T1 to T2 exists 
                okToDowngradeToNeither = lambdaOpt != null && CanDowngradeConversionFromLambdaToNeither(BetterResult.Right, lambdaOpt, type1, type2, ref useSiteDiagnostics, true);
                return BetterResult.Right;
            }

            var task_T = Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T);

            if ((object)task_T != null)
            {
                if (type1.OriginalDefinition == task_T)
                {
                    if (type2.OriginalDefinition == task_T)
                    {
                        // - T1 is Task<S1>, T2 is Task<S2>, and S1 is a better conversion target than S2
1999
                        return BetterConversionTarget(((NamedTypeSymbol)type1).TypeArgumentsNoUseSiteDiagnostics[0],
P
Pilchie 已提交
2000
                                                      ((NamedTypeSymbol)type2).TypeArgumentsNoUseSiteDiagnostics[0],
2001
                                                      ref useSiteDiagnostics);
P
Pilchie 已提交
2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031
                    }

                    // A shortcut, Task<T> type cannot satisfy other rules.
                    return BetterResult.Neither;
                }
                else if (type2.OriginalDefinition == task_T)
                {
                    // A shortcut, Task<T> type cannot satisfy other rules.
                    return BetterResult.Neither;
                }
            }

            NamedTypeSymbol d1;

            if ((object)(d1 = type1.GetDelegateType()) != null)
            {
                NamedTypeSymbol d2;

                if ((object)(d2 = type2.GetDelegateType()) != null)
                {
                    // - T1 is either a delegate type D1 or an expression tree type Expression<D1>,
                    //   T2 is either a delegate type D2 or an expression tree type Expression<D2>,
                    //   D1 has a return type S1 and one of the following holds:
                    MethodSymbol invoke1 = d1.DelegateInvokeMethod;
                    MethodSymbol invoke2 = d2.DelegateInvokeMethod;

                    if ((object)invoke1 != null && (object)invoke2 != null)
                    {
                        TypeSymbol r1 = invoke1.ReturnType;
                        TypeSymbol r2 = invoke2.ReturnType;
2032
                        BetterResult delegateResult = BetterResult.Neither;
P
Pilchie 已提交
2033 2034 2035 2036 2037 2038

                        if (r1.SpecialType != SpecialType.System_Void)
                        {
                            if (r2.SpecialType == SpecialType.System_Void)
                            {
                                // - D2 is void returning
2039
                                delegateResult = BetterResult.Left;
P
Pilchie 已提交
2040 2041 2042 2043 2044
                            }
                        }
                        else if (r2.SpecialType != SpecialType.System_Void)
                        {
                            // - D2 is void returning
2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071
                            delegateResult = BetterResult.Right;
                        }

                        if (delegateResult == BetterResult.Neither)
                        {
                            //  - D2 has a return type S2, and S1 is a better conversion target than S2
                            delegateResult = BetterConversionTarget(r1, r2, ref useSiteDiagnostics);
                        }

                        // Downgrade result to Neither if conversion used by the winner isn't actually valid method group conversion.
                        // This is necessary to preserve compatibility, otherwise we might dismiss "worse", but trully applicable candidate
                        // based on a "better", but, in reality, erroneous one.
                        if (node?.Kind == BoundKind.MethodGroup)
                        {
                            var group = (BoundMethodGroup)node;

                            if (delegateResult == BetterResult.Left)
                            {
                                if (IsMethodGroupConversionIncompatibleWithDelegate(group, d1, conv1))
                                {
                                    return BetterResult.Neither;
                                }
                            }
                            else if (delegateResult == BetterResult.Right && IsMethodGroupConversionIncompatibleWithDelegate(group, d2, conv2))
                            {
                                return BetterResult.Neither;
                            }
P
Pilchie 已提交
2072 2073
                        }

2074
                        return delegateResult;
P
Pilchie 已提交
2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106
                    }
                }

                // A shortcut, a delegate or an expression tree cannot satisfy other rules.
                return BetterResult.Neither;
            }
            else if (type2.GetDelegateType() != null)
            {
                // A shortcut, a delegate or an expression tree cannot satisfy other rules.
                return BetterResult.Neither;
            }

            // -T1 is a signed integral type and T2 is an unsigned integral type.Specifically:
            //    - T1 is sbyte and T2 is byte, ushort, uint, or ulong
            //    - T1 is short and T2 is ushort, uint, or ulong
            //    - T1 is int and T2 is uint, or ulong
            //    - T1 is long and T2 is ulong
            if (IsSignedIntegralType(type1))
            {
                if (IsUnsignedIntegralType(type2))
                {
                    return BetterResult.Left;
                }
            }
            else if (IsUnsignedIntegralType(type1) && IsSignedIntegralType(type2))
            {
                return BetterResult.Right;
            }

            return BetterResult.Neither;
        }

2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119
        private bool IsMethodGroupConversionIncompatibleWithDelegate(BoundMethodGroup node, NamedTypeSymbol delegateType, Conversion conv)
        {
            if (conv.IsMethodGroup)
            {
                DiagnosticBag ignore = DiagnosticBag.GetInstance();
                bool result = !_binder.MethodGroupIsCompatibleWithDelegate(node.ReceiverOpt, conv.IsExtensionMethod, conv.Method, delegateType, Location.None, ignore);
                ignore.Free();
                return result;
            }

            return false;
        }

P
Pilchie 已提交
2120 2121 2122 2123
        private bool CanDowngradeConversionFromLambdaToNeither(BetterResult currentResult, UnboundLambda lambda, TypeSymbol type1, TypeSymbol type2, ref HashSet<DiagnosticInfo> useSiteDiagnostics, bool fromTypeAnalysis)
        {
            // DELIBERATE SPEC VIOLATION: See bug 11961.
            // The native compiler uses one algorithm for determining betterness of lambdas and another one
2124
            // for everything else. This is wrong; the correct behavior is to do the type analysis of
P
Pilchie 已提交
2125 2126 2127 2128
            // the parameter types first, and then if necessary, do the lambda analysis. Native compiler
            // skips analysis of the parameter types when they are delegate types with identical parameter
            // lists and the corresponding argument is a lambda. 
            // There is a real-world code that breaks if we follow the specification, so we will try to fall
2129
            // back to the original behavior to avoid an ambiguity that wasn't an ambiguity before.
P
Pilchie 已提交
2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192

            NamedTypeSymbol d1;

            if ((object)(d1 = type1.GetDelegateType()) != null)
            {
                NamedTypeSymbol d2;

                if ((object)(d2 = type2.GetDelegateType()) != null)
                {
                    MethodSymbol invoke1 = d1.DelegateInvokeMethod;
                    MethodSymbol invoke2 = d2.DelegateInvokeMethod;

                    if ((object)invoke1 != null && (object)invoke2 != null)
                    {
                        if (!IdenticalParameters(invoke1.Parameters, invoke2.Parameters))
                        {
                            return true;
                        }

                        TypeSymbol r1 = invoke1.ReturnType;
                        TypeSymbol r2 = invoke2.ReturnType;

#if DEBUG
                        if (fromTypeAnalysis)
                        {
                            Debug.Assert((r1.SpecialType == SpecialType.System_Void) == (r2.SpecialType == SpecialType.System_Void));

                            // Since we are dealing with variance delegate conversion and delegates have identical parameter
                            // lists, return types must be different and neither can be void.
                            Debug.Assert(r1.SpecialType != SpecialType.System_Void);
                            Debug.Assert(r2.SpecialType != SpecialType.System_Void);
                            Debug.Assert(!Conversions.HasIdentityConversion(r1, r2));
                        }
#endif 

                        if (r1.SpecialType == SpecialType.System_Void)
                        {
                            if (r2.SpecialType == SpecialType.System_Void)
                            {
                                return true;
                            }

                            Debug.Assert(currentResult == BetterResult.Right);
                            return false;
                        }
                        else if (r2.SpecialType == SpecialType.System_Void)
                        {
                            Debug.Assert(currentResult == BetterResult.Left);
                            return false;
                        }

                        if (Conversions.HasIdentityConversion(r1, r2))
                        {
                            return true;
                        }

                        TypeSymbol x = lambda.InferReturnType(d1, ref useSiteDiagnostics);
                        if (x == null)
                        {
                            return true;
                        }

#if DEBUG
2193 2194 2195
                        if (fromTypeAnalysis)
                        {
                            // Since we are dealing with variance delegate conversion and delegates have identical parameter
C
Charles Stoner 已提交
2196
                            // lists, return types must be implicitly convertible in the same direction.
2197
                            // Or we might be dealing with error return types and we may have one error delegate matching exactly
C
Charles Stoner 已提交
2198
                            // while another not being an error and not convertible.
2199 2200 2201
                            Debug.Assert(
                                r1.IsErrorType() ||
                                r2.IsErrorType() ||
2202
                                currentResult == BetterConversionTarget(r1, r2, ref useSiteDiagnostics));
2203
                        }
P
Pilchie 已提交
2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376
#endif
                    }
                }
            }

            return false;
        }

        private static bool IdenticalParameters(ImmutableArray<ParameterSymbol> p1, ImmutableArray<ParameterSymbol> p2)
        {
            if (p1.IsDefault || p2.IsDefault)
            {
                // This only happens in error scenarios.
                return false;
            }

            if (p1.Length != p2.Length)
            {
                return false;
            }

            for (int i = 0; i < p1.Length; ++i)
            {
                var param1 = p1[i];
                var param2 = p2[i];

                if (param1.RefKind != param2.RefKind)
                {
                    return false;
                }

                if (!Conversions.HasIdentityConversion(param1.Type, param2.Type))
                {
                    return false;
                }
            }

            return true;
        }

        private static bool IsSignedIntegralType(TypeSymbol type)
        {
            if ((object)type != null && type.IsNullableType())
            {
                type = type.GetNullableUnderlyingType();
            }

            switch (type.GetSpecialTypeSafe())
            {
                case SpecialType.System_SByte:
                case SpecialType.System_Int16:
                case SpecialType.System_Int32:
                case SpecialType.System_Int64:
                    return true;

                default:
                    return false;
            }
        }

        private static bool IsUnsignedIntegralType(TypeSymbol type)
        {
            if ((object)type != null && type.IsNullableType())
            {
                type = type.GetNullableUnderlyingType();
            }

            switch (type.GetSpecialTypeSafe())
            {
                case SpecialType.System_Byte:
                case SpecialType.System_UInt16:
                case SpecialType.System_UInt32:
                case SpecialType.System_UInt64:
                    return true;

                default:
                    return false;
            }
        }

        private struct EffectiveParameters
        {
            internal readonly ImmutableArray<TypeSymbol> ParameterTypes;
            internal readonly ImmutableArray<RefKind> ParameterRefKinds;

            internal EffectiveParameters(ImmutableArray<TypeSymbol> types, ImmutableArray<RefKind> refKinds)
            {
                ParameterTypes = types;
                ParameterRefKinds = refKinds;
            }
        }

        private EffectiveParameters GetEffectiveParametersInNormalForm<TMember>(
            TMember member,
            int argumentCount,
            ImmutableArray<int> argToParamMap,
            ArrayBuilder<RefKind> argumentRefKinds,
            bool allowRefOmittedArguments)
            where TMember : Symbol
        {
            bool discarded;
            return GetEffectiveParametersInNormalForm(member, argumentCount, argToParamMap, argumentRefKinds, allowRefOmittedArguments, hasAnyRefOmittedArgument: out discarded);
        }

        private EffectiveParameters GetEffectiveParametersInNormalForm<TMember>(
            TMember member,
            int argumentCount,
            ImmutableArray<int> argToParamMap,
            ArrayBuilder<RefKind> argumentRefKinds,
            bool allowRefOmittedArguments,
            out bool hasAnyRefOmittedArgument) where TMember : Symbol
        {
            Debug.Assert(argumentRefKinds != null);

            hasAnyRefOmittedArgument = false;
            ImmutableArray<ParameterSymbol> parameters = member.GetParameters();

            // We simulate an extra parameter for vararg methods
            int parameterCount = member.GetParameterCount() + (member.GetIsVararg() ? 1 : 0);

            if (argumentCount == parameterCount && argToParamMap.IsDefaultOrEmpty)
            {
                ImmutableArray<RefKind> parameterRefKinds = member.GetParameterRefKinds();
                if (parameterRefKinds.IsDefaultOrEmpty || !parameterRefKinds.Any(refKind => refKind == RefKind.Ref))
                {
                    return new EffectiveParameters(member.GetParameterTypes(), parameterRefKinds);
                }
            }

            var types = ArrayBuilder<TypeSymbol>.GetInstance();
            ArrayBuilder<RefKind> refs = null;
            bool hasAnyRefArg = argumentRefKinds.Any();

            for (int arg = 0; arg < argumentCount; ++arg)
            {
                int parm = argToParamMap.IsDefault ? arg : argToParamMap[arg];
                // If this is the __arglist parameter, just skip it.
                if (parm == parameters.Length)
                {
                    continue;
                }
                var parameter = parameters[parm];
                types.Add(parameter.Type);

                RefKind argRefKind = hasAnyRefArg ? argumentRefKinds[arg] : RefKind.None;
                RefKind paramRefKind = GetEffectiveParameterRefKind(parameter, argRefKind, allowRefOmittedArguments, ref hasAnyRefOmittedArgument);

                if (refs == null)
                {
                    if (paramRefKind != RefKind.None)
                    {
                        refs = ArrayBuilder<RefKind>.GetInstance(arg, RefKind.None);
                        refs.Add(paramRefKind);
                    }
                }
                else
                {
                    refs.Add(paramRefKind);
                }
            }

            var refKinds = refs != null ? refs.ToImmutableAndFree() : default(ImmutableArray<RefKind>);
            return new EffectiveParameters(types.ToImmutableAndFree(), refKinds);
        }

        private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind argRefKind, bool allowRefOmittedArguments, ref bool hasAnyRefOmittedArgument)
        {
            var paramRefKind = parameter.RefKind;

            // Omit ref feature for COM interop: We can pass arguments by value for ref parameters if we are calling a method/property on an instance of a COM imported type.
            // We must ignore the 'ref' on the parameter while determining the applicability of argument for the given method call.
            // During argument rewriting, we will replace the argument value with a temporary local and pass that local by reference.

2377
            if (allowRefOmittedArguments && paramRefKind == RefKind.Ref && argRefKind == RefKind.None && !_binder.InAttributeArgument)
P
Pilchie 已提交
2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580
            {
                hasAnyRefOmittedArgument = true;
                return RefKind.None;
            }

            return paramRefKind;
        }

        private EffectiveParameters GetEffectiveParametersInExpandedForm<TMember>(
            TMember member,
            int argumentCount,
            ImmutableArray<int> argToParamMap,
            ArrayBuilder<RefKind> argumentRefKinds,
            bool allowRefOmittedArguments) where TMember : Symbol
        {
            bool discarded;
            return GetEffectiveParametersInExpandedForm(member, argumentCount, argToParamMap, argumentRefKinds, allowRefOmittedArguments, hasAnyRefOmittedArgument: out discarded);
        }

        private EffectiveParameters GetEffectiveParametersInExpandedForm<TMember>(
            TMember member,
            int argumentCount,
            ImmutableArray<int> argToParamMap,
            ArrayBuilder<RefKind> argumentRefKinds,
            bool allowRefOmittedArguments,
            out bool hasAnyRefOmittedArgument) where TMember : Symbol
        {
            Debug.Assert(argumentRefKinds != null);

            var types = ArrayBuilder<TypeSymbol>.GetInstance();
            var refs = ArrayBuilder<RefKind>.GetInstance();
            bool anyRef = false;
            var parameters = member.GetParameters();
            var elementType = ((ArrayTypeSymbol)parameters[parameters.Length - 1].Type).ElementType;
            bool hasAnyRefArg = argumentRefKinds.Any();
            hasAnyRefOmittedArgument = false;

            for (int arg = 0; arg < argumentCount; ++arg)
            {
                var parm = argToParamMap.IsDefault ? arg : argToParamMap[arg];
                var parameter = parameters[parm];
                types.Add(parm == parameters.Length - 1 ? elementType : parameter.Type);

                var argRefKind = hasAnyRefArg ? argumentRefKinds[arg] : RefKind.None;
                var paramRefKind = GetEffectiveParameterRefKind(parameter, argRefKind, allowRefOmittedArguments, ref hasAnyRefOmittedArgument);

                refs.Add(paramRefKind);
                if (paramRefKind != RefKind.None)
                {
                    anyRef = true;
                }
            }

            var refKinds = anyRef ? refs.ToImmutable() : default(ImmutableArray<RefKind>);
            refs.Free();
            return new EffectiveParameters(types.ToImmutableAndFree(), refKinds);
        }

        internal MemberResolutionResult<TMember> IsMemberApplicableInNormalForm<TMember>(
            TMember member,                // method or property
            TMember leastOverriddenMember, // method or property
            ArrayBuilder<TypeSymbol> typeArguments,
            AnalyzedArguments arguments,
            bool isMethodGroupConversion,
            bool allowRefOmittedArguments,
            bool inferWithDynamic,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
            where TMember : Symbol
        {
            // AnalyzeArguments matches arguments to parameter names and positions. 
            // For that purpose we use the most derived member.
            var argumentAnalysis = AnalyzeArguments(member, arguments, isMethodGroupConversion, expanded: false);
            if (!argumentAnalysis.IsValid)
            {
                return new MemberResolutionResult<TMember>(member, leastOverriddenMember, MemberAnalysisResult.ArgumentParameterMismatch(argumentAnalysis));
            }

            // Check after argument analysis, but before more complicated type inference and argument type validation.
            // NOTE: The diagnostic may not be reported (e.g. if the member is later removed as less-derived).
            if (member.HasUseSiteError)
            {
                return new MemberResolutionResult<TMember>(member, leastOverriddenMember, MemberAnalysisResult.UseSiteError());
            }

            bool hasAnyRefOmittedArgument;

            // To determine parameter types we use the originalMember.
            EffectiveParameters originalEffectiveParameters = GetEffectiveParametersInNormalForm(
                GetConstructedFrom(leastOverriddenMember),
                arguments.Arguments.Count,
                argumentAnalysis.ArgsToParamsOpt,
                arguments.RefKinds,
                allowRefOmittedArguments,
                out hasAnyRefOmittedArgument);

            Debug.Assert(!hasAnyRefOmittedArgument || allowRefOmittedArguments);

            // To determine parameter types we use the originalMember.
            EffectiveParameters constructedEffectiveParameters = GetEffectiveParametersInNormalForm(
                leastOverriddenMember,
                arguments.Arguments.Count,
                argumentAnalysis.ArgsToParamsOpt,
                arguments.RefKinds,
                allowRefOmittedArguments);

            // The member passed to the following call is returned in the result (possibly a constructed version of it).
            // The applicability is checked based on effective parameters passed in.
            return IsApplicable(
                member, leastOverriddenMember,
                typeArguments, arguments, originalEffectiveParameters, constructedEffectiveParameters,
                argumentAnalysis.ArgsToParamsOpt, hasAnyRefOmittedArgument,
                ref useSiteDiagnostics,
                inferWithDynamic);
        }

        private MemberResolutionResult<TMember> IsMemberApplicableInExpandedForm<TMember>(
            TMember member,                // method or property
            TMember leastOverriddenMember, // method or property
            ArrayBuilder<TypeSymbol> typeArguments,
            AnalyzedArguments arguments,
            bool allowRefOmittedArguments,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
            where TMember : Symbol
        {
            // AnalyzeArguments matches arguments to parameter names and positions. 
            // For that purpose we use the most derived member.
            var argumentAnalysis = AnalyzeArguments(member, arguments, isMethodGroupConversion: false, expanded: true);
            if (!argumentAnalysis.IsValid)
            {
                return new MemberResolutionResult<TMember>(member, leastOverriddenMember, MemberAnalysisResult.ArgumentParameterMismatch(argumentAnalysis));
            }

            // Check after argument analysis, but before more complicated type inference and argument type validation.
            // NOTE: The diagnostic may not be reported (e.g. if the member is later removed as less-derived).
            if (member.HasUseSiteError)
            {
                return new MemberResolutionResult<TMember>(member, leastOverriddenMember, MemberAnalysisResult.UseSiteError());
            }

            bool hasAnyRefOmittedArgument;

            // To determine parameter types we use the least derived member.
            EffectiveParameters originalEffectiveParameters = GetEffectiveParametersInExpandedForm(
                GetConstructedFrom(leastOverriddenMember),
                arguments.Arguments.Count,
                argumentAnalysis.ArgsToParamsOpt,
                arguments.RefKinds,
                allowRefOmittedArguments,
                out hasAnyRefOmittedArgument);

            Debug.Assert(!hasAnyRefOmittedArgument || allowRefOmittedArguments);

            // To determine parameter types we use the least derived member.
            EffectiveParameters constructedEffectiveParameters = GetEffectiveParametersInExpandedForm(
                leastOverriddenMember,
                arguments.Arguments.Count,
                argumentAnalysis.ArgsToParamsOpt,
                arguments.RefKinds,
                allowRefOmittedArguments);

            // The member passed to the following call is returned in the result (possibly a constructed version of it).
            // The applicability is checked based on effective parameters passed in.
            var result = IsApplicable(
                member, leastOverriddenMember,
                typeArguments, arguments, originalEffectiveParameters, constructedEffectiveParameters,
                argumentAnalysis.ArgsToParamsOpt, hasAnyRefOmittedArgument, ref useSiteDiagnostics);

            return result.Result.IsValid ?
                new MemberResolutionResult<TMember>(
                    result.Member,
                    result.LeastOverriddenMember,
                    MemberAnalysisResult.ExpandedForm(result.Result.ArgsToParamsOpt, result.Result.ConversionsOpt, hasAnyRefOmittedArgument)) :
                result;
        }

        private MemberResolutionResult<TMember> IsApplicable<TMember>(
            TMember member,                // method or property
            TMember leastOverriddenMember, // method or property 
            ArrayBuilder<TypeSymbol> typeArgumentsBuilder,
            AnalyzedArguments arguments,
            EffectiveParameters originalEffectiveParameters,
            EffectiveParameters constructedEffectiveParameters,
            ImmutableArray<int> argsToParamsMap,
            bool hasAnyRefOmittedArgument,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics,
            bool inferWithDynamic = false)
            where TMember : Symbol
        {
            bool ignoreOpenTypes;
            MethodSymbol method;
            EffectiveParameters effectiveParameters;
            if (member.Kind == SymbolKind.Method && (method = (MethodSymbol)(Symbol)member).Arity > 0)
            {
                if (typeArgumentsBuilder.Count == 0 && arguments.HasDynamicArgument && !inferWithDynamic)
                {
                    // Spec 7.5.4: Compile-time checking of dynamic overload resolution:
                    // * First, if F is a generic method and type arguments were provided, 
                    //   then those are substituted for the type parameters in the parameter list. 
                    //   However, if type arguments were not provided, no such substitution happens.
                    // * Then, any parameter whose type contains a an unsubstituted type parameter of F 
                    //   is elided, along with the corresponding arguments(s).

                    // We don't need to check constraints of types of the non-elided parameters since they 
C
Charles Stoner 已提交
2581
                    // have no effect on applicability of this candidate.
P
Pilchie 已提交
2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648
                    ignoreOpenTypes = true;
                    effectiveParameters = constructedEffectiveParameters;
                }
                else
                {
                    MethodSymbol leastOverriddenMethod = (MethodSymbol)(Symbol)leastOverriddenMember;

                    ImmutableArray<TypeSymbol> typeArguments;
                    if (typeArgumentsBuilder.Count > 0)
                    {
                        // generic type arguments explicitly specified at call-site:
                        typeArguments = typeArgumentsBuilder.ToImmutable();
                    }
                    else
                    {
                        // infer generic type arguments:
                        MemberAnalysisResult inferenceError;
                        typeArguments = InferMethodTypeArguments(method, leastOverriddenMethod.ConstructedFrom.TypeParameters, arguments, originalEffectiveParameters, out inferenceError, ref useSiteDiagnostics);
                        if (typeArguments.IsDefault)
                        {
                            return new MemberResolutionResult<TMember>(member, leastOverriddenMember, inferenceError);
                        }
                    }

                    member = (TMember)(Symbol)method.Construct(typeArguments);
                    leastOverriddenMember = (TMember)(Symbol)leastOverriddenMethod.ConstructedFrom.Construct(typeArguments);

                    // Spec (§7.6.5.1)
                    //   Once the (inferred) type arguments are substituted for the corresponding method type parameters, 
                    //   all constructed types in the parameter list of F satisfy *their* constraints (§4.4.4), 
                    //   and the parameter list of F is applicable with respect to A (§7.5.3.1).
                    //
                    // This rule is a bit complicated; let's take a look at an example. Suppose we have
                    // class X<U> where U : struct {}
                    // ...
                    // void M<T>(T t, X<T> xt) where T : struct {}
                    // void M(object o1, object o2) {}
                    //
                    // Suppose there is a call M("", null). Type inference infers that T is string.
                    // M<string> is then not an applicable candidate *NOT* because string violates the
                    // constraint on T. That is not checked until "final validation". Rather, the 
                    // method is not a candidate because string violates the constraint *on U*. 
                    // The constructed method has formal parameter type X<string>, which is not legal.
                    // In the case given, the generic method is eliminated and the object version wins.
                    //
                    // Note also that the constraints need to be checked on *all* the formal parameter
                    // types, not just the ones in the *effective parameter list*. If we had:
                    // void M<T>(T t, X<T> xt = null) where T : struct {}
                    // void M<T>(object o1, object o2 = null) where T : struct {}
                    // and a call M("") then type inference still works out that T is string, and
                    // the generic method still needs to be discarded, even though type inference
                    // never saw the second formal parameter.

                    ImmutableArray<TypeSymbol> parameterTypes = leastOverriddenMember.GetParameterTypes();
                    for (int i = 0; i < parameterTypes.Length; i++)
                    {
                        if (!parameterTypes[i].CheckAllConstraints(Conversions))
                        {
                            return new MemberResolutionResult<TMember>(member, leastOverriddenMember, MemberAnalysisResult.ConstructedParameterFailedConstraintsCheck(i));
                        }
                    }

                    // Types of constructed effective parameters might originate from a virtual/abstract method 
                    // that the current "method" overrides. If the virtual/abstract method is generic we constructed it 
                    // using the generic parameters of "method", so we can now substitute these type parameters 
                    // in the constructed effective parameters.

2649
                    var map = new TypeMap(method.TypeParameters, typeArguments.SelectAsArray(TypeMap.TypeSymbolAsTypeWithModifiers), allowAlpha: true);
P
Pilchie 已提交
2650 2651

                    effectiveParameters = new EffectiveParameters(
2652
                        map.SubstituteTypesWithoutModifiers(constructedEffectiveParameters.ParameterTypes),
P
Pilchie 已提交
2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691
                        constructedEffectiveParameters.ParameterRefKinds);

                    ignoreOpenTypes = false;
                }
            }
            else
            {
                effectiveParameters = constructedEffectiveParameters;
                ignoreOpenTypes = false;
            }

            return new MemberResolutionResult<TMember>(
                member,
                leastOverriddenMember,
                IsApplicable(member, effectiveParameters, arguments, argsToParamsMap, member.GetIsVararg(), hasAnyRefOmittedArgument, ignoreOpenTypes, ref useSiteDiagnostics));
        }

        private ImmutableArray<TypeSymbol> InferMethodTypeArguments(
            MethodSymbol method,
            ImmutableArray<TypeParameterSymbol> originalTypeParameters,
            AnalyzedArguments arguments,
            EffectiveParameters originalEffectiveParameters,
            out MemberAnalysisResult error,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            var argumentTypes = ArrayBuilder<TypeSymbol>.GetInstance();
            for (int arg = 0; arg < arguments.Arguments.Count; arg++)
            {
                argumentTypes.Add(arguments.Argument(arg).Type);
            }

            var args = arguments.Arguments.ToImmutable();

            // The reason why we pass the type parameters and formal parameter types
            // from the original definition, not the method as it exists as a member of 
            // a possibly constructed generic type, is exceedingly subtle. See the comments
            // in "Infer" for details.

            var inferenceResult = MethodTypeInferrer.Infer(
2692
                _binder,
P
Pilchie 已提交
2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708
                originalTypeParameters,
                method.ContainingType,
                originalEffectiveParameters.ParameterTypes,
                originalEffectiveParameters.ParameterRefKinds,
                argumentTypes.ToImmutableAndFree(),
                args,
                ref useSiteDiagnostics);

            if (inferenceResult.Success)
            {
                error = default(MemberAnalysisResult);
                return inferenceResult.InferredTypeArguments;
            }

            if (arguments.IsExtensionMethodInvocation)
            {
2709
                var inferredFromFirstArgument = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument(_binder.Conversions, method, originalEffectiveParameters.ParameterTypes, args, ref useSiteDiagnostics);
P
Pilchie 已提交
2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798
                if (inferredFromFirstArgument.IsDefault)
                {
                    error = MemberAnalysisResult.TypeInferenceExtensionInstanceArgumentFailed();
                    return default(ImmutableArray<TypeSymbol>);
                }
            }

            error = MemberAnalysisResult.TypeInferenceFailed();
            return default(ImmutableArray<TypeSymbol>);
        }

        private MemberAnalysisResult IsApplicable(
            Symbol candidate, // method or property
            EffectiveParameters parameters,
            AnalyzedArguments arguments,
            ImmutableArray<int> argsToParameters,
            bool isVararg,
            bool hasAnyRefOmittedArgument,
            bool ignoreOpenTypes,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            // The effective parameters are in the right order with respect to the arguments.
            //
            // The difference between "parameters" and "original parameters" is as follows. Suppose
            // we have class C<V> { static void M<T, U>(T t, U u, V v) { C<T>.M(1, t, t); } }
            // In the call, the "original parameters" are (T, U, V). The "constructed parameters",
            // not passed in here, are (T, U, T) because T is substituted for V; type inference then
            // infers that T is int and U is T.  The "parameters" are therefore (int, T, T).
            //
            // We add a "virtual parameter" for the __arglist.
            int paramCount = parameters.ParameterTypes.Length + (isVararg ? 1 : 0);

            Debug.Assert(paramCount == arguments.Arguments.Count);

            // For each argument in A, the parameter passing mode of the argument (i.e., value, ref, or out) is 
            // identical to the parameter passing mode of the corresponding parameter, and
            // * for a value parameter or a parameter array, an implicit conversion exists from the 
            //   argument to the type of the corresponding parameter, or
            // * for a ref or out parameter, the type of the argument is identical to the type of the corresponding 
            //   parameter. After all, a ref or out parameter is an alias for the argument passed.
            ArrayBuilder<Conversion> conversions = null;
            ArrayBuilder<int> badArguments = null;
            for (int argumentPosition = 0; argumentPosition < paramCount; argumentPosition++)
            {
                BoundExpression argument = arguments.Argument(argumentPosition);
                RefKind argumentRefKind = arguments.RefKind(argumentPosition);
                Conversion conversion;

                if (isVararg && argumentPosition == paramCount - 1)
                {
                    // Only an __arglist() expression is convertible.
                    if (argument.Kind == BoundKind.ArgListOperator)
                    {
                        conversion = Conversion.Identity;
                    }
                    else
                    {
                        badArguments = badArguments ?? ArrayBuilder<int>.GetInstance();
                        badArguments.Add(argumentPosition);
                        conversion = Conversion.NoConversion;
                    }
                }
                else
                {
                    RefKind parameterRefKind = parameters.ParameterRefKinds.IsDefault ? RefKind.None : parameters.ParameterRefKinds[argumentPosition];
                    conversion = CheckArgumentForApplicability(candidate, argument, argumentRefKind, parameters.ParameterTypes[argumentPosition], parameterRefKind, ignoreOpenTypes, ref useSiteDiagnostics);

                    if (!conversion.Exists ||
                        (arguments.IsExtensionMethodThisArgument(argumentPosition) && !Conversions.IsValidExtensionMethodThisArgConversion(conversion)))
                    {
                        badArguments = badArguments ?? ArrayBuilder<int>.GetInstance();
                        badArguments.Add(argumentPosition);
                    }
                }

                if (conversions != null)
                {
                    conversions.Add(conversion);
                }
                else if (!conversion.IsIdentity)
                {
                    conversions = ArrayBuilder<Conversion>.GetInstance(paramCount);
                    conversions.AddMany(Conversion.Identity, argumentPosition);
                    conversions.Add(conversion);
                }
            }

            MemberAnalysisResult result;
            var conversionsArray = conversions != null ? conversions.ToImmutableAndFree() : default(ImmutableArray<Conversion>);
2799
            if (badArguments != null)
P
Pilchie 已提交
2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876
            {
                result = MemberAnalysisResult.BadArgumentConversions(argsToParameters, badArguments.ToImmutableAndFree(), conversionsArray);
            }
            else
            {
                result = MemberAnalysisResult.NormalForm(argsToParameters, conversionsArray, hasAnyRefOmittedArgument);
            }

            return result;
        }

        private Conversion CheckArgumentForApplicability(
            Symbol candidate, // method or property
            BoundExpression argument,
            RefKind argRefKind,
            TypeSymbol parameterType,
            RefKind parRefKind,
            bool ignoreOpenTypes,
            ref HashSet<DiagnosticInfo> useSiteDiagnostics)
        {
            // Spec 7.5.3.1
            // For each argument in A, the parameter passing mode of the argument (i.e., value, ref, or out) is identical
            // to the parameter passing mode of the corresponding parameter, and
            // - for a value parameter or a parameter array, an implicit conversion (§6.1)
            //   exists from the argument to the type of the corresponding parameter, or
            // - for a ref or out parameter, the type of the argument is identical to the type of the corresponding parameter. 

            // RefKind has to match unless the ref kind is None and argument expression is of the type dynamic. This is a bug in Dev11 which we also implement. 
            // The spec is correct, this is not an intended behavior. We don't fix the bug to avoid a breaking change.
            if (argRefKind != parRefKind && !(argRefKind == RefKind.None && argument.HasDynamicType()))
            {
                return Conversion.NoConversion;
            }

            // TODO (tomat): the spec wording isn't final yet

            // Spec 7.5.4: Compile-time checking of dynamic overload resolution:
            // - Then, any parameter whose type is open (i.e. contains a type parameter; see §4.4.2) is elided, along with its corresponding parameter(s).
            // and
            // - The modified parameter list for F is applicable to the modified argument list in terms of section §7.5.3.1
            if (ignoreOpenTypes && parameterType.ContainsTypeParameter(parameterContainer: (MethodSymbol)candidate))
            {
                // defer applicability check to runtime:
                return Conversion.ImplicitDynamic;
            }

            if (argRefKind == RefKind.None)
            {
                var conversion = Conversions.ClassifyImplicitConversionFromExpression(argument, parameterType, ref useSiteDiagnostics);
                Debug.Assert((!conversion.Exists) || conversion.IsImplicit, "ClassifyImplicitConversion should only return implicit conversions");
                return conversion;
            }

            var argType = argument.Type;
            if ((object)argType != null && Conversions.HasIdentityConversion(argType, parameterType))
            {
                return Conversion.Identity;
            }
            else
            {
                return Conversion.NoConversion;
            }
        }

        private static TMember GetConstructedFrom<TMember>(TMember member) where TMember : Symbol
        {
            switch (member.Kind)
            {
                case SymbolKind.Property:
                    return member;
                case SymbolKind.Method:
                    return (TMember)(Symbol)(member as MethodSymbol).ConstructedFrom;
                default:
                    throw ExceptionUtilities.UnexpectedValue(member.Kind);
            }
        }
    }
2877
}