|
12 | 12 | using System.Linq; |
13 | 13 | using System.Threading; |
14 | 14 | using System.Threading.Tasks; |
| 15 | +using System.Xml.Linq; |
15 | 16 | using Microsoft.CodeAnalysis.CodeGen; |
16 | 17 | using Microsoft.CodeAnalysis.CSharp.Emit; |
17 | 18 | using Microsoft.CodeAnalysis.CSharp.Symbols; |
@@ -769,26 +770,9 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState) |
769 | 770 |
|
770 | 771 | try |
771 | 772 | { |
772 | | - // Local functions can be iterators as well as be async (lambdas can only be async), so we need to lower both iterators and async |
773 | | - IteratorStateMachine iteratorStateMachine; |
774 | | - BoundStatement loweredBody = IteratorRewriter.Rewrite(methodWithBody.Body, method, methodOrdinal, stateMachineStateDebugInfoBuilder, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out iteratorStateMachine); |
775 | | - StateMachineTypeSymbol stateMachine = iteratorStateMachine; |
776 | | - |
777 | | - if (!loweredBody.HasErrors) |
778 | | - { |
779 | | - AsyncStateMachine asyncStateMachine = null; |
780 | | - if (compilationState.Compilation.IsRuntimeAsyncEnabledIn(method)) |
781 | | - { |
782 | | - loweredBody = RuntimeAsyncRewriter.Rewrite(loweredBody, method, compilationState, diagnosticsThisMethod); |
783 | | - } |
784 | | - else |
785 | | - { |
786 | | - loweredBody = AsyncRewriter.Rewrite(loweredBody, method, methodOrdinal, stateMachineStateDebugInfoBuilder, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out asyncStateMachine); |
787 | | - } |
788 | | - |
789 | | - Debug.Assert((object)iteratorStateMachine == null || (object)asyncStateMachine == null); |
790 | | - stateMachine = stateMachine ?? asyncStateMachine; |
791 | | - } |
| 773 | + BoundStatement loweredBody; |
| 774 | + StateMachineTypeSymbol stateMachine; |
| 775 | + (loweredBody, stateMachine) = lowerMethodBody(methodWithBody, compilationState, methodOrdinal, stateMachineStateDebugInfoBuilder, variableSlotAllocatorOpt, diagnosticsThisMethod); |
792 | 776 |
|
793 | 777 | SetGlobalErrorIfTrue(diagnosticsThisMethod.HasAnyErrors()); |
794 | 778 |
|
@@ -844,6 +828,72 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState) |
844 | 828 | compilationState.CurrentImportChain = oldImportChain; |
845 | 829 | stateMachineStateDebugInfoBuilder.Free(); |
846 | 830 | } |
| 831 | + |
| 832 | + static (BoundStatement loweredBody, StateMachineTypeSymbol stateMachine) |
| 833 | + lowerMethodBody(TypeCompilationState.MethodWithBody methodWithBody, TypeCompilationState compilationState, int methodOrdinal, ArrayBuilder<StateMachineStateDebugInfo> stateMachineStateDebugInfoBuilder, VariableSlotAllocator variableSlotAllocatorOpt, BindingDiagnosticBag diagnosticsThisMethod) |
| 834 | + { |
| 835 | + var method = methodWithBody.Method; |
| 836 | + if (method is SynthesizedStateMachineMoveNextMethod { IsAsync: true }) |
| 837 | + { |
| 838 | + // "MoveNextAsync" is a runtime-async method, but it has already been lowered |
| 839 | + Debug.Assert(method.Name is WellKnownMemberNames.MoveNextAsyncMethodName); |
| 840 | + return (methodWithBody.Body, null); |
| 841 | + } |
| 842 | + |
| 843 | + // Local functions can be iterators as well as be async (lambdas can only be async), so we need to lower both iterators and async |
| 844 | + IteratorStateMachine iteratorStateMachine; |
| 845 | + BoundStatement loweredBody = IteratorRewriter.Rewrite(methodWithBody.Body, method, methodOrdinal, stateMachineStateDebugInfoBuilder, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out iteratorStateMachine); |
| 846 | + StateMachineTypeSymbol stateMachine = iteratorStateMachine; |
| 847 | + if (!loweredBody.HasErrors |
| 848 | + && !IsMissingYieldInAsyncIterator(loweredBody, method, diagnosticsThisMethod)) |
| 849 | + { |
| 850 | + if (compilationState.Compilation.IsRuntimeAsyncEnabledIn(method)) |
| 851 | + { |
| 852 | + if (method.IsAsyncReturningIAsyncEnumerable(method.DeclaringCompilation) |
| 853 | + || method.IsAsyncReturningIAsyncEnumerator(method.DeclaringCompilation)) |
| 854 | + { |
| 855 | + RuntimeAsyncIteratorStateMachine runtimeAsyncIteratorStateMachine; |
| 856 | + loweredBody = RuntimeAsyncIteratorRewriter.Rewrite(loweredBody, method, |
| 857 | + methodOrdinal, stateMachineStateDebugInfoBuilder, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, |
| 858 | + out runtimeAsyncIteratorStateMachine); |
| 859 | + |
| 860 | + Debug.Assert(runtimeAsyncIteratorStateMachine is not null); |
| 861 | + stateMachine = runtimeAsyncIteratorStateMachine; |
| 862 | + } |
| 863 | + else |
| 864 | + { |
| 865 | + loweredBody = RuntimeAsyncRewriter.Rewrite(loweredBody, method, compilationState, diagnosticsThisMethod); |
| 866 | + } |
| 867 | + } |
| 868 | + else |
| 869 | + { |
| 870 | + AsyncStateMachine asyncStateMachine; |
| 871 | + loweredBody = AsyncRewriter.Rewrite(loweredBody, method, methodOrdinal, stateMachineStateDebugInfoBuilder, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out asyncStateMachine); |
| 872 | + Debug.Assert((object)iteratorStateMachine == null || (object)asyncStateMachine == null); |
| 873 | + stateMachine = stateMachine ?? asyncStateMachine; |
| 874 | + } |
| 875 | + } |
| 876 | + |
| 877 | + return (loweredBody, stateMachine); |
| 878 | + } |
| 879 | + } |
| 880 | + |
| 881 | + private static bool IsMissingYieldInAsyncIterator(BoundStatement body, MethodSymbol method, BindingDiagnosticBag diagnostics) |
| 882 | + { |
| 883 | + CSharpCompilation compilation = method.DeclaringCompilation; |
| 884 | + bool isAsyncEnumerableOrEnumerator = method.IsAsyncReturningIAsyncEnumerable(compilation) || |
| 885 | + method.IsAsyncReturningIAsyncEnumerator(compilation); |
| 886 | + |
| 887 | + if (isAsyncEnumerableOrEnumerator && !method.IsIterator) |
| 888 | + { |
| 889 | + bool containsAwait = AsyncRewriter.AwaitDetector.ContainsAwait(body); |
| 890 | + diagnostics.Add(containsAwait ? ErrorCode.ERR_PossibleAsyncIteratorWithoutYield : ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, |
| 891 | + method.GetFirstLocation()); |
| 892 | + |
| 893 | + return true; |
| 894 | + } |
| 895 | + |
| 896 | + return false; |
847 | 897 | } |
848 | 898 |
|
849 | 899 | /// <summary> |
@@ -1588,26 +1638,42 @@ internal static BoundStatement LowerBodyOrInitializer( |
1588 | 1638 | BoundStatement bodyWithoutIterators = IteratorRewriter.Rewrite(bodyWithoutLambdas, method, methodOrdinal, stateMachineStateDebugInfoBuilder, lazyVariableSlotAllocator, compilationState, diagnostics, |
1589 | 1639 | out IteratorStateMachine iteratorStateMachine); |
1590 | 1640 |
|
1591 | | - if (bodyWithoutIterators.HasErrors) |
| 1641 | + if (bodyWithoutIterators.HasErrors |
| 1642 | + || IsMissingYieldInAsyncIterator(loweredBody, method, diagnostics)) |
1592 | 1643 | { |
1593 | 1644 | return bodyWithoutIterators; |
1594 | 1645 | } |
1595 | 1646 |
|
1596 | 1647 | BoundStatement bodyWithoutAsync; |
1597 | | - AsyncStateMachine asyncStateMachine = null; |
1598 | | - if (compilationState.Compilation.IsRuntimeAsyncEnabledIn(method)) |
| 1648 | + var compilation = compilationState.Compilation; |
| 1649 | + if (compilation.IsRuntimeAsyncEnabledIn(method)) |
1599 | 1650 | { |
1600 | | - bodyWithoutAsync = RuntimeAsyncRewriter.Rewrite(bodyWithoutIterators, method, compilationState, diagnostics); |
| 1651 | + if (method.IsAsyncReturningIAsyncEnumerable(compilation) |
| 1652 | + || method.IsAsyncReturningIAsyncEnumerator(compilation)) |
| 1653 | + { |
| 1654 | + RuntimeAsyncIteratorStateMachine runtimeAsyncIteratorStateMachine; |
| 1655 | + |
| 1656 | + bodyWithoutAsync = RuntimeAsyncIteratorRewriter.Rewrite(bodyWithoutIterators, method, |
| 1657 | + methodOrdinal, stateMachineStateDebugInfoBuilder, lazyVariableSlotAllocator, compilationState, diagnostics, |
| 1658 | + out runtimeAsyncIteratorStateMachine); |
| 1659 | + |
| 1660 | + Debug.Assert(runtimeAsyncIteratorStateMachine is not null); |
| 1661 | + stateMachineTypeOpt = runtimeAsyncIteratorStateMachine; |
| 1662 | + } |
| 1663 | + else |
| 1664 | + { |
| 1665 | + bodyWithoutAsync = RuntimeAsyncRewriter.Rewrite(bodyWithoutIterators, method, compilationState, diagnostics); |
| 1666 | + } |
1601 | 1667 | } |
1602 | 1668 | else |
1603 | 1669 | { |
| 1670 | + AsyncStateMachine asyncStateMachine = null; |
1604 | 1671 | bodyWithoutAsync = AsyncRewriter.Rewrite(bodyWithoutIterators, method, methodOrdinal, stateMachineStateDebugInfoBuilder, lazyVariableSlotAllocator, compilationState, diagnostics, |
1605 | 1672 | out asyncStateMachine); |
| 1673 | + Debug.Assert(iteratorStateMachine is null || asyncStateMachine is null); |
| 1674 | + stateMachineTypeOpt = (StateMachineTypeSymbol)iteratorStateMachine ?? asyncStateMachine; |
1606 | 1675 | } |
1607 | 1676 |
|
1608 | | - Debug.Assert(iteratorStateMachine is null || asyncStateMachine is null); |
1609 | | - stateMachineTypeOpt = (StateMachineTypeSymbol)iteratorStateMachine ?? asyncStateMachine; |
1610 | | - |
1611 | 1677 | return bodyWithoutAsync; |
1612 | 1678 | } |
1613 | 1679 | catch (BoundTreeVisitor.CancelledByStackGuardException ex) |
@@ -1666,8 +1732,7 @@ private static MethodBody GenerateMethodBody( |
1666 | 1732 | bool isAsyncStateMachine; |
1667 | 1733 | MethodSymbol kickoffMethod; |
1668 | 1734 |
|
1669 | | - if (method is SynthesizedStateMachineMethod stateMachineMethod && |
1670 | | - method.Name == WellKnownMemberNames.MoveNextMethodName) |
| 1735 | + if (method is SynthesizedStateMachineMoveNextMethod stateMachineMethod) |
1671 | 1736 | { |
1672 | 1737 | kickoffMethod = stateMachineMethod.StateMachineType.KickoffMethod; |
1673 | 1738 | Debug.Assert(kickoffMethod != null); |
|
0 commit comments