Align addition of a synthesized EqualityContract in records with the latest design.#45831
Align addition of a synthesized EqualityContract in records with the latest design.#45831AlekseyTs merged 2 commits intodotnet:masterfrom
Conversation
…latest design. Related to dotnet#45296. From specification: If the record is derived from `object`, the record type includes a synthesized readonly property equivalent to a property declared as follows: ```C# protected Type EqualityContract { get; }; ``` The property is `virtual` unless the record type is `sealed`. The property can be declared explicitly. It is an error if the explicit declaration does not match the expected signature or accessibility, or if the explicit declaration doesn't allow overiding it in a derived type and the record type is not `sealed`. If the record type is derived from a base record type `Base`, the record type includes a synthesized readonly property equivalent to a property declared as follows: ```C# protected override Type EqualityContract { get; }; ``` The property can be declared explicitly. It is an error if the explicit declaration does not match the expected signature or accessibility, or if the explicit declaration doesn't allow overiding it in a derived type and the record type is not `sealed`. It is an error if either synthesized, or explicitly declared property doesn't override a property with this signature in the record type `Base` (for example, if the property is missing in the `Base`, or sealed, or not virtual, etc.). The synthesized property returns `typeof(R)` where `R` is the record type.
| ReportProtectedMemberInSealedTypeError(location, diagnostics)) | ||
| { | ||
| diagnostics.Add(AccessCheck.GetProtectedMemberInSealedTypeError(ContainingType), location, this); | ||
| ; |
There was a problem hiding this comment.
Is this empty statement intended? #Closed
There was a problem hiding this comment.
| "; | ||
| var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll); | ||
| comp.VerifyEmitDiagnostics( | ||
| // (4,27): error CS8874: Record member 'A.EqualityContract' must return 'Type'. |
There was a problem hiding this comment.
nit: it seems like 'Type' here will be 'System.Type' in the actual diagnostic message #Resolved
There was a problem hiding this comment.
nit: it seems like 'Type' here will be 'System.Type' in the actual diagnostic message
This is exactly what the helper printed.
In reply to: 452387172 [](ancestors = 452387172)
| // https://github.com/dotnet/roslyn/issues/44903: Check explicit member has expected signature. | ||
| PropertySymbol equalityContract; | ||
|
|
||
| if (!memberSignatures.TryGetValue(targetProperty, out Symbol? existingEqualityContractProperty)) |
There was a problem hiding this comment.
FYI, added a note to test plan to confirm nullability expectations on this signature (if I implement protected virtual Type? EqualityContract { get; }, should the compiler give a warning?) #Resolved
| } | ||
| else if (ContainingType.IsSealed && this.DeclaredAccessibility.HasProtected() && !this.IsOverride) | ||
| else if (ContainingType.IsSealed && this.DeclaredAccessibility.HasProtected() && !this.IsOverride && | ||
| ReportProtectedMemberInSealedTypeError(location, diagnostics)) |
There was a problem hiding this comment.
ReportProtectedMemberInSealedTypeError [](start = 21, length = 38)
Consider keeping the diagnostic here, but just having a boolean property ShouldReportProtectedMemberInSealedTypeError (true for this type, false for derived type) #Resolved
| { | ||
| _property = property; | ||
| Name = name; | ||
| return null; |
There was a problem hiding this comment.
Can we reach this? If not, consider throwing as unreachable instead. #Resolved
There was a problem hiding this comment.
Can we reach this? If not, consider throwing as unreachable instead.
Yes, this method is called for both accessors, it is up to the method to decide if it should create one.
In reply to: 452479312 [](ancestors = 452479312)
|
|
||
| internal override ImmutableArray<string> GetAppliedConditionalSymbols() | ||
| => ImmutableArray<string>.Empty; | ||
| if (overridden is object && |
There was a problem hiding this comment.
overridden is object [](start = 20, length = 20)
nit: is this check necessary given that we've already checked .IsOverride? #Resolved
There was a problem hiding this comment.
nit: is this check necessary given that we've already checked .IsOverride?
Yes, there could be nothing to override, even when we want to, and IsOverride is simply a reflection of the intent
In reply to: 452481085 [](ancestors = 452481085)
| hasBody: false, | ||
| hasExpressionBody: false, | ||
| isIterator: false, | ||
| modifiers: new SyntaxTokenList(), |
There was a problem hiding this comment.
| comp.VerifyDiagnostics(); | ||
| CompileAndVerify(comp, expectedOutput: | ||
| @" | ||
| B.EqualityContract |
There was a problem hiding this comment.
I didn't understand why there are two calls to EqualityContract #Resolved
There was a problem hiding this comment.
I didn't understand why there are two calls to EqualityContract
From specification for record equals method:
If there is a base record type, the value of base.Equals(other) (a non-virtual call to public virtual bool Equals(Base? other)); otherwise the value of EqualityContract == other.EqualityContract.
In reply to: 452484887 [](ancestors = 452484887)
| // protected override System.Type EqualityContract | ||
| Diagnostic(ErrorCode.ERR_DoesNotOverrideBaseEqualityContract, "EqualityContract").WithArguments("G.EqualityContract", "B").WithLocation(21, 36) | ||
| ); | ||
| } |
There was a problem hiding this comment.
I couldn't find a test verifying IL for EqualityContract implementation. Consider adding one #Resolved
There was a problem hiding this comment.
I couldn't find a test verifying IL for EqualityContract implementation. Consider adding one
I am not changing anything about emitted IL and I assume that that kind of testing has been already done, if necessary.
In reply to: 452487751 [](ancestors = 452487751)
There was a problem hiding this comment.
I'm saying there doesn't seem to be any (that I could find), so may be a good occasion to cover, while in the area.
In reply to: 452488774 [](ancestors = 452488774,452487751)
There was a problem hiding this comment.
I'm saying there doesn't seem to be any (that I could find), so may be a good occasion to cover, while in the area.
Given simplicity of what this property does, I do not believe IL verification is warranted. Behavior tests are good enough. I will add assert in a couple of tests to verify the actual value the property returns.
In reply to: 452489813 [](ancestors = 452489813,452488774,452487751)
| } | ||
|
|
||
|
|
||
| reportNotOverridableAPIInRecord(equalityContract, diagnostics); |
There was a problem hiding this comment.
nit: double empty line above #Resolved
|
|
||
| internal override bool MustCallMethodsDirectly => false; | ||
| protected override SyntaxTokenList GetModifierTokens(SyntaxNode syntax) | ||
| => new SyntaxTokenList(); |
There was a problem hiding this comment.
nit: could we use a singleton rather than allocate? #Closed
There was a problem hiding this comment.
nit: could we use a singleton rather than allocate?
No allocation here, it is a struct
In reply to: 452488746 [](ancestors = 452488746)
There was a problem hiding this comment.
Related to #45296.
From specification:
If the record is derived from
object, the record type includes a synthesized readonly property equivalent to a property declared as follows:The property is
virtualunless the record type issealed.The property can be declared explicitly. It is an error if the explicit declaration does not match the expected signature or accessibility, or if the explicit declaration doesn't allow overiding it in a derived type and the record type is not
sealed.If the record type is derived from a base record type
Base, the record type includes a synthesized readonly property equivalent to a property declared as follows:The property can be declared explicitly. It is an error if the explicit declaration does not match the expected signature or accessibility, or if the explicit declaration doesn't allow overriding it in a derived type and the record type is not
sealed. It is an error if either synthesized, or explicitly declared property doesn't override a property with this signature in the record typeBase(for example, if the property is missing in theBase, or sealed, or not virtual, etc.).The synthesized property returns
typeof(R)whereRis the record type.