From 4f19a0ae69fab9c4ab12b877aefc00b790c7009b Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Mon, 13 Feb 2017 10:57:09 -0800 Subject: [PATCH] Return after first non-convertible argument in `OverloadResolution.IsApplicable` --- .../Portable/Binder/Binder_Expressions.cs | 4 +- .../Portable/Binder/Binder_Invocation.cs | 25 +++- .../OverloadResolution/OverloadResolution.cs | 119 ++++++++++++--- .../CSharpCompilerSemanticTest.csproj | 1 + .../Test/Semantic/Semantics/LambdaTests.cs | 76 ++++++++++ .../Semantics/OverloadResolutionPerfTests.cs | 136 ++++++++++++++++++ .../Semantics/OverloadResolutionTests.cs | 95 ++++++++++-- .../Portable/Binding/Binder_Symbols.vb | 83 ----------- 8 files changed, 414 insertions(+), 125 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 4e589e9d057..c8324fcac34 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -1496,7 +1496,7 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Diag case SymbolKind.Alias: { - var alias = symbol as AliasSymbol; + var alias = (AliasSymbol)symbol; symbol = alias.Target; switch (symbol.Kind) { @@ -1511,7 +1511,7 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Diag } case SymbolKind.RangeVariable: - return BindRangeVariable(node, symbol as RangeVariableSymbol, diagnostics); + return BindRangeVariable(node, (RangeVariableSymbol)symbol, diagnostics); default: throw ExceptionUtilities.UnexpectedValue(symbol.Kind); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 2b9042d7753..f49bf25e3b1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -1127,12 +1127,24 @@ private static bool IsUnboundGeneric(MethodSymbol method) return method.IsGenericMethod && method.ConstructedFrom() == method; } + // Arbitrary limit on the number of parameter lists from overload + // resolution candidates considered when binding argument types. + // Any additional parameter lists are ignored. + internal const int MaxParameterListsForErrorRecovery = 10; + private ImmutableArray BuildArgumentsForErrorRecovery(AnalyzedArguments analyzedArguments, ImmutableArray methods) { var parameterListList = ArrayBuilder>.GetInstance(); foreach (var m in methods) { - if (!IsUnboundGeneric(m) && m.ParameterCount > 0) parameterListList.Add(m.Parameters); + if (!IsUnboundGeneric(m) && m.ParameterCount > 0) + { + parameterListList.Add(m.Parameters); + if (parameterListList.Count == MaxParameterListsForErrorRecovery) + { + break; + } + } } var result = BuildArgumentsForErrorRecovery(analyzedArguments, parameterListList); @@ -1143,9 +1155,16 @@ private ImmutableArray BuildArgumentsForErrorRecovery(AnalyzedA private ImmutableArray BuildArgumentsForErrorRecovery(AnalyzedArguments analyzedArguments, ImmutableArray properties) { var parameterListList = ArrayBuilder>.GetInstance(); - foreach (var m in properties) + foreach (var p in properties) { - if (m.ParameterCount > 0) parameterListList.Add(m.Parameters); + if (p.ParameterCount > 0) + { + parameterListList.Add(p.Parameters); + if (parameterListList.Count == MaxParameterListsForErrorRecovery) + { + break; + } + } } var result = BuildArgumentsForErrorRecovery(analyzedArguments, parameterListList); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 502d97a271a..f665b5dcdb7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -312,13 +312,13 @@ private static bool OverloadResolutionResultIsValid(ArrayBuilder(ArrayBuilder useSiteDiagnostics) + private MemberAnalysisResult IsConstructorApplicableInNormalForm( + MethodSymbol constructor, + AnalyzedArguments arguments, + bool completeResults, + ref HashSet useSiteDiagnostics) { var argumentAnalysis = AnalyzeArguments(constructor, arguments, isMethodGroupConversion: false, expanded: false); // Constructors are never involved in method group conversion. if (!argumentAnalysis.IsValid) @@ -349,10 +353,23 @@ private MemberAnalysisResult IsConstructorApplicableInNormalForm(MethodSymbol co 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); + return IsApplicable( + constructor, + effectiveParameters, + arguments, + argumentAnalysis.ArgsToParamsOpt, + isVararg: constructor.IsVararg, + hasAnyRefOmittedArgument: false, + ignoreOpenTypes: false, + completeResults: completeResults, + useSiteDiagnostics: ref useSiteDiagnostics); } - private MemberAnalysisResult IsConstructorApplicableInExpandedForm(MethodSymbol constructor, AnalyzedArguments arguments, ref HashSet useSiteDiagnostics) + private MemberAnalysisResult IsConstructorApplicableInExpandedForm( + MethodSymbol constructor, + AnalyzedArguments arguments, + bool completeResults, + ref HashSet useSiteDiagnostics) { var argumentAnalysis = AnalyzeArguments(constructor, arguments, isMethodGroupConversion: false, expanded: true); if (!argumentAnalysis.IsValid) @@ -371,7 +388,16 @@ private MemberAnalysisResult IsConstructorApplicableInExpandedForm(MethodSymbol // 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); + var result = IsApplicable( + constructor, + effectiveParameters, + arguments, + argumentAnalysis.ArgsToParamsOpt, + isVararg: false, + hasAnyRefOmittedArgument: false, + ignoreOpenTypes: false, + completeResults: completeResults, + useSiteDiagnostics: ref useSiteDiagnostics); return result.IsValid ? MemberAnalysisResult.ExpandedForm(result.ArgsToParamsOpt, result.ConversionsOpt, hasAnyRefOmittedArgument: false) : result; } @@ -492,7 +518,16 @@ private MemberAnalysisResult IsConstructorApplicableInExpandedForm(MethodSymbol // Second, we need to determine if the method is applicable in its normal form or its expanded form. var normalResult = (allowUnexpandedForm || !IsValidParams(leastOverriddenMember)) - ? IsMemberApplicableInNormalForm(member, leastOverriddenMember, typeArguments, arguments, isMethodGroupConversion, allowRefOmittedArguments, inferWithDynamic, ref useSiteDiagnostics, completeResults: completeResults) + ? IsMemberApplicableInNormalForm( + member, + leastOverriddenMember, + typeArguments, + arguments, + isMethodGroupConversion: isMethodGroupConversion, + allowRefOmittedArguments: allowRefOmittedArguments, + inferWithDynamic: inferWithDynamic, + completeResults: completeResults, + useSiteDiagnostics: ref useSiteDiagnostics) : default(MemberResolutionResult); var result = normalResult; @@ -505,7 +540,14 @@ private MemberAnalysisResult IsConstructorApplicableInExpandedForm(MethodSymbol if (!isMethodGroupConversion && IsValidParams(leastOverriddenMember)) { - var expandedResult = IsMemberApplicableInExpandedForm(member, leastOverriddenMember, typeArguments, arguments, allowRefOmittedArguments, ref useSiteDiagnostics); + var expandedResult = IsMemberApplicableInExpandedForm( + member, + leastOverriddenMember, + typeArguments, + arguments, + allowRefOmittedArguments: allowRefOmittedArguments, + completeResults: completeResults, + useSiteDiagnostics: ref useSiteDiagnostics); if (PreferExpandedFormOverNormalForm(normalResult.Result, expandedResult.Result)) { result = expandedResult; @@ -2537,7 +2579,7 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind return new EffectiveParameters(types.ToImmutableAndFree(), refKinds); } - internal MemberResolutionResult IsMemberApplicableInNormalForm( + private MemberResolutionResult IsMemberApplicableInNormalForm( TMember member, // method or property TMember leastOverriddenMember, // method or property ArrayBuilder typeArguments, @@ -2545,8 +2587,8 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind bool isMethodGroupConversion, bool allowRefOmittedArguments, bool inferWithDynamic, - ref HashSet useSiteDiagnostics, - bool completeResults = false) + bool completeResults, + ref HashSet useSiteDiagnostics) where TMember : Symbol { // AnalyzeArguments matches arguments to parameter names and positions. @@ -2601,9 +2643,11 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind var applicableResult = IsApplicable( member, leastOverriddenMember, typeArguments, arguments, originalEffectiveParameters, constructedEffectiveParameters, - argumentAnalysis.ArgsToParamsOpt, hasAnyRefOmittedArgument, - ref useSiteDiagnostics, - inferWithDynamic); + argumentAnalysis.ArgsToParamsOpt, + hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, + inferWithDynamic: inferWithDynamic, + completeResults: completeResults, + useSiteDiagnostics: ref useSiteDiagnostics); // If we were producing complete results and had missing arguments, we pushed on in order to call IsApplicable for // type inference and lambda binding. In that case we still need to return the argument mismatch failure here. @@ -2621,6 +2665,7 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind ArrayBuilder typeArguments, AnalyzedArguments arguments, bool allowRefOmittedArguments, + bool completeResults, ref HashSet useSiteDiagnostics) where TMember : Symbol { @@ -2665,7 +2710,11 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind var result = IsApplicable( member, leastOverriddenMember, typeArguments, arguments, originalEffectiveParameters, constructedEffectiveParameters, - argumentAnalysis.ArgsToParamsOpt, hasAnyRefOmittedArgument, ref useSiteDiagnostics); + argumentAnalysis.ArgsToParamsOpt, + hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, + inferWithDynamic: false, + completeResults: completeResults, + useSiteDiagnostics: ref useSiteDiagnostics); return result.Result.IsValid ? new MemberResolutionResult( @@ -2684,8 +2733,9 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind EffectiveParameters constructedEffectiveParameters, ImmutableArray argsToParamsMap, bool hasAnyRefOmittedArgument, - ref HashSet useSiteDiagnostics, - bool inferWithDynamic = false) + bool inferWithDynamic, + bool completeResults, + ref HashSet useSiteDiagnostics) where TMember : Symbol { bool ignoreOpenTypes; @@ -2791,10 +2841,17 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind ignoreOpenTypes = false; } - return new MemberResolutionResult( + var applicableResult = IsApplicable( member, - leastOverriddenMember, - IsApplicable(member, effectiveParameters, arguments, argsToParamsMap, member.GetIsVararg(), hasAnyRefOmittedArgument, ignoreOpenTypes, ref useSiteDiagnostics)); + effectiveParameters, + arguments, + argsToParamsMap, + isVararg: member.GetIsVararg(), + hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, + ignoreOpenTypes: ignoreOpenTypes, + completeResults: completeResults, + useSiteDiagnostics: ref useSiteDiagnostics); + return new MemberResolutionResult(member, leastOverriddenMember, applicableResult); } private ImmutableArray InferMethodTypeArguments( @@ -2849,6 +2906,7 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind bool isVararg, bool hasAnyRefOmittedArgument, bool ignoreOpenTypes, + bool completeResults, ref HashSet useSiteDiagnostics) { // The effective parameters are in the right order with respect to the arguments. @@ -2903,8 +2961,20 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind 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))) + if (arguments.IsExtensionMethodThisArgument(argumentPosition) && !Conversions.IsValidExtensionMethodThisArgConversion(conversion)) + { + // Return early, without checking conversions of subsequent arguments, + // if the instance argument is not convertible to the 'this' parameter, + // even when 'completeResults' is requested. This avoids unnecessary + // lambda binding in particular, for instance, with LINQ expressions. + // Note that BuildArgumentsForErrorRecovery will still bind some number + // of overloads for the semantic model. + Debug.Assert(badArguments == null); + Debug.Assert(conversions == null); + return MemberAnalysisResult.BadArgumentConversions(argsToParameters, ImmutableArray.Create(argumentPosition), ImmutableArray.Create(conversion)); + } + + if (!conversion.Exists) { badArguments = badArguments ?? ArrayBuilder.GetInstance(); badArguments.Add(argumentPosition); @@ -2921,6 +2991,11 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind conversions.AddMany(Conversion.Identity, argumentPosition); conversions.Add(conversion); } + + if (badArguments != null && !completeResults) + { + break; + } } MemberAnalysisResult result; diff --git a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj index 741de412709..da08c299e6a 100644 --- a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj +++ b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj @@ -74,6 +74,7 @@ + diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 490a646b78d..8466e9701b0 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -3,6 +3,7 @@ using System; using System.IO; using System.Linq; +using System.Text; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; @@ -2272,6 +2273,81 @@ static void Main(string[] args) } } + // See MaxParameterListsForErrorRecovery. + [Fact] + public void BuildArgumentsForErrorRecovery_ManyOverloads() + { + BuildArgumentsForErrorRecovery_ManyOverloads_Internal(Binder.MaxParameterListsForErrorRecovery - 1, tooMany: false); + BuildArgumentsForErrorRecovery_ManyOverloads_Internal(Binder.MaxParameterListsForErrorRecovery, tooMany: true); + } + + private void BuildArgumentsForErrorRecovery_ManyOverloads_Internal(int n, bool tooMany) + { + var builder = new StringBuilder(); + builder.AppendLine("using System;"); + for (int i = 0; i < n; i++) + { + builder.AppendLine($"class C{i} {{ }}"); + } + builder.Append( +@"class A { } +class B { } +class C +{ + void M() + { + F(1, (t, a, b, c) => { }); + var o = this[(a, b, c) => { }]; + } +"); + // Too few parameters. + AppendLines(builder, n, i => $" void F(T t, Action a) {{ }}"); + AppendLines(builder, n, i => $" object this[Action a] => {i}"); + // Type inference failure. + AppendLines(builder, n, i => $" void F(T t, Action a) where U : T {{ }}"); + // Too many parameters. + AppendLines(builder, n, i => $" void F(T t, Action a) {{ }}"); + AppendLines(builder, n, i => $" object this[Action a] => {i}"); + builder.AppendLine("}"); + + var source = builder.ToString(); + var compilation = CreateCompilationWithMscorlibAndSystemCore(source); + var tree = compilation.SyntaxTrees[0]; + var sm = compilation.GetSemanticModel(tree); + var lambdas = tree.GetRoot().DescendantNodes().OfType().ToArray(); + + // F(1, (t, a, b, c) => { }); + var lambda = lambdas[0]; + var parameters = lambda.ParameterList.Parameters; + var parameter = (ParameterSymbol)sm.GetDeclaredSymbol(parameters[0]); + Assert.False(parameter.Type.IsErrorType()); + Assert.Equal("System.Int32 t", parameter.ToTestDisplayString()); + parameter = (ParameterSymbol)sm.GetDeclaredSymbol(parameters[1]); + Assert.False(parameter.Type.IsErrorType()); + Assert.Equal("A a", parameter.ToTestDisplayString()); + parameter = (ParameterSymbol)sm.GetDeclaredSymbol(parameters[3]); + Assert.Equal(tooMany, parameter.Type.IsErrorType()); + Assert.Equal(tooMany ? "? c" : "C c", parameter.ToTestDisplayString()); + + // var o = this[(a, b, c) => { }]; + lambda = lambdas[1]; + parameters = lambda.ParameterList.Parameters; + parameter = (ParameterSymbol)sm.GetDeclaredSymbol(parameters[0]); + Assert.False(parameter.Type.IsErrorType()); + Assert.Equal("A a", parameter.ToTestDisplayString()); + parameter = (ParameterSymbol)sm.GetDeclaredSymbol(parameters[2]); + Assert.Equal(tooMany, parameter.Type.IsErrorType()); + Assert.Equal(tooMany ? "? c" : "C c", parameter.ToTestDisplayString()); + } + + private static void AppendLines(StringBuilder builder, int n, Func getLine) + { + for (int i = 0; i < n; i++) + { + builder.AppendLine(getLine(i)); + } + } + [Fact] [WorkItem(13797, "https://github.com/dotnet/roslyn/issues/13797")] public void DelegateAsAction() diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs new file mode 100644 index 00000000000..d8bed659edc --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using System.Linq; +using System.Text; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + // Tests that should take a long time, perhaps even exceeding + // test timeout, without shortcuts in overload resolution. + public class OverloadResolutionPerfTests : CSharpTestBase + { + [Fact] + public void StaticMethodsWithLambda() + { + const int n = 100; + var builder = new StringBuilder(); + builder.AppendLine("using System;"); + for (int i = 0; i < n; i++) + { + builder.AppendLine($"class C{i} {{ }}"); + } + builder.AppendLine("static class S"); + builder.AppendLine("{"); + for (int i = 0; i < n; i++) + { + builder.AppendLine($" internal static void F(C{i} x, Action a) {{ F(x, y => F(y, z => F(z, w => {{ }}))); }}"); + } + builder.AppendLine("}"); + var source = builder.ToString(); + var comp = CreateCompilationWithMscorlibAndSystemCore(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void ConstructorsWithLambdaAndParams() + { + const int n = 100; + var builder = new StringBuilder(); + builder.AppendLine("using System;"); + for (int i = 0; i < n; i++) + { + builder.AppendLine($"class C{i} {{ }}"); + } + builder.AppendLine("class C"); + builder.AppendLine("{"); + for (int i = 0; i < n; i++) + { + builder.AppendLine($" internal static C F(C{i} x, params object[] args) => new C(x, y => F(y, args[1]), args[0]);"); + builder.AppendLine($" internal C(C{i} x, Func f, params object[] args) {{ }}"); + } + builder.AppendLine("}"); + var source = builder.ToString(); + var comp = CreateCompilationWithMscorlibAndSystemCore(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void ExtensionMethodsWithLambda() + { + const int n = 100; + var builder = new StringBuilder(); + builder.AppendLine("using System;"); + for (int i = 0; i < n; i++) + { + builder.AppendLine($"class C{i} {{ }}"); + } + builder.AppendLine("static class S"); + builder.AppendLine("{"); + for (int i = 0; i < n; i++) + { + builder.AppendLine($" internal static void F(this C{i} x, Action a) {{ x.F(y => y.F(z => z.F(w => {{ }}))); }}"); + } + builder.AppendLine("}"); + var source = builder.ToString(); + var comp = CreateCompilationWithMscorlibAndSystemCore(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void ExtensionMethodsWithLambdaAndParams() + { + const int n = 100; + var builder = new StringBuilder(); + builder.AppendLine("using System;"); + for (int i = 0; i < n; i++) + { + builder.AppendLine($"class C{i} {{ }}"); + } + builder.AppendLine("static class S"); + builder.AppendLine("{"); + for (int i = 0; i < n; i++) + { + builder.AppendLine($" internal static void F(this C{i} x, Action a, params object[] args) {{ x.F(y => y.F(z => z.F(w => {{ }}), args[1]), args[0]); }}"); + } + builder.AppendLine("}"); + var source = builder.ToString(); + var comp = CreateCompilationWithMscorlibAndSystemCore(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void ExtensionMethodsWithLambdaAndErrors() + { + const int n = 200; + var builder = new StringBuilder(); + builder.AppendLine("using System;"); + for (int i = 0; i < n; i++) + { + builder.AppendLine($"class C{i} {{ }}"); + } + builder.AppendLine("static class S"); + builder.AppendLine("{"); + for (int i = 0; i < n; i++) + { + if (i % 2 == 0) + { + builder.AppendLine($" internal static void F(this C{i} x) {{ x.G(y => y.G(z => z.F())); }}"); // No match for x.G(...). + } + else + { + builder.AppendLine($" internal static void G(this C{i} x, Action a) {{ }}"); + } + } + builder.AppendLine("}"); + var source = builder.ToString(); + var comp = CreateCompilationWithMscorlibAndSystemCore(source); + // error CS1929: 'Ci' does not contain a definition for 'G' and the best extension method overload 'S.G(C1, Action)' requires a receiver of type 'C1' + var diagnostics = Enumerable.Range(0, n / 2). + Select(i => Diagnostic(ErrorCode.ERR_BadInstanceArgType, "x").WithArguments($"C{i * 2}", "G", "S.G(C1, System.Action)", "C1")). + ToArray(); + comp.VerifyDiagnostics(diagnostics); + } + } +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs index 0556e650ba9..642a02aa349 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs @@ -1229,10 +1229,10 @@ public void TestRefOutAnonymousDelegate() using System.Linq.Expressions; class p { - static void Foo(ref Func a) { } + static void Foo(ref Func a) { } static void Bar(out Func a) { a = null; } - static void Foo2(ref Expression> a) { } + static void Foo2(ref Expression> a) { } static void Bar2(out Expression> a) { a = null; } static void Main() @@ -1259,7 +1259,6 @@ static void Main() Diagnostic(ErrorCode.ERR_BadArgType, "x => x").WithArguments("1", "lambda expression", "out System.Linq.Expressions.Expression>").WithLocation(17, 22)); } - [Fact, WorkItem(1157097, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1157097"), WorkItem(2298, "https://github.com/dotnet/roslyn/issues/2298")] public void TestOverloadResolutionTiebreaker() { @@ -1391,8 +1390,8 @@ public static void M() // Test6>(null); Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "Test6>").WithArguments("C.L", "S", "string").WithLocation(58, 9)); } - [Fact] + [Fact] public void TestBug9583() { var source = @@ -1432,8 +1431,8 @@ static void M() // System.Console.WriteLine(VoidReturning()); Diagnostic(ErrorCode.ERR_BadArgType, "VoidReturning()").WithArguments("1", "void", "bool").WithLocation(8, 34)); } - [Fact] + [Fact] public void TestBug6156() { TestOverloadResolutionWithDiff( @@ -1478,10 +1477,8 @@ class Out2 : Ref2 // C# says this overrides SLOT2 }"); } - [Fact] - - + [Fact] public void TestGenericMethods() { TestOverloadResolutionWithDiff( @@ -1503,9 +1500,8 @@ void M() } }"); } - [Fact] - + [Fact] public void TestDelegateBetterness() { TestOverloadResolutionWithDiff( @@ -1566,8 +1562,8 @@ void M() } "); } - [Fact] + [Fact] public void TestTieBreakers() { TestOverloadResolutionWithDiff( @@ -1837,9 +1833,9 @@ public static void Main2() Diagnostic(ErrorCode.ERR_BadArgCount, "Method2").WithArguments("Method2", "5"), Diagnostic(ErrorCode.ERR_BadArgCount, "Method2").WithArguments("Method2", "5")); } + [WorkItem(6353, "DevDiv_Projects/Roslyn")] [Fact()] - public void TestBaseAccessForAbstractMembers() { // Tests: @@ -1892,9 +1888,9 @@ public override U Property Diagnostic(ErrorCode.ERR_AbstractBaseCall, "base.Method(x, y)").WithArguments("Base3.Method(U, V)"), Diagnostic(ErrorCode.ERR_AbstractBaseCall, "base.Property").WithArguments("Base3.Property")); } + [WorkItem(6353, "DevDiv_Projects/Roslyn")] [Fact()] - public void TestBaseAccessForAbstractMembers1() { // Tests: @@ -1919,9 +1915,9 @@ public override void Method(A a, B b) CreateCompilationWithMscorlib(source).VerifyDiagnostics( Diagnostic(ErrorCode.ERR_AbstractBaseCall, "base.Method").WithArguments("Base.Method(A, B)")); } + [WorkItem(6353, "DevDiv_Projects/Roslyn")] [Fact()] - public void TestBaseAccessForAbstractMembers2() { var source = @" @@ -7480,7 +7476,6 @@ static void Main(string[] args) ); } - [Fact] [WorkItem(655409, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/655409")] public void TestBug655409() @@ -9163,5 +9158,75 @@ private class StringValues : List Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Assert").WithArguments("Test.Assert(T, T)").WithLocation(18, 9) ); } + + /// + /// Inapplicable extension methods with bad arguments, with overloads where + /// the instance argument can be converted to 'this' before overloads where the + /// instance argument cannot be converted. Overload resolution should choose + /// a method with convertible 'this', as with the native compiler. + /// + [Fact] + public void InapplicableExtensionMethods_1() + { + string source = +@"using System; +class A { } +class B { } +class C +{ + static void Main() + { + var a = new A(); + a.F(o => {}, a); + } +} +static class E +{ + internal static void F(this A x, Action y) { } + internal static void F(this A x, Action y, B z) { } + internal static void F(this B x, Action y) { } + internal static void F(this B x, Action y, A z) { } +}"; + var comp = CreateCompilationWithMscorlibAndSystemCore(source); + comp.VerifyDiagnostics( + // (9,22): error CS1503: Argument 3: cannot convert from 'A' to 'B' + // a.F(o => {}, a); + Diagnostic(ErrorCode.ERR_BadArgType, "a").WithArguments("3", "A", "B").WithLocation(9, 22)); + } + + /// + /// Inapplicable extension methods with bad arguments, with overloads where + /// the instance argument can be converted to 'this' after overloads where the + /// instance argument cannot be converted. Overload resolution should choose + /// a method where non-convertible 'this', as with the native compiler. + /// + [Fact] + public void InapplicableExtensionMethods_2() + { + string source = +@"using System; +class A { } +class B { } +class C +{ + static void Main() + { + var a = new A(); + a.F(o => {}, a); + } +} +static class E +{ + internal static void F(this B x, Action y) { } + internal static void F(this B x, Action y, A z) { } + internal static void F(this A x, Action y) { } + internal static void F(this A x, Action y, B z) { } +}"; + var comp = CreateCompilationWithMscorlibAndSystemCore(source); + comp.VerifyDiagnostics( + // (9,9): error CS1929: 'A' does not contain a definition for 'F' and the best extension method overload 'E.F(B, Action, A)' requires a receiver of type 'B' + // a.F(o => {}, a); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "a").WithArguments("A", "F", "E.F(B, System.Action, A)", "B").WithLocation(9, 9)); + } } } diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb index 51dcbb86d46..1ce32e06ad4 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Symbols.vb @@ -180,89 +180,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return constructedType End Function - ''' - ''' Resolves overloaded methods or constructors or properties for SemanticModel. - ''' - Friend Function ResolveOverloadedMembers(Of TMember As Symbol)( - members As ImmutableArray(Of TMember), - typeArguments As ImmutableArray(Of TypeSymbol), - arguments As ImmutableArray(Of ArgumentSyntax)) As OverloadResolutionResult(Of TMember) - - If members.IsDefault Then - members = ImmutableArray(Of TMember).Empty - End If - - Dim isProperties As Boolean = (GetType(TMember) Is GetType(PropertySymbol)) - Dim isMethods As Boolean = (GetType(TMember) Is GetType(MethodSymbol)) - If Not (isProperties OrElse isMethods) Then - Throw New ArgumentException("Must resolve overloads on PropertySymbol or MethodSymbol", NameOf(TMember)) - End If - If isProperties And Not typeArguments.IsEmpty Then - Throw New ArgumentException(VBResources.PropertiesCanNotHaveTypeArguments, NameOf(typeArguments)) - End If - - Dim boundArguments As ImmutableArray(Of BoundExpression) = Nothing - Dim argumentNames As ImmutableArray(Of String) = Nothing - Dim argumentNamesLocations As ImmutableArray(Of Location) = Nothing - Dim diagnostics = DiagnosticBag.GetInstance() - - Try - BindArgumentsAndNames(arguments, boundArguments, argumentNames, argumentNamesLocations, diagnostics) - Dim boundMemberGroup As BoundMethodOrPropertyGroup - - If isMethods Then - boundMemberGroup = New BoundMethodGroup(VisualBasicSyntaxTree.Dummy.GetRoot(Nothing), - If(typeArguments.IsEmpty, - Nothing, - New BoundTypeArguments(VisualBasicSyntaxTree.Dummy.GetRoot(Nothing), typeArguments)), - ImmutableArray.Create(Of MethodSymbol)(DirectCast(DirectCast(members.ToArray(), Symbol()), MethodSymbol())), - LookupResultKind.Good, Nothing, QualificationKind.Unqualified) - Else - boundMemberGroup = New BoundPropertyGroup(VisualBasicSyntaxTree.Dummy.GetRoot(Nothing), - ImmutableArray.Create(Of PropertySymbol)(DirectCast(DirectCast(members.ToArray(), Symbol()), PropertySymbol())), - LookupResultKind.Good, Nothing, QualificationKind.Unqualified) - End If - - ' do resolve overloads - Dim internalResult As OverloadResolution.OverloadResolutionResult = - OverloadResolution.MethodOrPropertyInvocationOverloadResolution( - boundMemberGroup, boundArguments, argumentNames, Me, includeEliminatedCandidates:=True, callerInfoOpt:=Nothing, - useSiteDiagnostics:=Nothing) - - ' process the result - Dim succeeded As Boolean = internalResult.BestResult.HasValue - Dim results(internalResult.Candidates.Length - 1) As MemberResolutionResult(Of TMember) - Dim validResult As MemberResolutionResult(Of TMember) ? = Nothing - Dim bestResult As MemberResolutionResult(Of TMember) ? = Nothing - - For i As Integer = 0 To internalResult.Candidates.Length - 1 Step 1 - If succeeded AndAlso internalResult.Candidates(i).State = VisualBasic.OverloadResolution.CandidateAnalysisResultState.Applicable Then - validResult = New MemberResolutionResult(Of TMember)(internalResult.Candidates(i), True) - bestResult = validResult - results(i) = validResult.Value - Else - results(i) = New MemberResolutionResult(Of TMember)(internalResult.Candidates(i), False) - End If - Next - - If Not bestResult.HasValue Then - Dim bestCandidates = ArrayBuilder(Of OverloadResolution.CandidateAnalysisResult).GetInstance() - - GetSetOfTheBestCandidates(internalResult, bestCandidates, ImmutableArray(Of Symbol).Empty) - - If bestCandidates.Count = 1 Then - bestResult = New MemberResolutionResult(Of TMember)(bestCandidates(0), False) - End If - - bestCandidates.Free() - End If - - Return New OverloadResolutionResult(Of TMember)(results.AsImmutableOrNull(), validResult, bestResult) - Finally - diagnostics.Free() - End Try - End Function - Friend Shared Function ReportUseSiteError(diagBag As DiagnosticBag, syntax As SyntaxNodeOrToken, symbol As Symbol) As Boolean Dim useSiteErrorInfo As DiagnosticInfo = symbol.GetUseSiteErrorInfo() -- GitLab