diff --git a/src/mono/mono/metadata/class-accessors.c b/src/mono/mono/metadata/class-accessors.c
index 4cc08da084bafc..f74b2dc4dbe704 100644
--- a/src/mono/mono/metadata/class-accessors.c
+++ b/src/mono/mono/metadata/class-accessors.c
@@ -578,6 +578,23 @@ mono_class_set_failure (MonoClass *klass, MonoErrorBoxed *boxed_error)
return TRUE;
}
+/**
+ * mono_class_set_deferred_failure:
+ * \param klass class in which the failure was detected
+
+ * This method marks the class with a deferred failure, indicating that a failure was detected but it will be processed during AOT runtime..
+ * Note that only the first failure is kept.
+ *
+ * LOCKING: Acquires the loader lock.
+ */
+void
+mono_class_set_deferred_failure (MonoClass *klass)
+{
+ mono_loader_lock ();
+ klass->has_deferred_failure = 1;
+ mono_loader_unlock ();
+}
+
/**
* mono_class_set_nonblittable:
* \param klass class which will be marked as not blittable.
diff --git a/src/mono/mono/metadata/class-getters.h b/src/mono/mono/metadata/class-getters.h
index 9eafbb4330bc84..c0e15059991ad6 100644
--- a/src/mono/mono/metadata/class-getters.h
+++ b/src/mono/mono/metadata/class-getters.h
@@ -49,6 +49,7 @@ MONO_CLASS_GETTER(m_class_is_simd_type, gboolean, , MonoClass, simd_type)
MONO_CLASS_GETTER(m_class_is_has_finalize_inited, gboolean, , MonoClass, has_finalize_inited)
MONO_CLASS_GETTER(m_class_is_fields_inited, gboolean, , MonoClass, fields_inited)
MONO_CLASS_GETTER(m_class_has_failure, gboolean, , MonoClass, has_failure)
+MONO_CLASS_GETTER(m_class_has_deferred_failure, gboolean, , MonoClass, has_deferred_failure)
MONO_CLASS_GETTER(m_class_has_weak_fields, gboolean, , MonoClass, has_weak_fields)
MONO_CLASS_GETTER(m_class_has_dim_conflicts, gboolean, , MonoClass, has_dim_conflicts)
MONO_CLASS_GETTER(m_class_get_parent, MonoClass *, , MonoClass, parent)
diff --git a/src/mono/mono/metadata/class-init.c b/src/mono/mono/metadata/class-init.c
index dc6e35862ea294..8c9286d4bf9b29 100644
--- a/src/mono/mono/metadata/class-init.c
+++ b/src/mono/mono/metadata/class-init.c
@@ -305,7 +305,7 @@ mono_class_setup_fields (MonoClass *klass)
}
if (m_class_is_inlinearray (klass) && m_class_inlinearray_value (klass) <= 0)
- mono_class_set_type_load_failure (klass, "Inline array length property must be positive.");
+ mono_class_set_deferred_type_load_failure_callback (klass, "Inline array length property must be positive.");
/* Get the real size */
explicit_size = mono_metadata_packing_from_typedef (klass->image, klass->type_token, &packing_size, &real_size);
@@ -376,8 +376,8 @@ mono_class_setup_fields (MonoClass *klass)
break;
}
if (m_class_is_inlinearray (klass)) {
- mono_class_set_type_load_failure (klass, "Inline array struct must not have explicit layout.");
- break;
+ if (mono_class_set_deferred_type_load_failure_callback (klass, "Inline array struct must not have explicit layout."))
+ break;
}
}
if (mono_type_has_exceptions (field->type)) {
@@ -2274,12 +2274,15 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
if (m_class_is_inlinearray (klass)) {
// Limit the max size of array instance to 1MiB
const guint32 struct_max_size = 1024 * 1024;
+ guint32 initial_size = size;
// If size overflows, it returns 0
size *= m_class_inlinearray_value (klass);
inlined_fields++;
if(size == 0 || size > struct_max_size) {
- mono_class_set_type_load_failure (klass, "Inline array struct size out of bounds, abnormally large.");
- break;
+ if (mono_class_set_deferred_type_load_failure_callback (klass, "Inline array struct size out of bounds, abnormally large."))
+ break;
+ else
+ size = initial_size;
}
}
@@ -2310,7 +2313,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
}
}
if (m_class_is_inlinearray (klass) && inlined_fields != 1)
- mono_class_set_type_load_failure (klass, "Inline array struct must have a single field.");
+ mono_class_set_deferred_type_load_failure_callback (klass, "Inline array struct must have a single field.");
break;
case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT: {
real_size = 0;
@@ -2991,6 +2994,14 @@ mono_class_init_internal (MonoClass *klass)
has_cached_info = mono_class_get_cached_class_info (klass, &cached_info);
+ /*
+ * If the class has a deferred failure, ignore the cached info and
+ * let the runtime go on the slow path of trying to setup the class
+ * layout at runtime.
+ */
+ if (has_cached_info && cached_info.has_deferred_failure)
+ has_cached_info = FALSE;
+
/* Compute instance size etc. */
init_sizes_with_info (klass, has_cached_info ? &cached_info : NULL);
if (mono_class_has_failure (klass))
diff --git a/src/mono/mono/metadata/class-internals.h b/src/mono/mono/metadata/class-internals.h
index 7915be4ed11839..68f83b16ab2fca 100644
--- a/src/mono/mono/metadata/class-internals.h
+++ b/src/mono/mono/metadata/class-internals.h
@@ -578,6 +578,7 @@ typedef struct MonoCachedClassInfo {
guint no_special_static_fields : 1;
guint is_generic_container : 1;
guint has_weak_fields : 1;
+ guint has_deferred_failure : 1;
guint32 cctor_token;
MonoImage *finalize_image;
guint32 finalize_token;
@@ -1062,9 +1063,6 @@ mono_register_jit_icall_info (MonoJitICallInfo *info, T func, const char *name,
#define mono_register_jit_icall(func, sig, no_wrapper) (mono_register_jit_icall_info (&mono_get_jit_icall_info ()->func, func, #func, (sig), (no_wrapper), NULL))
-gboolean
-mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...) MONO_ATTR_FORMAT_PRINTF(2,3);
-
MonoException*
mono_class_get_exception_for_failure (MonoClass *klass);
@@ -1268,6 +1266,9 @@ mono_error_set_for_class_failure (MonoError *orerror, const MonoClass *klass);
gboolean
mono_class_has_failure (const MonoClass *klass);
+gboolean
+mono_class_has_deferred_failure (const MonoClass *klass);
+
/* Kind specific accessors */
MONO_COMPONENT_API MonoGenericClass*
mono_class_get_generic_class (MonoClass *klass);
@@ -1429,6 +1430,9 @@ mono_class_find_enum_basetype (MonoClass *klass, MonoError *error);
gboolean
mono_class_set_failure (MonoClass *klass, MonoErrorBoxed *boxed_error);
+void
+mono_class_set_deferred_failure (MonoClass *klass);
+
gboolean
mono_class_set_type_load_failure_causedby_class (MonoClass *klass, const MonoClass *caused_by, const gchar* msg);
diff --git a/src/mono/mono/metadata/class-private-definition.h b/src/mono/mono/metadata/class-private-definition.h
index 08139a219c6934..ee104bb40a6668 100644
--- a/src/mono/mono/metadata/class-private-definition.h
+++ b/src/mono/mono/metadata/class-private-definition.h
@@ -83,6 +83,7 @@ struct _MonoClass {
guint has_weak_fields : 1; /* class has weak reference fields */
guint has_dim_conflicts : 1; /* Class has conflicting default interface methods */
guint any_field_has_auto_layout : 1; /* a field in this type's layout uses auto-layout */
+ guint has_deferred_failure : 1;
MonoClass *parent;
MonoClass *nested_in;
diff --git a/src/mono/mono/metadata/class.c b/src/mono/mono/metadata/class.c
index 5dc37fc02f8462..3b371bffdc7002 100644
--- a/src/mono/mono/metadata/class.c
+++ b/src/mono/mono/metadata/class.c
@@ -5943,36 +5943,11 @@ mono_class_has_failure (const MonoClass *klass)
return m_class_has_failure ((MonoClass*)klass) != 0;
}
-
-/**
- * mono_class_set_type_load_failure:
- * \param klass class in which the failure was detected
- * \param fmt \c printf -style error message string.
- *
- * Collect detected failure informaion in the class for later processing.
- * The error is stored as a MonoErrorBoxed as with mono_error_set_type_load_class()
- * Note that only the first failure is kept.
- *
- * LOCKING: Acquires the loader lock.
- *
- * \returns FALSE if a failure was already set on the class, or TRUE otherwise.
- */
gboolean
-mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...)
+mono_class_has_deferred_failure (const MonoClass *klass)
{
- ERROR_DECL (prepare_error);
- va_list args;
-
- if (mono_class_has_failure (klass))
- return FALSE;
-
- va_start (args, fmt);
- mono_error_vset_type_load_class (prepare_error, klass, fmt, args);
- va_end (args);
-
- MonoErrorBoxed *box = mono_error_box (prepare_error, m_class_get_image (klass));
- mono_error_cleanup (prepare_error);
- return mono_class_set_failure (klass, box);
+ g_assert (klass != NULL);
+ return m_class_has_deferred_failure ((MonoClass*)klass) != 0;
}
/**
diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c
index a5d36d9c939013..5a581216c2bf2e 100644
--- a/src/mono/mono/mini/aot-compiler.c
+++ b/src/mono/mono/mini/aot-compiler.c
@@ -7816,7 +7816,7 @@ emit_klass_info (MonoAotCompile *acfg, guint32 token)
} else {
gboolean has_nested = mono_class_get_nested_classes_property (klass) != NULL;
encode_value (m_class_get_vtable_size (klass), p, &p);
- encode_value ((m_class_has_weak_fields (klass) << 9) | (mono_class_is_gtd (klass) ? (1 << 8) : 0) | (no_special_static << 7) | (m_class_has_static_refs (klass) << 6) | (m_class_has_references (klass) << 5) | ((m_class_is_blittable (klass) << 4) | (has_nested ? 1 : 0) << 3) | (m_class_has_cctor (klass) << 2) | (m_class_has_finalize (klass) << 1) | m_class_is_ghcimpl (klass), p, &p);
+ encode_value ((m_class_has_deferred_failure (klass) << 10) | (m_class_has_weak_fields (klass) << 9) | (mono_class_is_gtd (klass) ? (1 << 8) : 0) | (no_special_static << 7) | (m_class_has_static_refs (klass) << 6) | (m_class_has_references (klass) << 5) | ((m_class_is_blittable (klass) << 4) | (has_nested ? 1 : 0) << 3) | (m_class_has_cctor (klass) << 2) | (m_class_has_finalize (klass) << 1) | m_class_is_ghcimpl (klass), p, &p);
if (m_class_has_cctor (klass))
encode_method_ref (acfg, mono_class_get_cctor (klass), p, &p);
if (m_class_has_finalize (klass))
diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c
index 1b273de6c0146e..495fc0310435a1 100644
--- a/src/mono/mono/mini/aot-runtime.c
+++ b/src/mono/mono/mini/aot-runtime.c
@@ -2498,6 +2498,7 @@ decode_cached_class_info (MonoAotModule *module, MonoCachedClassInfo *info, guin
info->no_special_static_fields = (flags >> 7) & 0x1;
info->is_generic_container = (flags >> 8) & 0x1;
info->has_weak_fields = (flags >> 9) & 0x1;
+ info->has_deferred_failure = (flags >> 10) & 0x1;
if (info->has_cctor) {
res = decode_method_ref (module, &ref, buf, &buf, error);
diff --git a/src/mono/mono/mini/driver.c b/src/mono/mono/mini/driver.c
index a5c95bd8ae56be..2d21c98dd4fc8d 100644
--- a/src/mono/mono/mini/driver.c
+++ b/src/mono/mono/mini/driver.c
@@ -1400,7 +1400,7 @@ main_thread_handler (gpointer user_data)
MonoAssembly **assemblies;
assemblies = g_new0 (MonoAssembly*, main_args->argc);
-
+ set_failure_type (DEFERRED_FAILURE);
/* Treat the other arguments as assemblies to compile too */
for (i = 0; i < main_args->argc; ++i) {
assembly = mono_domain_assembly_open_internal (mono_alc_get_default (), main_args->argv [i]);
@@ -1428,6 +1428,7 @@ main_thread_handler (gpointer user_data)
return;
}
+ set_failure_type (IMMEDIATE_FAILURE);
assembly = mono_domain_assembly_open_internal (mono_alc_get_default (), main_args->file);
if (!assembly){
fprintf (stderr, "Can not open image %s\n", main_args->file);
diff --git a/src/mono/mono/mini/mini-runtime.c b/src/mono/mono/mini/mini-runtime.c
index 094da4f03a247e..199aa8a59c01eb 100644
--- a/src/mono/mono/mini/mini-runtime.c
+++ b/src/mono/mono/mini/mini-runtime.c
@@ -1547,7 +1547,6 @@ mono_resolve_patch_target_ext (MonoMemoryManager *mem_manager, MonoMethod *metho
break;
case MONO_PATCH_INFO_VTABLE:
target = mono_class_vtable_checked (patch_info->data.klass, error);
- mono_error_assert_ok (error);
break;
case MONO_PATCH_INFO_DELEGATE_INFO: {
MonoDelegateClassMethodPair *del_tramp = patch_info->data.del_tramp;
diff --git a/src/mono/mono/utils/mono-error-internals.h b/src/mono/mono/utils/mono-error-internals.h
index 6ed41ef1945c5e..272d87bbdf32b2 100644
--- a/src/mono/mono/utils/mono-error-internals.h
+++ b/src/mono/mono/utils/mono-error-internals.h
@@ -319,6 +319,36 @@ mono_error_set_specific (MonoError *error, int error_code, const char *missing_m
void
mono_error_set_first_argument (MonoError *oerror, const char *first_argument);
+typedef enum {
+ DEFERRED_FAILURE, // Used during AOT compilation to defer failure for execution
+ IMMEDIATE_FAILURE // Used during runtime to indicate that the failure should be reported
+} FailureType;
+
+void
+set_failure_type (FailureType failure_type);
+
+/**
+ * TypeLoadFailureCallback:
+ * @param klass: Class in which the failure was detected.
+ * @param fmt: printf-style error message string.
+ *
+ * The callback is responsible for processing the failure information provided by the @klass parameter and the error message format string @fmt.
+ * If a deferred failure occurs, the callback should return FALSE to let the AOT compiler proceed with the class layout setup.
+ * Otherwise, if the callback returns TRUE, it indicates that the failure should be reported.
+ *
+ * @returns: TRUE if the failure is handled and the runtime should not proceed with class setup, FALSE if the failure should be deferred for runtime class setup.
+ *
+ */
+typedef gboolean (*TypeLoadFailureCallback)(MonoClass *klass, const char * fmt, ...) MONO_ATTR_FORMAT_PRINTF(2,3);
+
+extern TypeLoadFailureCallback mono_class_set_deferred_type_load_failure_callback;
+
+gboolean
+mono_class_set_deferred_type_load_failure (MonoClass *klass, const char * fmt, ...);
+
+gboolean
+mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...);
+
#if HOST_WIN32
#if HOST_X86 || HOST_AMD64
diff --git a/src/mono/mono/utils/mono-error.c b/src/mono/mono/utils/mono-error.c
index 821fc33c5cd2a6..3cb2cf7b8ef7b9 100644
--- a/src/mono/mono/utils/mono-error.c
+++ b/src/mono/mono/utils/mono-error.c
@@ -30,6 +30,8 @@
va_end (args); \
} while (0)
+TypeLoadFailureCallback mono_class_set_deferred_type_load_failure_callback = mono_class_set_type_load_failure;
+
static void
mono_error_set_generic_errorv (MonoError *oerror, const char *name_space, const char *name, const char *msg_format, va_list args);
@@ -908,3 +910,73 @@ mono_error_set_first_argument (MonoError *oerror, const char *first_argument)
to->first_argument = g_strdup (first_argument);
to->flags |= MONO_ERROR_FREE_STRINGS;
}
+
+/**
+ * mono_class_set_deferred_type_load_failure:
+ * \param klass class in which the failure was detected
+ * \param fmt \c printf -style error message string.
+ *
+ * Sets a deferred failure in the class and prints a warning message.
+ * The deferred failure allows the runtime to attempt setting up the class layout at runtime.
+ *
+ * LOCKING: Acquires the loader lock.
+ *
+ * \returns FALSE
+ */
+gboolean
+mono_class_set_deferred_type_load_failure (MonoClass *klass, const char * fmt, ...)
+{
+ if (!mono_class_has_deferred_failure (klass)) {
+ va_list args;
+
+ va_start (args, fmt);
+ g_warning ("Warning: %s", fmt, args);
+ va_end (args);
+
+ mono_class_set_deferred_failure (klass);
+ }
+
+ return FALSE;
+}
+
+/**
+ * mono_class_set_type_load_failure:
+ * \param klass class in which the failure was detected
+ * \param fmt \c printf -style error message string.
+ *
+ * Collect detected failure informaion in the class for later processing.
+ * The error is stored as a MonoErrorBoxed as with mono_error_set_type_load_class()
+ * Note that only the first failure is kept.
+ *
+ * LOCKING: Acquires the loader lock.
+ *
+ * \returns TRUE
+ */
+gboolean
+mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...)
+{
+ if (!mono_class_has_failure (klass)) {
+ ERROR_DECL (prepare_error);
+ va_list args;
+
+ va_start (args, fmt);
+ mono_error_vset_type_load_class (prepare_error, klass, fmt, args);
+ va_end (args);
+
+ MonoErrorBoxed *box = mono_error_box (prepare_error, m_class_get_image (klass));
+ mono_error_cleanup (prepare_error);
+ mono_class_set_failure (klass, box);
+ }
+
+ return TRUE;
+}
+
+void set_failure_type(FailureType failure_type) {
+ if (failure_type == DEFERRED_FAILURE) {
+ mono_class_set_deferred_type_load_failure_callback = mono_class_set_deferred_type_load_failure;
+ } else if (failure_type == IMMEDIATE_FAILURE) {
+ mono_class_set_deferred_type_load_failure_callback = mono_class_set_type_load_failure;
+ } else {
+ g_assert_not_reached ();
+ }
+}
\ No newline at end of file
diff --git a/src/tests/issues.targets b/src/tests/issues.targets
index d09d7b84f16daf..9a51fd6eb46569 100644
--- a/src/tests/issues.targets
+++ b/src/tests/issues.targets
@@ -2777,6 +2777,9 @@
Needs coreclr build
+
+ https://github.com/dotnet/runtime/issues/86327
+