Skip to content
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

Merged
merged 16 commits into from
Jul 11, 2021

Conversation

Youssef1313
Copy link
Member

Fixes #50949

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

@cston cston May 13, 2021

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

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

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

@jcouv I made the assumption that it will work based on another test doing the same. But it looks like the other test has dead code path. But double checking from CI run (#53413)

Copy link
Contributor

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

@Youssef1313 Youssef1313 marked this pull request as draft May 13, 2021 22:20
@RikkiGibson
Copy link
Contributor

Is it expected that code gen for with-expressions is also able to remove a cast when the covariant record clone is used?

@jcouv jcouv added this to the C# 10 milestone May 14, 2021
@jcouv jcouv added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label May 14, 2021
@cston
Copy link
Member

cston commented May 14, 2021

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?

@jcouv
Copy link
Member

jcouv commented May 14, 2021

@cston Here's my understanding: Records shipped with .NET 5 which supports covariant returns. Using new language features on older frameworks is unsupported.
So I think the two scenarios where records exist but covariant returns don't are (1) compiler tests and (2) unsupported usages.

@jaredpar
Copy link
Member

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?

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 net5.0 and above. It is not supported for pretty much any scenario to compile against net5.0 and then run against an older target framework. That's true irrespective of whether or not you take advantage of covariant returns.

What we need to be careful about in this change is:

  1. Ensuring that we only emit the covariant return when on a target framework that supports it.
  2. Working through the tricky cases
    1. Mixing covariant override with base types that used non-covariant override
    2. Doing a covariant override where the new type is less accessible than the base type (not sure if that is or isn't legal).
    3. etc ...

@Youssef1313
Copy link
Member Author

Youssef1313 commented May 14, 2021

  1. Ensuring that we only emit the covariant return when on a target framework that supports it.

@jaredpar Isn't that guaranteed by checking ContainingAssembly.RuntimeSupportsCovariantReturnsOfClasses? This is now covered by tests that run on both .NET Standard and .NET 5


Mixing covariant override with base types that used non-covariant override

What should be expected here? Using covariant return even if the base type didn't?

@AlekseyTs
Copy link
Contributor

So I think the two scenarios where records exist but covariant returns don't are (1) compiler tests and (2) unsupported usages.

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)]
Copy link
Contributor

Choose a reason for hiding this comment

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

TargetFramework.NetCoreApp

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.

Copy link
Member Author

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

Copy link
Contributor

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.

Copy link
Member Author

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
Copy link
Contributor

@AlekseyTs AlekseyTs May 14, 2021

Choose a reason for hiding this comment

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

comp.Assembly.RuntimeSupportsCovariantReturnsOfClasses

I think we would want to add an assert that this value is true when RuntimeUtilities.IsCoreClrRuntime is true. Same for the other PR that fixes dead code in tests #Closed

Copy link
Contributor

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)]
Copy link
Contributor

@AlekseyTs AlekseyTs May 14, 2021

Choose a reason for hiding this comment

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

NetCoreApp

Sme comment as for the other test #Closed


public record C(int I) : B(I);";

var compA = CreateCompilation(sourceA);
Copy link
Contributor

@AlekseyTs AlekseyTs May 14, 2021

Choose a reason for hiding this comment

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

CreateCompilation

Since we are not trying to run the code, consider using target framework NetStandard20 here, rather than standard #Closed


[Theory, WorkItem(44902, "https://github.com/dotnet/roslyn/issues/44902")]
[CombinatorialData]
public void CrossAssemblySupportingAndNotSupportingCovariantReturns(bool useCompilationReference)
Copy link
Member Author

@Youssef1313 Youssef1313 May 14, 2021

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

Copy link
Contributor

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.

@Youssef1313 Youssef1313 marked this pull request as ready for review May 18, 2021 18:20
@jcouv
Copy link
Member

jcouv commented May 18, 2021

I am not sure what are you suggesting. This has no effect on what compiler should do and what should be tested.

@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.
Jared gave the same answer in more details.

@jcouv
Copy link
Member

jcouv commented May 31, 2021

@Youssef1313 There's a remaining test failure in CI (CrossAssemblySupportingAndNotSupportingCovariantReturns).

public static void Main()
{
var c = new C() { X = 1 };
var c2 = c with { X = 2 };
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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)

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 14) with test nit

@jcouv jcouv requested a review from AlekseyTs June 23, 2021 18:26
@@ -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 ?
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 25, 2021

Choose a reason for hiding this comment

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

!ContainingAssembly.RuntimeSupportsCovariantReturnsOfClasses

Consider reordering conditions. #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (commit 15)

@Youssef1313
Copy link
Member Author

@jcouv Is this ready to merge?

@jcouv
Copy link
Member

jcouv commented Jul 11, 2021

Thanks for the ping. Yes this has two sign-offs. Let's merge.
Thanks @Youssef1313

@jcouv jcouv merged commit 8eb425e into dotnet:main Jul 11, 2021
@ghost ghost modified the milestones: C# 10, Next Jul 11, 2021
@Youssef1313 Youssef1313 deleted the record-clone-covariant-ret branch July 11, 2021 06:19
@allisonchou allisonchou modified the milestones: Next, 17.0.P3 Jul 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Community The pull request was submitted by a contributor who is not a Microsoft employee.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Records should use covariant returns when available
7 participants