From 8d19e5a1c59ef9530fc0b3baf41013c6a6218b9b Mon Sep 17 00:00:00 2001 From: Rodrigo Kumpera Date: Wed, 14 Feb 2018 11:58:15 -0500 Subject: [PATCH] [runtime] Unboxing of nullable enums should throw for boxed ints. (#6958) * [runtime] Unboxing of nullable enums should throw for boxed ints. Fixes gh #6666. For some reason, one can't unbox the underlying type of an enum into a nullable. This is counter intuitive to the unboxing rules of enums, but it's what dotnet does. * [interp] Unboxing of nullable enmus should throw for boxed ints fixes https://github.com/mono/mono/issues/6666 for the interpreter --- mcs/class/corlib/LinkerDescriptor/mscorlib.xml | 3 +++ mcs/class/corlib/System/Nullable.cs | 11 +++++++++++ mono/mini/interp/transform.c | 12 ++++++++++-- mono/mini/method-to-ir.c | 7 ++++++- mono/mini/objects.cs | 25 +++++++++++++++++++++++++ 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/mcs/class/corlib/LinkerDescriptor/mscorlib.xml b/mcs/class/corlib/LinkerDescriptor/mscorlib.xml index 4d6b7cf095c..e879311ed1c 100644 --- a/mcs/class/corlib/LinkerDescriptor/mscorlib.xml +++ b/mcs/class/corlib/LinkerDescriptor/mscorlib.xml @@ -288,6 +288,9 @@ + + + diff --git a/mcs/class/corlib/System/Nullable.cs b/mcs/class/corlib/System/Nullable.cs index 32c4aa0885b..2d8a297e4ce 100644 --- a/mcs/class/corlib/System/Nullable.cs +++ b/mcs/class/corlib/System/Nullable.cs @@ -186,6 +186,17 @@ namespace System return null; return (T) o; } + + static T? UnboxExact (object o) + { + if (o == null) + return null; + if (o.GetType() != typeof (T)) + throw new InvalidCastException(); + + return (T) o; + } + #pragma warning restore 169 } } diff --git a/mono/mini/interp/transform.c b/mono/mini/interp/transform.c index d65cebb2f5b..5fb36c8080d 100644 --- a/mono/mini/interp/transform.c +++ b/mono/mini/interp/transform.c @@ -2936,7 +2936,11 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, unsig } if (mono_class_is_nullable (klass)) { - MonoMethod *target_method = mono_class_get_method_from_name (klass, "Unbox", 1); + MonoMethod *target_method; + if (mono_class_get_nullable_param (klass)->enumtype) + target_method = mono_class_get_method_from_name (klass, "UnboxExact", 1); + else + target_method = mono_class_get_method_from_name (klass, "Unbox", 1); /* td->ip is incremented by interp_transform_call */ interp_transform_call (td, method, target_method, domain, generic_context, is_bb_start, body_start_offset, NULL, FALSE, error, FALSE); goto_if_nok (error, exit); @@ -2970,7 +2974,11 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, unsig SET_TYPE (td->sp - 1, stack_type [mt], klass); td->ip += 5; } else if (mono_class_is_nullable (klass)) { - MonoMethod *target_method = mono_class_get_method_from_name (klass, "Unbox", 1); + MonoMethod *target_method; + if (mono_class_get_nullable_param (klass)->enumtype) + target_method = mono_class_get_method_from_name (klass, "UnboxExact", 1); + else + target_method = mono_class_get_method_from_name (klass, "Unbox", 1); /* td->ip is incremented by interp_transform_call */ interp_transform_call (td, method, target_method, domain, generic_context, is_bb_start, body_start_offset, NULL, FALSE, error, FALSE); diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index c0f18d7105d..52526b8cac8 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -3453,7 +3453,12 @@ mini_emit_check_array_type (MonoCompile *cfg, MonoInst *obj, MonoClass *array_cl static MonoInst* handle_unbox_nullable (MonoCompile* cfg, MonoInst* val, MonoClass* klass, int context_used) { - MonoMethod* method = mono_class_get_method_from_name (klass, "Unbox", 1); + MonoMethod* method; + + if (mono_class_get_nullable_param (klass)->enumtype) + method = mono_class_get_method_from_name (klass, "UnboxExact", 1); + else + method = mono_class_get_method_from_name (klass, "Unbox", 1); if (context_used) { MonoInst *rgctx, *addr; diff --git a/mono/mini/objects.cs b/mono/mini/objects.cs index e0cea4e8ba9..0663c9c1d35 100644 --- a/mono/mini/objects.cs +++ b/mono/mini/objects.cs @@ -1836,6 +1836,31 @@ ncells ) { return 0; } + enum FooEnum { Bar } + //https://github.com/mono/mono/issues/6666 + public static int test_0_bad_unbox_nullable_of_enum () { + try { + var enumValue = FooEnum.Bar; + object value = (int)enumValue; + var res = (FooEnum?)value; // Should throw + } catch (InvalidCastException) { + return 0; + } + return 1; + } + + //https://github.com/mono/mono/issues/6666 + public static int test_0_unbox_nullable_of_enum () { + try { + var enumValue = FooEnum.Bar; + object value = (object)enumValue; + var res = (FooEnum?)value; // Should not throw + } catch (InvalidCastException) { + return 1; + } + return 0; + } + static void decode (out sbyte v) { byte tmp = 134; v = (sbyte)tmp; -- 2.11.4.GIT