From 709c2e41bf99a43cf4bf8b4db6dd8896db3b5ec0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:06:05 +0000 Subject: [PATCH 1/4] Initial plan From 963daeaf40f56640736e6d3ae4f24cc650ab4bfd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:54:36 +0000 Subject: [PATCH 2/4] Fix TypeLoadException for abstract covariant Task overrides Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com> --- src/coreclr/vm/method.cpp | 10 +++ .../covariant-return/covariant-returns.cs | 69 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 444cb8f20c9e3c..845bbb749eeffd 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -1537,6 +1537,16 @@ DWORD MethodDesc::GetAttrs() const _ASSERTE(!"If this ever fires, then this method should return HRESULT"); return 0; } + + if (IsReturnDroppingThunk()) + { + // A return-dropping thunk is synthesized by the runtime and always has an implementation - + // it calls the ordinary async variant virtually and drops the result. + // The metadata method that the thunk is derived from may be abstract (i.e. when the covariant + // override that needs the thunk is abstract), but the thunk itself never is. + dwAttributes &= ~mdAbstract; + } + return dwAttributes; } diff --git a/src/tests/async/covariant-return/covariant-returns.cs b/src/tests/async/covariant-return/covariant-returns.cs index f01801c2c6c45c..3b66e84d2c888e 100644 --- a/src/tests/async/covariant-return/covariant-returns.cs +++ b/src/tests/async/covariant-return/covariant-returns.cs @@ -294,3 +294,72 @@ public static void TestAsyncInterfaceGenericMethod() } } } + +namespace AbstractCovariantReturn +{ + // A covariant Task -> Task override may be abstract. + // The runtime still has to provide an async variant that matches the void-returning + // async variant of the base, otherwise concrete derived types cannot be loaded. + public class Program + { + internal static string Trace; + + [Fact] + public static void TestAbstractCovariantOverride() + { + Trace = null; + Base b = new Derived(); + CallBase(b).GetAwaiter().GetResult(); + Assert.Equal("Derived.M1;", Trace); + + Trace = null; + Assert.Equal(42, CallMid(new Derived()).GetAwaiter().GetResult()); + Assert.Equal("Derived.M1;", Trace); + } + + [Fact] + public static void TestAbstractCovariantOverrideProperty() + { + Base b = new Derived(); + Assert.Equal(42, CallBaseProperty(b).GetAwaiter().GetResult()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static async Task CallBase(Base b) => await b.M1(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static async Task CallMid(Mid m) => await m.M1(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static async Task CallBaseProperty(Base b) + { + await b.Task; + return await ((Mid)b).Task; + } + + public abstract class Base + { + public abstract Task M1(); + + public abstract Task Task { get; } + } + + public abstract class Mid : Base + { + public abstract override Task M1(); + + public abstract override Task Task { get; } + } + + public sealed class Derived : Mid + { + public override Task M1() + { + Trace += "Derived.M1;"; + return System.Threading.Tasks.Task.FromResult(42); + } + + public override Task Task => System.Threading.Tasks.Task.FromResult(42); + } + } +} From 468102cf8b92186e408c39c623759892f9617ad4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 23 Sep 2026 00:11:15 +0000 Subject: [PATCH 3/4] Test assembly-wide loading of abstract covariant overrides Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com> --- src/tests/async/covariant-return/covariant-returns.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tests/async/covariant-return/covariant-returns.cs b/src/tests/async/covariant-return/covariant-returns.cs index 3b66e84d2c888e..110d6c2ef50846 100644 --- a/src/tests/async/covariant-return/covariant-returns.cs +++ b/src/tests/async/covariant-return/covariant-returns.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Xunit; @@ -304,6 +305,14 @@ public class Program { internal static string Trace; + [Fact] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This test intentionally exercises Assembly.GetTypes().")] + public static void TestAssemblyGetTypes() + { + _ = typeof(Program).Assembly.GetTypes(); + } + [Fact] public static void TestAbstractCovariantOverride() { From 9fc27279bf95972c21031d73a42bbece44488abe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 23 Sep 2026 15:01:14 +0000 Subject: [PATCH 4/4] Mask mdAbstract for return-dropping thunks in bmtMDMethod::GetDeclAttrs Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com> --- src/coreclr/vm/methodtablebuilder.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h index 937e1dd11caa01..eba629739a0c3a 100644 --- a/src/coreclr/vm/methodtablebuilder.h +++ b/src/coreclr/vm/methodtablebuilder.h @@ -1090,7 +1090,21 @@ class MethodTableBuilder // Returns the metadata declaration attributes for this method. DWORD GetDeclAttrs() const - { LIMITED_METHOD_CONTRACT; return m_dwDeclAttrs; } + { + LIMITED_METHOD_CONTRACT; + + DWORD dwDeclAttrs = m_dwDeclAttrs; + if (hasAsyncFlags(m_asyncMethodFlags, AsyncMethodFlags::ReturnDroppingThunk)) + { + // A return-dropping thunk is synthesized by the runtime and always has an implementation - + // it calls the ordinary async variant virtually and drops the result. + // The metadata method that the thunk is derived from may be abstract (i.e. when the covariant + // override that needs the thunk is abstract), but the thunk itself never is. + dwDeclAttrs &= ~mdAbstract; + } + + return dwDeclAttrs; + } //----------------------------------------------------------------------------------------- // Returns the metadata implementation attributes for this method.