Replies: 4 comments 10 replies
-
I think people will be very confused to find that it works for struct unions but not class unions. Also, will that work for AOT shared generics? |
Beta Was this translation helpful? Give feedback.
-
I have proposed a general solution to this and a broader range of situations in the runtime repo (dotnet/runtime#105923), but I'd definitely appreciate seeing this addressed! |
Beta Was this translation helpful? Give feedback.
-
I think if there's going to be any runtime support for these sorts of type tests, it should be in the VM in the casting support. It could still use the same sort of annotations (attribute on the case type pointing back to the union type), but it should be implemented at the same layer of our other "special casting case" support, like For the purposes of the language, it doesn't really matter who implements the special casting rules, just that they're runtime implemented and that the C# compiler and spec can specify that an unboxing cast would lower to a regular cast instruction. This ends up being a special case of @IS4Code's proposal I guess (though marginally less footguns and slightly simpler implementation because it's a more constrained scenario). As a further optimization, the JIT could optimize these casts, but it should start as a VM feature if there's any runtime support at all. |
Beta Was this translation helpful? Give feedback.
-
I'm not sure I agree that this is needed. I would change this more to: we could also consider... Instead |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I'm opening this discussion to provide some feedback on the TypeUnions proposal.
In
csharplang/proposals/TypeUnions.md
Line 336 in dcea688
we have
Today we are specializing code for value-type type parameters, so that
IsType<A>
will be specialized to its own code anyway.Given this fact, here we can ask the JIT for some help: we can change the JIT to generate additional code for testing type against the union, only for specialized code whose type arguments are union structs.
Do note that this doesn't mean we are relying on the JIT impl details to implement the type testing. In general, we need to support union structs for all cases involving type testing, here what I'm talking about is an optimization that JIT can be smart so that it won't regress cases that are not involving union structs.
Preparation
We use a marker interface for all type unions and the C# compiler always emit code that do the type test against the marker interface.
Besides, the C# compiler emits an attribute
[TypeUnionCase(typeof(<containing union struct>))]
for union struct cases. Also it emits a generic version ofTryGet<T>
for use by the VM or the JIT.Say now we have
It got lowered to
By the way, by adopting the marker interface, existing libraries like
OneOf
and etc. can also implementITypeUnion
so that they can get the language support for free.Option 1: Implementation in VM, Optimization in JIT
Just like what we are going to do for
ref struct
in type arguments, we use the same intrinsic methodsbool RuntimeHelpers.IsInstanceOf<TFrom, TTo>(TFrom source)
andTTo RuntimeHelpers.CastTo<TFrom, TTo>(TFrom source)
for type testing and type casting.For type testing, the C# compiler always emit:
The implementation will be handled by
IsInstanceOf
, and will be optimzied by the JIT, while Roslyn doesn't need to do anything else.Then the two intrinsic methods will be updated to support boxed struct unions, and because the JIT can see the exact type argument in value type cases, they can be specialized and expanded during compile time so that
TTo
is a case member of a type union, it will be imported assource is U u && u.TryGet<TTo>(out _);
This can make sure we always get the correct result on any VM: for VMs that don't recognize the intrinsics, we pay for some overhead to run the fallback implementation in
IsInstanceOf
andCastTo
; for others, there will be no performance penalty at all.Option 2: Optimization in JIT
For type testing, the C# compiler always emit:
When the JIT generates code for a method
Foo<A>
, it looks forTypeUnionCaseAttribute
on the type argumentA
whenA
is a value type. If the attribute is found, in the importer stage, the JIT fetches the union typeU
from the attribute and inserts additional code to do type test against the union so that the codebecomes
while for others like
Foo<B>
whereB
is a non-union struct or a class, the codegen won't be changed so the performance of type test against non-union types won't be regressed.Note
We may also consider to update the implementation of
Type.IsSubclassOf
to support this forSystem.Type
.Type.IsSubclassOf
is a JIT intrinsic so we can do this smart without regressing performance for most cases while providing fallback code for runtime that doesn't support this intrinsic. And the IL sequencebox - isinst
may also need to be updated, just like what we did forref struct
, so that the behavior can be consistent.Conclusion
In this way we can support type testing and casting for boxed union struct, also we don't regress the performance for cases other than union struct.
Beta Was this translation helpful? Give feedback.
All reactions