From d2b53a93ea9ac6da31ee1d86f3781c279ffe3d43 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 22 Jun 2026 22:05:39 +0200 Subject: [PATCH] [mono] Defer NEWOBJ type-load failures to runtime in AOT mode When the AOT compiler failed to resolve the constructor target of a newobj because the declaring type fails to load (e.g. an invalid covariant-return override), it set a type-load error on the compile and aborted compilation of the whole method, excluding it from the AOT image. Under full-AOT (aot-only), calling such a method then attempted a JIT compile and threw ExecutionEngineException ('JIT compile while running in aot-only mode') instead of the TypeLoadException the runtime is expected to raise at the point of use. Handle this the same way the LDFLD path already does: in AOT, turn the method into one that throws the TypeLoadException at runtime via method_make_alwaysthrow_typeloadfailure, matching the JIT behavior. The change only affects the type-load error path; normal newobj compilation is unaffected. Re-enables the Loader/classloader/MethodImpl/CovariantReturns/UnitTest/UnitTest_GVM_TypeLoadException test on Mono (the IsNativeAot annotation is kept). --- src/mono/mono/mini/method-to-ir.c | 30 +++++++++++++++++-- .../UnitTest_GVM_TypeLoadException.il | 4 --- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index f4e661edd4eea2..4234d41e3406b3 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -9409,6 +9409,20 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b MonoInst *vtable_arg = NULL; cmethod = mini_get_method (cfg, method, token, NULL, generic_context); + /* + * In AOT mode, a type-load failure while resolving the constructor (e.g. an + * invalid covariant override on the declaring type) should turn the method + * into one that throws TypeLoadException at runtime, matching the JIT + * behavior, instead of aborting compilation. Aborting would exclude the + * method from the AOT image, and calling it under full-AOT would surface a + * confusing 'JIT compile while running in aot-only mode' error instead. + */ + if (cfg->compile_aot && !is_ok (cfg->error) && mono_error_get_error_code (cfg->error) == MONO_ERROR_TYPE_LOAD) { + clear_cfg_error (cfg); + INLINE_FAILURE ("type load error"); + method_make_alwaysthrow_typeloadfailure (cfg, cmethod ? cmethod->klass : NULL); + goto all_bbs_done; + } CHECK_CFG_ERROR; fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, cfg->error); @@ -9416,8 +9430,20 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b mono_save_token_info (cfg, image, token, cmethod); - if (mono_class_has_failure (cmethod->klass) || !mono_class_init_internal (cmethod->klass)) - TYPE_LOAD_ERROR (cmethod->klass); + if (mono_class_has_failure (cmethod->klass) || !mono_class_init_internal (cmethod->klass)) { + if (!cfg->compile_aot) + TYPE_LOAD_ERROR (cmethod->klass); + /* + * In AOT mode, rather than aborting compilation of the whole method + * (which would exclude it from the AOT image and make a full-AOT call + * site throw a confusing 'JIT compile while running in aot-only mode' + * error), turn the method into one that throws the TypeLoadException at + * runtime, matching the JIT behavior. + */ + INLINE_FAILURE ("type load error"); + method_make_alwaysthrow_typeloadfailure (cfg, cmethod->klass); + goto all_bbs_done; + } context_used = mini_method_check_context_used (cfg, cmethod); diff --git a/src/tests/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/UnitTest_GVM_TypeLoadException.il b/src/tests/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/UnitTest_GVM_TypeLoadException.il index 65df84dbdafa64..cb631bf64f517f 100644 --- a/src/tests/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/UnitTest_GVM_TypeLoadException.il +++ b/src/tests/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/UnitTest_GVM_TypeLoadException.il @@ -478,10 +478,6 @@ 01 00 00 00 ) .custom instance void [Microsoft.DotNet.XUnitExtensions]Xunit.ActiveIssueAttribute::.ctor(string, class [mscorlib]System.Type, string[]) = {string('Tests that expect TypeLoadException') type([TestLibrary]TestLibrary.Utilities) string[1] ('IsNativeAot') } - .custom instance void [Microsoft.DotNet.XUnitExtensions]Xunit.ActiveIssueAttribute::.ctor(string, valuetype [Microsoft.DotNet.XUnitExtensions]Xunit.TestRuntimes) = { - string('https://github.com/dotnet/runtime/issues/129508') - int32(0x2) // Mono - } .entrypoint .maxstack 2 .locals init ( bool result )