From edd64670617223c473051921268c4fc1962407a0 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Sat, 13 Nov 2021 21:31:32 -0500 Subject: [PATCH] [mono] Disable partial generic sharing for gparams with non-enum constraints. (#59437) If a generic argument is a primitive type, and it has an interface constraint that enums don't implement, then partial sharing for that instance is not useful, since only the specific primitive type instance will be able to use the shared version. Fixes https://github.com/dotnet/runtime/issues/54910. --- src/mono/mono/mini/mini-generic-sharing.c | 75 ++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/src/mono/mono/mini/mini-generic-sharing.c b/src/mono/mono/mini/mini-generic-sharing.c index 69e0a16..e69da90 100644 --- a/src/mono/mono/mini/mini-generic-sharing.c +++ b/src/mono/mono/mini/mini-generic-sharing.c @@ -3324,6 +3324,16 @@ mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_ty return mono_generic_context_is_sharable_full (context, allow_type_vars, partial_sharing_supported ()); } +static gboolean +is_primitive_inst (MonoGenericInst *inst) +{ + for (int i = 0; i < inst->type_argc; ++i) { + if (!MONO_TYPE_IS_PRIMITIVE (inst->type_argv [i])) + return FALSE; + } + return TRUE; +} + /* * mono_method_is_generic_impl: * @method: a method @@ -3350,18 +3360,45 @@ mono_method_is_generic_impl (MonoMethod *method) static gboolean has_constraints (MonoGenericContainer *container) { - //int i; + int i; - return FALSE; - /* g_assert (container->type_argc > 0); g_assert (container->type_params); for (i = 0; i < container->type_argc; ++i) - if (container->type_params [i].constraints) + if (container->type_params [i].info.constraints) return TRUE; return FALSE; - */ +} + +/* + * Return whenever GPARAM can be instantiated with an enum. + */ +static gboolean +gparam_can_be_enum (MonoGenericParam *gparam) +{ + if (!gparam->info.constraints) + return TRUE; + /* + * If a constraint is an interface which is not implemented by Enum, then the gparam can't be + * instantiated with an enum. + */ + for (int cindex = 0; gparam->info.constraints [cindex]; cindex ++) { + MonoClass *k = gparam->info.constraints [cindex]; + if (MONO_CLASS_IS_INTERFACE_INTERNAL (k)) { + MonoClass **enum_ifaces = m_class_get_interfaces (mono_defaults.enum_class); + gboolean is_enum_iface = FALSE; + for (int i = 0; i < m_class_get_interface_count (mono_defaults.enum_class); i++) { + if (k == enum_ifaces [i]) { + is_enum_iface = TRUE; + break; + } + } + if (!is_enum_iface) + return FALSE; + } + } + return TRUE; } static gboolean @@ -3488,21 +3525,39 @@ mono_method_is_generic_sharable_full (MonoMethod *method, gboolean allow_type_va g_assert (inflated->declaring); +#if FALSE if (inflated->declaring->is_generic) { - if (has_constraints (mono_method_get_generic_container (inflated->declaring))) - return FALSE; + if (has_constraints (mono_method_get_generic_container (inflated->declaring))) { + } } +#endif } if (mono_class_is_ginst (method->klass)) { - if (!mono_generic_context_is_sharable_full (&mono_class_get_generic_class (method->klass)->context, allow_type_vars, allow_partial)) + MonoGenericContext *ctx = &mono_class_get_generic_class (method->klass)->context; + if (!mono_generic_context_is_sharable_full (ctx, allow_type_vars, allow_partial)) return FALSE; g_assert (mono_class_get_generic_class (method->klass)->container_class && mono_class_is_gtd (mono_class_get_generic_class (method->klass)->container_class)); - if (has_constraints (mono_class_get_generic_container (mono_class_get_generic_class (method->klass)->container_class))) - return FALSE; + /* + * If all the parameters are primitive types and constraints prevent + * them from being instantiated with enums, then only the primitive + * type instantiation is possible, thus sharing is not useful. + * Happens with generic math interfaces. + */ + if ((!ctx->class_inst || is_primitive_inst (ctx->class_inst)) && + (!ctx->method_inst || is_primitive_inst (ctx->method_inst))) { + MonoGenericContainer *container = mono_class_get_generic_container (mono_class_get_generic_class (method->klass)->container_class); + if (has_constraints (container)) { + g_assert (ctx->class_inst->type_argc == container->type_argc); + for (int i = 0; i < container->type_argc; ++i) { + if (!gparam_can_be_enum (&container->type_params [i])) + return FALSE; + } + } + } } if (mono_class_is_gtd (method->klass) && !allow_type_vars) -- 2.7.4