diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs index cc325aebdccfc67645bf0c320e5ec7c4566ccb5f..ec9d6a08eae55ef63b7160660632b63089786e81 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ExplicitInterfaceHelpers.cs @@ -207,7 +207,7 @@ public static string GetMemberNameWithoutInterfaceName(string fullName) // interface in its base class list that contains a member ..." MultiDictionary.ValueSet set = containingType.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics[explicitInterfaceNamedType]; int setCount = set.Count; - if (setCount == 0 || !set.Contains(explicitInterfaceNamedType)) + if (setCount == 0 || !set.Contains(explicitInterfaceNamedType, TypeSymbol.EqualsObliviousNullableModifierMatchesAny)) { //we'd like to highlight just the type part of the name var explicitInterfaceSyntax = explicitInterfaceSpecifierSyntax.Name; diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index 1efba690bbaa9e33674c46bf7650831421922f65..b4c8c2f76e66f0578dfa4db3f9f1756982409513 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -125,6 +125,8 @@ private InterfaceInfo GetInterfaceInfo() internal static readonly EqualityComparer EqualsIgnoringNullableComparer = new TypeSymbolComparer(TypeCompareKind.IgnoreNullableModifiersForReferenceTypes); + internal static readonly EqualityComparer EqualsObliviousNullableModifierMatchesAny = new TypeSymbolComparer(TypeCompareKind.ObliviousNullableModifierMatchesAny); + internal static readonly EqualityComparer EqualsAllIgnoreOptionsPlusNullableWithUnknownMatchesAnyComparer = new TypeSymbolComparer(TypeCompareKind.AllIgnoreOptions & ~(TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 14ff53f6f9300d91562403e4a0324719d7c0ace2..a3227b787211b92d75383fbd2a1d4b53a8016488 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -1180,6 +1180,16 @@ internal static bool IsPrimitiveRecursiveStruct(this TypeSymbol t) /// internal static int ComputeHashCode(this NamedTypeSymbol type) { + Debug.Assert(!type.Equals(type.OriginalDefinition, TypeCompareKind.AllIgnoreOptions) || wasConstructedForAnnotations(type)); + + if (wasConstructedForAnnotations(type)) + { + // A type that uses its own type parameters as type arguments was constructed only for the purpose of adding annotations. + // In that case we'll use the hash from the definition. + + return type.OriginalDefinition.GetHashCode(); + } + int code = type.OriginalDefinition.GetHashCode(); code = Hash.Combine(type.ContainingType, code); @@ -1209,6 +1219,28 @@ internal static int ComputeHashCode(this NamedTypeSymbol type) code++; } return code; + + static bool wasConstructedForAnnotations(NamedTypeSymbol type) + { + do + { + var typeArguments = type.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; + var typeParameters = type.OriginalDefinition.TypeParameters; + + for (int i = 0; i < typeArguments.Length; i++) + { + if (!typeParameters[i].Equals(typeArguments[i].Type.OriginalDefinition)) + { + return false; + } + } + + type = type.ContainingType; + } + while (type is object && !type.IsDefinition); + + return true; + } } /// diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 86a77f966eefb1e9006e619ffd01a412c9910de5..3a1d212cb592ab4878589f4f2d89533632855c15 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -85180,6 +85180,319 @@ static void Main() CompileAndVerify(source, targetFramework: TargetFramework.StandardAndCSharp, options: WithNonNullTypesTrue()); } + [Fact] + [WorkItem(30673, "https://github.com/dotnet/roslyn/issues/30673")] + public void TypeSymbolGetHashCode_Annotated() + { + var text = @" +class C where T : class +{ + C M() => throw null!; +} +"; + + var comp = CreateNullableCompilation(text); + var type = comp.GetMember("C"); + Assert.Equal("C", type.ToTestDisplayString(includeNonNullable: true)); + Assert.True(type.IsDefinition); + + var type2 = comp.GetMember("C.M").ReturnType; + Assert.Equal("C", type2.ToTestDisplayString(includeNonNullable: true)); + Assert.False(type2.IsDefinition); + + AssertHashCodesMatch(type, type2); + } + + [Fact] + [WorkItem(30673, "https://github.com/dotnet/roslyn/issues/30673")] + public void TypeSymbolGetHashCode_NotAnnotated() + { + var text = @" +class C where T : class +{ + C M() => throw null!; +} +"; + + var comp = CreateNullableCompilation(text); + var type = comp.GetMember("C"); + Assert.Equal("C", type.ToTestDisplayString(includeNonNullable: true)); + Assert.True(type.IsDefinition); + + var type2 = comp.GetMember("C.M").ReturnType; + Assert.Equal("C", type2.ToTestDisplayString(includeNonNullable: true)); + Assert.False(type2.IsDefinition); + + AssertHashCodesMatch(type, type2); + } + + [Fact] + [WorkItem(30673, "https://github.com/dotnet/roslyn/issues/30673")] + public void TypeSymbolGetHashCode_ContainingType() + { + var text = @" +class C where T : class +{ + interface I { } +} +"; + + var comp = CreateNullableCompilation(text); + var iDefinition = comp.GetMember("C.I"); + Assert.Equal("C.I", iDefinition.ToTestDisplayString(includeNonNullable: true)); + Assert.True(iDefinition.IsDefinition); + + var cDefinition = iDefinition.ContainingType; + Assert.Equal("C", cDefinition.ToTestDisplayString(includeNonNullable: true)); + Assert.True(cDefinition.IsDefinition); + + var c2 = cDefinition.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(cDefinition.TypeParameters.Single(), NullableAnnotation.NotAnnotated))); + Assert.Equal("C", c2.ToTestDisplayString(includeNonNullable: true)); + Assert.False(c2.IsDefinition); + + AssertHashCodesMatch(cDefinition, c2); + + var i2 = c2.GetTypeMember("I"); + Assert.Equal("C.I", i2.ToTestDisplayString(includeNonNullable: true)); + Assert.False(i2.IsDefinition); + + AssertHashCodesMatch(iDefinition, i2); + + var c3 = cDefinition.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(cDefinition.TypeParameters.Single(), NullableAnnotation.Annotated))); + Assert.Equal("C", c3.ToTestDisplayString(includeNonNullable: true)); + Assert.False(c3.IsDefinition); + + AssertHashCodesMatch(cDefinition, c3); + + var i3 = c3.GetTypeMember("I"); + Assert.Equal("C.I", i3.ToTestDisplayString(includeNonNullable: true)); + Assert.False(i3.IsDefinition); + + AssertHashCodesMatch(iDefinition, i3); + } + + [Fact] + [WorkItem(30673, "https://github.com/dotnet/roslyn/issues/30673")] + public void TypeSymbolGetHashCode_ContainingType_GenericNestedType() + { + var text = @" +class C where T : class +{ + interface I where U : class { } +} +"; + + var comp = CreateNullableCompilation(text); + var iDefinition = comp.GetMember("C.I"); + Assert.Equal("C.I", iDefinition.ToTestDisplayString(includeNonNullable: true)); + Assert.True(iDefinition.IsDefinition); + + // Construct from iDefinition with annotated U from iDefinition + var i1 = iDefinition.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(iDefinition.TypeParameters.Single(), NullableAnnotation.Annotated))); + Assert.Equal("C.I", i1.ToTestDisplayString(includeNonNullable: true)); + AssertHashCodesMatch(iDefinition, i1); + + var cDefinition = iDefinition.ContainingType; + Assert.Equal("C", cDefinition.ToTestDisplayString(includeNonNullable: true)); + Assert.True(cDefinition.IsDefinition); + + // Construct from cDefinition with unannotated T from cDefinition + var c2 = cDefinition.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(cDefinition.TypeParameters.Single(), NullableAnnotation.NotAnnotated))); + var i2 = c2.GetTypeMember("I"); + Assert.Equal("C.I", i2.ToTestDisplayString(includeNonNullable: true)); + Assert.Same(i2.OriginalDefinition, iDefinition); + AssertHashCodesMatch(i2, iDefinition); + + // Construct from i2 with U from iDefinition + var i2a = i2.Construct(iDefinition.TypeParameters.Single()); + Assert.Equal("C.I", i2a.ToTestDisplayString(includeNonNullable: true)); + AssertHashCodesMatch(iDefinition, i2a); + + // Construct from i2 with annotated U from iDefinition + var i2b = i2.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(iDefinition.TypeParameters.Single(), NullableAnnotation.Annotated))); + Assert.Equal("C.I", i2b.ToTestDisplayString(includeNonNullable: true)); + AssertHashCodesMatch(iDefinition, i2b); + + // Construct from i2 with U from i2 + var i2c = i2.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(i2.TypeParameters.Single(), NullableAnnotation.Annotated))); + Assert.Equal("C.I", i2c.ToTestDisplayString(includeNonNullable: true)); + AssertHashCodesMatch(iDefinition, i2c); + + var c3 = cDefinition.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(cDefinition.TypeParameters.Single(), NullableAnnotation.Annotated))); + var i3 = c3.GetTypeMember("I"); + Assert.Equal("C.I", i3.ToTestDisplayString(includeNonNullable: true)); + AssertHashCodesMatch(iDefinition, i3); + + var i3b = i3.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(i3.TypeParameters.Single(), NullableAnnotation.Annotated))); + Assert.Equal("C.I", i3b.ToTestDisplayString(includeNonNullable: true)); + AssertHashCodesMatch(iDefinition, i3b); + + // Construct from cDefinition with modified T from cDefinition + var modifiers = ImmutableArray.Create(CSharpCustomModifier.CreateOptional(comp.GetSpecialType(SpecialType.System_Object))); + var c4 = cDefinition.Construct(ImmutableArray.Create(TypeWithAnnotations.Create(cDefinition.TypeParameters.Single(), customModifiers: modifiers))); + Assert.Equal("C", c4.ToTestDisplayString()); + Assert.False(c4.IsDefinition); + AssertHashCodesMatch(cDefinition, c4); + + var i4 = c4.GetTypeMember("I"); + Assert.Equal("C.I", i4.ToTestDisplayString()); + Assert.Same(i4.OriginalDefinition, iDefinition); + Assert.False(iDefinition.Equals(i4, TypeCompareKind.ConsiderEverything)); + Assert.False(iDefinition.Equals(i4, TypeCompareKind.CLRSignatureCompareOptions)); + Assert.True(iDefinition.Equals(i4, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds)); + Assert.Equal(iDefinition.GetHashCode(), i4.GetHashCode()); + } + + private static void AssertHashCodesMatch(TypeSymbol c, TypeSymbol c2) + { + Assert.False(c.Equals(c2)); + Assert.True(c.Equals(c2, TypeCompareKind.AllIgnoreOptions)); + + Assert.Equal(c2.GetHashCode(), c.GetHashCode()); + } + + [Fact] + [WorkItem(35619, "https://github.com/dotnet/roslyn/issues/35619")] + public void ExplicitInterface_UsingOuterDefinition_Simple() + { + var text = @" +class Outer +{ + protected internal interface Interface + { + void Method(); + } + internal class C : Outer.Interface + { + void Interface.Method() + { + } + } +} +"; + var comp = CreateNullableCompilation(text); + comp.VerifyDiagnostics(); + } + + [Fact] + [WorkItem(35619, "https://github.com/dotnet/roslyn/issues/35619")] + public void ExplicitInterface_UsingOuterDefinition() + { + var text = @" +class Outer where T : class +{ + internal class Inner where U : class + { + protected internal interface Interface + { + void Method(); + } + // The implemented interface is Outer.Inner.Interface + internal class Derived6 : Outer.Inner.Interface + { + // The explicit interface is Outer.Inner.Interface + void Inner.Interface.Method() + { + } + } + } +} +"; + CreateCompilation(text, options: WithNonNullTypesTrue()).VerifyDiagnostics(); + } + + [Fact] + [WorkItem(35619, "https://github.com/dotnet/roslyn/issues/35619")] + public void ExplicitInterface_WithExplicitOuter() + { + var text = @" +class Outer where T : class +{ + internal class Inner where U : class + { + protected internal interface Interface + { + void Method(); + } + // The implemented interface is Outer.Inner.Interface + internal class Derived6 : Outer.Inner.Interface + { + // The explicit interface is Outer.Inner.Interface + void Outer.Inner.Interface.Method() + { + } + } + } +} +"; + + CreateCompilation(text, options: WithNonNullTypesTrue()).VerifyDiagnostics(); + } + + [Fact] + [WorkItem(30677, "https://github.com/dotnet/roslyn/issues/30677")] + public void ExplicitInterface_WithExplicitOuter_DisabledT() + { + var text = @" +class Outer where T : class +{ + internal class Inner where U : class + { + protected internal interface Interface + { + void Method(); + } + // The implemented interface is Outer.Inner.Interface + internal class Derived6 : Outer.Inner.Interface + { + // The explicit interface is Outer.Inner.Interface + void Outer< +#nullable disable + T +#nullable enable + >.Inner.Interface.Method() + { + } + } + } +} +"; + + CreateCompilation(text, options: WithNonNullTypesTrue()).VerifyDiagnostics(); + } + + [Fact] + [WorkItem(30677, "https://github.com/dotnet/roslyn/issues/30677")] + public void ExplicitInterface_WithExplicitOuter_DisabledT_ImplementedInterfaceMatches() + { + var text = @" +class Outer where T : class +{ + internal class Inner where U : class + { + protected internal interface Interface + { + void Method(); + } + // The implemented interface is Outer.Inner.Interface + internal class Derived6 : Inner.Interface + { + // The explicit interface is Outer.Inner.Interface + void Outer< +#nullable disable + T +#nullable enable + >.Inner.Interface.Method() + { + } + } + } +} +"; + + CreateCompilation(text, options: WithNonNullTypesTrue()).VerifyDiagnostics(); + } + [Fact] [WorkItem(30677, "https://github.com/dotnet/roslyn/issues/30677")] public void TestErrorsImplementingGenericNestedInterfaces_Explicit() @@ -85222,22 +85535,13 @@ internal class Derived6 : Outer.Inner.Interface } "; - // https://github.com/dotnet/roslyn/issues/30677, https://github.com/dotnet/roslyn/issues/30673 - The following errors are unexpected: - // (20,22): error CS0540: 'Outer.Inner.Derived4.Derived5.Method(T, U[], List, Dictionary)': containing type does not implement interface 'Outer.Inner.Interface' - // (30,22): error CS0540: 'Outer.Inner.Derived4.Derived6.Outer.Inner.Interface.Method(T, U[], List, Dictionary)': containing type does not implement interface 'Outer.Inner.Interface' CreateCompilation(text, options: WithNonNullTypesTrue()).VerifyDiagnostics( // (14,39): error CS0535: 'Outer.Inner.Derived4.Derived5' does not implement interface member 'Outer.Inner.Interface.Method(T, U[], List, Dictionary)' // internal class Derived5 : Outer.Inner.Interface Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Outer.Inner.Interface").WithArguments("Outer.Inner.Derived4.Derived5", "Outer.Inner.Interface.Method(T, U[], System.Collections.Generic.List, System.Collections.Generic.Dictionary)").WithLocation(14, 39), - // (20,22): error CS0540: 'Outer.Inner.Derived4.Derived5.Method(T, U[], List, Dictionary)': containing type does not implement interface 'Outer.Inner.Interface' - // void Inner.Interface.Method(T a, U[] b, List c, Dictionary D) - Diagnostic(ErrorCode.ERR_ClassDoesntImplementInterface, "Inner.Interface").WithArguments("Outer.Inner.Derived4.Derived5.Method(T, U[], System.Collections.Generic.List, System.Collections.Generic.Dictionary)", "Outer.Inner.Interface").WithLocation(20, 22), - // (20,47): error CS0539: 'Outer.Inner.Derived4.Derived5.Method(T, U[], List, Dictionary)' in explicit interface declaration is not a member of interface + // (20,47): error CS0539: 'Outer.Inner.Derived4.Derived5.Method(T, U[], List, Dictionary)' in explicit interface declaration is not found among members of the interface that can be implemented // void Inner.Interface.Method(T a, U[] b, List c, Dictionary D) - Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "Method").WithArguments("Outer.Inner.Derived4.Derived5.Method(T, U[], System.Collections.Generic.List, System.Collections.Generic.Dictionary)").WithLocation(20, 47), - // (30,22): error CS0540: 'Outer.Inner.Derived4.Derived6.Outer.Inner.Interface.Method(T, U[], List, Dictionary)': containing type does not implement interface 'Outer.Inner.Interface' - // void Inner.Interface.Method(T a, U[] b, List c, Dictionary D) - Diagnostic(ErrorCode.ERR_ClassDoesntImplementInterface, "Inner.Interface").WithArguments("Outer.Inner.Derived4.Derived6.Outer.Inner.Interface.Method(T, U[], System.Collections.Generic.List, System.Collections.Generic.Dictionary)", "Outer.Inner.Interface").WithLocation(30, 22) + Diagnostic(ErrorCode.ERR_InterfaceMemberNotFound, "Method").WithArguments("Outer.Inner.Derived4.Derived5.Method(T, U[], System.Collections.Generic.List, System.Collections.Generic.Dictionary)").WithLocation(20, 47) ); } @@ -85270,12 +85574,7 @@ internal class Derived3 : Interface } "; - // https://github.com/dotnet/roslyn/issues/30677, https://github.com/dotnet/roslyn/issues/30673 - Expect no errors - CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics( - // (18,18): error CS0540: 'Outer.Inner.Derived3.Outer.Inner.Interface.Method(T, U[], List, Dictionary)': containing type does not implement interface 'Outer.Inner.Interface' - // void Inner.Interface.Method(T a, U[] B, List C, Dictionary d) - Diagnostic(ErrorCode.ERR_ClassDoesntImplementInterface, "Inner.Interface").WithArguments("Outer.Inner.Derived3.Outer.Inner.Interface.Method(T, U[], System.Collections.Generic.List, System.Collections.Generic.Dictionary)", "Outer.Inner.Interface").WithLocation(18, 18) - ); + CreateCompilation(source, options: WithNonNullTypesTrue()).VerifyDiagnostics(); } [Fact] @@ -85380,17 +85679,11 @@ class C : I2 } } "; - var expected = new DiagnosticDescription[] { - // (4,10): warning CS8643: Nullability of reference types in explicit interface specifier doesn't match interface implemented by the type. - // void I1>.M() - Diagnostic(ErrorCode.WRN_NullabilityMismatchInExplicitlyImplementedInterface, "I1>").WithLocation(4, 10) - }; - var comp2 = CreateCompilation(source2, options: WithNonNullTypesTrue(), references: new[] { comp1.EmitToImageReference() }); - comp2.VerifyDiagnostics(expected); + comp2.VerifyDiagnostics(); var comp3 = CreateCompilation(source2, options: WithNonNullTypesTrue(), references: new[] { comp1.ToMetadataReference() }); - comp3.VerifyDiagnostics(expected); + comp3.VerifyDiagnostics(); } [Fact] @@ -85642,10 +85935,7 @@ class C : I2, var expected = new DiagnosticDescription[] { // (2,7): warning CS8645: 'I1>' is already listed in the interface list on type 'C' with different nullability of reference types. // class C : I2, - Diagnostic(ErrorCode.WRN_DuplicateInterfaceWithNullabilityMismatchInBaseList, "C").WithArguments("I1>", "C").WithLocation(2, 7), - // (7,10): warning CS8643: Nullability of reference types in explicit interface specifier doesn't match interface implemented by the type. - // void I1>.M() - Diagnostic(ErrorCode.WRN_NullabilityMismatchInExplicitlyImplementedInterface, "I1>").WithLocation(7, 10) + Diagnostic(ErrorCode.WRN_DuplicateInterfaceWithNullabilityMismatchInBaseList, "C").WithArguments("I1>", "C").WithLocation(2, 7) }; var comp2 = CreateCompilation(source2, options: WithNonNullTypesTrue(), references: new[] { comp1.EmitToImageReference() }); @@ -85690,17 +85980,12 @@ class C : I2, } } "; - var expected = new DiagnosticDescription[] { - // (9,10): warning CS8643: Nullability of reference types in explicit interface specifier doesn't match interface implemented by the type. - // void I1>.M() - Diagnostic(ErrorCode.WRN_NullabilityMismatchInExplicitlyImplementedInterface, "I1>").WithLocation(9, 10) - }; var comp2 = CreateCompilation(source2, options: WithNonNullTypesTrue(), references: new[] { comp1.EmitToImageReference() }); - comp2.VerifyDiagnostics(expected); + comp2.VerifyDiagnostics(); var comp3 = CreateCompilation(source2, options: WithNonNullTypesTrue(), references: new[] { comp1.ToMetadataReference() }); - comp3.VerifyDiagnostics(expected); + comp3.VerifyDiagnostics(); } [Fact] diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedErrorType.vb b/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedErrorType.vb index ad1512b642a3b8485aed7ade4e1fe5b596e42682..111261dbdb6e54c7ec2926998d87cd68330ac362 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedErrorType.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedErrorType.vb @@ -221,6 +221,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Property Public Overrides Function GetHashCode() As Integer + If Me._substitution.WasConstructedForModifiers() Then + Return OriginalDefinition.GetHashCode() + End If + Dim _hash As Integer = _fullInstanceType.GetHashCode() _hash = Hash.Combine(ContainingType, _hash) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedNamedType.vb b/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedNamedType.vb index 60d7051eea0fd05d540c02f9043065f48c5af8ea..fd500272128b7893b18f3cdeaa38e3928ee320ef 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedNamedType.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/SubstitutedNamedType.vb @@ -491,6 +491,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Public Overrides Function GetHashCode() As Integer Dim _hash As Integer = OriginalDefinition.GetHashCode() + If Me._substitution.WasConstructedForModifiers() Then + Return _hash + End If + _hash = Hash.Combine(ContainingType, _hash) ' There is a circularity problem here with alpha-renamed type parameters. @@ -978,6 +982,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Function Public Overrides Function GetHashCode() As Integer + If Me._substitution.WasConstructedForModifiers() Then + Return OriginalDefinition.GetHashCode() + End If + Dim _hash As Integer = MyBase.GetHashCode() For Each typeArgument In TypeArgumentsNoUseSiteDiagnostics @@ -1201,7 +1209,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return Me End Function - End Class ''' diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeSubstitution.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeSubstitution.vb index 076f10ec039cd68be02ec83a1426845480455c52..58bea7673a0b38e968a1b8d7ec0a5c686e5dfd93 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeSubstitution.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeSubstitution.vb @@ -904,5 +904,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return customModifiers End Function + Public Function WasConstructedForModifiers() As Boolean + For Each pair In _pairs + If Not pair.Key.Equals(pair.Value.Type.OriginalDefinition) Then + Return False + End If + Next + + Return If(_parent Is Nothing, True, _parent.WasConstructedForModifiers()) + End Function + End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb index bdbc5f0c2ef1b7676f5b6c6e22f53527241f64ea..88de308944e26fb2a3ecfa56ce7ee0f846af1079 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbolExtensions.vb @@ -285,12 +285,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim t1IsDefinition = t1.IsDefinition Dim t2IsDefinition = t2.IsDefinition - If (t1IsDefinition <> t2IsDefinition) AndAlso - Not ((compareKind And TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) <> 0 AndAlso - (DirectCast(t1, NamedTypeSymbol).HasTypeArgumentsCustomModifiers OrElse DirectCast(t2, NamedTypeSymbol).HasTypeArgumentsCustomModifiers)) Then - Return False - End If - If Not (t1IsDefinition AndAlso t2IsDefinition) Then ' This is a generic instantiation case If Not t1.OriginalDefinition.Equals(t2.OriginalDefinition) Then @@ -303,20 +297,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Do - If (compareKind And Global.Microsoft.CodeAnalysis.TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) = 0 AndAlso - Not HasSameTypeArgumentCustomModifiers(container1, container2) Then - - Return False - End If + If Not (container1 Is container1.ConstructedFrom AndAlso container2 Is container2.ConstructedFrom) Then + ' No need to compare type arguments on those containers when they didn't add type arguments (that would cause cycles) - Dim args1 As ImmutableArray(Of TypeSymbol) = container1.TypeArgumentsNoUseSiteDiagnostics - Dim args2 As ImmutableArray(Of TypeSymbol) = container2.TypeArgumentsNoUseSiteDiagnostics + If (compareKind And Global.Microsoft.CodeAnalysis.TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) = 0 AndAlso + Not HasSameTypeArgumentCustomModifiers(container1, container2) Then - For i As Integer = 0 To args1.Length - 1 Step 1 - If Not args1(i).IsSameType(args2(i), compareKind) Then Return False End If - Next + + Dim args1 As ImmutableArray(Of TypeSymbol) = container1.TypeArgumentsNoUseSiteDiagnostics + Dim args2 As ImmutableArray(Of TypeSymbol) = container2.TypeArgumentsNoUseSiteDiagnostics + + For i As Integer = 0 To args1.Length - 1 Step 1 + If Not args1(i).IsSameType(args2(i), compareKind) Then + Return False + End If + Next + End If container1 = container1.ContainingType container2 = container2.ContainingType @@ -326,16 +324,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Exit Do End If - If (container1.IsDefinition <> container2.IsDefinition) AndAlso - Not ((compareKind And TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) <> 0 AndAlso - (container1.HasTypeArgumentsCustomModifiers OrElse container2.HasTypeArgumentsCustomModifiers)) Then - - Return False - End If Loop Return True End If + ElseIf kind = SymbolKind.TypeParameter Then + If Not t1.OriginalDefinition.Equals(t2.OriginalDefinition) Then + Return False ' different definition + End If + + Return t1.ContainingType.IsSameType(t2.ContainingType, compareKind) End If Return t1.Equals(t2) diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb index 5461054d79753c31fc16fcf8997893f3dc5fa09f..cf441c1a52900da52d7014600293f5653433bff7 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Source/TypeTests.vb @@ -4,6 +4,7 @@ Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.Test.Utilities Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Roslyn.Test.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests @@ -3782,6 +3783,169 @@ End Interface Assert.True(i1.IsExplicitDefinitionOfNoPiaLocalType) End Sub + + + + Public Sub TypeSymbolGetHashCode_ContainingType_GenericNestedType() + Dim compilation = CompilationUtils.CreateCompilationWithMscorlib40( + + +Public Class C(Of T) + Public Interface I(Of U) + End Interface +End Class + +) + + AssertNoDeclarationDiagnostics(compilation) + Dim modifiers = ImmutableArray.Create(VisualBasicCustomModifier.CreateOptional(compilation.GetSpecialType(SpecialType.System_Object))) + + Dim iDefinition = compilation.GetMember(Of NamedTypeSymbol)("C.I") + Assert.Equal("C(Of T).I(Of U)", iDefinition.ToTestDisplayString()) + Assert.True(iDefinition.IsDefinition) + + ' Construct from iDefinition with modified U from iDefinition + Dim modifiedU = ImmutableArray.Create(New TypeWithModifiers(iDefinition.TypeParameters.Single(), modifiers)) + Dim i1 = iDefinition.Construct(TypeSubstitution.Create(iDefinition, iDefinition.TypeParameters, modifiedU)) + Assert.Equal("C(Of T).I(Of U modopt(System.Object))", i1.ToTestDisplayString()) + AssertHashCodesMatch(iDefinition, i1) + + Dim cDefinition = iDefinition.ContainingType + Assert.Equal("C(Of T)", cDefinition.ToTestDisplayString()) + Assert.True(cDefinition.IsDefinition) + + ' Construct from cDefinition with modified T from cDefinition + Dim modifiedT = ImmutableArray.Create(New TypeWithModifiers(cDefinition.TypeParameters.Single(), modifiers)) + Dim c2 = cDefinition.Construct(TypeSubstitution.Create(cDefinition, cDefinition.TypeParameters, modifiedT)) + Dim i2 = c2.GetTypeMember("I") + Assert.Equal("C(Of T modopt(System.Object)).I(Of U)", i2.ToTestDisplayString()) + Assert.Same(i2.OriginalDefinition, iDefinition) + AssertHashCodesMatch(iDefinition, i2) + + ' Construct from i2 with U from iDefinition + Dim i2a = i2.Construct(iDefinition.TypeParameters.Single()) + Assert.Equal("C(Of T modopt(System.Object)).I(Of U)", i2a.ToTestDisplayString()) + AssertHashCodesMatch(iDefinition, i2a) + + ' Construct from i2 (reconstructed) with modified U from iDefinition + Dim i2b = iDefinition.Construct(TypeSubstitution.Create(iDefinition, + ImmutableArray.Create(cDefinition.TypeParameters.Single(), iDefinition.TypeParameters.Single()), + ImmutableArray.Create(modifiedT.Single(), modifiedU.Single()))) + Assert.Equal("C(Of T modopt(System.Object)).I(Of U modopt(System.Object))", i2b.ToTestDisplayString()) + AssertHashCodesMatch(iDefinition, i2b) + + ' Construct from cDefinition with modified T from cDefinition + Dim c4 = cDefinition.Construct(TypeSubstitution.Create(cDefinition, cDefinition.TypeParameters, modifiedT)) + Assert.Equal("C(Of T modopt(System.Object))", c4.ToTestDisplayString()) + Assert.False(c4.IsDefinition) + AssertHashCodesMatch(cDefinition, c4) + + Dim i4 = c4.GetTypeMember("I") + Assert.Equal("C(Of T modopt(System.Object)).I(Of U)", i4.ToTestDisplayString()) + Assert.Same(i4.OriginalDefinition, iDefinition) + AssertHashCodesMatch(iDefinition, i4) + End Sub + + + + Public Sub TypeSymbolGetHashCode_ContainingType_GenericNestedType_Nested() + Dim compilation = CompilationUtils.CreateCompilationWithMscorlib40( + + +Public Class C(Of T) + Public Class C2(Of U) + Public Interface I(Of V) + End Interface + End Class +End Class + +) + + AssertNoDeclarationDiagnostics(compilation) + Dim modifiers = ImmutableArray.Create(VisualBasicCustomModifier.CreateOptional(compilation.GetSpecialType(SpecialType.System_Object))) + + Dim iDefinition = compilation.GetMember(Of NamedTypeSymbol)("C.C2.I") + Assert.Equal("C(Of T).C2(Of U).I(Of V)", iDefinition.ToTestDisplayString()) + Assert.True(iDefinition.IsDefinition) + + Dim c2Definition = iDefinition.ContainingType + Dim cDefinition = c2Definition.ContainingType + Dim modifiedT = New TypeWithModifiers(cDefinition.TypeParameters.Single(), modifiers) + Dim modifiedU = New TypeWithModifiers(c2Definition.TypeParameters.Single(), modifiers) + Dim modifiedV = New TypeWithModifiers(iDefinition.TypeParameters.Single(), modifiers) + + Dim i = iDefinition.Construct(TypeSubstitution.Create(iDefinition, + ImmutableArray.Create(cDefinition.TypeParameters.Single(), c2Definition.TypeParameters.Single(), iDefinition.TypeParameters.Single()), + ImmutableArray.Create(modifiedT, modifiedU, modifiedV))) + Assert.Equal("C(Of T modopt(System.Object)).C2(Of U modopt(System.Object)).I(Of V modopt(System.Object))", i.ToTestDisplayString()) + AssertHashCodesMatch(iDefinition, i) + End Sub + + + + Public Sub TypeSymbolGetHashCode_SubstitutedErrorType() + Dim missing = CompilationUtils.CreateCompilationWithMscorlib40( + + +Public Class C(Of T) + Public Class D(Of U) + End Class +End Class + +) + AssertNoDeclarationDiagnostics(missing) + + Dim reference = CompilationUtils.CreateCompilationWithMscorlib40( + + +Public Class Reference(Of T, U) + Inherits C(Of T).D(Of U) +End Class + +, references:={missing.EmitToImageReference()}) + AssertNoDeclarationDiagnostics(reference) + + Dim compilation = CompilationUtils.CreateCompilationWithMscorlib40( + + +Public Class Program(Of V, W) + Inherits Reference(Of V, W) +End Class + +, references:={reference.EmitToImageReference()}) + + compilation.AssertTheseDiagnostics() + + Dim modifiers = ImmutableArray.Create(VisualBasicCustomModifier.CreateOptional(compilation.GetSpecialType(SpecialType.System_Object))) + + Dim programType = compilation.GlobalNamespace.GetTypeMember("Program") + Dim errorType = programType.BaseType.BaseType + + Dim definition = errorType.OriginalDefinition + Assert.Equal("Microsoft.CodeAnalysis.VisualBasic.Symbols.SubstitutedErrorType", errorType.GetType().ToString()) + Assert.Equal("C(Of )[missing].D(Of )[missing]", definition.ToTestDisplayString()) + Assert.True(definition.IsDefinition) + + ' Construct from definition with modified U from definition + Dim modifiedU = ImmutableArray.Create(New TypeWithModifiers(definition.TypeParameters.Single(), modifiers)) + Dim t1 = definition.Construct(TypeSubstitution.Create(definition, definition.TypeParameters, modifiedU)) + Assert.Equal("C(Of )[missing].D(Of modopt(System.Object))[missing]", t1.ToTestDisplayString()) + AssertHashCodesMatch(definition, t1) + End Sub + + Private Shared Sub AssertHashCodesMatch(c As TypeSymbol, c2 As TypeSymbol) + Assert.False(c.IsSameType(c2, TypeCompareKind.ConsiderEverything)) + Assert.True(c.IsSameType(c2, (TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds Or TypeCompareKind.IgnoreTupleNames))) + + Assert.Equal(c2.GetHashCode(), c.GetHashCode()) + End Sub End Class End Namespace