From fdb1cfd4d6526f99c3f7b9d33b367e677873fc88 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Sun, 26 Apr 2015 17:38:41 -0700 Subject: [PATCH] =?UTF-8?q?Adjust=20constraints=20check=20for=20reduced=20?= =?UTF-8?q?extension=20methods=20to=20avoid=20failures=20for=20type=20para?= =?UTF-8?q?meters=20that=20couldn=E2=80=99t=20be=20inferred=20from=20the?= =?UTF-8?q?=20first=20argument.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #2288. --- .../OverloadResolution/MethodTypeInference.cs | 12 ++--- .../Symbols/MethodSymbolExtensions.cs | 44 +++++++++++++++-- .../Symbol/Symbols/ExtensionMethodTests.cs | 49 +++++++++++++++++++ 3 files changed, 93 insertions(+), 12 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index c3e63194f80..4767a09c6b0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -2575,18 +2575,12 @@ private bool InferTypeArgumentsFromFirstArgument(ref HashSet use } /// - /// Return the inferred type arguments using the original type - /// parameters for any type arguments that were not inferred. + /// Return the inferred type arguments using null + /// for any type arguments that were not inferred. /// private ImmutableArray GetInferredTypeArguments() { - var typeArgs = ArrayBuilder.GetInstance(); - for (int i = 0; i < _methodTypeParameters.Length; i++) - { - var typeArg = _fixedResults[i] ?? _methodTypeParameters[i]; - typeArgs.Add(typeArg); - } - return typeArgs.ToImmutableAndFree(); + return _fixedResults.AsImmutable(); } private static bool IsReallyAType(TypeSymbol type) diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs index 0132731323c..518f951e776 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs @@ -73,12 +73,35 @@ public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol m return null; } + int firstNullInTypeArgs = -1; + + // For the purpose of constraint checks we use error type symbol in place of type arguments that we couldn't infer from the first argument. + // This prevents constraint checking from failing for corresponding type parameters. + var typeArgsForConstraintsCheck = typeArgs; + for (int i = 0; i < typeArgsForConstraintsCheck.Length; i++) + { + if ((object)typeArgsForConstraintsCheck[i] == null) + { + firstNullInTypeArgs = i; + var builder = ArrayBuilder.GetInstance(); + builder.AddRange(typeArgs, firstNullInTypeArgs); + + for (; i < typeArgsForConstraintsCheck.Length; i++) + { + builder.Add(typeArgsForConstraintsCheck[i] ?? ErrorTypeSymbol.UnknownResultType); + } + + typeArgsForConstraintsCheck = builder.ToImmutableAndFree(); + break; + } + } + // Check constraints. var diagnosticsBuilder = ArrayBuilder.GetInstance(); var typeParams = method.TypeParameters; - var substitution = new TypeMap(typeParams, typeArgs); + var substitution = new TypeMap(typeParams, typeArgsForConstraintsCheck); ArrayBuilder useSiteDiagnosticsBuilder = null; - var success = method.CheckConstraints(conversions, substitution, method.TypeParameters, typeArgs, compilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder); + var success = method.CheckConstraints(conversions, substitution, typeParams, typeArgsForConstraintsCheck, compilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder); diagnosticsBuilder.Free(); if (useSiteDiagnosticsBuilder != null && useSiteDiagnosticsBuilder.Count > 0) @@ -99,7 +122,22 @@ public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol m return null; } - return method.Construct(typeArgs); + // For the purpose of construction we use original type parameters in place of type arguments that we couldn't infer from the first argument. + var typeArgsForConstruct = typeArgs; + if (firstNullInTypeArgs != -1) + { + var builder = ArrayBuilder.GetInstance(); + builder.AddRange(typeArgs, firstNullInTypeArgs); + + for (int i = firstNullInTypeArgs; i < typeArgsForConstruct.Length; i++) + { + builder.Add(typeArgsForConstruct[i] ?? typeParams[i]); + } + + typeArgsForConstruct = builder.ToImmutableAndFree(); + } + + return method.Construct(typeArgsForConstruct); } internal static bool IsSynthesizedLambda(this MethodSymbol method) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs index 367cb312c48..65b54d18d9a 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs @@ -3615,5 +3615,54 @@ public static void Foo(this int x) // using X = N.S; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using X = N.S;").WithLocation(4, 5)); } + + [WorkItem(1094849, "DevDiv"), WorkItem(2288, "https://github.com/dotnet/roslyn/issues/2288")] + [Fact] + public void LookupSymbolsWithPartialInference() + { + var source = +@" +using System.Collections.Generic; + +namespace ConsoleApplication22 +{ + static class Program + { + static void Main(string[] args) + { + } + + internal static void GetEnumerableDisposable1(this IEnumerable enumerable) + where TEnumerator : struct , IEnumerator + { + } + + internal static void GetEnumerableDisposable2(this IEnumerable enumerable) + where TEnumerator : struct + { + } + + private static void Overlaps(IEnumerable other) where TEnumerator : struct, IEnumerator + { + other.GetEnumerableDisposable1(); + } + } +}"; + var compilation = CreateCompilationWithMscorlib(source, new[] { SystemCoreRef }); + + compilation.VerifyDiagnostics(); + var syntaxTree = compilation.SyntaxTrees.Single(); + var model = compilation.GetSemanticModel(syntaxTree); + + var member = (MemberAccessExpressionSyntax)syntaxTree.GetRoot().DescendantNodes().OfType().Single().Expression; + Assert.Equal("other.GetEnumerableDisposable1", member.ToString()); + + var type = model.GetTypeInfo(member.Expression).Type; + Assert.Equal("System.Collections.Generic.IEnumerable", type.ToTestDisplayString()); + + var symbols = model.LookupSymbols(member.Expression.EndPosition, type, includeReducedExtensionMethods: true).Select(s => s.Name).ToArray(); + Assert.Contains("GetEnumerableDisposable2", symbols); + Assert.Contains("GetEnumerableDisposable1", symbols); + } } } -- GitLab