Avoid unnecessary defensive copy for constrained calls on readonly struct receivers#82432
Open
stevenelliottjr wants to merge 1 commit intodotnet:mainfrom
Open
Avoid unnecessary defensive copy for constrained calls on readonly struct receivers#82432stevenelliottjr wants to merge 1 commit intodotnet:mainfrom
stevenelliottjr wants to merge 1 commit intodotnet:mainfrom
Conversation
…ruct receivers When calling a base class virtual method (like ToString, GetHashCode, Equals) on a readonly struct receiver via constrained callvirt, the compiler previously used AddressKind.Writeable for the receiver address. This caused unnecessary defensive copies when the receiver was accessed through a readonly context (e.g., a readonly method's 'this' or a readonly field). Since readonly structs guarantee non-mutation for all their methods, and the constrained call either resolves to a non-mutating method or boxes the value (which copies it anyway), the original receiver is never mutated. This change uses AddressKind.ReadOnly for readonly value type receivers, eliminating the defensive copy. Fixes dotnet#76288 Also resolves the optimization noted in dotnet#66365
AlekseyTs
reviewed
Feb 18, 2026
| "; | ||
| var comp = CreateCompilation(text, options: TestOptions.ReleaseDll); | ||
| comp.VerifyDiagnostics( | ||
| // (4,15): warning CS0649: Field 'X.a' is never assigned to, and will always have its default value |
Contributor
AlekseyTs
reviewed
Feb 18, 2026
| } | ||
| "; | ||
| var comp = CreateCompilation(text, options: TestOptions.ReleaseDll); | ||
| comp.VerifyDiagnostics( |
Contributor
Contributor
|
Done with review pass (commit 1) |
Contributor
|
@dotnet/roslyn-compiler For a second review |
CyrusNajmabadi
approved these changes
Feb 18, 2026
jjonescz
approved these changes
Feb 19, 2026
| var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Passes, expectedOutput: @"Program+S1Program+S1"); | ||
|
|
||
| // We may be able to optimize this case (ie. avoid defensive copy) since the struct and the caller are in the same module | ||
| // Tracked by https://github.com/dotnet/roslyn/issues/66365 |
Member
There was a problem hiding this comment.
Consider marking the issue as closed by the PR (writing literally closes #66365 in the PR's description)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ToString(),GetHashCode(),Equals()) on readonly struct receivers viaconstrained. callvirt, the compiler previously usedAddressKind.Writeablefor the receiver addressthis, or a directthisreference in a readonly struct method)AddressKind.ReadOnlyfor readonly value type receivers, eliminating the defensive copyExample (from #76288):
Before (21 bytes, 1 local):
After (18 bytes, 0 locals):
Fixes #76288
Also resolves the optimization noted in #66365
Test plan
InvokeOnThisBaseMethodstest to reflect eliminated defensive copy forToString()on readonly structthisNoDefensiveCopy_ReadonlyStructField_ConstrainedCallverifying the exact scenario from Unnecessary defensive copy onreadonly structwhen performing a constrained call #76288