-
Notifications
You must be signed in to change notification settings - Fork 8.1k
(#12430) Improve detection of mutable value types #12495
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
Conversation
db778ef to
3ca57b0
Compare
src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Outdated
Show resolved
Hide resolved
src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Outdated
Show resolved
Hide resolved
src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Outdated
Show resolved
Hide resolved
|
@PoshChan please retry all |
|
@vexx32, successfully started retry of |
src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Outdated
Show resolved
Hide resolved
src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Outdated
Show resolved
Hide resolved
src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Outdated
Show resolved
Hide 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.
We should compare a performance type.IsPrimitive and type.IsEnum vs IsDefine() before remove old code.
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 custom attribute targets old runtimes.
It is for edge scenarios to target old runtimes with modern compiler.
It will lose value over time.
So the addition could only have value for Windows PowerShell but not for PowerShell Core and we can remove it.
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.
Binary modules targeting netstandard2.0 will still generate the custom attribute as the compiler can't be sure the attribute will be present when the DLL is loaded.
At what point are we considered to have completely dropped support for netstandard2.0 modules? Doing so effectively means we no longer support cross-compatibility with WinPS modules at all, and module authors have to pick either 5.1 or the new version to ship modules for.
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 so effectively means we no longer support cross-compatibility with WinPS modules at all
No :-) In this case, we do not need to look back. What is current status of the code?
- no security issues
- no performance issues
- no functional issues (do you know real broken scenarios? I think they don’t exist at all because it’s all for performance)
So only our intention is to benefit from modern runtime to reduce false positives.
I think "world wide plan" is to move community to .Net 6.0 LTS. That means Netstandard2.1. In any case, the old code will decrease over time, and if we did not address (the performance) it earlier, then it does not make sense to address it to the future paying with performance due to new code without an urgent need.
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.
Not sure I follow you through to the conclusion there. Are you saying we should consciously break the compatibility with netstandard2.0 here and now, or are you saying we should leave behind the old, ineffective code as a fallback?
If you're saying it's not worth changing at all... I don't really agree. PS has to handle mutable from immutable structs a bit differently in a few cases that I see in the code. Being able to yield less false positives from this method should (in theory) allow the engine to take the more efficient code path more often -- though, certainly, the cases where we need to be working with structs in PS directly aren't that common.
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.
Are you saying we should consciously break the compatibility with netstandard2.0 here and now, or are you saying we should leave behind the old, ineffective code as a fallback?
We can not break "compatibility with netstandard2.0" because the method is only for performance.
Yes, I ask you do not see back because it makes no sense - we have not real issues and real scenario to address and measure - measure it mandatory for performance changes. False positives on the old way will be reduced over time by themselves.
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.
What does the method return for the type before and after the change?
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 the method was returning true in this case before this change, then the method is actually wrong. That's part of why I made this PR, because the assumptions this method was using are very flawed. We can pretty easily verify that datetime is immutable:
None of its fields can be modified; it's not mutable. Any methods on the type simply return a new instance, and don't modify the original. The fact that it's decorated with the appropriate compiler attribute also confirms this.
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 code that is relying on these false assumptions somehow, I'm happy to fix that too. I intend to investigate the CI failures tonight.
By your own comments, though, this method should only be used for optimizations. I doubt the CI failure is actually caused by relying on the logic, but I will verify.
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.
CIs fail on this. To save your time - it is Datetime.
The only thing that is going to change with datetime is that when you assign one to a variable it will be done faster.
The current behavior is that it will be detected as mutable and a pointless copy will be done to ensure changes aren't persisted. But it's immutable, there will be no changes, so no copy is required.
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.
@vexx32 Please revert the questionable change. The PR will be merged for non-LTS version and will affecte most of users only after next PowerShell LTS - in the time this will be not actual 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.
What is a private field is not readonly but a property (used the field) is?
I think we should revert the code and enhance a code for methods below to take into account new readonly methods.
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 private field can still mutate, e.g., if the type provides a method that modifies the private field. Without checking all the fields, we cannot be sure the struct is immutable.
Also, even if we could check it more heuristically like you suggest, it's likely that doing more complex logic would be more expensive, since we'd need to make several different checks and verify a wider scope of things than just checking all the fields.
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 re-read my counterexample:
What is a private field is not readonly but a property (used the field) is?
using System;
struct S
{
private int a;
S(int p)
{
a = 1;
}
public int GetInt => a;
public readonly int GetIntMethod()
{
// we could a = 2 if not readonly
return a;
}
}You code return true but must false. See then compare with private readonly int a;.
Also the method is all for performance so we shouldn't address all rare cases. We should only reduce false positives in original code.
even if we could check it more heuristically like you suggest
I mean new C# 8.0 feature https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#readonly-instance-members
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.
Right, but that's a limited case, and doesn't account for (as an example) a method being provided that mutates the private field. We don't have a way to ensure that a settable field is not being set, so it will reduce the false positives more effectively to simply check for settable fields. Relying on the attribute is good but will still result in false negatives (the method returning false when the value type is in fact mutable) which I think we should definitely avoid.
I'm not sure I necessarily agree with your reasoning that it's only worth improving in certain ways. If we choose to compare lists of properties and methods as well as fields, in many cases we will end up needing to make more reflection calls than only iterating the fields.
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.
but that's a limited case
Whole the method is limited case because it is only for value types and most of calls will fast return on first condition type.IsPrimitive.
But it is BINDER code that is very-very sensitive and we need strong motivation to change it. Currently no use case exists we should address and no reliable tests and measures too.
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.
Right, that's why the current flawed assumptions are concerning.
But if it helps, I can add a case for primitives/enums back in, it most likely would short-circuit things a bit quicker.
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.
Added the short-circuiting for enums and primitives back in and tidied things up a little bit.
3ca57b0 to
e1a93df
Compare
src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Outdated
Show resolved
Hide resolved
src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Outdated
Show resolved
Hide resolved
src/System.Management.Automation/engine/runtime/Binding/Binders.cs
Outdated
Show resolved
Hide resolved
db17a39 to
5f21a13
Compare
|
@PoshChan please retry all |
|
@vexx32, did not find any matching pull request checks |
|
This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days. |
|
Open PRs should not be assigned to milestone, so they are not assigned to the wrong milestone after they are merged. For backport consideration, use a |
|
Merging this in so we can get early validation for it in the next preview |
|
🎉 Handy links: |
@rjmholt Have you a plan for the validation? I still have concerns about the implementation. |
|
@iSazonov can you summarise your concerns here for other readers? With the changes in a release we might be able to get more users trying this out. We can then set up a decision on whether to revert this, require changes or leave it as is before the next release. |
Oh, every step in this change raises questions. You can see it in my comments above.
Ideally, we should have seen good perf win in some scenarios. What is the scenarios and where is perf tests? Do we cover all new .Net features? |

PR Summary
Currently, we have some flawed assumptions around what defines a mutable value type. Rather than make a more complicated series of checks and making assumptions, we can simply look at whether the type is defined with the
IsReadOnlyAttributethat the compiler uses to guarantee a value type is immutable.As a fallback, it's much safer and less complex to enumerate all public and private fields on the type, and verify whether they're all read-only.
PR Context
Resolves #12430. See the issue for discussion and further context.
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:or[ WIP ]to the beginning of the title (theWIPbot will keep its status check atPendingwhile the prefix is present) and remove the prefix when the PR is ready.