Skip to content

Commit

Permalink
Implement IEquatable<T> in record types (#44994)
Browse files Browse the repository at this point in the history
  • Loading branch information
cston authored Jun 11, 2020
1 parent de34e36 commit 12fbed0
Show file tree
Hide file tree
Showing 5 changed files with 626 additions and 8 deletions.
1 change: 0 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,6 @@ private static void LookupMembersInInterfaceOnly(
LookupMembersInInterfacesWithoutInheritance(current, GetBaseInterfaces(type, basesBeingResolved, ref useSiteDiagnostics),
name, arity, basesBeingResolved, options, originalBinder, accessThroughType, diagnose, ref useSiteDiagnostics);
}

}

private static ImmutableArray<NamedTypeSymbol> GetBaseInterfaces(NamedTypeSymbol type, ConsList<TypeSymbol> basesBeingResolved, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,11 +263,11 @@ private ImmutableArray<SynthesizedExplicitImplementationForwardingMethod> Comput

if (useSiteDiagnostic != null && useSiteDiagnostic.DefaultSeverity == DiagnosticSeverity.Error)
{
diagnostics.Add(useSiteDiagnostic, GetImplementsLocation(@interface));
diagnostics.Add(useSiteDiagnostic, GetImplementsLocationOrFallback(@interface));
}
else
{
diagnostics.Add(ErrorCode.ERR_UnimplementedInterfaceMember, GetImplementsLocation(@interface) ?? this.Locations[0], this, interfaceMember);
diagnostics.Add(ErrorCode.ERR_UnimplementedInterfaceMember, GetImplementsLocationOrFallback(@interface), this, interfaceMember);
}
}
}
Expand All @@ -277,7 +277,7 @@ private ImmutableArray<SynthesizedExplicitImplementationForwardingMethod> Comput
break;

case HasBaseTypeDeclaringInterfaceResult.IgnoringNullableMatch:
diagnostics.Add(ErrorCode.WRN_NullabilityMismatchInInterfaceImplementedByBase, GetImplementsLocation(@interface) ?? this.Locations[0], this, interfaceMember);
diagnostics.Add(ErrorCode.WRN_NullabilityMismatchInInterfaceImplementedByBase, GetImplementsLocationOrFallback(@interface), this, interfaceMember);
break;

default:
Expand Down Expand Up @@ -324,7 +324,14 @@ private ImmutableArray<SynthesizedExplicitImplementationForwardingMethod> Comput

protected abstract Location GetCorrespondingBaseListLocation(NamedTypeSymbol @base);

internal Location GetImplementsLocation(NamedTypeSymbol implementedInterface)
#nullable enable
private Location GetImplementsLocationOrFallback(NamedTypeSymbol implementedInterface)
{
return GetImplementsLocation(implementedInterface) ?? this.Locations[0];
}

internal Location? GetImplementsLocation(NamedTypeSymbol implementedInterface)
#nullable restore
{
// We ideally want to identify the interface location in the base list with an exact match but
// will fall back and use the first derived interface if exact interface is not present.
Expand Down Expand Up @@ -1381,6 +1388,8 @@ private static bool OverrideHasCorrectAccessibility(Symbol overridden, Symbol ov
}
}

#nullable enable

/// <summary>
/// It is invalid for a type to directly (vs through a base class) implement two interfaces that
/// unify (i.e. are the same for some substitution of type parameters).
Expand Down Expand Up @@ -1423,7 +1432,7 @@ private void CheckInterfaceUnification(DiagnosticBag diagnostics)
TypeSymbol.Equals(interface1.OriginalDefinition, interface2.OriginalDefinition, TypeCompareKind.ConsiderEverything2) &&
interface1.CanUnifyWith(interface2))
{
if (GetImplementsLocation(interface1).SourceSpan.Start > GetImplementsLocation(interface2).SourceSpan.Start)
if (GetImplementsLocationOrFallback(interface1).SourceSpan.Start > GetImplementsLocationOrFallback(interface2).SourceSpan.Start)
{
// Mention interfaces in order of their appearance in the base list, for consistency.
var temp = interface1;
Expand All @@ -1437,6 +1446,8 @@ private void CheckInterfaceUnification(DiagnosticBag diagnostics)
}
}

#nullable restore

/// <summary>
/// Though there is a method that C# considers to be an implementation of the interface method, that
/// method may not be considered an implementation by the CLR. In particular, implicit implementation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,16 @@ private Tuple<NamedTypeSymbol, ImmutableArray<NamedTypeSymbol>> MakeDeclaredBase

HashSet<DiagnosticInfo> useSiteDiagnostics = null;

if (declaration.Kind == DeclarationKind.Record)
{
var type = DeclaringCompilation.GetWellKnownType(WellKnownType.System_IEquatable_T).Construct(this);
if (baseInterfaces.IndexOf(type, SymbolEqualityComparer.AllIgnoreOptions) < 0)
{
baseInterfaces.Add(type);
type.AddUseSiteDiagnostics(ref useSiteDiagnostics);
}
}

if ((object)baseType != null)
{
Debug.Assert(baseTypeLocation != null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ internal sealed class SymbolEqualityComparer : EqualityComparer<Symbol>

internal static readonly EqualityComparer<Symbol> ObliviousNullableModifierMatchesAny = new SymbolEqualityComparer(TypeCompareKind.ObliviousNullableModifierMatchesAny);

internal static readonly EqualityComparer<Symbol> AllIgnoreOptions = new SymbolEqualityComparer(TypeCompareKind.AllIgnoreOptions);

internal static readonly EqualityComparer<Symbol> AllIgnoreOptionsPlusNullableWithUnknownMatchesAny =
new SymbolEqualityComparer(TypeCompareKind.AllIgnoreOptions & ~(TypeCompareKind.IgnoreNullableModifiersForReferenceTypes));

Expand Down
Loading

0 comments on commit 12fbed0

Please sign in to comment.