diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index d88ed59a8f1963..9d64ea5721f733 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -1368,6 +1368,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed case NI_System_Type_get_IsValueType: case NI_System_Type_get_IsPrimitive: case NI_System_Type_get_IsByRefLike: + case NI_System_Type_get_IsGenericType: case NI_System_Type_GetTypeFromHandle: case NI_System_String_get_Length: case NI_System_Buffers_Binary_BinaryPrimitives_ReverseEndianness: diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index ff087f04e4b0e9..47b25ab6255515 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -2726,6 +2726,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_System_Type_get_IsByRefLike: case NI_System_Type_IsAssignableFrom: case NI_System_Type_IsAssignableTo: + case NI_System_Type_get_IsGenericType: // Lightweight intrinsics case NI_System_String_get_Chars: @@ -3318,6 +3319,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, case NI_System_Type_get_IsValueType: case NI_System_Type_get_IsPrimitive: case NI_System_Type_get_IsByRefLike: + case NI_System_Type_get_IsGenericType: { // Optimize // @@ -3364,6 +3366,27 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, retNode = gtNewFalse(); } break; + case NI_System_Type_get_IsGenericType: + // a type is a generic type if there is at least one type argument that is + // some valid type, so we can always just check the one at index 0 for this. + // This will work on open generic types as well, and the returned type handle + // will be some handle that represents the (non constructed) type parameter. + if (info.compCompHnd->getTypeInstantiationArgument(hClass, 0) != NO_CLASS_HANDLE) + { + retNode = gtNewTrue(); + } + else if ((info.compCompHnd->getClassAttribs(hClass) & CORINFO_FLG_SHAREDINST) != 0) + { + // if we have no type arguments, check that the type itself is not __Canon, and + // simply skip expanding the intrinsic in that case, rather than incorrectly + // hardcoding 'false' as the resulting expression. + retNode = nullptr; + } + else + { + retNode = gtNewFalse(); + } + break; default: NO_WAY("Intrinsic not supported in this path."); @@ -9006,6 +9029,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Type_get_IsPrimitive; } + else if (strcmp(methodName, "get_IsGenericType") == 0) + { + result = NI_System_Type_get_IsGenericType; + } else if (strcmp(methodName, "get_IsByRefLike") == 0) { result = NI_System_Type_get_IsByRefLike; diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index 2e68fd501243ce..8e13a6d3f5b506 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -79,6 +79,7 @@ enum NamedIntrinsic : unsigned short NI_System_Type_get_IsPrimitive, NI_System_Type_get_IsByRefLike, NI_System_Type_get_TypeHandle, + NI_System_Type_get_IsGenericType, NI_System_Type_IsAssignableFrom, NI_System_Type_IsAssignableTo, NI_System_Type_op_Equality, diff --git a/src/libraries/System.Private.CoreLib/src/System/Type.cs b/src/libraries/System.Private.CoreLib/src/System/Type.cs index 1d798a494047e5..1de872d1e6a254 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Type.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Type.cs @@ -57,7 +57,7 @@ public bool IsInterface public virtual bool IsGenericParameter => false; public virtual bool IsGenericTypeParameter => IsGenericParameter && DeclaringMethod is null; public virtual bool IsGenericMethodParameter => IsGenericParameter && DeclaringMethod != null; - public virtual bool IsGenericType => false; + public virtual bool IsGenericType { [Intrinsic] get => false; } public virtual bool IsGenericTypeDefinition => false; public virtual bool IsSZArray => throw NotImplemented.ByDesign; diff --git a/src/tests/JIT/Intrinsics/TypeIntrinsics.cs b/src/tests/JIT/Intrinsics/TypeIntrinsics.cs index 218f17e3d17932..93faad7571661f 100644 --- a/src/tests/JIT/Intrinsics/TypeIntrinsics.cs +++ b/src/tests/JIT/Intrinsics/TypeIntrinsics.cs @@ -133,6 +133,7 @@ public static int TestEntryPoint() GetEnumUnderlyingType.TestGetEnumUnderlyingType(); IsPrimitiveTests(); + IsGenericTypeTests(); return 100 + _errors; } @@ -186,6 +187,73 @@ private static void IsPrimitiveTests() IsFalse(typeof(Dictionary<,>).IsPrimitive); } + private static void IsGenericTypeTests() + { + IsFalse(typeof(bool).IsGenericType); + IsFalse(typeof(char).IsGenericType); + IsFalse(typeof(sbyte).IsGenericType); + IsFalse(typeof(byte).IsGenericType); + IsFalse(typeof(short).IsGenericType); + IsFalse(typeof(ushort).IsGenericType); + IsFalse(typeof(int).IsGenericType); + IsFalse(typeof(uint).IsGenericType); + IsFalse(typeof(long).IsGenericType); + IsFalse(typeof(ulong).IsGenericType); + IsFalse(typeof(float).IsGenericType); + IsFalse(typeof(double).IsGenericType); + IsFalse(typeof(nint).IsGenericType); + IsFalse(typeof(nuint).IsGenericType); + IsFalse(typeof(IntPtr).IsGenericType); + IsFalse(typeof(UIntPtr).IsGenericType); + IsFalse(typeof(Enum).IsGenericType); + IsFalse(typeof(ValueType).IsGenericType); + IsFalse(typeof(SimpleEnum).IsGenericType); + IsFalse(typeof(IntPtrEnum).IsGenericType); + IsFalse(typeof(FloatEnum).IsGenericType); + IsFalse(typeof(decimal).IsGenericType); + IsFalse(typeof(TimeSpan).IsGenericType); + IsFalse(typeof(DateTime).IsGenericType); + IsFalse(typeof(DateTimeOffset).IsGenericType); + IsFalse(typeof(Guid).IsGenericType); + IsFalse(typeof(Half).IsGenericType); + IsFalse(typeof(DateOnly).IsGenericType); + IsFalse(typeof(TimeOnly).IsGenericType); + IsFalse(typeof(Int128).IsGenericType); + IsFalse(typeof(UInt128).IsGenericType); + IsFalse(typeof(string).IsGenericType); + IsFalse(typeof(object).IsGenericType); + IsFalse(typeof(RuntimeArgumentHandle).IsGenericType); + IsFalse(typeof(DerivedGenericSimpleClass).IsGenericType); + IsFalse(typeof(int[]).IsGenericType); + IsFalse(typeof(int[,]).IsGenericType); + IsFalse(typeof(int*).IsGenericType); + IsFalse(typeof(void*).IsGenericType); + IsFalse(typeof(delegate*).IsGenericType); + + IsTrue(typeof(GenericSimpleClass).IsGenericType); + IsTrue(typeof(GenericSimpleClass<>).IsGenericType); + IsTrue(typeof(GenericSimpleClass.Nested).IsGenericType); + IsTrue(typeof(GenericSimpleClass<>.Nested).IsGenericType); + IsTrue(typeof(GenericEnumClass).IsGenericType); + IsTrue(typeof(GenericEnumClass<>).IsGenericType); + IsTrue(typeof(IGenericInterface).IsGenericType); + IsTrue(typeof(IGenericInterface<>).IsGenericType); + IsTrue(typeof(GenericStruct).IsGenericType); + IsTrue(typeof(GenericStruct<>).IsGenericType); + IsTrue(typeof(SimpleEnum?).IsGenericType); + IsTrue(typeof(int?).IsGenericType); + IsTrue(typeof(IntPtr?).IsGenericType); + IsTrue(typeof(Nullable<>).IsGenericType); + IsTrue(typeof(Dictionary).IsGenericType); + IsTrue(typeof(Dictionary<,>).IsGenericType); + IsTrue(typeof(List).IsGenericType); + IsTrue(typeof(List<>).IsGenericType); + IsTrue(typeof(Action<>).IsGenericType); + IsTrue(typeof(Action).IsGenericType); + IsTrue(typeof(Func).IsGenericType); + IsTrue(typeof(Func<,>).IsGenericType); + } + private static int _varInt = 42; private static int? _varNullableInt = 42; private static decimal _varDecimal = 42M; @@ -258,6 +326,17 @@ static void ThrowsNRE(Action action, [CallerLineNumber] int line = 0, [CallerFil } } +public class GenericSimpleClass +{ + public class Nested + { + } +} + +public class DerivedGenericSimpleClass : GenericSimpleClass +{ +} + public class GenericEnumClass where T : Enum { public T field;