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

Fix modification of readonly locals through ref fields #74255

Merged
merged 5 commits into from
Jul 10, 2024

Conversation

jjonescz
Copy link
Member

@jjonescz jjonescz commented Jul 3, 2024

Fixes #73741.

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Jul 3, 2024
@jjonescz jjonescz marked this pull request as ready for review July 3, 2024 18:11
@jjonescz jjonescz requested a review from a team as a code owner July 3, 2024 18:11

ref struct R(ref V v)
{
public ref V V = ref v;
Copy link
Member

Choose a reason for hiding this comment

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

The submitted repro has these as readonly ref fields. Let's add some more tests with different variants of readonlyness sprinkled throughout. It would also be good to test variables that come from ref foreach loops.

@jjonescz jjonescz requested review from 333fred and a team July 4, 2024 10:26
@@ -60,6 +60,7 @@ private static bool NeedsByValueFieldAccess(BoundExpression? receiver, FieldSymb
{
if (fieldSymbol.IsStatic ||
!fieldSymbol.ContainingType.IsValueType ||
fieldSymbol.RefKind is RefKind.Ref ||
Copy link
Contributor

Choose a reason for hiding this comment

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

In L76 we are effectively checking RefKind != RefKind.None on the localSymbol, should we also do that here for the fieldSymbol? Is it possible we need this when we have a ref-readonly field whose referent value is changing "during" the expression?

Copy link
Member Author

Choose a reason for hiding this comment

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

If we have a ref readonly field, there are binding errors if we try to change its referent value, so I think we cannot observe the difference (the IsByValue computed here is used by Binder.HasHome in codegen to determine whether the field should be stored in a temp but we never get there if we have binding errors). But it makes sense to me to check what you suggest.

@jjonescz jjonescz requested a review from RikkiGibson July 8, 2024 10:36
public void MutatingThroughRefFields_01(
[CombinatorialValues("ref", "")] string eRef,
[CombinatorialValues("readonly", "")] string vReadonly)
{
Copy link
Member

Choose a reason for hiding this comment

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

Consider adding a smoke test for LINQ iteration variables and using variables. They have the same "can't assign" semantics as foreach iteration variables and hence are possibly subject to the same issue.

Copy link
Member Author

@jjonescz jjonescz Jul 9, 2024

Choose a reason for hiding this comment

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

There are tests for using variables added in this PR in CodeGenUsingStatementTests.cs.
LINQ iteration variables aren't affected by this bug (the iteration variable is not a LocalSymbol so the BoundFieldAccess has always IsByValue set to false), but I will add similar tests.

@jjonescz jjonescz requested a review from jaredpar July 9, 2024 11:20
@jjonescz jjonescz enabled auto-merge (squash) July 10, 2024 07:23
@jjonescz jjonescz merged commit 28f0ccf into dotnet:main Jul 10, 2024
24 checks passed
@jjonescz jjonescz deleted the 73741-RefForeach branch July 10, 2024 08:16
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Jul 10, 2024
@dotlogix
Copy link

@jjonescz when will this be publically available can I test this somehow?
Will this be available in .Net8 or only .Net9+?

@jaredpar
Copy link
Member

It will be in the .NET 9.0 SDK and VS 17.12

@jjonescz
Copy link
Member Author

can I test this somehow?

If you want to test sooner you can install this package: https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-tools/NuGet/Microsoft.Net.Compilers.Toolset

@dotlogix
Copy link

dotlogix commented Aug 30, 2024

@jjonescz @jaredpar I finally had some time to test it and it looks like it works as expected now. Thx for your effort. One question tough. Shouldn't this be backported as it is a compiler bug in previous versions as well and .NET 8 is still the LTS?

@jjonescz
Copy link
Member Author

Shouldn't this be backported as it is a compiler bug in previous versions as well and .NET 8 is still the LTS?

Not all bugs meet the bar. In this case it seems to me better not to backport it since it's a behavioral (i.e., silent) breaking change.

@dotlogix
Copy link

Shouldn't this be backported as it is a compiler bug in previous versions as well and .NET 8 is still the LTS?

Not all bugs meet the bar. In this case it seems to me better not to backport it since it's a behavioral (i.e., silent) breaking change.

Imho it is not really a breaking change rather than a bug fix of sth that should actually work.

It's quite unfortunate because many users will skip .Net 9 because it is not an LTS would be really nice to see this back in .NET 8 but I guess I have to wait till .Net 10 then which is the next LTS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unexpected behavior when using ref structs in foreach loop
5 participants