Fix TypeLoadException when async Task<T> overrides Task#124062
Conversation
…tion Co-authored-by: elinor-fung <47805090+elinor-fung@users.noreply.github.com>
Co-authored-by: elinor-fung <47805090+elinor-fung@users.noreply.github.com>
- Only check pMD (derived class method) for async variant - Move check before resolving pParentMD to save unnecessary work - AsyncVariantMethods should only override AsyncVariantMethods Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
src/tests/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/AsyncCovariantReturn.cs
Show resolved
Hide resolved
src/tests/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/AsyncCovariantReturn.cs
Outdated
Show resolved
Hide resolved
Let test harness handle exceptions rather than catching them explicitly. Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
|
Change TestEntryPoint from int to void since it unconditionally returns 100. Test only validates type loading succeeds, no conditional logic needed. Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Done in e719797. Converted TestEntryPoint to void return since it only validates type loading and doesn't need conditional exit codes. |
src/tests/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/AsyncCovariantReturn.cs
Outdated
Show resolved
Hide resolved
…est/AsyncCovariantReturn.cs
There was a problem hiding this comment.
Pull request overview
Fixes a CoreCLR type-loading failure where runtime-generated async-variant methods (with unwrapped return types) were incorrectly included in covariant return type validation, causing TypeLoadException for otherwise valid overrides.
Changes:
- Skip async-variant methods during
ValidateMethodsWithCovariantReturnTypesto avoid validating synthetic signatures. - Add a regression test project covering an
async Task<T>override of aTaskvirtual method. - Add test source that forces creation/loading of the relevant override shape.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/coreclr/vm/class.cpp | Skips covariant return validation for async-variant methods to prevent false TypeLoadException. |
| src/tests/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/AsyncCovariantReturn.csproj | Adds a new test project for the regression scenario. |
| src/tests/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/AsyncCovariantReturn.cs | Adds a regression test ensuring type loading succeeds for the async/covariant-return scenario. |
src/tests/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/AsyncCovariantReturn.cs
Show resolved
Hide resolved
src/tests/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/AsyncCovariantReturn.cs
Show resolved
Hide resolved
## Description
Runtime throws `TypeLoadException` for valid C# code where `async
Task<T>` overrides `Task`. This breaks applications like DevBetterWeb on
SDK 11.0.100-preview.1.26066.111.
```csharp
public abstract class Base {
public abstract Task HandleAsync();
}
public class Derived : Base {
// Valid C# - compiles but runtime rejects
public override async Task<string> HandleAsync() {
await Task.Yield();
return string.Empty;
}
}
```
Async methods generate variant methods with unwrapped return types
(`string` vs `Task<string>`) for the async calling convention. The
covariant return type validator treats these synthetic methods as
invalid overrides.
## Changes
- **src/coreclr/vm/class.cpp**: Skip async variant methods in
`ValidateMethodsWithCovariantReturnTypes`. Check is placed early (right
after resolving the derived class method) to avoid unnecessary work
resolving the parent method when validation will be skipped. Only checks
the derived class method since async variant methods should only
override other async variant methods.
- **src/tests/.../AsyncCovariantReturn.cs**: Regression test that
validates type loading succeeds without throwing TypeLoadException. Test
uses void return type following standard test pattern for tests without
conditional logic.
Pattern matches existing async variant handling in
`comdelegate.cpp:1046` and `dispatchinfo.cpp:2585`.
<!-- START COPILOT CODING AGENT TIPS -->
---
✨ Let Copilot coding agent [set things up for
you](https://github.com/dotnet/runtime/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: elinor-fung <47805090+elinor-fung@users.noreply.github.com>
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
| <PropertyGroup> | ||
| <RequiresProcessIsolation>true</RequiresProcessIsolation> | ||
| </PropertyGroup> |
There was a problem hiding this comment.
Why does this require process isolation?
| // | ||
| // This test ensures that such valid code can be loaded without throwing. | ||
|
|
||
| GC.KeepAlive(new Derived()); |
There was a problem hiding this comment.
Do we have a plan to add actual test coverage for covariant returns? The test here inherits whatever is the global runtime-async setting, but I believe it might be useful to have real coverage under tests/async that tests: actually calling the method, pairs of "base is runtime-async, override is not" and inverted.
This test has a minimal value because it surgically only checks type load and nothing else. We don't get much bang for our RequiresProcessIsolation buck.
Description
Runtime throws
TypeLoadExceptionfor valid C# code whereasync Task<T>overridesTask. This breaks applications like DevBetterWeb on SDK 11.0.100-preview.1.26066.111.Async methods generate variant methods with unwrapped return types (
stringvsTask<string>) for the async calling convention. The covariant return type validator treats these synthetic methods as invalid overrides.Changes
ValidateMethodsWithCovariantReturnTypes. Check is placed early (right after resolving the derived class method) to avoid unnecessary work resolving the parent method when validation will be skipped. Only checks the derived class method since async variant methods should only override other async variant methods.Pattern matches existing async variant handling in
comdelegate.cpp:1046anddispatchinfo.cpp:2585.✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.