Allow exact devirtualization for value-type variant instantiations in ilc#129905
Allow exact devirtualization for value-type variant instantiations in ilc#129905jtschuster wants to merge 1 commit into
Conversation
… NativeAOT PR dotnet#123476 made getExactClasses bail for any variant type to avoid unsoundly folding variant casts (e.g. Action<string> vs Action<object>). That guard is over-broad: variance conversions only happen through reference conversions of the variant type arguments, so a type instantiated with only value-type variant arguments cannot be substituted via variance and its implementor list is still complete. This regressed exact devirtualization of the contravariant IEqualityComparer<T> calls in Dictionary<TKey, TValue>.FindValue for value-type keys, inflating the method and slowing the hot path (issue dotnet#129895). Bail only when a variant generic parameter is instantiated with a reference (GC) type, matching CastingHelper's IsBoxedAndCanCastTo which requires IsGCPointer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib |
There was a problem hiding this comment.
Pull request overview
This PR refines CorInfoImpl.getExactClasses behavior in the NativeAOT ILCompiler JIT interface so that variance only blocks exact-class reporting when variant generic parameters are instantiated with reference types, restoring exact devirtualization opportunities for value-type instantiations.
Changes:
- Split the
type == null || type.HasVarianceearly-bail to allow more granular variance handling. - Introduce a helper to detect whether a variant type instantiation includes any reference-type variant arguments.
- Use that helper to decide when variance makes
getExactClassesunsound.
| if (((GenericParameterDesc)genericParameters[i]).Variance != GenericVariance.None | ||
| && typeInstantiation[i].IsGCPointer) |
| // so reporting an exact class set would be unsound. This only happens through | ||
| // reference conversions of the variant arguments, so it's still safe when every | ||
| // variant argument is a value type (e.g. the contravariant IEqualityComparer<int> |
There was a problem hiding this comment.
This is not true, variance can play role for valuetypes too. I had no confidence in being able to capture all the rules when variance and whole program analysis is involved. We had several bugs in this area.
// True
Console.WriteLine(G() is ICollection<uint>);
static object G() => new int[0];
There was a problem hiding this comment.
The array case does get handled elsewhere, but if this sounds like it'll just turn into whack-a-mole I'll close this PR.
Dictionary<int,int>'s getter regressed slightly in .NET 11 NativeAOT (see semi-related #129895).
PR #123476 made getExactClasses bail for any variant type to avoid unsoundly folding variant casts (e.g. Action vs Action). That guard is slightly over-broad: variance is only relevant for reference types and can be ignored for ValueType type arguments.
This regressed exact devirtualization of the contravariant IEqualityComparer calls in Dictionary<TKey, TValue>.FindValue for value-type keys, inflating the method and slowing the hot path.
We can tighten the condition for bailing early and do so only when a variant generic parameter is instantiated with a reference type.