diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index e99825a728b33..fed56e7dc92a4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -114115,5 +114115,77 @@ public Base(bool b) {} // Program(int x) : base((T() || M(out string? s6)) && s6.Length > 1) {} // 7 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s6").WithLocation(48, 66)); } + + [Fact] + [WorkItem(38183, "https://github.com/dotnet/roslyn/issues/38183")] + public void UnboundGenericTypeReference_StructConstraint() + { + var source = +@"class Program +{ + static void Main(string[] args) + { + F>(); + } + + static void F() + { + if (typeof(T).GetGenericTypeDefinition() == typeof(Boxed<>)) + { + } + } +} + +class Boxed + where T : struct +{ + public bool Equals(Boxed? other) => + false; + + public override bool Equals(object? obj) => + Equals(obj as Boxed); + + public override int GetHashCode() => + 0; +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue(TestOptions.ReleaseExe)); + CompileAndVerify(comp, expectedOutput: ""); + } + + [Fact] + [WorkItem(38183, "https://github.com/dotnet/roslyn/issues/38183")] + public void UnboundGenericTypeReference_ClassConstraint() + { + var source = +@"class Program +{ + static void Main(string[] args) + { + F>(); + } + + static void F() + { + if (typeof(T).GetGenericTypeDefinition() == typeof(Boxed<>)) + { + } + } +} + +class Boxed + where T : class +{ + public bool Equals(Boxed? other) => + false; + + public override bool Equals(object? obj) => + Equals(obj as Boxed); + + public override int GetHashCode() => + 0; +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue(TestOptions.ReleaseExe)); + CompileAndVerify(comp, expectedOutput: ""); + } } } diff --git a/src/Compilers/Core/Portable/CodeGen/TokenMap.cs b/src/Compilers/Core/Portable/CodeGen/TokenMap.cs index c4588f678af63..c02730df23790 100644 --- a/src/Compilers/Core/Portable/CodeGen/TokenMap.cs +++ b/src/Compilers/Core/Portable/CodeGen/TokenMap.cs @@ -3,11 +3,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Threading; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeGen @@ -25,9 +21,14 @@ internal sealed class TokenMap where T : class { private readonly ConcurrentDictionary _itemIdentityToToken = new ConcurrentDictionary(ReferenceEqualityComparer.Instance); - private readonly Dictionary _itemToToken = new Dictionary(); + private readonly Dictionary _itemToToken; private readonly ArrayBuilder _items = new ArrayBuilder(); + internal TokenMap(IEqualityComparer comparer) + { + _itemToToken = new Dictionary(comparer); + } + public uint GetOrAddTokenFor(T item, out bool referenceAdded) { uint tmp; diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index f3b74224ef3e8..e189afa01b5ab 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -30,7 +30,7 @@ internal abstract class CommonPEModuleBuilder : Cci.IUnit, Cci.IModuleReference internal Cci.IMethodReference DebugEntryPoint; private readonly ConcurrentDictionary _methodBodyMap; - private readonly TokenMap _referencesInILMap = new TokenMap(); + private readonly TokenMap _referencesInILMap = new TokenMap(MetadataEntityReferenceComparer.ConsiderEverything); private readonly ItemTokenMap _stringsInILMap = new ItemTokenMap(); private readonly ItemTokenMap _sourceDocumentsInILMap = new ItemTokenMap(); diff --git a/src/Compilers/Core/Portable/Emit/MetadataEntityReferenceComparer.cs b/src/Compilers/Core/Portable/Emit/MetadataEntityReferenceComparer.cs new file mode 100644 index 0000000000000..3cbe08d1960f9 --- /dev/null +++ b/src/Compilers/Core/Portable/Emit/MetadataEntityReferenceComparer.cs @@ -0,0 +1,44 @@ +// 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 System.Collections.Generic; +using Microsoft.CodeAnalysis.Symbols; + +namespace Microsoft.CodeAnalysis.Emit +{ + internal sealed class MetadataEntityReferenceComparer : IEqualityComparer + { + internal static readonly MetadataEntityReferenceComparer ConsiderEverything = new MetadataEntityReferenceComparer(TypeCompareKind.ConsiderEverything); + + private readonly TypeCompareKind _compareKind; + + private MetadataEntityReferenceComparer(TypeCompareKind compareKind) + { + _compareKind = compareKind; + } + + public bool Equals(Cci.IReference x, Cci.IReference y) + { + if (x is null) + { + return y is null; + } + else if (ReferenceEquals(x, y)) + { + return true; + } + else if (x is ISymbolInternal sx && y is ISymbolInternal sy) + { + return sx.Equals(sy, _compareKind); + } + else + { + return x.Equals(y); + } + } + + public int GetHashCode(Cci.IReference obj) + { + return obj?.GetHashCode() ?? 0; + } + } +}