diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 6c8d70e68d211..8566b12552ade 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -2453,7 +2453,7 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas if (!strcmp (tm, "InternalGetHashCode")) { *op = MINT_INTRINS_GET_HASHCODE; } else if (!strcmp (tm, "GetType")) { - if (constrained_class && m_class_is_valuetype (constrained_class)) { + if (constrained_class && m_class_is_valuetype (constrained_class) && !mono_class_is_nullable (constrained_class)) { // If constrained_class is valuetype we already know its type. // Resolve GetType to a constant so we can fold type comparisons ERROR_DECL(error); @@ -2470,8 +2470,16 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas return TRUE; } else { if (constrained_class) { - // deref the managed pointer to get the object - interp_add_ins (td, MINT_LDIND_I); + if (mono_class_is_nullable (constrained_class)) { + // We can't determine the behavior here statically because we don't know if the + // nullable vt has a value or not. If it has a value, the result type is + // m_class_get_cast_class (constrained_class), otherwise GetType should throw NRE. + interp_add_ins (td, MINT_BOX_NULLABLE_PTR); + td->last_ins->data [0] = get_data_item_index (td, constrained_class); + } else { + // deref the managed pointer to get the object + interp_add_ins (td, MINT_LDIND_I); + } td->sp--; interp_ins_set_sreg (td->last_ins, td->sp [0].local); push_simple_type (td, STACK_TYPE_O); diff --git a/src/tests/JIT/Directed/nullabletypes/gettype.cs b/src/tests/JIT/Directed/nullabletypes/gettype.cs new file mode 100644 index 0000000000000..15bbef729d26f --- /dev/null +++ b/src/tests/JIT/Directed/nullabletypes/gettype.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + public IEnumerable Data { get; set; } + + public C() { } + + public bool Check() + { + return Data.ElementAt(0).GetType() == typeof(bool); + } +} + +public class P +{ + public static int Main() + { + C c = new(); + + // Try a nullable with value + c.Data = new List { true }; + if(!c.Check()) + return 666; + + // Try a nullable without value. Should throw NRE + c.Data = new List { new Nullable() }; + + bool thrown = false; + try + { + c.Check(); + } + catch(NullReferenceException) + { + thrown = true; + } + if(!thrown) + return 667; + return 100; + } +} + diff --git a/src/tests/JIT/Directed/nullabletypes/gettype_d.csproj b/src/tests/JIT/Directed/nullabletypes/gettype_d.csproj new file mode 100644 index 0000000000000..749b1f23c8975 --- /dev/null +++ b/src/tests/JIT/Directed/nullabletypes/gettype_d.csproj @@ -0,0 +1,13 @@ + + + Exe + 1 + + + Full + False + + + + + diff --git a/src/tests/JIT/Directed/nullabletypes/gettype_do.csproj b/src/tests/JIT/Directed/nullabletypes/gettype_do.csproj new file mode 100644 index 0000000000000..34744812c8927 --- /dev/null +++ b/src/tests/JIT/Directed/nullabletypes/gettype_do.csproj @@ -0,0 +1,13 @@ + + + Exe + 1 + + + Full + True + + + + + diff --git a/src/tests/JIT/Directed/nullabletypes/gettype_r.csproj b/src/tests/JIT/Directed/nullabletypes/gettype_r.csproj new file mode 100644 index 0000000000000..fea75d031b0ed --- /dev/null +++ b/src/tests/JIT/Directed/nullabletypes/gettype_r.csproj @@ -0,0 +1,13 @@ + + + Exe + 1 + + + None + False + + + + + diff --git a/src/tests/JIT/Directed/nullabletypes/gettype_ro.csproj b/src/tests/JIT/Directed/nullabletypes/gettype_ro.csproj new file mode 100644 index 0000000000000..f51b4298ce4d6 --- /dev/null +++ b/src/tests/JIT/Directed/nullabletypes/gettype_ro.csproj @@ -0,0 +1,13 @@ + + + Exe + 1 + + + None + True + + + + +