Skip to content

Commit 98b82ec

Browse files
authored
Consider nullability when comparing IReference instances during emit (#38249)
1 parent 036d931 commit 98b82ec

File tree

4 files changed

+123
-6
lines changed

4 files changed

+123
-6
lines changed

src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114115,5 +114115,77 @@ public Base(bool b) {}
114115114115
// Program(int x) : base((T() || M<string?>(out string? s6)) && s6.Length > 1) {} // 7
114116114116
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s6").WithLocation(48, 66));
114117114117
}
114118+
114119+
[Fact]
114120+
[WorkItem(38183, "https://github.com/dotnet/roslyn/issues/38183")]
114121+
public void UnboundGenericTypeReference_StructConstraint()
114122+
{
114123+
var source =
114124+
@"class Program
114125+
{
114126+
static void Main(string[] args)
114127+
{
114128+
F<Boxed<int>>();
114129+
}
114130+
114131+
static void F<T>()
114132+
{
114133+
if (typeof(T).GetGenericTypeDefinition() == typeof(Boxed<>))
114134+
{
114135+
}
114136+
}
114137+
}
114138+
114139+
class Boxed<T>
114140+
where T : struct
114141+
{
114142+
public bool Equals(Boxed<T>? other) =>
114143+
false;
114144+
114145+
public override bool Equals(object? obj) =>
114146+
Equals(obj as Boxed<T>);
114147+
114148+
public override int GetHashCode() =>
114149+
0;
114150+
}";
114151+
var comp = CreateCompilation(source, options: WithNonNullTypesTrue(TestOptions.ReleaseExe));
114152+
CompileAndVerify(comp, expectedOutput: "");
114153+
}
114154+
114155+
[Fact]
114156+
[WorkItem(38183, "https://github.com/dotnet/roslyn/issues/38183")]
114157+
public void UnboundGenericTypeReference_ClassConstraint()
114158+
{
114159+
var source =
114160+
@"class Program
114161+
{
114162+
static void Main(string[] args)
114163+
{
114164+
F<Boxed<object>>();
114165+
}
114166+
114167+
static void F<T>()
114168+
{
114169+
if (typeof(T).GetGenericTypeDefinition() == typeof(Boxed<>))
114170+
{
114171+
}
114172+
}
114173+
}
114174+
114175+
class Boxed<T>
114176+
where T : class
114177+
{
114178+
public bool Equals(Boxed<T>? other) =>
114179+
false;
114180+
114181+
public override bool Equals(object? obj) =>
114182+
Equals(obj as Boxed<T>);
114183+
114184+
public override int GetHashCode() =>
114185+
0;
114186+
}";
114187+
var comp = CreateCompilation(source, options: WithNonNullTypesTrue(TestOptions.ReleaseExe));
114188+
CompileAndVerify(comp, expectedOutput: "");
114189+
}
114118114190
}
114119114191
}

src/Compilers/Core/Portable/CodeGen/TokenMap.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@
33
using System;
44
using System.Collections.Concurrent;
55
using System.Collections.Generic;
6-
using System.Diagnostics;
7-
using System.Runtime.CompilerServices;
8-
using System.Threading;
96
using Microsoft.CodeAnalysis.PooledObjects;
10-
using Microsoft.CodeAnalysis.Text;
117
using Roslyn.Utilities;
128

139
namespace Microsoft.CodeAnalysis.CodeGen
@@ -25,9 +21,14 @@ internal sealed class TokenMap<T> where T : class
2521
{
2622
private readonly ConcurrentDictionary<T, uint> _itemIdentityToToken = new ConcurrentDictionary<T, uint>(ReferenceEqualityComparer.Instance);
2723

28-
private readonly Dictionary<T, uint> _itemToToken = new Dictionary<T, uint>();
24+
private readonly Dictionary<T, uint> _itemToToken;
2925
private readonly ArrayBuilder<T> _items = new ArrayBuilder<T>();
3026

27+
internal TokenMap(IEqualityComparer<T> comparer)
28+
{
29+
_itemToToken = new Dictionary<T, uint>(comparer);
30+
}
31+
3132
public uint GetOrAddTokenFor(T item, out bool referenceAdded)
3233
{
3334
uint tmp;

src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal abstract class CommonPEModuleBuilder : Cci.IUnit, Cci.IModuleReference
3030
internal Cci.IMethodReference DebugEntryPoint;
3131

3232
private readonly ConcurrentDictionary<IMethodSymbol, Cci.IMethodBody> _methodBodyMap;
33-
private readonly TokenMap<Cci.IReference> _referencesInILMap = new TokenMap<Cci.IReference>();
33+
private readonly TokenMap<Cci.IReference> _referencesInILMap = new TokenMap<Cci.IReference>(MetadataEntityReferenceComparer.ConsiderEverything);
3434
private readonly ItemTokenMap<string> _stringsInILMap = new ItemTokenMap<string>();
3535
private readonly ItemTokenMap<Cci.DebugSourceDocument> _sourceDocumentsInILMap = new ItemTokenMap<Cci.DebugSourceDocument>();
3636

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using System.Collections.Generic;
4+
using Microsoft.CodeAnalysis.Symbols;
5+
6+
namespace Microsoft.CodeAnalysis.Emit
7+
{
8+
internal sealed class MetadataEntityReferenceComparer : IEqualityComparer<Cci.IReference>
9+
{
10+
internal static readonly MetadataEntityReferenceComparer ConsiderEverything = new MetadataEntityReferenceComparer(TypeCompareKind.ConsiderEverything);
11+
12+
private readonly TypeCompareKind _compareKind;
13+
14+
private MetadataEntityReferenceComparer(TypeCompareKind compareKind)
15+
{
16+
_compareKind = compareKind;
17+
}
18+
19+
public bool Equals(Cci.IReference x, Cci.IReference y)
20+
{
21+
if (x is null)
22+
{
23+
return y is null;
24+
}
25+
else if (ReferenceEquals(x, y))
26+
{
27+
return true;
28+
}
29+
else if (x is ISymbolInternal sx && y is ISymbolInternal sy)
30+
{
31+
return sx.Equals(sy, _compareKind);
32+
}
33+
else
34+
{
35+
return x.Equals(y);
36+
}
37+
}
38+
39+
public int GetHashCode(Cci.IReference obj)
40+
{
41+
return obj?.GetHashCode() ?? 0;
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)