diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs index 2ddee6b20035e99ec2398756c9550ce7b80fed7f..05830b59ba8ad1297ace75cafdf2305bf851d0ed 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs @@ -243,7 +243,8 @@ internal class MemberSignatureComparer : IEqualityComparer considerTypeConstraints: false, // valid invoke is never generic considerCallingConvention: false, // valid invoke is never static considerRefOutDifference: true, - considerCustomModifiers: true); + considerCustomModifiers: true, + ignoreDynamic: false); // Compare the "unqualified" part of the member name (no explicit part) private readonly bool _considerName; @@ -266,6 +267,9 @@ internal class MemberSignatureComparer : IEqualityComparer // Consider custom modifiers on/in parameters and return types (if return is considered). private readonly bool _considerCustomModifiers; + // Ignore Object vs. Dynamic difference + private readonly bool _ignoreDynamic; + private MemberSignatureComparer( bool considerName, bool considerExplicitlyImplementedInterfaces, @@ -273,7 +277,8 @@ internal class MemberSignatureComparer : IEqualityComparer bool considerTypeConstraints, bool considerCallingConvention, bool considerRefOutDifference, - bool considerCustomModifiers) + bool considerCustomModifiers, + bool ignoreDynamic = true) { Debug.Assert(!considerExplicitlyImplementedInterfaces || considerName, "Doesn't make sense to consider interfaces separately from name."); @@ -284,6 +289,7 @@ internal class MemberSignatureComparer : IEqualityComparer _considerCallingConvention = considerCallingConvention; _considerRefOutDifference = considerRefOutDifference; _considerCustomModifiers = considerCustomModifiers; + _ignoreDynamic = ignoreDynamic; } #region IEqualityComparer Members @@ -328,12 +334,13 @@ public bool Equals(Symbol member1, Symbol member2) var typeMap1 = GetTypeMap(member1); var typeMap2 = GetTypeMap(member2); - if (_considerReturnType && !HaveSameReturnTypes(member1, typeMap1, member2, typeMap2, _considerCustomModifiers)) + if (_considerReturnType && !HaveSameReturnTypes(member1, typeMap1, member2, typeMap2, _considerCustomModifiers, _ignoreDynamic)) { return false; } - if (member1.GetParameterCount() > 0 && !HaveSameParameterTypes(member1.GetParameters(), typeMap1, member2.GetParameters(), typeMap2, _considerRefOutDifference, _considerCustomModifiers)) + if (member1.GetParameterCount() > 0 && !HaveSameParameterTypes(member1.GetParameters(), typeMap1, member2.GetParameters(), typeMap2, + _considerRefOutDifference, _considerCustomModifiers, _ignoreDynamic)) { return false; } @@ -426,10 +433,10 @@ public int GetHashCode(Symbol member) public static bool HaveSameReturnTypes(MethodSymbol member1, MethodSymbol member2, bool considerCustomModifiers) { - return HaveSameReturnTypes(member1, GetTypeMap(member1), member2, GetTypeMap(member2), considerCustomModifiers); + return HaveSameReturnTypes(member1, GetTypeMap(member1), member2, GetTypeMap(member2), considerCustomModifiers, ignoreDynamic: true); } - private static bool HaveSameReturnTypes(Symbol member1, TypeMap typeMap1, Symbol member2, TypeMap typeMap2, bool considerCustomModifiers) + private static bool HaveSameReturnTypes(Symbol member1, TypeMap typeMap1, Symbol member2, TypeMap typeMap2, bool considerCustomModifiers, bool ignoreDynamic) { TypeSymbol unsubstitutedReturnType1; ImmutableArray returnTypeCustomModifiers1; @@ -463,8 +470,8 @@ private static bool HaveSameReturnTypes(Symbol member1, TypeMap typeMap1, Symbol // the runtime compares custom modifiers using (effectively) SequenceEqual return considerCustomModifiers ? - returnType1.Equals(returnType2, ignoreDynamic: true) : - returnType1.Type.Equals(returnType2.Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true); + returnType1.Equals(returnType2, ignoreDynamic: ignoreDynamic) : + returnType1.Type.Equals(returnType2.Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: ignoreDynamic); } private static TypeMap GetTypeMap(Symbol member) @@ -588,7 +595,8 @@ private static void SubstituteConstraintTypes(ImmutableArray types, } } - private static bool HaveSameParameterTypes(ImmutableArray params1, TypeMap typeMap1, ImmutableArray params2, TypeMap typeMap2, bool considerRefOutDifference, bool considerCustomModifiers) + private static bool HaveSameParameterTypes(ImmutableArray params1, TypeMap typeMap1, ImmutableArray params2, TypeMap typeMap2, + bool considerRefOutDifference, bool considerCustomModifiers, bool ignoreDynamic) { Debug.Assert(params1.Length == params2.Length); @@ -605,12 +613,12 @@ private static bool HaveSameParameterTypes(ImmutableArray param // the runtime compares custom modifiers using (effectively) SequenceEqual if (considerCustomModifiers) { - if (!type1.Equals(type2, ignoreDynamic: true) || (param1.CountOfCustomModifiersPrecedingByRef != param2.CountOfCustomModifiersPrecedingByRef)) + if (!type1.Equals(type2, ignoreDynamic: ignoreDynamic) || (param1.CountOfCustomModifiersPrecedingByRef != param2.CountOfCustomModifiersPrecedingByRef)) { return false; } } - else if (!type1.Type.Equals(type2.Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true)) + else if (!type1.Type.Equals(type2.Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: ignoreDynamic)) { return false; } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index b05477e96342794b0302ab4ab2be716e07f3732a..d556466231a09bde9fac32fd284b4eb324c87c97 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public partial class SyntaxBinderTests : CompilingTestBase + public partial class LambdaTests : CompilingTestBase { [Fact, WorkItem(608181, "DevDiv")] public void BadInvocationInLambda() @@ -1320,5 +1320,81 @@ static void Main() // Expression> x = y => y = y; Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAssignment, "y = y").WithLocation(9, 45)); } + + [Fact, WorkItem(5363, "https://github.com/dotnet/roslyn/issues/5363")] + public void ReturnInferenceCache_Dynamic_vs_Object_01() + { + var source = +@" +using System; +using System.Collections; +using System.Collections.Generic; + +public static class Program +{ + public static void Main(string[] args) + { + IEnumerable dynX = null; + + // CS1061 'object' does not contain a definition for 'Text'... + // tooltip on 'var' shows IColumn instead of IEnumerable + var result = dynX.Select(_ => _.Text); + } + + public static IColumn Select(this IColumn source, Func selector) + { + throw new NotImplementedException(); + } + + public static IEnumerable Select(this IEnumerable source, Func selector) + { + System.Console.WriteLine(""Select""); + return null; + } +} + +public interface IColumn { } +"; + var compilation = CreateCompilationWithMscorlib(source, new[] { SystemCoreRef, CSharpRef }, options: TestOptions.ReleaseExe); + CompileAndVerify(compilation, expectedOutput: "Select"); + } + + [Fact, WorkItem(5363, "https://github.com/dotnet/roslyn/issues/5363")] + public void ReturnInferenceCache_Dynamic_vs_Object_02() + { + var source = +@" +using System; +using System.Collections; +using System.Collections.Generic; + +public static class Program +{ + public static void Main(string[] args) + { + IEnumerable dynX = null; + + // CS1061 'object' does not contain a definition for 'Text'... + // tooltip on 'var' shows IColumn instead of IEnumerable + var result = dynX.Select(_ => _.Text); + } + + public static IEnumerable Select(this IEnumerable source, Func selector) + { + System.Console.WriteLine(""Select""); + return null; + } + + public static IColumn Select(this IColumn source, Func selector) + { + throw new NotImplementedException(); + } +} + +public interface IColumn { } +"; + var compilation = CreateCompilationWithMscorlib(source, new[] { SystemCoreRef, CSharpRef }, options: TestOptions.ReleaseExe); + CompileAndVerify(compilation, expectedOutput: "Select"); + } } }