-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5327,5 +5327,125 @@ public static class Extensions | |
}"; | ||
CompileAndVerify(source, parseOptions: TestOptions.Regular9, expectedOutput: "123123"); | ||
} | ||
|
||
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73741")] | ||
public void MutatingThroughRefFields_01() | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a smoke test for LINQ iteration variables and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are tests for |
||
var source = """ | ||
using System; | ||
|
||
V[] arr = new V[3]; | ||
|
||
foreach (var r in new E(arr)) | ||
{ | ||
r.V.F++; | ||
} | ||
|
||
foreach (var v in arr) Console.Write(v.F); | ||
|
||
ref struct E(V[] arr) | ||
{ | ||
int i; | ||
public E GetEnumerator() => this; | ||
public R Current => new(ref arr[i - 1]); | ||
public bool MoveNext() => i++ < arr.Length; | ||
} | ||
|
||
ref struct R(ref V v) | ||
{ | ||
public ref V V = ref v; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The submitted repro has these as |
||
} | ||
|
||
struct V | ||
{ | ||
public int F; | ||
} | ||
"""; | ||
CompileAndVerify(source, targetFramework: TargetFramework.Net70, | ||
verify: Verification.Fails, | ||
expectedOutput: ExecutionConditionUtil.IsDesktop ? null : "111").VerifyDiagnostics(); | ||
} | ||
|
||
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73741")] | ||
public void MutatingThroughRefFields_02() | ||
{ | ||
var source = """ | ||
using System; | ||
|
||
V[] arr = new V[3]; | ||
|
||
foreach (var r in new E(arr)) | ||
{ | ||
r.V.F += 2; | ||
} | ||
|
||
foreach (var v in arr) Console.Write(v.F); | ||
|
||
ref struct E(V[] arr) | ||
{ | ||
int i; | ||
public E GetEnumerator() => this; | ||
public R Current => new(ref arr[i - 1]); | ||
public bool MoveNext() => i++ < arr.Length; | ||
} | ||
|
||
ref struct R(ref V v) | ||
{ | ||
public ref V V = ref v; | ||
} | ||
|
||
struct V | ||
{ | ||
public int F; | ||
} | ||
"""; | ||
CompileAndVerify(source, targetFramework: TargetFramework.Net70, | ||
verify: Verification.Fails, | ||
expectedOutput: ExecutionConditionUtil.IsDesktop ? null : "222").VerifyDiagnostics(); | ||
} | ||
|
||
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73741")] | ||
public void MutatingThroughRefFields_03() | ||
{ | ||
var source = """ | ||
using System; | ||
|
||
V[] arr = new V[3]; | ||
|
||
foreach (var r in new E(arr)) | ||
{ | ||
r.V.S.Inc(); | ||
} | ||
|
||
foreach (var v in arr) Console.Write(v.S.F); | ||
|
||
ref struct E(V[] arr) | ||
{ | ||
int i; | ||
public E GetEnumerator() => this; | ||
public R Current => new(ref arr[i - 1]); | ||
public bool MoveNext() => i++ < arr.Length; | ||
} | ||
|
||
ref struct R(ref V v) | ||
{ | ||
public ref V V = ref v; | ||
} | ||
|
||
struct V | ||
{ | ||
public S S; | ||
} | ||
|
||
struct S | ||
{ | ||
public int F; | ||
public void Inc() => F++; | ||
} | ||
"""; | ||
CompileAndVerify(source, targetFramework: TargetFramework.Net70, | ||
verify: Verification.Fails, | ||
expectedOutput: ExecutionConditionUtil.IsDesktop ? null : "111").VerifyDiagnostics(); | ||
} | ||
} | ||
} |
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.
In L76 we are effectively checking
RefKind != RefKind.None
on thelocalSymbol
, should we also do that here for thefieldSymbol
? Is it possible we need this when we have a ref-readonly field whose referent value is changing "during" the expression?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.
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 (theIsByValue
computed here is used byBinder.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.