fix: find async state machines in optimized builds - #500
Open
iamAdarshh wants to merge 1 commit into
Open
iamAdarshh wants to merge 1 commit into
iamAdarshh wants to merge 1 commit into
Conversation
HandleAsync located the state machine by taking the first newobj in the async method whose type has a MoveNext method. In optimized builds the compiler emits async state machines as structs, which are initialized in place instead of created with newobj. Nothing matched, the fallback analysed the small outer method instead of MoveNext, and every dependency in the method body was lost. HandleIterator used the same lookup; it only worked because iterator state machines are always classes. Read the state machine type from the method's AsyncStateMachineAttribute or IteratorStateMachineAttribute instead, and keep the existing fallback for when it cannot be resolved. None of the test assemblies set <Optimize>, so they always had class state machines. Add OptimizedAssembly, which is always optimized, with an async and an iterator method, and a guard test that its async state machine really is a struct. MethodCallDependenciesAreFoundInAsyncMethod was skipped in Release builds because of this bug; it now passes there, so run it again and drop the SkipInReleaseBuildTheory attribute that only it used. Fixes TNG#498 Signed-off-by: Adarsh Choudhary <adarshchoudhary087@gmail.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #500 +/- ##
==========================================
- Coverage 86.33% 86.25% -0.09%
==========================================
Files 260 260
Lines 12496 12498 +2
Branches 1216 1219 +3
==========================================
- Hits 10789 10780 -9
- Misses 1372 1379 +7
- Partials 335 339 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #498
Problem
HandleAsyncfound an async method's state machine by taking the firstnewobjin the method whose type has aMoveNextmethod. In optimized (Release) builds the compiler emits async state machines as structs, which are initialized in place instead of created withnewobj. Nothing matched, so the fallback analysed the small outer method instead ofMoveNext, and every dependency inside the method body was lost. For example, aNotDependOnAnyrule on a class whose async method uses the forbidden type passed.HandleIteratorused the same lookup. It only worked because iterator state machines are always classes.Fix
GetStateMachineType()next toIsAsync()/IsIterator()inMonoCecilMemberExtensions. It reads the state machine type from the method'sAsyncStateMachineAttributeorIteratorStateMachineAttributeand resolves it.HandleAsyncandHandleIteratorinstead of thenewobjlookup. The existing fallback still applies when the type can't be resolved or has noMoveNext.Tests
None of the test assemblies set
<Optimize>, so they were always built with class state machines. That's why the existing tests didn't catch this.TestAssemblies/OptimizedAssemblywith<Optimize>true</Optimize>, containing an async method and an iterator method that call another type.OptimizedStateMachineDependenciesTestschecks that the dependency is found and that aNotDependOnAnyrule fails, for both methods. A guard test asserts that the async state machine really is a struct, so the test can't silently stop covering this path if<Optimize>is ever removed.MethodCallDependenciesAreFoundInAsyncMethod. It was marked[SkipInReleaseBuildTheory]in e75fe2b because of this bug. It fails in Release without the fix and passes with it.SkipInReleaseBuildTheoryhad no other users, so I removed it.dotnet build,dotnet test -c Debugandmise run checkpass.Related gaps (not in this PR)
CreateMethodBodyDependenciesRecursivehandles iterators but never callsHandleAsync, so async lambdas and local functions are still missed. This looks like Analyse dependencies of async lambdas #230.IAsyncEnumerablecarryAsyncIteratorStateMachineAttribute, whichIsAsync()doesn't check for.MethodMemberSyntaxElementsTests.HaveDependencyInMethodBodyToTestandNotHaveDependencyInMethodBodyToTestfail onmainwhen the tests run with-c Release. CI runs its tests in Debug, so it doesn't see them.