-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use covariant return for synthesized record clone #53404
Conversation
return (ReturnType: VirtualCloneInBase() is { } baseClone ? | ||
baseClone.ReturnTypeWithAnnotations : // Use covariant returns when available | ||
return (ReturnType: VirtualCloneInBase() is { } baseClone && !ContainingAssembly.RuntimeSupportsCovariantReturnsOfClasses ? | ||
baseClone.ReturnTypeWithAnnotations : | ||
TypeWithAnnotations.Create(isNullableEnabled: true, ContainingType), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please include a test that demonstrates the change. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was expecting to see CI failures since this code path is already extensively tested. Will look and see why no tests are failing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests for records don't vary the RuntimeSupportsCovariantReturnsOfClasses
value from corlib, and I don't know what value is there in the corlib that we reference.
One way to control this is to define your own tiny corlib in a test using CreateEmptyCompilation
. See RecordTests.ObjectGetHashCode_15
for example.
Another way is to use an old vs. new target framework (targetFramework
in CreateCompilation
) assuming that the available options are sufficient to achieve the desired effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One way to control this is to define your own tiny corlib in a test using CreateEmptyCompilation
I prefer that we don't do this, we want to test against real frameworks and verify runtime behavior as well. Achieving that with a private tiny corlib is going to be non trivial. I think that testing both modes should be rather simple. It looks like record tests use default TargetFramework. That means that, when tests are run on desktop they are run against desktop (doesn't support covariant returns) and, when run on CoreCLR they are run against CoreCLR (supports covariant returns). We should be able to observe difference between these two modes. However, it looks like default target framework for tests on CoreClr is NetStandard20 (doesn't say that the feature will be supported at runtime), we should be able to explicitly request NetCoreApp framework. I think TargetFramework.StandardLatest would give us what we want for desktop and for CoreCLR. The expectations in the tests will have to be conditional based on the platform
Is it expected that code gen for with-expressions is also able to remove a cast when the covariant record clone is used? |
If the compiler generates a covariant return when compiled against a particular runtime, is the resulting assembly usable with a runtime that does not support covariant returns? If not, is there a way to opt-out of the change to the synthesized clone method? |
@cston Here's my understanding: Records shipped with .NET 5 which supports covariant returns. Using new language features on older frameworks is unsupported. |
No but this shouldn't present a problem. We only allow covariant returns when the runtime flags in corelib indicate that covariant returns are supported. That flag is only present on What we need to be careful about in this change is:
|
@jaredpar Isn't that guaranteed by checking ContainingAssembly.RuntimeSupportsCovariantReturnsOfClasses? This is now covered by tests that run on both .NET Standard and .NET 5
What should be expected here? Using covariant return even if the base type didn't? |
I am not sure what are you suggesting. This has no effect on what compiler should do and what should be tested. In reply to: 840994639 |
[InlineData(true)] | ||
public void CopyCtor(bool useCompilationReference) | ||
[InlineData(false, TargetFramework.Standard)] | ||
[InlineData(false, TargetFramework.NetCoreApp)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doing this is probably going to create a problem when a test is running on desktop. We will take advantage of covariant returns at build, but the runtime won't support that when we try to execute the test environment. I suggest always using TargetFramework.StandardLatest target and simply adjust test expectations accordingly based on the platform that executes the unit-test. There is a RuntimeUtilities.IsCoreClrRuntime
API to detect that, for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AlekseyTs From other tests that used .NET Standard only, they never had runtime support for new features. See #53416 where the tests I'm modifying there had an unreachable code path. (See #53413 where the test succeeded when I was throwing an exception in the unreachable code path).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From other tests that used .NET Standard only, they never had runtime support for new features.
There is a difference between TargetFramework.Standard and TargetFramework.StandardLatest, I believe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @AlekseyTs!
comp.VerifyDiagnostics( | ||
// (9,17): error CS0082: Type 'C' already reserves a member called 'get_P' with the same parameter types | ||
// record C(object P) | ||
Diagnostic(ErrorCode.ERR_MemberReserved, "P").WithArguments("get_P", "C").WithLocation(9, 17)); | ||
|
||
var expectedClone = comp.Assembly.RuntimeSupportsCovariantReturnsOfClasses |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am suggesting doing this in every test that is supposed to observe the difference based on Assembly.RuntimeSupportsCovariantReturnsOfClasses
[Fact] | ||
public void Equality_14() | ||
[Theory] | ||
[InlineData(TargetFramework.NetCoreApp)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
public record C(int I) : B(I);"; | ||
|
||
var compA = CreateCompilation(sourceA); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
[Theory, WorkItem(44902, "https://github.com/dotnet/roslyn/issues/44902")] | ||
[CombinatorialData] | ||
public void CrossAssemblySupportingAndNotSupportingCovariantReturns(bool useCompilationReference) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AlekseyTs Would you mind looking at this test? Couldn't get rid of the extra diagnostics (WRN_UnifyReferenceMajMin). #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind looking at this test? Couldn't get rid of the extra diagnostics (WRN_UnifyReferenceMajMin).
It is probably unavoidable given the current set of reference assemblies. I suggest filtering them out
or suppressig in compilation options to reduce the noise.
@AlekseyTs I didn't make any suggestion, I was merely addressing Chuck's concern. There is no real-world & supported scenario where this change would result in an "unusable" assembly as he described it. The only times we're going to hit such situation are either compiler tests or unsupported. |
@Youssef1313 There's a remaining test failure in CI ( |
public static void Main() | ||
{ | ||
var c = new C() { X = 1 }; | ||
var c2 = c with { X = 2 }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we extract a method for with
on C
and one on D
, and verify IL just on that? That would make the difference more compact and clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jcouv Sorry for missing this comment. I updated the test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. We probably no longer need to verify IL for Main
(CHelper
and DHelper
are the important ones)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM Thanks (iteration 14) with test nit
@@ -97,8 +97,8 @@ static bool modifiersAreValid(DeclarationModifiers modifiers) | |||
|
|||
protected override (TypeWithAnnotations ReturnType, ImmutableArray<ParameterSymbol> Parameters, bool IsVararg, ImmutableArray<TypeParameterConstraintClause> DeclaredConstraintsForOverrideOrImplementation) MakeParametersAndBindReturnType(BindingDiagnosticBag diagnostics) | |||
{ | |||
return (ReturnType: VirtualCloneInBase() is { } baseClone ? | |||
baseClone.ReturnTypeWithAnnotations : // Use covariant returns when available | |||
return (ReturnType: VirtualCloneInBase() is { } baseClone && !ContainingAssembly.RuntimeSupportsCovariantReturnsOfClasses ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (commit 15)
@jcouv Is this ready to merge? |
Thanks for the ping. Yes this has two sign-offs. Let's merge. |
Fixes #50949