Skip to content

Align addition of a synthesized EqualityContract in records with the latest design.#45831

Merged
AlekseyTs merged 2 commits intodotnet:masterfrom
AlekseyTs:Records_16
Jul 10, 2020
Merged

Align addition of a synthesized EqualityContract in records with the latest design.#45831
AlekseyTs merged 2 commits intodotnet:masterfrom
AlekseyTs:Records_16

Conversation

@AlekseyTs
Copy link
Contributor

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:

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:

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 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 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.

…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);
;
Copy link
Member

@RikkiGibson RikkiGibson Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this empty statement intended? #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this empty statement intended?

Yes.


In reply to: 452374801 [](ancestors = 452374801)

";
var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseDll);
comp.VerifyEmitDiagnostics(
// (4,27): error CS8874: Record member 'A.EqualityContract' must return 'Type'.
Copy link
Member

@RikkiGibson RikkiGibson Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: it seems like 'Type' here will be 'System.Type' in the actual diagnostic message #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

@AlekseyTs
Copy link
Contributor Author

@cston, @jcouv, @dotnet/roslyn-compiler Please review, need a second sign-off.

// https://github.com/dotnet/roslyn/issues/44903: Check explicit member has expected signature.
PropertySymbol equalityContract;

if (!memberSignatures.TryGetValue(targetProperty, out Symbol? existingEqualityContractProperty))
Copy link
Member

@jcouv jcouv Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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))
Copy link
Member

@jcouv jcouv Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Member

@jcouv jcouv Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we reach this? If not, consider throwing as unreachable instead. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 &&
Copy link
Member

@jcouv jcouv Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overridden is object [](start = 20, length = 20)

nit: is this check necessary given that we've already checked .IsOverride? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(),
Copy link
Member

@jcouv jcouv Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

singleton here too? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

singleton here too?

Same here, it is a struct


In reply to: 452481371 [](ancestors = 452481371)

comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput:
@"
B.EqualityContract
Copy link
Member

@jcouv jcouv Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't understand why there are two calls to EqualityContract #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
);
}
Copy link
Member

@jcouv jcouv Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find a test verifying IL for EqualityContract implementation. Consider adding one #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Member

@jcouv jcouv Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: double empty line above #Resolved


internal override bool MustCallMethodsDirectly => false;
protected override SyntaxTokenList GetModifierTokens(SyntaxNode syntax)
=> new SyntaxTokenList();
Copy link
Member

@jcouv jcouv Jul 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could we use a singleton rather than allocate? #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could we use a singleton rather than allocate?

No allocation here, it is a struct


In reply to: 452488746 [](ancestors = 452488746)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thanks


In reply to: 452489159 [](ancestors = 452489159,452488746)

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Thanks (iteration 1)

@jcouv jcouv self-assigned this Jul 9, 2020
@AlekseyTs AlekseyTs merged commit 32da307 into dotnet:master Jul 10, 2020
@ghost ghost added this to the Next milestone Jul 10, 2020
@JoeRobich JoeRobich modified the milestones: Next, 16.8.P1 Jul 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants