From 6d239420d8c1bf3029d1eec01c0c9c34c7c63e86 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:51:55 +0000 Subject: [PATCH 1/2] Initial plan From ed56a2ba1ff8d9080ff86a14c48a8b598b23c415 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:22:14 +0000 Subject: [PATCH 2/2] Fix JIT assert with UnsafeAccessor and delegate ctor Remove overly strict assert in fgOptimizeDelegateConstructor that requires targetMethodHnd to be null when ldftnToken is null. This can legitimately happen when a GT_FTN_ADDR is found through pattern matching but there's no ldftn;newobj pattern in the IL (e.g., when using UnsafeAccessor to construct a delegate with a function pointer obtained through inlining). Also guard the R2R path against null ldftnToken when oper is GT_FTN_ADDR. Co-authored-by: EgorBo <523221+EgorBo@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/a4ba0013-a61a-4244-a64c-9fb1e6a7d080 --- src/coreclr/jit/flowgraph.cpp | 6 +---- .../UnsafeAccessors/UnsafeAccessorsTests.cs | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index f8c7c1dbb7a3e5..b684391d7d00ba 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1104,10 +1104,6 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, targetMethodHnd = ldftnToken->m_token.hMethod; } - else - { - assert(targetMethodHnd == nullptr); - } #ifdef FEATURE_READYTORUN if (IsAot()) @@ -1158,7 +1154,7 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, } } // ReadyToRun has this optimization for a non-virtual function pointers only for now. - else if (oper == GT_FTN_ADDR) + else if (oper == GT_FTN_ADDR && ldftnToken != nullptr) { JITDUMP("optimized\n"); diff --git a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs index fcdb479c1a6353..53817d683d204a 100644 --- a/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs +++ b/src/tests/baseservices/compilerservices/UnsafeAccessors/UnsafeAccessorsTests.cs @@ -135,6 +135,32 @@ public static void Verify_CallCtorWithEmptyNotNullName() extern static UserDataClass CallPrivateConstructorWithEmptyName(); } + interface IDelegateTarget + { + static virtual object GetValue() => "DelegateTargetValue"; + } + + class DelegateTargetImpl : IDelegateTarget { } + + delegate object DelegateFromUnsafeAccessor(); + + [UnsafeAccessor(UnsafeAccessorKind.Constructor)] + extern static DelegateFromUnsafeAccessor ConstructDelegate(object o, IntPtr p); + + static class DelegateTargetResolver where T : IDelegateTarget + { + public static unsafe delegate* Resolve() => &T.GetValue; + } + + [Fact] + public static unsafe void Verify_ConstructDelegateFromFunctionPointer() + { + Console.WriteLine($"Running {nameof(Verify_ConstructDelegateFromFunctionPointer)}"); + + var del = ConstructDelegate(null, (nint)DelegateTargetResolver.Resolve()); + Assert.Equal("DelegateTargetValue", del()); + } + [Fact] public static void Verify_CallCtorAsMethod() {