diff options
author | monojenkins <jo.shields+jenkins@xamarin.com> | 2020-06-26 21:16:03 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-26 21:16:03 +0300 |
commit | c51181418271d83b6dca1c344acc557d0e85075f (patch) | |
tree | 8d71dbeb4ac125d09843b27c9bd8fc4038be4f1a /sdks/versions.mk | |
parent | f562eb8869b9ad412e5174d4c46b7be35a4c1783 (diff) |
[metadata] Implement PreserveBaseOverridesAttribute and covariant returns support (#19927)
Covariant return implementation for Mono.
---
There are also a couple of general Mono fixes in here, too:
1. Interpreter support for `MONO_TYPE_FNPTR` in `mint_type()`
2. a "signature assignment" mode for `mono_class_is_assignable_from` that doesn't treat `IntPtr[]` and `long[]` (or `int[]`) as interchangeable
3. support for unmanaged pointer comparisons in `mono_class_is_assignable_from`
4. Factor `mono_byref_type_is_assignable_from` out of `ves_icall_RuntimeTypeHandle_type_is_assignable_from` and make it generally usable.
---
For covariant returns, there are a couple of pieces here:
1. Method overrides come in two flavors: implicit (matching by name and sig) and explicit (using a `.override` directive).
* for explicit overrides, we have to check that the override has a signature that is subsumed by (its return type is a subtype of the return type of) the previous override, if any, and the declared method.
* for implicit overrides, we have to check that the override has a signature that is subsumed by the previous most derived override (not just the one that we happened to find with a precisely matching signature).
2. If any override is marked by `[PreserveBaseOverridesAttribute]` then we need to find all the other slots that may have methods from the override chain and update them. (The attribute is expected to be applied to a `newslot virtual` method that is an explicit `.override` of some other method).
There's an interesting test case where a class has _both_ an implicit and and explicit override for the same slot, arrived via different methods - in this case, the implicit one is supposed to be ignored. We implement this by doing the implicit slot filling first, then the explicit, and saving the covariant return checking until after both are done.
---
Because the method (`MethodImpl`) is both an override of another virtual method,
and is itself a `newslot`, it will occupy (at least) 2 slots in the vtable -
its own newslot and the `MethodDecl` that it is overriding.
If a third class subclasses the class with the `MethodImpl` or one of its
descendents and itself overrides either the `MethodDecl` or (one of the)
`MethodImpl` (either implicitly or with another explicit .override) the
`PreserveBaseOverridesAttribute` is a signal to apply the new override that it
should apply to all the slots.
The way this works is when processing the overrides, we walk up the class
hierarchy looking at the contents of the impl slot and if any of them have the
attribute, we make sure to add a mapping to the "`override_map`" to replace any
occurrences of a previous impl with the newest (most derived) impl.
An (existing) later pass goes through the vtable and applies the override_map.
Contributes to dotnet/runtime#37509
* [metadata] Implement support for PreserveBaseOverridesAttribute
The attribute is expected to be applied to a `newslot virtual` method that
is an explicit .override of some other method.
Becuase the method (MethodImpl) is both an override of another virtual method,
and is itself a `newslot`, it will occupy (at least) 2 slots in the vtable -
its own newslot and the MethodDecl that it is overriding.
If a third class subclasses the class with the MethodImpl or one of its
descendents and itself overrides either the MethodDecl or (one of the)
MethodImpl (either implicitly or with another explicit .override) the
PreserveBaseOverridesAttribute is a signal to apply the new override that it
should apply to all the slots.
The way this works is when processing the overrides, we walk up the class
hierarchy looking at the contents of the impl slot and if any of them have the
attribute, we make sure to add a mapping to the "override_map" to replace any
occurrences of a previous impl with the newest (most derived) impl.
An (existing) later pass goes through the vtable and applies the override_map.
* add FIXME for dynamic images
* scan entire chain of inheritance, don't stop at the class with the attribute
Makes OverrideMoreDerivedReturn.il pass
* UnitTestDelegates is also passing
* [metadata] Add mono_metadata_signature_equal_no_ret
* inflate signature->ret when doing covariant checking
* [class] Add mono_class_signature_is_assignable_from_checked
In ECMA I.8.7.1 "assignment compatability for signature types" we need to
distinguish IntPtr[] from int32[] and int64[]. The ordinary cast_class for an
array type in Mono implements the "intermediate type" relation from I.8.7.3
"general assignment compatability" - IntPtr[] gets treated as inter-castable
with int32[] or int64[], depending on platform pointer size.
The new mono_class_signature_is_assignable_from_checked function does the finer
relation for signatures, while the old mono_class_signature_is_assignable_from_checked
preserves the existing behavior used elsewhere in the runtime.
* Check every method previously in the slot against the new impl
* UnitTest_GVM passes
* [metadata] Don't infinite loop in mono_class_get_flags for a FNPTR
When a MonoClass is a MONO_TYPE_FNPTR, the element_class is itself. We should
probably not do that, but meanwhile, make `mono_class_get_flags` return
something else for function pointer types.
Makes mono_class_is_interface not overflow the stack
* [metadata] Set MonoClass:cast_class for pointer types to the intermediate type
Ignore signedness differences and set cast_class to the storage type, same as
for arrays. This is used by mono_class_is_assignable_from when comparing
IntPtr* vs ulong*, for example.
* [metadata] Make mono_class_is_assignable_from work for unmanaged pointer types
The issue is that we need to compare intermediate type or the reduced type of
the element type, depending on whether we're doing compatability or general
assignment compatability (upshot: ulong* and long* are always inter-assignable,
but IntPtr* is not in signatures, but is for general storage)
* [metadata] Pull mono_type_byref_is_assignable_from out of ves_icall_RuntimeTypeHandle_type_is_assignable_from
Make it a separate internal function
* [metadata] mono_type_byref_is_assignable_from for valuetypes and class types should check for identity
X& and Y& where both are either a reference types or valuetypes are only
assignable if X and Y are identical. The old code was checking for identity
for valuetypes, but for reference types it would allow any two reference types
at all, which is incorrect.
* [class-init] Check for byref types in covariant return signatures
Lets us pass Loader/classloader/MethodImpl/CovariantReturns/UnitTest/CompatibleWithTest.il from the CoreCLR testsuite
* [interp] handle MONO_TYPE_FNPTR in mint_type
It's just a pointer-sized integer, as far as the interpreter's type discipline is concerned
* [class-init] Use a bit for covariant return method impls
Allows us to check implicit overrides against previous impl's sig when the previous impl potentially had a covariant return type.
Also use the bit to decide if we need to check signatures of the entire impl chain for explicit instantiations
* ReturnTypeValidation unit tests work on Mono now
* [class-init] check covariant vtable slots after overloads are applied
The check has to be done after both implicit and explicit overrides have been
applied because some implicit overrides must be ignored. If the check is done
as soon as we detect that a covariant return method override is needed, we will
incorrectly check implicit overrides that are to be ignored because a later
explicit override applied to the same slot.
* [metadata] Fix byref IsAssignableFrom
The reflection API has an interesting behavior, whereas the ECMA definition is
stricter.
```
public interface I {}
public class B {}
public class D : B, I {}
public struct S : I { }
class Program {
static void Main(string[] args)
{
var tb = typeof (B).MakeByRefType ();
var td = typeof (D).MakeByRefType ();
var ti = typeof (I).MakeByRefType ();
var ts = typeof (S).MakeByRefType ();
Console.WriteLine ($"{tb.FullName} is assignable from {td.FullName} ? {tb.IsAssignableFrom (td) }");
Console.WriteLine ($"{td.FullName} is assignable from {tb.FullName} ? {td.IsAssignableFrom (tb) }");
Console.WriteLine ($"{ti.FullName} is assignable from {td.FullName} ? {ti.IsAssignableFrom (td) }");
Console.WriteLine ($"{td.FullName} is assignable from {ti.FullName} ? {td.IsAssignableFrom (ti) }");
Console.WriteLine ($"{ti.FullName} is assignable from {ts.FullName} ? {ti.IsAssignableFrom (ts) }");
Console.WriteLine ($"{ts.FullName} is assignable from {ti.FullName} ? {ts.IsAssignableFrom (ti) }");
}
}
// Expected Output:
// B& is assignable from D& ? True
// D& is assignable from B& ? False
// I& is assignable from D& ? True
// D& is assignable from I& ? False
// I& is assignable from S& ? False
// S& is assignable from I& ? False
```
* rename mono_class_signature_is_assignable
No need for _checked suffix
* rename mono_byref_type_is_assignable_from
* [class-init] Move vtable setup to a separate file
The code is getting quite long, move it to a separate file
Diffstat (limited to 'sdks/versions.mk')
0 files changed, 0 insertions, 0 deletions