Skip to content

Commit

Permalink
Simplify equivalence comparers
Browse files Browse the repository at this point in the history
  • Loading branch information
CyrusNajmabadi committed Aug 28, 2024
1 parent a3dbd80 commit 842e645
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,33 @@ namespace Microsoft.CodeAnalysis.Shared.Utilities;

internal partial class SymbolEquivalenceComparer
{
private class EquivalenceVisitor(
private sealed class EquivalenceVisitor(
SymbolEquivalenceComparer symbolEquivalenceComparer,
bool compareMethodTypeParametersByIndex,
bool objectAndDynamicCompareEqually)
bool compareMethodTypeParametersByIndex)
{

public bool AreEquivalent(ISymbol? x, ISymbol? y, Dictionary<INamedTypeSymbol, INamedTypeSymbol>? equivalentTypesWithDifferingAssemblies)
{
if (ReferenceEquals(x, y))
{
return true;
}

if (x == null || y == null)
{
return false;
}

var xKind = GetKindAndUnwrapAlias(ref x);
var yKind = GetKindAndUnwrapAlias(ref y);

// Normally, if they're different types, then they're not the same.
if (xKind != yKind)
{
// Special case. If we're comparing signatures then we want to compare 'object'
// and 'dynamic' as the same. However, since they're different types, we don't
// want to bail out using the above check.
if (objectAndDynamicCompareEqually)
// Special case. If we're comparing signatures then we want to compare 'object' and 'dynamic' as the
// same. However, since they're different types, we don't want to bail out using the above check.
if (symbolEquivalenceComparer._objectAndDynamicCompareEqually)
{
return (xKind == SymbolKind.DynamicType && IsObjectType(y)) ||
(yKind == SymbolKind.DynamicType && IsObjectType(x));
if ((xKind == SymbolKind.DynamicType && IsObjectType(y)) ||
(yKind == SymbolKind.DynamicType && IsObjectType(x)))
{
return true;
}
}

return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,19 @@ namespace Microsoft.CodeAnalysis.Shared.Utilities;

internal partial class SymbolEquivalenceComparer
{
private class GetHashCodeVisitor
private sealed class GetHashCodeVisitor
{
private readonly SymbolEquivalenceComparer _symbolEquivalenceComparer;
private readonly bool _compareMethodTypeParametersByIndex;
private readonly bool _objectAndDynamicCompareEqually;
private readonly Func<int, IParameterSymbol, int> _parameterAggregator;
private readonly Func<int, ISymbol, int> _symbolAggregator;

public GetHashCodeVisitor(
SymbolEquivalenceComparer symbolEquivalenceComparer,
bool compareMethodTypeParametersByIndex,
bool objectAndDynamicCompareEqually)
bool compareMethodTypeParametersByIndex)
{
_symbolEquivalenceComparer = symbolEquivalenceComparer;
_compareMethodTypeParametersByIndex = compareMethodTypeParametersByIndex;
_objectAndDynamicCompareEqually = objectAndDynamicCompareEqually;
_parameterAggregator = (acc, sym) => Hash.Combine(symbolEquivalenceComparer.ParameterEquivalenceComparer.GetHashCode(sym), acc);
_symbolAggregator = (acc, sym) => GetHashCode(sym, acc);
}
Expand All @@ -45,7 +42,7 @@ public int GetHashCode(ISymbol? x, int currentHash)
// want to bail out using the above check.

if (x.Kind == SymbolKind.DynamicType ||
(_objectAndDynamicCompareEqually && IsObjectType(x)))
(_symbolEquivalenceComparer._objectAndDynamicCompareEqually && IsObjectType(x)))
{
return Hash.Combine(GetNullableAnnotationsHashCode((ITypeSymbol)x), Hash.Combine(typeof(IDynamicTypeSymbol), currentHash));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ namespace Microsoft.CodeAnalysis.Shared.Utilities;

internal partial class SymbolEquivalenceComparer
{
internal class SignatureTypeSymbolEquivalenceComparer(SymbolEquivalenceComparer symbolEquivalenceComparer) : IEqualityComparer<ITypeSymbol?>
internal sealed class SignatureTypeSymbolEquivalenceComparer(SymbolEquivalenceComparer symbolEquivalenceComparer) : IEqualityComparer<ITypeSymbol?>
{
public bool Equals(ITypeSymbol? x, ITypeSymbol? y)
=> this.Equals(x, y, null);

public bool Equals(ITypeSymbol? x, ITypeSymbol? y, Dictionary<INamedTypeSymbol, INamedTypeSymbol>? equivalentTypesWithDifferingAssemblies)
=> symbolEquivalenceComparer.GetEquivalenceVisitor(compareMethodTypeParametersByIndex: true, symbolEquivalenceComparer._objectAndDynamicCompareEqually).AreEquivalent(x, y, equivalentTypesWithDifferingAssemblies);
=> symbolEquivalenceComparer.GetEquivalenceVisitor(compareMethodTypeParametersByIndex: true).AreEquivalent(x, y, equivalentTypesWithDifferingAssemblies);

public int GetHashCode(ITypeSymbol? x)
=> symbolEquivalenceComparer.GetGetHashCodeVisitor(compareMethodTypeParametersByIndex: true, symbolEquivalenceComparer._objectAndDynamicCompareEqually).GetHashCode(x, currentHash: 0);
=> symbolEquivalenceComparer.GetGetHashCodeVisitor(compareMethodTypeParametersByIndex: true).GetHashCode(x, currentHash: 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,18 @@ public SymbolEquivalenceComparer(
using var equivalenceVisitors = TemporaryArray<EquivalenceVisitor>.Empty;
using var getHashCodeVisitors = TemporaryArray<GetHashCodeVisitor>.Empty;

AddVisitors(compareMethodTypeParametersByIndex: true, objectAndDynamicCompareEqually: true);
AddVisitors(compareMethodTypeParametersByIndex: true, objectAndDynamicCompareEqually: false);
AddVisitors(compareMethodTypeParametersByIndex: false, objectAndDynamicCompareEqually: true);
AddVisitors(compareMethodTypeParametersByIndex: false, objectAndDynamicCompareEqually: false);
AddVisitors(compareMethodTypeParametersByIndex: true);
AddVisitors(compareMethodTypeParametersByIndex: false);

_equivalenceVisitors = equivalenceVisitors.ToImmutableAndClear();
_getHashCodeVisitors = getHashCodeVisitors.ToImmutableAndClear();

return;

void AddVisitors(bool compareMethodTypeParametersByIndex, bool objectAndDynamicCompareEqually)
void AddVisitors(bool compareMethodTypeParametersByIndex)
{
equivalenceVisitors.Add(new(this, compareMethodTypeParametersByIndex, objectAndDynamicCompareEqually));
getHashCodeVisitors.Add(new(this, compareMethodTypeParametersByIndex, objectAndDynamicCompareEqually));
equivalenceVisitors.Add(new(this, compareMethodTypeParametersByIndex));
getHashCodeVisitors.Add(new(this, compareMethodTypeParametersByIndex));
}
}

Expand All @@ -107,27 +105,21 @@ public static SymbolEquivalenceComparer Create(
// here. So, instead, when asking if parameters are equal, we pass an appropriate flag so
// that method type parameters are just compared by index and nothing else.
private EquivalenceVisitor GetEquivalenceVisitor(
bool compareMethodTypeParametersByIndex = false, bool objectAndDynamicCompareEqually = false)
bool compareMethodTypeParametersByIndex = false)
{
var visitorIndex = GetVisitorIndex(compareMethodTypeParametersByIndex, objectAndDynamicCompareEqually);
var visitorIndex = GetVisitorIndex(compareMethodTypeParametersByIndex);
return _equivalenceVisitors[visitorIndex];
}

private GetHashCodeVisitor GetGetHashCodeVisitor(
bool compareMethodTypeParametersByIndex, bool objectAndDynamicCompareEqually)
bool compareMethodTypeParametersByIndex)
{
var visitorIndex = GetVisitorIndex(compareMethodTypeParametersByIndex, objectAndDynamicCompareEqually);
var visitorIndex = GetVisitorIndex(compareMethodTypeParametersByIndex);
return _getHashCodeVisitors[visitorIndex];
}

private static int GetVisitorIndex(bool compareMethodTypeParametersByIndex, bool objectAndDynamicCompareEqually)
=> (compareMethodTypeParametersByIndex, objectAndDynamicCompareEqually) switch
{
(true, true) => 0,
(true, false) => 1,
(false, true) => 2,
(false, false) => 3,
};
private static int GetVisitorIndex(bool compareMethodTypeParametersByIndex)
=> compareMethodTypeParametersByIndex ? 0 : 1;

public bool ReturnTypeEquals(IMethodSymbol x, IMethodSymbol y, Dictionary<INamedTypeSymbol, INamedTypeSymbol>? equivalentTypesWithDifferingAssemblies = null)
=> GetEquivalenceVisitor().ReturnTypesAreEquivalent(x, y, equivalentTypesWithDifferingAssemblies);
Expand All @@ -154,7 +146,7 @@ private bool EqualsCore(ISymbol? x, ISymbol? y, Dictionary<INamedTypeSymbol, INa
=> GetEquivalenceVisitor().AreEquivalent(x, y, equivalentTypesWithDifferingAssemblies);

public int GetHashCode(ISymbol? x)
=> GetGetHashCodeVisitor(compareMethodTypeParametersByIndex: false, objectAndDynamicCompareEqually: false).GetHashCode(x, currentHash: 0);
=> GetGetHashCodeVisitor(compareMethodTypeParametersByIndex: false).GetHashCode(x, currentHash: 0);

private static ISymbol UnwrapAlias(ISymbol symbol)
=> symbol.IsKind(SymbolKind.Alias, out IAliasSymbol? alias) ? alias.Target : symbol;
Expand Down

0 comments on commit 842e645

Please sign in to comment.