-
Notifications
You must be signed in to change notification settings - Fork 4.6k
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
Covariant Returns Feature #35308
Covariant Returns Feature #35308
Conversation
…derived types of the return type on the MethodDecl. Feature limitations: - Only supports MethodImpls and MethodDecls on classes (no interfaces/valuetypes). - MethodImpl and MethodDecl cannot be on the same type. - Interface/valuetypes not supported as covariant return types. Changes are mostly a boolean flag being passed around to allow for covariant type checking in method signatures. Generics are handled by correctly keeping track of the substitution chain while traversing the base type hierarchy for type comparison. All method signature comparisons uses metadata for checking, without loading any type (with some exceptions). Validation for the `ValidateMethodImplRemainsInEffectAttribute` is performed at the very last step of `CLASS_LOAD_EXACTPARENTS`. Inludes unit tests to cover positive/negative scenarios: - Non-generics - Generics, with various levels of complexities on generic instantiations and substitutions - GVMs - Delegates - Tests for validation of the existance/absence of the ValidateMethodImplRemainsInEffectAttribute - Interface and valuetype scenarios (negative scenarios)
Posting this to start getting feedback while working on the Crossgen2 changes. @davidwrighton @jkotas Who can I talk to regarding the Mono side of the work? |
...te.CoreLib/src/System/Runtime/CompilerServices/ValidateMethodImplRemainsInEffectAttribute.cs
Outdated
Show resolved
Hide resolved
...tests/src/Loader/classloader/MethodImpl/CovariantReturns/Validation/OverrideSameSigAsDecl.il
Outdated
Show resolved
Hide resolved
@@ -0,0 +1,83 @@ | |||
# Covariant Return Methods | |||
|
|||
Covariant return methods is a runtime feature designed to support the [covariant return types](https://github.com/dotnet/csharplang/blob/master/proposals/covariant-returns.md) and [records](https://github.com/dotnet/csharplang/blob/master/proposals/records.md) C# language features posed for C# 9.0. |
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.
Could you please add a chapter on this to https://github.com/dotnet/runtime/blob/master/docs/design/specs/Ecma-335-Augments.md ? Try to match the style and language used by ECMA-335 (e.g. in the ideal case - we would just copy&paste it over once we figure out how to make edits to ECMA-335).
We should have the Mono side figured out before starting the work next time. |
… fails Fix a couple of issues in IL tests
I agree such change has to be coordinated from the beginning as we now need to re-prioritize other work otherwise we ship with incompatible runtimes for net5. Please loop @lambdageek and @vargaz on type-system design decisions from the beginning. |
The validation around the |
…ing, and use CanCastTo instead of signature-based checking, to be allow for type compatibility based on ECMA I.8.7.1. Fixed behavior of the ValidateMethodImplRemainsInEffectAttribute. Updates to tests
Fixed an assert
@janvorli, For instance something like the following
|
Based on PR feedback, I've added test cases to verify the load level asserts change.
@davidwrighton I've added a commit with a test based on your suggestion and tested it with debug build of runtime. It works as expected. |
The generic variance tests look good. Looking through the tests I think we need to have Nullable tests as well to ensure that CanCastTo isn't causing a problem. If you want to be particular, testing to see if int? and int are compatible-with. They should not be for this calculation. |
The attempt to overload method with int32 return value by a method with Nullable<int32> was asserting in CanCastTo. This change fixes it by pre-checking this case before calling into CanCastTo. I've also slightly refactored the ValidateMethodsWithCovariantReturnTypes by extracting the compatible-with stuff into IsCompatibleWith method. Added tests to test various compatible-with differences from the originally used CanCastTo
@davidwrighton I've tested and fixed the nullable case and refactored the code a tiny bit by introducing IsCompatibleWith method that wraps around the CanCastTo and does some of the pre-filtering as you've suggested. |
Have we gotten api signoff on RequireMethodImplToRemainInEffectAttribute yet? Otherwise I think this is implemented and the logic looks good. |
Doesn't seem so, #35315. |
src/coreclr/src/vm/class.cpp
Outdated
@@ -1067,10 +1090,7 @@ void ClassLoader::ValidateMethodsWithCovariantReturnTypes(MethodTable* pMT) | |||
{ | |||
GCX_COOP(); |
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.
It might be better to move GCX_COOP inside the IsCompatibleWith helper when needed (not all checks need 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.
Thanks Fadi, it makes sense. I've made that change. Btw, I am still using your branch for finishing this work, so I'd like to ask you to keep it until it is merged.
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.
Thanks Jan, yes I’ll keep the branch around
Do we need to port that to crossgen2 as well? In general I don't believe the CompatibleWithTest.il test that tests the CanCastTo pre-filtering is going to work with crossgen2 and optimizations enabled - I expect RyuJIT to optimize out the It might be better to do newobj/callvirt to ensure the virtual method resolution in crossgen2 is actually hit and we test what we want to test. |
I've tested it with crossgen2 and optimizations enabled and the test was passing (that means the parts that should succeed were succeeding and the parts that should fail were failing). But it makes sense to change the newobj/pop to newobj / callvirt.
Right, I've missed that |
- Change the CompatibleWithTest to call virtual methods on test classes instead of just instantiating the classes. - Update crossgen2 similar to the runtime to have IsCompatibleWith method doing the same pre-filtering before calling CanCastTo.
@MichalStrehovsky I've made changes based on your feedback. |
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.
Thanks!
|
||
private static bool IsEquivalentTo(this TypeDesc thisType, TypeDesc otherType) | ||
{ | ||
// TODO: Once type equivalence is implemented, this implementation needs to be enhanced to honor 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.
Is type equivalence used for anything outside COM?
We don't intend to model COM in the managed type system so if this is exclusive to COM, the use of this can be replaced with ==
(we can safely use refence equality in the type system; no need to call Equals
).
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.
I think that it is used just by COM, @davidwrighton can you please confirm that?
return false; | ||
} | ||
|
||
return otherType.CanCastTo(thisType); |
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.
This is still going to throw if thisType or otherType is a pointer or function pointer. Do we need to handle that? (I had that feedback in #35308 (comment) but it probably got lost.
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.
Ah, let me add those to the test suite. It seems pointers cannot be compatible-with each other unless they are the same. I am actually not sure if the function pointers correspond to "method-signature compatible-with" case in the ECMA spec.
The mono doesn't implement the feature yet
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.
This looks good now. I believe we may have some small amount of cleanup around error conditions about pointers and function pointers, but we should handle those as bugs.
Are we happy about the perf of virtual method resolution after this change? #35308 (comment) It feels like this attribute should be cached the same way we cache e.g. |
While not great, and I agree we should probably cache it, the perf impact isn't that significant in crossgen2 due to the relatively low amount of virtual resolution it performs. We'll probably need to adjust that when we port this to the CoreRT codebase where virtual resolution performance is much more important. However, that is bug fix level work that doesn't need to block checkin. |
I am going to create an issue tracking all the additional changes that were mentioned here so that they don't get lost. |
Allowing return types on MethodImpl to be derived types of the return type on the MethodDecl.
Feature limitations:
MethodImpls where the signature matches for all parameters except for the return type are conditionally treated as valid during enumerating/processing, but flagged for further return type checking at a later stage of type load.
For every type being loaded LOAD_EXACTPARENTS stage, check every flagged overriding method for return type compatibility with the overriden method (using
CanCastTo()
), and process the MethodImpls with the RequireMethodImplToRemainInEffect attribute.Inludes unit tests to cover positive/negative scenarios:
TODOs:
RequireMethodImplToRemainInEffectAttribute
and add it to System.Runtime reference assembly (and update tests to reference it from there instead of S.P.C)cc @dotnet/crossgen-contrib