Skip to content
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

Type.IsAssignableTo #40326

Merged
merged 9 commits into from
Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ internal static object CreateDefaultEqualityComparer(Type type)
result = new ByteEqualityComparer();
}
// If T implements IEquatable<T> return a GenericEqualityComparer<T>
else if (typeof(IEquatable<>).MakeGenericType(type).IsAssignableFrom(type))
else if (type.IsAssignableTo(typeof(IEquatable<>).MakeGenericType(type)))
{
result = CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer<int>), runtimeType);
}
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3730,6 +3730,7 @@ class Compiler

void impImportLeave(BasicBlock* block);
void impResetLeaveBlock(BasicBlock* block, unsigned jmpAddr);
GenTreeIntCon* impTypeIsAssignable(GenTree* typeTo, GenTree* typeFrom);
GenTree* impIntrinsic(GenTree* newobjThis,
CORINFO_CLASS_HANDLE clsHnd,
CORINFO_METHOD_HANDLE method,
Expand Down
93 changes: 62 additions & 31 deletions src/coreclr/src/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4126,43 +4126,26 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,

case NI_System_Type_IsAssignableFrom:
{
// Optimize patterns like:
//
// typeof(TTo).IsAssignableFrom(typeof(TTFrom))
// valueTypeVar.GetType().IsAssignableFrom(typeof(TTFrom))
//
// to true/false
GenTree* typeTo = impStackTop(1).val;
GenTree* typeFrom = impStackTop(0).val;

if (typeTo->IsCall() && typeFrom->IsCall())
GenTreeIntCon* node = impTypeIsAssignable(typeTo, typeFrom);
if (node != nullptr)
benaadams marked this conversation as resolved.
Show resolved Hide resolved
{
// make sure both arguments are `typeof()`
CORINFO_METHOD_HANDLE hTypeof = eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE);
if ((typeTo->AsCall()->gtCallMethHnd == hTypeof) && (typeFrom->AsCall()->gtCallMethHnd == hTypeof))
{
CORINFO_CLASS_HANDLE hClassTo =
gtGetHelperArgClassHandle(typeTo->AsCall()->gtCallArgs->GetNode());
CORINFO_CLASS_HANDLE hClassFrom =
gtGetHelperArgClassHandle(typeFrom->AsCall()->gtCallArgs->GetNode());

if (hClassTo == NO_CLASS_HANDLE || hClassFrom == NO_CLASS_HANDLE)
{
break;
}
retNode = node;
}
break;
}

TypeCompareState castResult = info.compCompHnd->compareTypesForCast(hClassFrom, hClassTo);
if (castResult == TypeCompareState::May)
{
// requires runtime check
// e.g. __Canon, COMObjects, Nullable
break;
}
case NI_System_Type_IsAssignableTo:
{
GenTree* typeTo = impStackTop(0).val;
GenTree* typeFrom = impStackTop(1).val;

retNode = gtNewIconNode((castResult == TypeCompareState::Must) ? 1 : 0);
impPopStack(); // drop both CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE calls
impPopStack();
}
GenTreeIntCon* node = impTypeIsAssignable(typeTo, typeFrom);
if (node != nullptr)
{
retNode = node;
}
break;
}
Expand Down Expand Up @@ -4368,6 +4351,50 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
return retNode;
}

GenTreeIntCon* Compiler::impTypeIsAssignable(GenTree* typeTo, GenTree* typeFrom)
{
// Optimize patterns like:
//
// typeof(TTo).IsAssignableFrom(typeof(TTFrom))
// valueTypeVar.GetType().IsAssignableFrom(typeof(TTFrom))
// typeof(TTFrom).IsAssignableTo(typeof(TTo))
// typeof(TTFrom).IsAssignableTo(valueTypeVar.GetType())
//
// to true/false

if (typeTo->IsCall() && typeFrom->IsCall())
{
// make sure both arguments are `typeof()`
CORINFO_METHOD_HANDLE hTypeof = eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE);
if ((typeTo->AsCall()->gtCallMethHnd == hTypeof) && (typeFrom->AsCall()->gtCallMethHnd == hTypeof))
{
CORINFO_CLASS_HANDLE hClassTo = gtGetHelperArgClassHandle(typeTo->AsCall()->gtCallArgs->GetNode());
CORINFO_CLASS_HANDLE hClassFrom = gtGetHelperArgClassHandle(typeFrom->AsCall()->gtCallArgs->GetNode());

if (hClassTo == NO_CLASS_HANDLE || hClassFrom == NO_CLASS_HANDLE)
{
return nullptr;
}

TypeCompareState castResult = info.compCompHnd->compareTypesForCast(hClassFrom, hClassTo);
if (castResult == TypeCompareState::May)
{
// requires runtime check
// e.g. __Canon, COMObjects, Nullable
return nullptr;
}

GenTreeIntCon* retNode = gtNewIconNode((castResult == TypeCompareState::Must) ? 1 : 0);
impPopStack(); // drop both CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE calls
impPopStack();

return retNode;
}
}

return nullptr;
}

GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method,
CORINFO_SIG_INFO* sig,
var_types callType,
Expand Down Expand Up @@ -4614,6 +4641,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
{
result = NI_System_Type_IsAssignableFrom;
}
else if (strcmp(methodName, "IsAssignableTo") == 0)
{
result = NI_System_Type_IsAssignableTo;
}
}
}
#if defined(TARGET_XARCH) || defined(TARGET_ARM64)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/src/jit/namedintrinsiclist.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ enum NamedIntrinsic : unsigned short
NI_System_GC_KeepAlive,
NI_System_Type_get_IsValueType,
NI_System_Type_IsAssignableFrom,
NI_System_Type_IsAssignableTo,

// These are used by HWIntrinsics but are defined more generally
// to allow dead code optimization and handle the recursion case
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ internal void ToString(TraceFormat traceFormat, StringBuilder sb)
bool methodChanged = false;
if (declaringType != null && declaringType.IsDefined(typeof(CompilerGeneratedAttribute), inherit: false))
{
isAsync = typeof(IAsyncStateMachine).IsAssignableFrom(declaringType);
if (isAsync || typeof(IEnumerator).IsAssignableFrom(declaringType))
isAsync = declaringType.IsAssignableTo(typeof(IAsyncStateMachine));
if (isAsync || declaringType.IsAssignableTo(typeof(IEnumerator)))
{
methodChanged = TryResolveStateMachineMethod(ref mb, out declaringType);
}
Expand Down
1 change: 1 addition & 0 deletions src/libraries/System.Private.CoreLib/src/System/Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public virtual Type[] GetGenericParameterConstraints()
public bool IsPrimitive => IsPrimitiveImpl();
protected abstract bool IsPrimitiveImpl();
public bool IsValueType { [Intrinsic] get => IsValueTypeImpl(); }
public bool IsAssignableTo([NotNullWhen(true)] Type? targetType) => targetType?.IsAssignableFrom(this) ?? false;
benaadams marked this conversation as resolved.
Show resolved Hide resolved
protected virtual bool IsValueTypeImpl() => IsSubclassOf(typeof(ValueType));

public virtual bool IsSignatureType => false;
Expand Down
1 change: 1 addition & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4337,6 +4337,7 @@ protected Type() { }
public abstract object? InvokeMember(string name, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder? binder, object? target, object?[]? args, System.Reflection.ParameterModifier[]? modifiers, System.Globalization.CultureInfo? culture, string[]? namedParameters);
protected abstract bool IsArrayImpl();
public virtual bool IsAssignableFrom([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] System.Type? c) { throw null; }
public bool IsAssignableTo([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] System.Type? targetType) { throw null; }
protected abstract bool IsByRefImpl();
protected abstract bool IsCOMObjectImpl();
protected virtual bool IsContextfulImpl() { throw null; }
Expand Down
4 changes: 4 additions & 0 deletions src/mono/mono/metadata/reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -3131,7 +3131,11 @@ mono_reflection_call_is_assignable_to (MonoClass *klass, MonoClass *oklass, Mono
error_init (error);

if (method == NULL) {
#ifdef ENABLE_NETCORE
method = mono_class_get_method_from_name_checked (mono_class_get_type_builder_class (), "IsAssignableToInternal", 1, 0, error);
#else
method = mono_class_get_method_from_name_checked (mono_class_get_type_builder_class (), "IsAssignableTo", 1, 0, error);
#endif
mono_error_assert_ok (error);
vargaz marked this conversation as resolved.
Show resolved Hide resolved
g_assert (method);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ protected override TypeAttributes GetAttributeFlagsImpl()
}

[DynamicDependency(nameof(state))] // Automatically keeps all previous fields too due to StructLayout
[DynamicDependency(nameof(IsAssignableTo))] // Used from reflection.c: mono_reflection_call_is_assignable_to
[DynamicDependency(nameof(IsAssignableToInternal))] // Used from reflection.c: mono_reflection_call_is_assignable_to
internal TypeBuilder(ModuleBuilder mb, TypeAttributes attr, int table_idx)
{
this.parent = null;
Expand All @@ -107,7 +107,7 @@ internal TypeBuilder(ModuleBuilder mb, TypeAttributes attr, int table_idx)
Justification = "Linker doesn't analyze ResolveUserType but it's an identity function")]

[DynamicDependency(nameof(state))] // Automatically keeps all previous fields too due to StructLayout
[DynamicDependency(nameof(IsAssignableTo))] // Used from reflection.c: mono_reflection_call_is_assignable_to
[DynamicDependency(nameof(IsAssignableToInternal))] // Used from reflection.c: mono_reflection_call_is_assignable_to
internal TypeBuilder(ModuleBuilder mb, string fullname, TypeAttributes attr, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]Type? parent, Type[]? interfaces, PackingSize packing_size, int type_size, Type? nesting_type)
{
int sep_index;
Expand Down Expand Up @@ -1718,7 +1718,7 @@ public override bool IsAssignableFrom([NotNullWhen(true)] Type? c)
}

// FIXME: "arrays"
internal bool IsAssignableTo([NotNullWhen(true)] Type? c)
internal bool IsAssignableToInternal([NotNullWhen(true)] Type? c)
{
if (c == this)
return true;
Expand Down