-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Proposed: C# compiler could use flow analysis to track types in variables permitting the use of a constrained
instruction on virtual calls.
#22092
Comments
This has nothing to do with pattern-matching. It produces the same instructions for public static bool Equals(IEquatable<TKey> key, TKey key1)
{
return key.Equals(key1);
} |
I see, though, why you suggest the constraint (since the receiver is "known" to be a |
constrained
instruction on virtual calls.
As background... (from https://github.com/dotnet/coreclr/issues/12877#issuecomment-329247453) The scenario I'm trying to cover is calling struct generics if the type implements the the interface; so in effect getting the benefit of having a generic constraint without one being present. The example I was looking at was the default comparer on dictionary; which currently goes via hoops and inheritance to implement the constrained However while that works, it also ends up with virtual calls for
As highlighted by ravendb in their fast-dictionary blog post; and the same thing could be achieved with the default comparer and struct keys in the regular dictionary. Using a non-inherited internal class DictionaryEquatableComparer<T> where T : IEquatable<T>
{
public bool Equals(T x, T y) => x.Equals(y);
public int GetHashCode(T obj) => obj.GetHashCode();
}
public class OverridableComparer<TKey> where TKey : IEquatable<TKey>
{
private static readonly DictionaryEquatableComparer<TKey> s_comparer = new DictionaryEquatableComparer<TKey>();
private IEqualityComparer<TKey> _comparer;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool KeyEquals(TKey key0, TKey key1)
{
return _comparer == null ? s_comparer.Equals(key0, key1) : _comparer.Equals(key0, key1);
}
} However can't be used in dictionary as So was looking to see if pattern matching could solve it; as at Jit time everything is known about the key type (if struct; i.e. whether it implements That was the hope and motivation anyway :) |
It is (logically) at this point however isn't it? case IEquatable<TKey> key:
return key.Equals(key1); // <---- Its both |
Specifically I'd like to do something like this for private static readonly EqualityComparer<TKey> s_defaultComparer;
private readonly IEqualityComparer<TKey> _customComparer;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool KeyEquals(TKey key0, TKey key1)
{
bool result;
if (_customComparer == null)
{
switch (key0)
{
case IEquatable<TKey> key:
result = key.Equals(key1);
break;
default:
result = s_defaultComparer.Equals(key0, key1);
break;
}
}
else
{
result = _customComparer.Equals(key0, key1);
}
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int KeyHashCode(TKey key)
{
int result;
if (_customComparer == null)
{
switch (key)
{
case IEquatable<TKey> k:
result = k.GetHashCode();
break;
default:
result = s_defaultComparer.GetHashCode(key);
break;
}
}
else
{
result = _customComparer.GetHashCode(key);
}
return result & 0x7FFFFFFF;
} |
The constrained call serves to avoid boxing the receiver. But in this case the receiver is already a reference type. |
The proposed use of the |
Because its actually? var key = key1 as IEquatable<TKey> Maybe I want something more like? switch (typeof(TKey))
{
case IEquatable<TKey>:
result = key0.Equals(key1); // bind to IEquatable<TKey>.Equals not Object.Equals |
That's not correct IL either, as |
Loosing the perf wins at typeof(IEquatable<>).MakeGenericType(typeof(TKey)).IsAssignableFrom(typeof(TKey)) Will raise something over at csharplang Thank you for looking at it |
Boxing for
isinst
aside https://github.com/dotnet/coreclr/issues/12877 it would be nice if Pattern Matching issued aconstrained
instruction prior tocallvirt
where a generic type was matched to an interfaceVersion Used:
Steps to Reproduce:
Where
Expected Behavior:
il output
Actual Behavior:
il output
/cc @gafter @AlekseyTs @agocke
The text was updated successfully, but these errors were encountered: