未验证 提交 9b82fcd1 编写于 作者: C Charles Stoner 提交者: GitHub

Records: Support EqualityContract in Equals (#44882)

上级 c9adb9f7
......@@ -4,7 +4,6 @@
#nullable enable
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.PooledObjects;
......@@ -249,18 +248,18 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,
/// Contains methods related to synthesizing bound nodes in lowered form
/// that does not need any processing before passing to codegen
/// </summary>
internal static partial class MethodBodySynthesizer
internal static class MethodBodySynthesizer
{
/// <summary>
/// Given a set of fields, produce an expression that is true when all of the given fields on
/// `this` are equal to the fields on <paramref name="otherReceiver" /> according to the
/// default EqualityComparer.
/// </summary>
public static BoundExpression GenerateFieldEquals<TList>(
public static BoundExpression GenerateFieldEquals(
BoundExpression? initialExpression,
BoundExpression otherReceiver,
TList fields,
SyntheticBoundNodeFactory F) where TList : IReadOnlyList<FieldSymbol>
ArrayBuilder<FieldSymbol> fields,
SyntheticBoundNodeFactory F)
{
Debug.Assert(fields.Count > 0);
......
......@@ -2,20 +2,15 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
/// <summary>
/// A structure used to lexically order symbols. For performance, it's important that this be
/// a STRUCTURE, and be able to be returned from a symbol without doing any additional allocations (even
/// if nothing is cached yet.)
/// if nothing is cached yet).
/// </summary>
internal struct LexicalSortKey
{
......@@ -43,18 +38,14 @@ public int Position
public static readonly LexicalSortKey NotInitialized = new LexicalSortKey() { _treeOrdinal = -1, _position = -1 };
// Put Record Equals right before synthesized constructors.
public static LexicalSortKey SynthesizedRecordEquals => new LexicalSortKey() { _treeOrdinal = int.MaxValue, _position = int.MaxValue - 5 };
public static LexicalSortKey SynthesizedRecordObjEquals => new LexicalSortKey() { _treeOrdinal = int.MaxValue, _position = int.MaxValue - 4 };
// Put other synthesized members right before synthesized constructors.
public static LexicalSortKey GetSynthesizedMemberKey(int offset) => new LexicalSortKey() { _treeOrdinal = int.MaxValue, _position = int.MaxValue - 2 - offset };
// Dev12 compiler adds synthetic constructors to the child list after adding all other members.
// Methods are emitted in the children order, but synthetic cctors would be deferred
// until later when it is known if they can be optimized or not.
// As a result the last emitted method tokens are synthetic ctor and then synthetic cctor (if not optimized)
// Since it is not too hard, we will try keeping the same order just to be easy on metadata diffing tools and such.
public static readonly LexicalSortKey SynthesizedRecordCopyCtor = new LexicalSortKey() { _treeOrdinal = int.MaxValue, _position = int.MaxValue - 3 };
public static readonly LexicalSortKey SynthesizedRecordCtor = new LexicalSortKey() { _treeOrdinal = int.MaxValue, _position = int.MaxValue - 2 };
public static readonly LexicalSortKey SynthesizedCtor = new LexicalSortKey() { _treeOrdinal = int.MaxValue, _position = int.MaxValue - 1 };
public static readonly LexicalSortKey SynthesizedCCtor = new LexicalSortKey() { _treeOrdinal = int.MaxValue, _position = int.MaxValue };
......
......@@ -2989,10 +2989,16 @@ private void AddSynthesizedRecordMembersIfNecessary(MembersAndInitializersBuilde
addCopyCtor();
addCloneMethod();
var thisEquals = addThisEquals();
PropertySymbol equalityContract = addEqualityContract();
var otherEqualsMethods = ArrayBuilder<MethodSymbol>.GetInstance();
getOtherEquals(otherEqualsMethods, equalityContract);
var thisEquals = addThisEquals(equalityContract, otherEqualsMethod: otherEqualsMethods.Count == 0 ? null : otherEqualsMethods[0]);
addOtherEquals(otherEqualsMethods, equalityContract, thisEquals);
addObjectEquals(thisEquals);
addHashCode();
otherEqualsMethods.Free();
memberSignatures.Free();
return;
......@@ -3015,7 +3021,7 @@ SynthesizedRecordConstructor addCtor(RecordDeclarationSyntax declWithParameters)
void addCopyCtor()
{
var ctor = new SynthesizedRecordCopyCtor(this, diagnostics);
var ctor = new SynthesizedRecordCopyCtor(this, memberOffset: members.Count);
if (!memberSignatures.ContainsKey(ctor))
{
members.Add(ctor);
......@@ -3087,7 +3093,7 @@ static bool hidesInheritedMember(Symbol symbol, NamedTypeSymbol type)
void addObjectEquals(MethodSymbol thisEquals)
{
var objEquals = new SynthesizedRecordObjEquals(this, thisEquals);
var objEquals = new SynthesizedRecordObjEquals(this, thisEquals, memberOffset: members.Count);
if (!memberSignatures.ContainsKey(objEquals))
{
// https://github.com/dotnet/roslyn/issues/44617: Don't add if the overridden method is sealed
......@@ -3097,7 +3103,7 @@ void addObjectEquals(MethodSymbol thisEquals)
void addHashCode()
{
var hashCode = new SynthesizedRecordGetHashCode(this);
var hashCode = new SynthesizedRecordGetHashCode(this, memberOffset: members.Count);
if (!memberSignatures.ContainsKey(hashCode))
{
// https://github.com/dotnet/roslyn/issues/44617: Don't add if the overridden method is sealed
......@@ -3105,9 +3111,35 @@ void addHashCode()
}
}
MethodSymbol addThisEquals()
static PropertySymbol? getInheritedEqualityContract(NamedTypeSymbol type)
{
var thisEquals = new SynthesizedRecordEquals(this);
while ((type = type.BaseTypeNoUseSiteDiagnostics) is object)
{
var members = type.GetMembers(SynthesizedRecordEqualityContractProperty.PropertyName);
// https://github.com/dotnet/roslyn/issues/44903: Check explicit member has expected signature.
if (members.FirstOrDefault(m => m is PropertySymbol property && property.ParameterCount == 0) is PropertySymbol property)
{
return property;
}
}
return null;
}
PropertySymbol addEqualityContract()
{
var property = new SynthesizedRecordEqualityContractProperty(this, isOverride: getInheritedEqualityContract(this) is object);
// https://github.com/dotnet/roslyn/issues/44903: Check explicit member has expected signature.
if (!memberSignatures.ContainsKey(property))
{
members.Add(property);
members.Add(property.GetMethod);
}
return property;
}
MethodSymbol addThisEquals(PropertySymbol equalityContract, MethodSymbol? otherEqualsMethod)
{
var thisEquals = new SynthesizedRecordEquals(this, parameterType: this, isOverride: false, equalityContract, otherEqualsMethod, memberOffset: members.Count);
if (!memberSignatures.TryGetValue(thisEquals, out var existing))
{
members.Add(thisEquals);
......@@ -3115,6 +3147,48 @@ MethodSymbol addThisEquals()
}
return (MethodSymbol)existing;
}
static void getOtherEquals(ArrayBuilder<MethodSymbol> otherEqualsMethods, PropertySymbol equalityContract)
{
while ((equalityContract = equalityContract.OverriddenProperty) is object)
{
var member = equalityContract.ContainingType.GetMembers("Equals").FirstOrDefault(m =>
{
if (m is MethodSymbol method)
{
var parameters = method.Parameters;
if (parameters.Length == 1 && parameters[0].Type.Equals(m.ContainingType, TypeCompareKind.AllIgnoreOptions))
{
return true;
}
}
return false;
});
// https://github.com/dotnet/roslyn/issues/44903: Check explicit member has expected signature.
if (member is MethodSymbol method)
{
otherEqualsMethods.Add(method);
}
}
}
void addOtherEquals(ArrayBuilder<MethodSymbol> otherEqualsMethods, PropertySymbol equalityContract, MethodSymbol thisEquals)
{
foreach (var otherEqualsMethod in otherEqualsMethods)
{
var method = new SynthesizedRecordEquals(
this,
parameterType: otherEqualsMethod.Parameters[0].Type,
isOverride: true,
equalityContract,
otherEqualsMethod: thisEquals,
memberOffset: members.Count);
if (!memberSignatures.ContainsKey(method))
{
members.Add(method);
}
}
}
}
private void AddSynthesizedConstructorsIfNecessary(ArrayBuilder<Symbol> members, ArrayBuilder<ArrayBuilder<FieldOrPropertyInitializer.Builder>> staticInitializers, DiagnosticBag diagnostics)
......
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class SynthesizedRecordCopyCtor : SynthesizedInstanceConstructor
{
private readonly int _memberOffset;
public SynthesizedRecordCopyCtor(
SourceMemberContainerTypeSymbol containingType,
DiagnosticBag diagnostics)
int memberOffset)
: base(containingType)
{
_memberOffset = memberOffset;
Parameters = ImmutableArray.Create(SynthesizedParameterSymbol.Create(
this,
TypeWithAnnotations.Create(
......@@ -29,12 +30,7 @@ internal sealed class SynthesizedRecordCopyCtor : SynthesizedInstanceConstructor
public override ImmutableArray<ParameterSymbol> Parameters { get; }
internal override LexicalSortKey GetLexicalSortKey()
{
// We need a separate sort key because struct records will have two synthesized
// constructors: the record constructor, and the parameterless constructor
return LexicalSortKey.SynthesizedRecordCopyCtor;
}
internal override LexicalSortKey GetLexicalSortKey() => LexicalSortKey.GetSynthesizedMemberKey(_memberOffset);
internal override void GenerateMethodBodyStatements(SyntheticBoundNodeFactory F, ArrayBuilder<BoundStatement> statements, DiagnosticBag diagnostics)
{
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Reflection;
using Microsoft.Cci;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class SynthesizedRecordEqualityContractProperty : PropertySymbol
{
internal const string PropertyName = "EqualityContract";
public SynthesizedRecordEqualityContractProperty(NamedTypeSymbol containingType, bool isOverride)
{
ContainingType = containingType;
IsOverride = isOverride;
TypeWithAnnotations = TypeWithAnnotations.Create(containingType.DeclaringCompilation.GetWellKnownType(WellKnownType.System_Type), NullableAnnotation.NotAnnotated);
GetMethod = new GetAccessorSymbol(this);
}
public override NamedTypeSymbol ContainingType { get; }
public override MethodSymbol GetMethod { get; }
public override MethodSymbol? SetMethod => null;
public override RefKind RefKind => RefKind.None;
public override TypeWithAnnotations TypeWithAnnotations { get; }
public override ImmutableArray<CustomModifier> RefCustomModifiers => ImmutableArray<CustomModifier>.Empty;
public override ImmutableArray<ParameterSymbol> Parameters => ImmutableArray<ParameterSymbol>.Empty;
public override bool IsIndexer => false;
public override bool IsImplicitlyDeclared => true;
public override ImmutableArray<PropertySymbol> ExplicitInterfaceImplementations => ImmutableArray<PropertySymbol>.Empty;
public override Symbol ContainingSymbol => ContainingType;
public override ImmutableArray<Location> Locations => ContainingType.Locations;
public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences => ImmutableArray<SyntaxReference>.Empty;
public override Accessibility DeclaredAccessibility => Accessibility.Protected;
public override bool IsStatic => false;
public override bool IsVirtual => true;
public override bool IsOverride { get; }
public override bool IsAbstract => false;
public override bool IsSealed => false;
public override bool IsExtern => false;
internal override bool HasSpecialName => false;
internal override CallingConvention CallingConvention => CallingConvention.HasThis;
internal override bool MustCallMethodsDirectly => false;
internal override ObsoleteAttributeData? ObsoleteAttributeData => null;
public override string Name => PropertyName;
public override ImmutableArray<CSharpAttributeData> GetAttributes() => ImmutableArray<CSharpAttributeData>.Empty;
private sealed class GetAccessorSymbol : SynthesizedInstanceMethodSymbol
{
private readonly SynthesizedRecordEqualityContractProperty _property;
public GetAccessorSymbol(SynthesizedRecordEqualityContractProperty property)
{
_property = property;
Name = SourcePropertyAccessorSymbol.GetAccessorName(
PropertyName,
getNotSet: true,
isWinMdOutput: false /* unused for getters */);
}
public override string Name { get; }
public override MethodKind MethodKind => MethodKind.PropertyGet;
public override int Arity => 0;
public override bool IsExtensionMethod => false;
public override bool HidesBaseMethodsByName => false;
public override bool IsVararg => false;
public override bool ReturnsVoid => false;
public override bool IsAsync => false;
public override RefKind RefKind => RefKind.None;
public override TypeWithAnnotations ReturnTypeWithAnnotations => _property.TypeWithAnnotations;
public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None;
public override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations => ImmutableArray<TypeWithAnnotations>.Empty;
public override ImmutableArray<TypeParameterSymbol> TypeParameters => ImmutableArray<TypeParameterSymbol>.Empty;
public override ImmutableArray<ParameterSymbol> Parameters => _property.Parameters;
public override ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations => ImmutableArray<MethodSymbol>.Empty;
public override ImmutableArray<CustomModifier> RefCustomModifiers => _property.RefCustomModifiers;
public override Symbol AssociatedSymbol => _property;
public override Symbol ContainingSymbol => _property.ContainingSymbol;
public override ImmutableArray<Location> Locations => _property.Locations;
public override Accessibility DeclaredAccessibility => _property.DeclaredAccessibility;
public override bool IsStatic => _property.IsStatic;
public override bool IsVirtual => _property.IsVirtual;
public override bool IsOverride => _property.IsOverride;
public override bool IsAbstract => _property.IsAbstract;
public override bool IsSealed => _property.IsSealed;
public override bool IsExtern => _property.IsExtern;
public override ImmutableHashSet<string> ReturnNotNullIfParameterNotNull => ImmutableHashSet<string>.Empty;
internal override bool HasSpecialName => _property.HasSpecialName;
internal override MethodImplAttributes ImplementationAttributes => MethodImplAttributes.Managed;
internal override bool HasDeclarativeSecurity => false;
internal override MarshalPseudoCustomAttributeData? ReturnValueMarshallingInformation => null;
internal override bool RequiresSecurityObject => false;
internal override CallingConvention CallingConvention => CallingConvention.HasThis;
internal override bool GenerateDebugInfo => false;
public override DllImportData? GetDllImportData() => null;
internal override ImmutableArray<string> GetAppliedConditionalSymbols()
=> ImmutableArray<string>.Empty;
internal override IEnumerable<SecurityAttribute> GetSecurityInformation()
=> Array.Empty<SecurityAttribute>();
internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => !IsOverride;
internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) => true;
internal override bool SynthesizesLoweredBoundBody => true;
internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
{
var F = new SyntheticBoundNodeFactory(this, this.GetNonNullSyntaxNode(), compilationState, diagnostics);
F.CurrentFunction = this;
F.CloseMethod(F.Block(F.Return(F.Typeof(ContainingType))));
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
......@@ -13,17 +13,48 @@
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
/// <summary>
/// A strongly-typed `public bool Equals(T other)` method.
/// There are two types of strongly-typed Equals methods:
/// the strongly-typed virtual method where T is the containing type; and
/// overrides of the strongly-typed virtual methods from base record types.
/// </summary>
internal sealed class SynthesizedRecordEquals : SynthesizedInstanceMethodSymbol
{
private readonly PropertySymbol _equalityContract;
private readonly MethodSymbol? _otherEqualsMethod;
private readonly int _memberOffset;
public override NamedTypeSymbol ContainingType { get; }
public SynthesizedRecordEquals(NamedTypeSymbol containingType)
public SynthesizedRecordEquals(
NamedTypeSymbol containingType,
TypeSymbol parameterType,
bool isOverride,
PropertySymbol equalityContract,
MethodSymbol? otherEqualsMethod,
int memberOffset)
{
var compilation = containingType.DeclaringCompilation;
bool isStruct = parameterType.IsStructType();
_equalityContract = equalityContract;
_otherEqualsMethod = otherEqualsMethod;
_memberOffset = memberOffset;
ContainingType = containingType;
if (containingType.IsStructType())
IsOverride = isOverride;
Parameters = ImmutableArray.Create(SynthesizedParameterSymbol.Create(
this,
TypeWithAnnotations.Create(parameterType, nullableAnnotation: isStruct ? NullableAnnotation.NotAnnotated : NullableAnnotation.Annotated),
ordinal: 0,
isStruct ? RefKind.In : RefKind.None));
ReturnTypeWithAnnotations = TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Boolean));
if (isStruct)
{
// If the record type is a struct, the parameter is marked 'in'
containingType.DeclaringCompilation.EnsureIsReadOnlyAttributeExists(
compilation.EnsureIsReadOnlyAttributeExists(
diagnostics: null,
location: Location.None,
modifyCompilation: true);
......@@ -48,19 +79,9 @@ public SynthesizedRecordEquals(NamedTypeSymbol containingType)
public override RefKind RefKind => RefKind.None;
public override ImmutableArray<ParameterSymbol> Parameters
=> ImmutableArray.Create<ParameterSymbol>(SynthesizedParameterSymbol.Create(
this,
TypeWithAnnotations.Create(
isNullableEnabled: true,
ContainingType,
isAnnotated: true),
ordinal: 0,
ContainingType.IsStructType() ? RefKind.In : RefKind.None));
public override ImmutableArray<ParameterSymbol> Parameters { get; }
public override TypeWithAnnotations ReturnTypeWithAnnotations => TypeWithAnnotations.Create(
isNullableEnabled: true,
ContainingType.DeclaringCompilation.GetSpecialType(SpecialType.System_Boolean));
public override TypeWithAnnotations ReturnTypeWithAnnotations { get; }
public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None;
......@@ -85,9 +106,9 @@ public override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations
public override bool IsStatic => false;
public override bool IsVirtual => false;
public override bool IsVirtual => true;
public override bool IsOverride => false;
public override bool IsOverride { get; }
public override bool IsAbstract => false;
......@@ -97,7 +118,7 @@ public override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations
internal override bool HasSpecialName => false;
internal override LexicalSortKey GetLexicalSortKey() => LexicalSortKey.SynthesizedRecordEquals;
internal override LexicalSortKey GetLexicalSortKey() => LexicalSortKey.GetSynthesizedMemberKey(_memberOffset);
internal override MethodImplAttributes ImplementationAttributes => MethodImplAttributes.Managed;
......@@ -119,48 +140,119 @@ internal override ImmutableArray<string> GetAppliedConditionalSymbols()
internal override IEnumerable<SecurityAttribute> GetSecurityInformation()
=> Array.Empty<SecurityAttribute>();
internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false;
internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => !IsOverride;
internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) => false;
internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) => true;
internal override bool SynthesizesLoweredBoundBody => true;
// Consider the following types:
// record A(int X);
// record B(int X, int Y) : A(X);
// record C(int X, int Y, int Z) : B(X, Y);
//
// Each record class defines a strongly-typed Equals method, with derived
// types overriding the methods from base classes:
// class A
// {
// public virtual bool Equals(A other) => other != null && EqualityContract == other.EqualityContract && X == other.X;
// }
// class B : A
// {
// public virtual bool Equals(B other) => base.Equals((A)other) && Y == other.Y;
// public override bool Equals(A other) => Equals(other as B);
// }
// class C : B
// {
// public virtual bool Equals(C other) => base.Equals((B)other) && Z == other.Z;
// public override bool Equals(B other) => Equals(other as C);
// public override bool Equals(A other) => Equals(other as C);
// }
internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics)
{
var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics);
// Compare all of the record properties in this class with the argument properties
// Body:
// {
// return other != null && `comparisons`;
// }
var other = F.Parameter(Parameters[0]);
BoundExpression? retExpr = null;
if (!ContainingType.IsStructType())
BoundExpression? retExpr;
if (IsOverride)
{
retExpr = F.ObjectNotEqual(other, F.Null(F.SpecialType(SpecialType.System_Object)));
// This method is an override of a strongly-typed Equals method from a base record type.
// The definition of the method is as follows, and _otherEqualsMethod
// is the method to delegate to (see B.Equals(A), C.Equals(A), C.Equals(B) above):
//
// override bool Equals(Base other) => Equals(other as Derived);
retExpr = F.Call(
F.This(),
_otherEqualsMethod!,
F.As(other, ContainingType));
}
var fields = ArrayBuilder<FieldSymbol>.GetInstance();
foreach (var f in ContainingType.GetFieldsToEmit())
else
{
if (!f.IsStatic)
// This method is the strongly-typed Equals method where the parameter type is
// the containing type.
if (_otherEqualsMethod is null)
{
fields.Add(f);
// There are no base record types.
// The definition of the method is as follows (see A.Equals(A) above):
//
// virtual bool Equals(T other) =>
// other != null &&
// EqualityContract == other.EqualityContract &&
// field1 == other.field1 && ... && fieldN == other.fieldN;
// other != null
retExpr = other.Type.IsStructType() ?
null :
F.ObjectNotEqual(other, F.Null(F.SpecialType(SpecialType.System_Object)));
// EqualityContract == other.EqualityContract
var contractsEqual = F.Binary(
BinaryOperatorKind.ObjectEqual,
F.SpecialType(SpecialType.System_Boolean),
F.Property(F.This(), _equalityContract),
F.Property(other, _equalityContract));
retExpr = retExpr is null ? contractsEqual : F.LogicalAnd(retExpr, contractsEqual);
}
else
{
// There are base record types.
// The definition of the method is as follows, and _otherEqualsMethod
// is the corresponding method on the nearest base record type to
// delegate to (see B.Equals(B), C.Equals(C) above):
//
// virtual bool Equals(Derived other) =>
// base.Equals((Base)other) &&
// field1 == other.field1 && ... && fieldN == other.fieldN;
retExpr = F.Call(
F.Base(_otherEqualsMethod.ContainingType),
_otherEqualsMethod!,
F.Convert(_otherEqualsMethod.Parameters[0].Type, other));
}
// field1 == other.field1 && ... && fieldN == other.fieldN
// https://github.com/dotnet/roslyn/issues/44895: Should compare fields from non-record base classes.
var fields = ArrayBuilder<FieldSymbol>.GetInstance();
foreach (var f in ContainingType.GetFieldsToEmit())
{
if (!f.IsStatic)
{
fields.Add(f);
}
}
if (fields.Count > 0)
{
retExpr = MethodBodySynthesizer.GenerateFieldEquals(
retExpr,
other,
fields,
F);
}
fields.Free();
}
if (fields.Count > 0)
{
retExpr = MethodBodySynthesizer.GenerateFieldEquals(
retExpr,
other,
fields,
F);
}
fields.Free();
F.CloseMethod(F.Block(F.Return(retExpr)));
}
}
}
\ No newline at end of file
}
......@@ -14,11 +14,13 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class SynthesizedRecordGetHashCode : SynthesizedInstanceMethodSymbol
{
public override NamedTypeSymbol ContainingType { get; }
private readonly int _memberOffset;
public override NamedTypeSymbol ContainingType { get; }
public SynthesizedRecordGetHashCode(NamedTypeSymbol containingType)
public SynthesizedRecordGetHashCode(NamedTypeSymbol containingType, int memberOffset)
{
_memberOffset = memberOffset;
ContainingType = containingType;
}
......@@ -42,7 +44,7 @@ public SynthesizedRecordGetHashCode(NamedTypeSymbol containingType)
public override RefKind RefKind => RefKind.None;
internal override LexicalSortKey GetLexicalSortKey() => LexicalSortKey.SynthesizedRecordObjEquals;
internal override LexicalSortKey GetLexicalSortKey() => LexicalSortKey.GetSynthesizedMemberKey(_memberOffset);
public override TypeWithAnnotations ReturnTypeWithAnnotations => TypeWithAnnotations.Create(
isNullableEnabled: true,
......
......@@ -15,23 +15,24 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols
internal sealed class SynthesizedRecordObjEquals : SynthesizedInstanceMethodSymbol
{
private readonly MethodSymbol _typedRecordEquals;
private readonly int _memberOffset;
public override NamedTypeSymbol ContainingType { get; }
public override ImmutableArray<ParameterSymbol> Parameters { get; }
public SynthesizedRecordObjEquals(NamedTypeSymbol containingType, MethodSymbol typedRecordEquals)
public SynthesizedRecordObjEquals(NamedTypeSymbol containingType, MethodSymbol typedRecordEquals, int memberOffset)
{
var compilation = containingType.DeclaringCompilation;
_typedRecordEquals = typedRecordEquals;
_memberOffset = memberOffset;
ContainingType = containingType;
Parameters = ImmutableArray.Create<ParameterSymbol>(SynthesizedParameterSymbol.Create(
this,
TypeWithAnnotations.Create(
isNullableEnabled: true,
containingType.DeclaringCompilation.GetSpecialType(SpecialType.System_Object),
isAnnotated: true),
TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Object), NullableAnnotation.Annotated),
ordinal: 0,
RefKind.None));
ReturnTypeWithAnnotations = TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Boolean));
}
public override string Name => "Equals";
......@@ -52,11 +53,9 @@ public SynthesizedRecordObjEquals(NamedTypeSymbol containingType, MethodSymbol t
public override RefKind RefKind => RefKind.None;
internal override LexicalSortKey GetLexicalSortKey() => LexicalSortKey.SynthesizedRecordObjEquals;
internal override LexicalSortKey GetLexicalSortKey() => LexicalSortKey.GetSynthesizedMemberKey(_memberOffset);
public override TypeWithAnnotations ReturnTypeWithAnnotations => TypeWithAnnotations.Create(
isNullableEnabled: true,
ContainingType.DeclaringCompilation.GetSpecialType(SpecialType.System_Boolean));
public override TypeWithAnnotations ReturnTypeWithAnnotations { get; }
public override FlowAnalysisAnnotations ReturnTypeFlowAnalysisAnnotations => FlowAnalysisAnnotations.None;
......@@ -145,7 +144,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState,
{
// For classes:
// return this.Equals(param as ContainingType);
expression = F.InstanceCall(F.This(), "Equals", F.As(paramAccess, ContainingType));
expression = F.Call(F.This(), _typedRecordEquals, F.As(paramAccess, ContainingType));
}
F.CloseMethod(F.Block(ImmutableArray.Create<BoundStatement>(F.Return(expression))));
......
......@@ -2174,16 +2174,18 @@ public record C(int i)
var cMembers = comp.GlobalNamespace.GetMember<NamedTypeSymbol>("C").GetMembers();
AssertEx.SetEqual(new[] {
"C C.Clone()",
"System.Type C.EqualityContract.get",
"System.Type C.EqualityContract { get; }",
"C..ctor(System.Int32 i)",
"System.Int32 C.<i>k__BackingField",
"System.Int32 C.i.get",
"void modreq(System.Runtime.CompilerServices.IsExternalInit) C.i.init",
"System.Int32 C.i { get; init; }",
"void C.M()",
"System.Boolean C.Equals(C? )",
"System.Boolean C.Equals(System.Object? )",
"System.Int32 C.GetHashCode()",
"C..ctor(C )",
"C..ctor(System.Int32 i)" }, cMembers.ToTestDisplayStrings());
"System.Boolean C.Equals(System.Object? )",
"System.Boolean C.Equals(C? )",
"C..ctor(C )" }, cMembers.ToTestDisplayStrings());
foreach (var member in cMembers)
{
......
......@@ -6,10 +6,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Test.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
......@@ -49,6 +47,7 @@ record C(int x, int y)
"System.Int32 System.Object.GetHashCode()",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop
......@@ -83,6 +82,7 @@ public void PositionalRecord2()
"System.Object System.Object.MemberwiseClone()",
"void System.Object.Finalize()",
"System.String System.Object.ToString()",
"System.Type C<T>.EqualityContract { get; }",
"System.Type System.Object.GetType()"),
s_pop
);
......@@ -113,6 +113,7 @@ public void NominalRecord()
"System.Object System.Object.MemberwiseClone()",
"void System.Object.Finalize()",
"System.String System.Object.ToString()",
"System.Type C<T>.EqualityContract { get; }",
"System.Type System.Object.GetType()",
};
var expectedNames = MakeExpectedSymbols(
......@@ -1718,6 +1719,7 @@ record C(int X) : Base`(X`)
"System.Int32 X",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop,
......@@ -1733,6 +1735,7 @@ record C(int X) : Base`(X`)
"System.Int32 System.Object.GetHashCode()",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop
......@@ -1765,6 +1768,7 @@ public void RecordBaseArguments_02()
"System.Int32 System.Object.GetHashCode()",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop
......@@ -1802,6 +1806,7 @@ public void RecordBaseArguments_03()
"System.Int32 System.Object.GetHashCode()",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop,
......@@ -1817,6 +1822,7 @@ public void RecordBaseArguments_03()
"System.Int32 System.Object.GetHashCode()",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop
......@@ -1855,6 +1861,7 @@ public void RecordBaseArguments_04()
"System.Int32 X",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop,
......@@ -1870,6 +1877,7 @@ public void RecordBaseArguments_04()
"System.Int32 System.Object.GetHashCode()",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop,
......@@ -1885,6 +1893,7 @@ public void RecordBaseArguments_04()
"System.Int32 System.Object.GetHashCode()",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop
......@@ -1922,6 +1931,7 @@ public void RecordBaseArguments_05()
"System.Int32 System.Object.GetHashCode()",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop,
......@@ -1937,6 +1947,7 @@ public void RecordBaseArguments_05()
"System.Int32 X",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop,
......@@ -1952,6 +1963,7 @@ public void RecordBaseArguments_05()
"System.Int32 System.Object.GetHashCode()",
"System.Object System.Object.MemberwiseClone()",
"System.String System.Object.ToString()",
"System.Type C.EqualityContract { get; }",
"System.Type System.Object.GetType()",
"void System.Object.Finalize()"),
s_pop
......
......@@ -313,31 +313,36 @@ .maxstack 2
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: isinst ""C""
IL_0007: call ""bool C.Equals(C)""
IL_0007: callvirt ""bool C.Equals(C)""
IL_000c: ret
}");
verifier.VerifyIL("C.Equals(C)", @"
{
// Code size 52 (0x34)
// Code size 66 (0x42)
.maxstack 3
IL_0000: ldarg.1
IL_0001: brfalse.s IL_0032
IL_0003: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0008: ldarg.0
IL_0009: ldfld ""int C.<X>k__BackingField""
IL_000e: ldarg.1
IL_000f: ldfld ""int C.<X>k__BackingField""
IL_0014: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0019: brfalse.s IL_0032
IL_001b: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0020: ldarg.0
IL_0021: ldfld ""int C.<Y>k__BackingField""
IL_0026: ldarg.1
IL_0027: ldfld ""int C.<Y>k__BackingField""
IL_002c: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0031: ret
IL_0032: ldc.i4.0
IL_0033: ret
IL_0001: brfalse.s IL_0040
IL_0003: ldarg.0
IL_0004: callvirt ""System.Type C.EqualityContract.get""
IL_0009: ldarg.1
IL_000a: callvirt ""System.Type C.EqualityContract.get""
IL_000f: bne.un.s IL_0040
IL_0011: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0016: ldarg.0
IL_0017: ldfld ""int C.<X>k__BackingField""
IL_001c: ldarg.1
IL_001d: ldfld ""int C.<X>k__BackingField""
IL_0022: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0027: brfalse.s IL_0040
IL_0029: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_002e: ldarg.0
IL_002f: ldfld ""int C.<Y>k__BackingField""
IL_0034: ldarg.1
IL_0035: ldfld ""int C.<Y>k__BackingField""
IL_003a: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_003f: ret
IL_0040: ldc.i4.0
IL_0041: ret
}");
}
......@@ -406,33 +411,38 @@ public static void Main()
True");
verifier.VerifyIL("C.Equals(C)", @"
{
// Code size 76 (0x4c)
// Code size 90 (0x5a)
.maxstack 3
IL_0000: ldarg.1
IL_0001: brfalse.s IL_004a
IL_0003: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0008: ldarg.0
IL_0009: ldfld ""int C.<X>k__BackingField""
IL_000e: ldarg.1
IL_000f: ldfld ""int C.<X>k__BackingField""
IL_0014: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0019: brfalse.s IL_004a
IL_001b: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0020: ldarg.0
IL_0021: ldfld ""int C.<Y>k__BackingField""
IL_0026: ldarg.1
IL_0027: ldfld ""int C.<Y>k__BackingField""
IL_002c: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0031: brfalse.s IL_004a
IL_0033: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0038: ldarg.0
IL_0039: ldfld ""int C.Z""
IL_003e: ldarg.1
IL_003f: ldfld ""int C.Z""
IL_0044: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0049: ret
IL_004a: ldc.i4.0
IL_004b: ret
IL_0001: brfalse.s IL_0058
IL_0003: ldarg.0
IL_0004: callvirt ""System.Type C.EqualityContract.get""
IL_0009: ldarg.1
IL_000a: callvirt ""System.Type C.EqualityContract.get""
IL_000f: bne.un.s IL_0058
IL_0011: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0016: ldarg.0
IL_0017: ldfld ""int C.<X>k__BackingField""
IL_001c: ldarg.1
IL_001d: ldfld ""int C.<X>k__BackingField""
IL_0022: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0027: brfalse.s IL_0058
IL_0029: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_002e: ldarg.0
IL_002f: ldfld ""int C.<Y>k__BackingField""
IL_0034: ldarg.1
IL_0035: ldfld ""int C.<Y>k__BackingField""
IL_003a: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_003f: brfalse.s IL_0058
IL_0041: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0046: ldarg.0
IL_0047: ldfld ""int C.Z""
IL_004c: ldarg.1
IL_004d: ldfld ""int C.Z""
IL_0052: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0057: ret
IL_0058: ldc.i4.0
IL_0059: ret
}");
}
......@@ -488,26 +498,31 @@ public static void Main()
True");
verifier.VerifyIL("C.Equals(C)", @"
{
// Code size 52 (0x34)
// Code size 66 (0x42)
.maxstack 3
IL_0000: ldarg.1
IL_0001: brfalse.s IL_0032
IL_0003: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0008: ldarg.0
IL_0009: ldfld ""int C.<X>k__BackingField""
IL_000e: ldarg.1
IL_000f: ldfld ""int C.<X>k__BackingField""
IL_0014: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0019: brfalse.s IL_0032
IL_001b: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0020: ldarg.0
IL_0021: ldfld ""int C.<Y>k__BackingField""
IL_0026: ldarg.1
IL_0027: ldfld ""int C.<Y>k__BackingField""
IL_002c: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0031: ret
IL_0032: ldc.i4.0
IL_0033: ret
IL_0001: brfalse.s IL_0040
IL_0003: ldarg.0
IL_0004: callvirt ""System.Type C.EqualityContract.get""
IL_0009: ldarg.1
IL_000a: callvirt ""System.Type C.EqualityContract.get""
IL_000f: bne.un.s IL_0040
IL_0011: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0016: ldarg.0
IL_0017: ldfld ""int C.<X>k__BackingField""
IL_001c: ldarg.1
IL_001d: ldfld ""int C.<X>k__BackingField""
IL_0022: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0027: brfalse.s IL_0040
IL_0029: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_002e: ldarg.0
IL_002f: ldfld ""int C.<Y>k__BackingField""
IL_0034: ldarg.1
IL_0035: ldfld ""int C.<Y>k__BackingField""
IL_003a: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_003f: ret
IL_0040: ldc.i4.0
IL_0041: ret
}");
}
......@@ -539,26 +554,31 @@ public static void Main()
True");
verifier.VerifyIL("C.Equals(C)", @"
{
// Code size 52 (0x34)
// Code size 66 (0x42)
.maxstack 3
IL_0000: ldarg.1
IL_0001: brfalse.s IL_0032
IL_0003: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0008: ldarg.0
IL_0009: ldfld ""int C.<X>k__BackingField""
IL_000e: ldarg.1
IL_000f: ldfld ""int C.<X>k__BackingField""
IL_0014: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0019: brfalse.s IL_0032
IL_001b: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0020: ldarg.0
IL_0021: ldfld ""int C.<Y>k__BackingField""
IL_0026: ldarg.1
IL_0027: ldfld ""int C.<Y>k__BackingField""
IL_002c: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0031: ret
IL_0032: ldc.i4.0
IL_0033: ret
IL_0001: brfalse.s IL_0040
IL_0003: ldarg.0
IL_0004: callvirt ""System.Type C.EqualityContract.get""
IL_0009: ldarg.1
IL_000a: callvirt ""System.Type C.EqualityContract.get""
IL_000f: bne.un.s IL_0040
IL_0011: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0016: ldarg.0
IL_0017: ldfld ""int C.<X>k__BackingField""
IL_001c: ldarg.1
IL_001d: ldfld ""int C.<X>k__BackingField""
IL_0022: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0027: brfalse.s IL_0040
IL_0029: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_002e: ldarg.0
IL_002f: ldfld ""int C.<Y>k__BackingField""
IL_0034: ldarg.1
IL_0035: ldfld ""int C.<Y>k__BackingField""
IL_003a: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_003f: ret
IL_0040: ldc.i4.0
IL_0041: ret
}");
}
......@@ -589,33 +609,38 @@ public static void Main()
True");
verifier.VerifyIL("C.Equals(C)", @"
{
// Code size 76 (0x4c)
// Code size 90 (0x5a)
.maxstack 3
IL_0000: ldarg.1
IL_0001: brfalse.s IL_004a
IL_0003: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0008: ldarg.0
IL_0009: ldfld ""int C.<X>k__BackingField""
IL_000e: ldarg.1
IL_000f: ldfld ""int C.<X>k__BackingField""
IL_0014: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0019: brfalse.s IL_004a
IL_001b: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0020: ldarg.0
IL_0021: ldfld ""int C.<Y>k__BackingField""
IL_0026: ldarg.1
IL_0027: ldfld ""int C.<Y>k__BackingField""
IL_002c: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0031: brfalse.s IL_004a
IL_0033: call ""System.Collections.Generic.EqualityComparer<System.Action> System.Collections.Generic.EqualityComparer<System.Action>.Default.get""
IL_0038: ldarg.0
IL_0039: ldfld ""System.Action C.E""
IL_003e: ldarg.1
IL_003f: ldfld ""System.Action C.E""
IL_0044: callvirt ""bool System.Collections.Generic.EqualityComparer<System.Action>.Equals(System.Action, System.Action)""
IL_0049: ret
IL_004a: ldc.i4.0
IL_004b: ret
IL_0001: brfalse.s IL_0058
IL_0003: ldarg.0
IL_0004: callvirt ""System.Type C.EqualityContract.get""
IL_0009: ldarg.1
IL_000a: callvirt ""System.Type C.EqualityContract.get""
IL_000f: bne.un.s IL_0058
IL_0011: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0016: ldarg.0
IL_0017: ldfld ""int C.<X>k__BackingField""
IL_001c: ldarg.1
IL_001d: ldfld ""int C.<X>k__BackingField""
IL_0022: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0027: brfalse.s IL_0058
IL_0029: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_002e: ldarg.0
IL_002f: ldfld ""int C.<Y>k__BackingField""
IL_0034: ldarg.1
IL_0035: ldfld ""int C.<Y>k__BackingField""
IL_003a: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_003f: brfalse.s IL_0058
IL_0041: call ""System.Collections.Generic.EqualityComparer<System.Action> System.Collections.Generic.EqualityComparer<System.Action>.Default.get""
IL_0046: ldarg.0
IL_0047: ldfld ""System.Action C.E""
IL_004c: ldarg.1
IL_004d: ldfld ""System.Action C.E""
IL_0052: callvirt ""bool System.Collections.Generic.EqualityComparer<System.Action>.Equals(System.Action, System.Action)""
IL_0057: ret
IL_0058: ldc.i4.0
IL_0059: ret
}");
}
......@@ -894,38 +919,43 @@ .maxstack 2
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: isinst ""C""
IL_0007: call ""bool C.Equals(C)""
IL_0007: callvirt ""bool C.Equals(C)""
IL_000c: ret
}");
verifier.VerifyIL("C.Equals(C)", @"
{
// Code size 76 (0x4c)
// Code size 90 (0x5a)
.maxstack 3
IL_0000: ldarg.1
IL_0001: brfalse.s IL_004a
IL_0003: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0008: ldarg.0
IL_0009: ldfld ""int C.X""
IL_000e: ldarg.1
IL_000f: ldfld ""int C.X""
IL_0014: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0019: brfalse.s IL_004a
IL_001b: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0020: ldarg.0
IL_0021: ldfld ""int C.<Y>k__BackingField""
IL_0026: ldarg.1
IL_0027: ldfld ""int C.<Y>k__BackingField""
IL_002c: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0031: brfalse.s IL_004a
IL_0033: call ""System.Collections.Generic.EqualityComparer<System.Action> System.Collections.Generic.EqualityComparer<System.Action>.Default.get""
IL_0038: ldarg.0
IL_0039: ldfld ""System.Action C.E""
IL_003e: ldarg.1
IL_003f: ldfld ""System.Action C.E""
IL_0044: callvirt ""bool System.Collections.Generic.EqualityComparer<System.Action>.Equals(System.Action, System.Action)""
IL_0049: ret
IL_004a: ldc.i4.0
IL_004b: ret
IL_0001: brfalse.s IL_0058
IL_0003: ldarg.0
IL_0004: callvirt ""System.Type C.EqualityContract.get""
IL_0009: ldarg.1
IL_000a: callvirt ""System.Type C.EqualityContract.get""
IL_000f: bne.un.s IL_0058
IL_0011: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_0016: ldarg.0
IL_0017: ldfld ""int C.X""
IL_001c: ldarg.1
IL_001d: ldfld ""int C.X""
IL_0022: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_0027: brfalse.s IL_0058
IL_0029: call ""System.Collections.Generic.EqualityComparer<int> System.Collections.Generic.EqualityComparer<int>.Default.get""
IL_002e: ldarg.0
IL_002f: ldfld ""int C.<Y>k__BackingField""
IL_0034: ldarg.1
IL_0035: ldfld ""int C.<Y>k__BackingField""
IL_003a: callvirt ""bool System.Collections.Generic.EqualityComparer<int>.Equals(int, int)""
IL_003f: brfalse.s IL_0058
IL_0041: call ""System.Collections.Generic.EqualityComparer<System.Action> System.Collections.Generic.EqualityComparer<System.Action>.Default.get""
IL_0046: ldarg.0
IL_0047: ldfld ""System.Action C.E""
IL_004c: ldarg.1
IL_004d: ldfld ""System.Action C.E""
IL_0052: callvirt ""bool System.Collections.Generic.EqualityComparer<System.Action>.Equals(System.Action, System.Action)""
IL_0057: ret
IL_0058: ldc.i4.0
IL_0059: ret
}");
}
......@@ -964,6 +994,8 @@ record C
var members = comp.GlobalNamespace.GetTypeMember("C").GetMembers();
AssertEx.Equal(new[] {
"C! C.Clone()",
"System.Type! C.EqualityContract.get",
"System.Type! C.EqualityContract { get; }",
"System.Int32 C.<X>k__BackingField",
"System.Int32 C.X { get; init; }",
"System.Int32 C.X.get",
......@@ -972,9 +1004,9 @@ record C
"System.String! C.Y { get; init; }",
"System.String! C.Y.get",
"void C.Y.init",
"System.Boolean C.Equals(C? )",
"System.Boolean C.Equals(System.Object? )",
"System.Int32 C.GetHashCode()",
"System.Boolean C.Equals(System.Object? )",
"System.Boolean C.Equals(C? )",
"C.C(C! )",
"C.C()",
}, members.Select(m => m.ToTestDisplayString(includeNonNullable: true)));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册