diff --git a/docs/compilers/CSharp/Runtime Async Design.md b/docs/compilers/CSharp/Runtime Async Design.md index 077f233a9a924..6019da47c7252 100644 --- a/docs/compilers/CSharp/Runtime Async Design.md +++ b/docs/compilers/CSharp/Runtime Async Design.md @@ -57,7 +57,7 @@ namespace System.Runtime.CompilerServices; public enum MethodImplOptions { - Async = 1024 + Async = 0x2000 } ``` @@ -135,9 +135,11 @@ For any `await expr` with where `expr` has type `E`, the compiler will attempt t 2. There is an identity or implicit reference conversion from `E` to the type of `P`. 4. Otherwise, if `Mi` has a generic arity of 1 with type param `Tm`, all of the following must be true, or `Mi` is removed: 1. The return type is `Tm` - 2. There is an identity or implicit reference conversion from `E`'s unsubstituted definition to `P` - 3. `E`'s type argument, `Te`, is valid to substitute for `Tm` -6. If only one `Mi` remains, that method is used for the following rewrites. Otherwise, we instead move to [await any other type]. + 2. The generic parameter of `E` is `Te` + 3. `Ti` satisfies any constraints on `Tm` + 4. `Mie` is `Mi` with `Te` substituted for `Tm`, and `Pe` is the resulting parameter of `Mie` + 5. There is an identity or implicit reference conversion from `E` to the type of `Pe` +5. If only one `Mi` remains, that method is used for the following rewrites. Otherwise, we instead move to [await any other type]. We'll generally rewrite `await expr` into `System.Runtime.CompilerServices.AsyncHelpers.Await(expr)`. A number of different example scenarios for this are covered below. The main interesting deviations are when `struct` rvalues need to be hoisted across an `await`, and exception handling rewriting. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 163868061b55d..3b1f53346f852 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -4708,6 +4708,10 @@ internal SafeContext GetValEscape(BoundExpression expr, SafeContext localScopeDe // only possible in error cases (if possible at all) return localScopeDepth; + case BoundKind.ArgList: + // Only possible in error scenarios in runtime async (arglist operators are disallowed in runtime async methods) + return localScopeDepth; + case BoundKind.ConvertedSwitchExpression: case BoundKind.UnconvertedSwitchExpression: var switchExpr = (BoundSwitchExpression)expr; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs index ce13976db5419..2788139ff9769 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs @@ -7,6 +7,7 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -37,7 +38,7 @@ private BoundAwaitExpression BindAwait(BoundExpression expression, SyntaxNode no // The expression await t is classified the same way as the expression (t).GetAwaiter().GetResult(). Thus, // if the return type of GetResult is void, the await-expression is classified as nothing. If it has a // non-void return type T, the await-expression is classified as a value of type T. - TypeSymbol awaitExpressionType = info.GetResult?.ReturnType ?? (hasErrors ? CreateErrorType() : Compilation.DynamicType); + TypeSymbol awaitExpressionType = (info.GetResult ?? info.RuntimeAsyncAwaitCall?.Method)?.ReturnType ?? (hasErrors ? CreateErrorType() : Compilation.DynamicType); return new BoundAwaitExpression(node, expression, info, debugInfo: default, awaitExpressionType, hasErrors); } @@ -48,21 +49,23 @@ internal void ReportBadAwaitDiagnostics(SyntaxNodeOrToken nodeOrToken, BindingDi hasErrors |= ReportBadAwaitContext(nodeOrToken, diagnostics); } - internal BoundAwaitableInfo BindAwaitInfo(BoundAwaitableValuePlaceholder placeholder, SyntaxNode node, BindingDiagnosticBag diagnostics, ref bool hasErrors, BoundExpression? expressionOpt = null) + internal BoundAwaitableInfo BindAwaitInfo(BoundAwaitableValuePlaceholder getAwaiterPlaceholder, SyntaxNode node, BindingDiagnosticBag diagnostics, ref bool hasErrors, BoundExpression? expressionOpt = null) { bool hasGetAwaitableErrors = !GetAwaitableExpressionInfo( - expressionOpt ?? placeholder, - placeholder, + expressionOpt ?? getAwaiterPlaceholder, + getAwaiterPlaceholder, out bool isDynamic, out BoundExpression? getAwaiter, out PropertySymbol? isCompleted, out MethodSymbol? getResult, getAwaiterGetResultCall: out _, + out BoundCall? runtimeAsyncAwaitCall, + out BoundAwaitableValuePlaceholder? runtimeAsyncAwaitPlaceholder, node, diagnostics); hasErrors |= hasGetAwaitableErrors; - return new BoundAwaitableInfo(node, placeholder, isDynamic: isDynamic, getAwaiter, isCompleted, getResult, hasErrors: hasGetAwaitableErrors) { WasCompilerGenerated = true }; + return new BoundAwaitableInfo(node, getAwaiterPlaceholder, isDynamic: isDynamic, getAwaiter, isCompleted, getResult, runtimeAsyncAwaitCall, runtimeAsyncAwaitPlaceholder, hasErrors: hasGetAwaitableErrors) { WasCompilerGenerated = true }; } /// @@ -123,7 +126,7 @@ private bool CouldBeAwaited(BoundExpression expression) return false; } - return GetAwaitableExpressionInfo(expression, getAwaiterGetResultCall: out _, + return GetAwaitableExpressionInfo(expression, getAwaiterGetResultCall: out _, runtimeAsyncAwaitCall: out _, node: syntax, diagnostics: BindingDiagnosticBag.Discarded); } @@ -242,10 +245,11 @@ private bool ReportBadAwaitContext(SyntaxNodeOrToken nodeOrToken, BindingDiagnos internal bool GetAwaitableExpressionInfo( BoundExpression expression, out BoundExpression? getAwaiterGetResultCall, + out BoundCall? runtimeAsyncAwaitCall, SyntaxNode node, BindingDiagnosticBag diagnostics) { - return GetAwaitableExpressionInfo(expression, expression, out _, out _, out _, out _, out getAwaiterGetResultCall, node, diagnostics); + return GetAwaitableExpressionInfo(expression, expression, out _, out _, out _, out _, out getAwaiterGetResultCall, out runtimeAsyncAwaitCall, out _, node, diagnostics); } private bool GetAwaitableExpressionInfo( @@ -256,6 +260,8 @@ private bool GetAwaitableExpressionInfo( out PropertySymbol? isCompleted, out MethodSymbol? getResult, out BoundExpression? getAwaiterGetResultCall, + out BoundCall? runtimeAsyncAwaitCall, + out BoundAwaitableValuePlaceholder? runtimeAsyncAwaitCallPlaceholder, SyntaxNode node, BindingDiagnosticBag diagnostics) { @@ -266,6 +272,8 @@ private bool GetAwaitableExpressionInfo( isCompleted = null; getResult = null; getAwaiterGetResultCall = null; + runtimeAsyncAwaitCall = null; + runtimeAsyncAwaitCallPlaceholder = null; if (!ValidateAwaitedExpression(expression, node, diagnostics)) { @@ -274,10 +282,20 @@ private bool GetAwaitableExpressionInfo( if (expression.HasDynamicType()) { + // https://github.com/dotnet/roslyn/issues/79762: Handle runtime async here isDynamic = true; return true; } + var isRuntimeAsyncEnabled = Compilation.IsRuntimeAsyncEnabledIn(this.ContainingMemberOrLambda); + + // When RuntimeAsync is enabled, we first check for whether there is an AsyncHelpers.Await method that can handle the expression. + + if (isRuntimeAsyncEnabled && tryGetRuntimeAwaitHelper(expression, out runtimeAsyncAwaitCallPlaceholder, out runtimeAsyncAwaitCall, diagnostics)) + { + return true; + } + if (!GetGetAwaiterMethod(getAwaiterArgument, node, diagnostics, out getAwaiter)) { return false; @@ -286,7 +304,235 @@ private bool GetAwaitableExpressionInfo( TypeSymbol awaiterType = getAwaiter.Type!; return GetIsCompletedProperty(awaiterType, node, expression.Type!, diagnostics, out isCompleted) && AwaiterImplementsINotifyCompletion(awaiterType, node, diagnostics) - && GetGetResultMethod(getAwaiter, node, expression.Type!, diagnostics, out getResult, out getAwaiterGetResultCall); + && GetGetResultMethod(getAwaiter, node, expression.Type!, diagnostics, out getResult, out getAwaiterGetResultCall) + && (!isRuntimeAsyncEnabled || getRuntimeAwaitAwaiter(awaiterType, out runtimeAsyncAwaitCall, out runtimeAsyncAwaitCallPlaceholder, expression.Syntax, diagnostics)); + + bool tryGetRuntimeAwaitHelper(BoundExpression expression, out BoundAwaitableValuePlaceholder? placeholder, out BoundCall? runtimeAwaitCall, BindingDiagnosticBag diagnostics) + { + // For any `await expr` with where `expr` has type `E`, the compiler will attempt to match it to a helper method in `System.Runtime.CompilerServices.AsyncHelpers`. The following algorithm is used: + + // 1. If `E` has generic arity greater than 1, no match is found and instead move to [await any other type]. + // 2. `System.Runtime.CompilerServices.AsyncHelpers` from corelib (the library that defines `System.Object` and has no references) is fetched. + // 3. All methods named `Await` are put into a group called `M`. + // 4. For every `Mi` in `M`: + // 1. If `Mi`'s generic arity does not match `E`, it is removed. + // 2. If `Mi` takes more than 1 parameter (named `P`), it is removed. + // 3. If `Mi` has a generic arity of 0, all of the following must be true, or `Mi` is removed: + // 1. The return type is `System.Void` + // 2. There is an identity or implicit reference conversion from `E` to the type of `P`. + // 4. Otherwise, if `Mi` has a generic arity of 1 with type param `Tm`, all of the following must be true, or `Mi` is removed: + // 2. The generic parameter of `E` is `Te` + // 3. `Ti` satisfies any constraints on `Tm` + // 4. `Mie` is `Mi` with `Te` substituted for `Tm`, and `Pe` is the resulting parameter of `Mie` + // 5. There is an identity or implicit reference conversion from `E` to the type of `Pe` + // 6. If only one `Mi` remains, that method is used for the following rewrites. Otherwise, we instead move to [await any other type]. + runtimeAwaitCall = null; + placeholder = null; + + if (expression.Type is not NamedTypeSymbol { Arity: 0 or 1 } exprType) + { + return false; + } + + var asyncHelpersType = GetSpecialType(InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers, diagnostics, expression.Syntax); + if (asyncHelpersType.IsErrorType()) + { + return false; + } + + var awaitMembers = asyncHelpersType.GetMembers("Await"); + + foreach (var member in awaitMembers) + { + if (!isApplicableMethod(exprType, member, node, diagnostics, this, out MethodSymbol? method, out Conversion argumentConversion)) + { + continue; + } + + if (runtimeAwaitCall is not null) + { + runtimeAwaitCall = null; + placeholder = null; + return false; + } + + placeholder = new BoundAwaitableValuePlaceholder(expression.Syntax, expression.Type); + + BoundExpression argument = CreateConversion(placeholder, argumentConversion, destination: method.Parameters[0].Type, diagnostics); + + if (argument is BoundConversion) + { + argument.WasCompilerGenerated = true; + } + + runtimeAwaitCall = new BoundCall( + expression.Syntax, + receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.False, + method, + [argument], + argumentNamesOpt: default, + argumentRefKindsOpt: default, + isDelegateCall: false, + expanded: false, + invokedAsExtensionMethod: false, + argsToParamsOpt: default, + defaultArguments: default, + resultKind: LookupResultKind.Viable, + method.ReturnType) + { + WasCompilerGenerated = true + }; + } + + if (runtimeAwaitCall is null) + { + return false; + } + + reportObsoleteDiagnostics(this, diagnostics, runtimeAwaitCall.Method, expression.Syntax); + return true; + + static bool isApplicableMethod( + NamedTypeSymbol exprType, + Symbol member, + SyntaxNode node, + BindingDiagnosticBag diagnostics, + Binder @this, + [NotNullWhen(true)] out MethodSymbol? awaitMethod, + out Conversion conversion) + { + conversion = default; + awaitMethod = null; + if (member is not MethodSymbol method + || method.Arity != exprType.Arity + || method.ParameterCount != 1) + { + return false; + } + + if (method.Arity == 0) + { + if (method.ReturnsVoid && isValidConversion(exprType, method, node, diagnostics, @this, out conversion)) + { + awaitMethod = method; + return true; + } + else + { + return false; + } + } + else + { + var unsubstitutedReturnType = method.ReturnType; + if ((object)unsubstitutedReturnType != method.TypeArgumentsWithAnnotations[0].Type) + { + return false; + } + + var substitutedMethod = method.Construct(exprType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics); + var tempDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics); + if (!ConstraintsHelper.CheckConstraints( + substitutedMethod, + new ConstraintsHelper.CheckConstraintsArgs(@this.Compilation, @this.Conversions, includeNullability: false, node.Location, tempDiagnostics))) + { + tempDiagnostics.Free(); + return false; + } + + if (!isValidConversion(exprType, substitutedMethod, node, diagnostics, @this, out conversion)) + { + tempDiagnostics.Free(); + return false; + } + + awaitMethod = substitutedMethod; + diagnostics.AddRangeAndFree(tempDiagnostics); + return true; + } + } + + static bool isValidConversion(TypeSymbol exprType, MethodSymbol method, SyntaxNode node, BindingDiagnosticBag diagnostics, Binder @this, out Conversion conversion) + { + CompoundUseSiteInfo useSiteInfo = @this.GetNewCompoundUseSiteInfo(diagnostics); + conversion = @this.Conversions.ClassifyImplicitConversionFromType( + exprType, + method.Parameters[0].Type, + ref useSiteInfo); + + var result = conversion is { IsImplicit: true, Kind: ConversionKind.Identity or ConversionKind.ImplicitReference }; + if (result) + { + diagnostics.Add(node, useSiteInfo); + } + + return result; + } + } + + bool getRuntimeAwaitAwaiter(TypeSymbol awaiterType, out BoundCall? runtimeAwaitAwaiterCall, out BoundAwaitableValuePlaceholder? placeholder, SyntaxNode syntax, BindingDiagnosticBag diagnostics) + { + // Use site info is discarded because we don't actually do this conversion, we just need to know which generic + // method to call. The helpers are generic, so the final call will actually just be an identity conversion. + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + var useUnsafeAwait = Compilation.Conversions.ClassifyImplicitConversionFromType( + awaiterType, + Compilation.GetSpecialType(InternalSpecialType.System_Runtime_CompilerServices_ICriticalNotifyCompletion), + ref discardedUseSiteInfo).IsImplicit; + + var awaitMethod = (MethodSymbol?)GetSpecialTypeMember( + useUnsafeAwait + ? SpecialMember.System_Runtime_CompilerServices_AsyncHelpers__UnsafeAwaitAwaiter_TAwaiter + : SpecialMember.System_Runtime_CompilerServices_AsyncHelpers__AwaitAwaiter_TAwaiter, + diagnostics, + syntax); + + if (awaitMethod is null) + { + runtimeAwaitAwaiterCall = null; + placeholder = null; + return false; + } + + Debug.Assert(awaitMethod is { Arity: 1 }); + + var runtimeAwaitAwaiterMethod = awaitMethod.Construct(awaiterType); + ConstraintsHelper.CheckConstraints( + runtimeAwaitAwaiterMethod, + new ConstraintsHelper.CheckConstraintsArgs(this.Compilation, this.Conversions, includeNullability: false, syntax.Location, diagnostics)); + + reportObsoleteDiagnostics(this, diagnostics, runtimeAwaitAwaiterMethod, syntax); + + placeholder = new BoundAwaitableValuePlaceholder(syntax, awaiterType); + + runtimeAwaitAwaiterCall = new BoundCall( + syntax, + receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.False, + runtimeAwaitAwaiterMethod, + [placeholder], + argumentNamesOpt: default, + argumentRefKindsOpt: default, + isDelegateCall: false, + expanded: false, + invokedAsExtensionMethod: false, + argsToParamsOpt: default, + defaultArguments: default, + resultKind: LookupResultKind.Viable, + runtimeAwaitAwaiterMethod.ReturnType) + { + WasCompilerGenerated = true + }; + + return true; + } + + static void reportObsoleteDiagnostics(Binder @this, BindingDiagnosticBag diagnostics, MethodSymbol method, SyntaxNode syntax) + { + @this.ReportDiagnosticsIfObsolete(diagnostics, method, syntax, hasBaseReceiver: false); + @this.ReportDiagnosticsIfObsolete(diagnostics, method.ContainingType, syntax, hasBaseReceiver: false); + } } /// diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index a51556e9c5999..edc5a1bdea0da 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -265,6 +265,12 @@ private BoundExpression BindArgListOperator(InvocationExpressionSyntax node, Bin { bool hasErrors = analyzedArguments.HasErrors; + if (IsInAsyncMethod() && Compilation.IsRuntimeAsyncEnabledIn(ContainingMemberOrLambda)) + { + // Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + diagnostics.Add(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, node, ContainingMemberOrLambda); + } + // We allow names, oddly enough; M(__arglist(x : 123)) is legal. We just ignore them. TypeSymbol objType = GetSpecialType(SpecialType.System_Object, diagnostics, node); for (int i = 0; i < analyzedArguments.Arguments.Count; ++i) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 4603d0051c276..9916068baa36e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1694,12 +1694,12 @@ NamespaceOrTypeOrAliasSymbolWithAnnotations convertToUnboundGenericType() } } - internal NamedTypeSymbol GetSpecialType(SpecialType typeId, BindingDiagnosticBag diagnostics, SyntaxNode node) + internal NamedTypeSymbol GetSpecialType(ExtendedSpecialType typeId, BindingDiagnosticBag diagnostics, SyntaxNode node) { return GetSpecialType(this.Compilation, typeId, node, diagnostics); } - internal static NamedTypeSymbol GetSpecialType(CSharpCompilation compilation, SpecialType typeId, SyntaxNode node, BindingDiagnosticBag diagnostics) + internal static NamedTypeSymbol GetSpecialType(CSharpCompilation compilation, ExtendedSpecialType typeId, SyntaxNode node, BindingDiagnosticBag diagnostics) { NamedTypeSymbol typeSymbol = compilation.GetSpecialType(typeId); Debug.Assert((object)typeSymbol != null, "Expect an error type if special type isn't found"); @@ -1707,7 +1707,7 @@ internal static NamedTypeSymbol GetSpecialType(CSharpCompilation compilation, Sp return typeSymbol; } - internal static NamedTypeSymbol GetSpecialType(CSharpCompilation compilation, SpecialType typeId, Location location, BindingDiagnosticBag diagnostics) + internal static NamedTypeSymbol GetSpecialType(CSharpCompilation compilation, ExtendedSpecialType typeId, Location location, BindingDiagnosticBag diagnostics) { NamedTypeSymbol typeSymbol = compilation.GetSpecialType(typeId); Debug.Assert((object)typeSymbol != null, "Expect an error type if special type isn't found"); diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 3d8da7b955392..1f3ac35d1a2b3 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -263,7 +263,7 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno var placeholder = new BoundAwaitableValuePlaceholder(expr, builder.MoveNextInfo?.Method.ReturnType ?? CreateErrorType()); awaitInfo = BindAwaitInfo(placeholder, expr, diagnostics, ref hasErrors); - if (!hasErrors && awaitInfo.GetResult?.ReturnType.SpecialType != SpecialType.System_Boolean) + if (!hasErrors && (awaitInfo.GetResult ?? awaitInfo.RuntimeAsyncAwaitCall?.Method)?.ReturnType.SpecialType != SpecialType.System_Boolean) { diagnostics.Add(ErrorCode.ERR_BadGetAsyncEnumerator, expr.Location, getEnumeratorMethod.ReturnTypeWithAnnotations, getEnumeratorMethod); hasErrors = true; diff --git a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs index 69c785044109a..3ba2d2818f8a2 100644 --- a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs @@ -1045,6 +1045,11 @@ private void GetAwaitableInstancePlaceholders(ArrayBuilder<(BoundValuePlaceholde { placeholders.Add((placeholder, SafeContextAndLocation.Create(valEscapeScope))); } + + if (awaitableInfo.RuntimeAsyncAwaitCallPlaceholder is { } runtimePlaceholder) + { + placeholders.Add((runtimePlaceholder, SafeContextAndLocation.Create(valEscapeScope))); + } } public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) diff --git a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs index 9ccb48982189f..9e0573cb6964a 100644 --- a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs @@ -150,7 +150,7 @@ internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNo if (awaitableTypeOpt is null) { - awaitOpt = new BoundAwaitableInfo(syntax, awaitableInstancePlaceholder: null, isDynamic: true, getAwaiter: null, isCompleted: null, getResult: null) { WasCompilerGenerated = true }; + awaitOpt = new BoundAwaitableInfo(syntax, awaitableInstancePlaceholder: null, isDynamic: true, getAwaiter: null, isCompleted: null, getResult: null, runtimeAsyncAwaitCall: null, runtimeAsyncAwaitCallPlaceholder: null) { WasCompilerGenerated = true }; } else { diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundAwaitableInfo.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundAwaitableInfo.cs new file mode 100644 index 0000000000000..b4a2888d989b8 --- /dev/null +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundAwaitableInfo.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp; + +partial class BoundAwaitableInfo +{ + private partial void Validate() + { + if (RuntimeAsyncAwaitCall is not null) + { + Debug.Assert(RuntimeAsyncAwaitCall.Method.ContainingType.ExtendedSpecialType == InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers); + Debug.Assert(RuntimeAsyncAwaitCallPlaceholder is not null); + + switch (RuntimeAsyncAwaitCall.Method.Name) + { + case "Await": + Debug.Assert(GetAwaiter is null); + Debug.Assert(IsCompleted is null); + Debug.Assert(GetResult is null); + break; + + case "AwaitAwaiter": + case "UnsafeAwaitAwaiter": + Debug.Assert(GetAwaiter is not null); + Debug.Assert(IsCompleted is not null); + Debug.Assert(GetResult is not null); + break; + + default: + Debug.Fail($"Unexpected RuntimeAsyncAwaitCall: {RuntimeAsyncAwaitCall.Method.Name}"); + break; + } + } + + Debug.Assert(GetAwaiter is not null || RuntimeAsyncAwaitCall is not null || IsDynamic || HasErrors); + } +} diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index fad4b595c41ca..6900c90d9ce5f 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -695,13 +695,19 @@ - + + + + + diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 35d99a2543dae..b8038cd892afc 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -8221,6 +8221,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ '{0}': extension member names cannot be the same as their extended type + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + This extension block collides with another extension block. They result in conflicting content-based type names in metadata, so must be in separate enclosing static classes. diff --git a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs index f1bcf5757fc9e..7e62d676d7f0a 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs @@ -170,11 +170,24 @@ private LocalDefinition LazyReturnTemp ? LocalSlotConstraints.None : LocalSlotConstraints.ByRef; + var returnTypeWithAnnotations = _method.ReturnTypeWithAnnotations; + if (_method.IsAsync && _module.Compilation.IsRuntimeAsyncEnabledIn(_method)) + { + // The return type of the method is either Task or ValueTask. The il of the method is + // actually going to appear to return a T, not the wrapper task type. So we need to + // translate the return type to the actual type that will be returned. + + var returnType = returnTypeWithAnnotations.Type; + Debug.Assert(((InternalSpecialType)returnType.OriginalDefinition.ExtendedSpecialType) is InternalSpecialType.System_Threading_Tasks_ValueTask_T or InternalSpecialType.System_Threading_Tasks_Task_T); + + returnTypeWithAnnotations = ((NamedTypeSymbol)returnType).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0]; + } + var bodySyntax = _methodBodySyntaxOpt; if (_ilEmitStyle == ILEmitStyle.Debug && bodySyntax != null) { int syntaxOffset = _method.CalculateLocalSyntaxOffset(LambdaUtilities.GetDeclaratorPosition(bodySyntax), bodySyntax.SyntaxTree); - var localSymbol = new SynthesizedLocal(_method, _method.ReturnTypeWithAnnotations, SynthesizedLocalKind.FunctionReturnValue, bodySyntax); + var localSymbol = new SynthesizedLocal(_method, returnTypeWithAnnotations, SynthesizedLocalKind.FunctionReturnValue, bodySyntax); result = _builder.LocalSlotManager.DeclareLocal( type: _module.Translate(localSymbol.Type, bodySyntax, _diagnostics.DiagnosticBag), @@ -190,7 +203,7 @@ private LocalDefinition LazyReturnTemp } else { - result = AllocateTemp(_method.ReturnType, _boundBody.Syntax, slotConstraints); + result = AllocateTemp(returnTypeWithAnnotations.Type, _boundBody.Syntax, slotConstraints); } _returnTemp = result; @@ -308,7 +321,10 @@ private void HandleReturn() { _builder.MarkLabel(s_returnLabel); - Debug.Assert(_method.ReturnsVoid == (_returnTemp == null)); + Debug.Assert(_method.ReturnsVoid == (_returnTemp == null) + || (_method.IsAsync + && _module.Compilation.IsRuntimeAsyncEnabledIn(_method) + && ((InternalSpecialType)_method.ReturnType.ExtendedSpecialType) is InternalSpecialType.System_Threading_Tasks_Task or InternalSpecialType.System_Threading_Tasks_ValueTask)); if (_emitPdbSequencePoints && !_method.IsIterator && !_method.IsAsync) { diff --git a/src/Compilers/CSharp/Portable/Compilation/AwaitExpressionInfo.cs b/src/Compilers/CSharp/Portable/Compilation/AwaitExpressionInfo.cs index 336c7acb96b1d..4f6f8f01e474a 100644 --- a/src/Compilers/CSharp/Portable/Compilation/AwaitExpressionInfo.cs +++ b/src/Compilers/CSharp/Portable/Compilation/AwaitExpressionInfo.cs @@ -10,6 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp /// /// Structure containing all semantic information about an await expression. /// + // https://github.com/dotnet/roslyn/issues/79818: Add runtime async info public readonly struct AwaitExpressionInfo : IEquatable { public IMethodSymbol? GetAwaiterMethod { get; } diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index b060e303bb32b..f909412fef4d3 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -312,6 +312,42 @@ internal bool IsNullableAnalysisEnabledAlways }; } + /// + /// Returns true if this method should be processed with runtime async handling instead + /// of compiler async state machine generation. + /// + internal bool IsRuntimeAsyncEnabledIn(Symbol? symbol) + { + if (!Assembly.RuntimeSupportsAsyncMethods) + { + return false; + } + + if (symbol is not MethodSymbol method) + { + return false; + } + + Debug.Assert(ReferenceEquals(method.ContainingAssembly, Assembly)); + + var methodReturn = method.ReturnType.OriginalDefinition; + if (((InternalSpecialType)methodReturn.ExtendedSpecialType) is not ( + InternalSpecialType.System_Threading_Tasks_Task or + InternalSpecialType.System_Threading_Tasks_Task_T or + InternalSpecialType.System_Threading_Tasks_ValueTask or + InternalSpecialType.System_Threading_Tasks_ValueTask_T)) + { + return false; + } + + return symbol switch + { + SourceMethodSymbol { IsRuntimeAsyncEnabledInMethod: ThreeState.True } => true, + SourceMethodSymbol { IsRuntimeAsyncEnabledInMethod: ThreeState.False } => false, + _ => Feature("runtime-async") == "on" + }; + } + /// /// The language version that was used to parse the syntax trees of this compilation. /// @@ -2204,12 +2240,17 @@ internal bool ReturnsAwaitableToVoidOrInt(MethodSymbol method, BindingDiagnostic var syntax = method.ExtractReturnTypeSyntax(); var dumbInstance = new BoundLiteral(syntax, ConstantValue.Null, namedType); var binder = GetBinder(syntax); - BoundExpression? result; - var success = binder.GetAwaitableExpressionInfo(dumbInstance, out result, syntax, diagnostics); + var success = binder.GetAwaitableExpressionInfo(dumbInstance, out BoundExpression? result, out BoundCall? runtimeAwaitCall, syntax, diagnostics); RoslynDebug.Assert(!namedType.IsDynamic()); - return success && - (result!.Type!.IsVoidType() || result.Type!.SpecialType == SpecialType.System_Int32); + if (!success) + { + return false; + } + + Debug.Assert(result is { Type: not null } || runtimeAwaitCall is { Type: not null }); + var returnType = result?.Type ?? runtimeAwaitCall!.Type; + return returnType.IsVoidType() || returnType.SpecialType == SpecialType.System_Int32; } /// diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 9ebab381994f6..1efec8c675dad 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -774,8 +774,15 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState) if (!loweredBody.HasErrors) { - AsyncStateMachine asyncStateMachine; - loweredBody = AsyncRewriter.Rewrite(loweredBody, method, methodOrdinal, stateMachineStateDebugInfoBuilder, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out asyncStateMachine); + AsyncStateMachine asyncStateMachine = null; + if (compilationState.Compilation.IsRuntimeAsyncEnabledIn(method)) + { + loweredBody = RuntimeAsyncRewriter.Rewrite(loweredBody, method, compilationState, diagnosticsThisMethod); + } + else + { + loweredBody = AsyncRewriter.Rewrite(loweredBody, method, methodOrdinal, stateMachineStateDebugInfoBuilder, variableSlotAllocatorOpt, compilationState, diagnosticsThisMethod, out asyncStateMachine); + } Debug.Assert((object)iteratorStateMachine == null || (object)asyncStateMachine == null); stateMachine = stateMachine ?? asyncStateMachine; @@ -1577,10 +1584,19 @@ internal static BoundStatement LowerBodyOrInitializer( return bodyWithoutIterators; } - BoundStatement bodyWithoutAsync = AsyncRewriter.Rewrite(bodyWithoutIterators, method, methodOrdinal, stateMachineStateDebugInfoBuilder, lazyVariableSlotAllocator, compilationState, diagnostics, - out AsyncStateMachine asyncStateMachine); + BoundStatement bodyWithoutAsync; + AsyncStateMachine asyncStateMachine = null; + if (compilationState.Compilation.IsRuntimeAsyncEnabledIn(method)) + { + bodyWithoutAsync = RuntimeAsyncRewriter.Rewrite(bodyWithoutIterators, method, compilationState, diagnostics); + } + else + { + bodyWithoutAsync = AsyncRewriter.Rewrite(bodyWithoutIterators, method, methodOrdinal, stateMachineStateDebugInfoBuilder, lazyVariableSlotAllocator, compilationState, diagnostics, + out asyncStateMachine); + } - Debug.Assert((object)iteratorStateMachine == null || (object)asyncStateMachine == null); + Debug.Assert(iteratorStateMachine is null || asyncStateMachine is null); stateMachineTypeOpt = (StateMachineTypeSymbol)iteratorStateMachine ?? asyncStateMachine; return bodyWithoutAsync; diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index edc7b77412294..277e2ccca661f 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2422,6 +2422,7 @@ internal enum ErrorCode ERR_InterpolatedStringHandlerArgumentDisallowed = 9325, ERR_MemberNameSameAsExtendedType = 9326, ERR_FeatureNotAvailableInVersion14 = 9327, + ERR_UnsupportedFeatureInRuntimeAsync = 9328, ERR_ExtensionBlockCollision = 9329, // Note: you will need to do the following after adding errors: diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index a2380cf1739f7..d46ba612c46bf 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -638,6 +638,7 @@ or ErrorCode.ERR_PossibleAsyncIteratorWithoutYield or ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait or ErrorCode.ERR_RefLocalAcrossAwait or ErrorCode.ERR_DataSectionStringLiteralHashCollision + or ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync // Update src\Features\CSharp\Portable\Diagnostics\LanguageServer\CSharpLspBuildOnlyDiagnostics.cs // and TestIsBuildOnlyDiagnostic in src\Compilers\CSharp\Test\Syntax\Diagnostics\DiagnosticTest.cs // whenever new values are added here. diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 0094469cbef5c..9281b156bf687 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -2127,30 +2127,38 @@ public BoundArrayLength Update(BoundExpression expression, TypeSymbol type) internal sealed partial class BoundAwaitableInfo : BoundNode { - public BoundAwaitableInfo(SyntaxNode syntax, BoundAwaitableValuePlaceholder? awaitableInstancePlaceholder, bool isDynamic, BoundExpression? getAwaiter, PropertySymbol? isCompleted, MethodSymbol? getResult, bool hasErrors = false) - : base(BoundKind.AwaitableInfo, syntax, hasErrors || awaitableInstancePlaceholder.HasErrors() || getAwaiter.HasErrors()) + public BoundAwaitableInfo(SyntaxNode syntax, BoundAwaitableValuePlaceholder? awaitableInstancePlaceholder, bool isDynamic, BoundExpression? getAwaiter, PropertySymbol? isCompleted, MethodSymbol? getResult, BoundCall? runtimeAsyncAwaitCall, BoundAwaitableValuePlaceholder? runtimeAsyncAwaitCallPlaceholder, bool hasErrors = false) + : base(BoundKind.AwaitableInfo, syntax, hasErrors || awaitableInstancePlaceholder.HasErrors() || getAwaiter.HasErrors() || runtimeAsyncAwaitCall.HasErrors() || runtimeAsyncAwaitCallPlaceholder.HasErrors()) { this.AwaitableInstancePlaceholder = awaitableInstancePlaceholder; this.IsDynamic = isDynamic; this.GetAwaiter = getAwaiter; this.IsCompleted = isCompleted; this.GetResult = getResult; + this.RuntimeAsyncAwaitCall = runtimeAsyncAwaitCall; + this.RuntimeAsyncAwaitCallPlaceholder = runtimeAsyncAwaitCallPlaceholder; + Validate(); } + [Conditional("DEBUG")] + private partial void Validate(); + public BoundAwaitableValuePlaceholder? AwaitableInstancePlaceholder { get; } public bool IsDynamic { get; } public BoundExpression? GetAwaiter { get; } public PropertySymbol? IsCompleted { get; } public MethodSymbol? GetResult { get; } + public BoundCall? RuntimeAsyncAwaitCall { get; } + public BoundAwaitableValuePlaceholder? RuntimeAsyncAwaitCallPlaceholder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitAwaitableInfo(this); - public BoundAwaitableInfo Update(BoundAwaitableValuePlaceholder? awaitableInstancePlaceholder, bool isDynamic, BoundExpression? getAwaiter, PropertySymbol? isCompleted, MethodSymbol? getResult) + public BoundAwaitableInfo Update(BoundAwaitableValuePlaceholder? awaitableInstancePlaceholder, bool isDynamic, BoundExpression? getAwaiter, PropertySymbol? isCompleted, MethodSymbol? getResult, BoundCall? runtimeAsyncAwaitCall, BoundAwaitableValuePlaceholder? runtimeAsyncAwaitCallPlaceholder) { - if (awaitableInstancePlaceholder != this.AwaitableInstancePlaceholder || isDynamic != this.IsDynamic || getAwaiter != this.GetAwaiter || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(isCompleted, this.IsCompleted) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(getResult, this.GetResult)) + if (awaitableInstancePlaceholder != this.AwaitableInstancePlaceholder || isDynamic != this.IsDynamic || getAwaiter != this.GetAwaiter || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(isCompleted, this.IsCompleted) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(getResult, this.GetResult) || runtimeAsyncAwaitCall != this.RuntimeAsyncAwaitCall || runtimeAsyncAwaitCallPlaceholder != this.RuntimeAsyncAwaitCallPlaceholder) { - var result = new BoundAwaitableInfo(this.Syntax, awaitableInstancePlaceholder, isDynamic, getAwaiter, isCompleted, getResult, this.HasErrors); + var result = new BoundAwaitableInfo(this.Syntax, awaitableInstancePlaceholder, isDynamic, getAwaiter, isCompleted, getResult, runtimeAsyncAwaitCall, runtimeAsyncAwaitCallPlaceholder, this.HasErrors); result.CopyAttributes(this); return result; } @@ -10013,6 +10021,8 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor { this.Visit(node.AwaitableInstancePlaceholder); this.Visit(node.GetAwaiter); + this.Visit(node.RuntimeAsyncAwaitCall); + this.Visit(node.RuntimeAsyncAwaitCallPlaceholder); return null; } public override BoundNode? VisitAwaitExpression(BoundAwaitExpression node) @@ -11172,7 +11182,9 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor MethodSymbol? getResult = this.VisitMethodSymbol(node.GetResult); BoundAwaitableValuePlaceholder? awaitableInstancePlaceholder = (BoundAwaitableValuePlaceholder?)this.Visit(node.AwaitableInstancePlaceholder); BoundExpression? getAwaiter = (BoundExpression?)this.Visit(node.GetAwaiter); - return node.Update(awaitableInstancePlaceholder, node.IsDynamic, getAwaiter, isCompleted, getResult); + BoundCall? runtimeAsyncAwaitCall = (BoundCall?)this.Visit(node.RuntimeAsyncAwaitCall); + BoundAwaitableValuePlaceholder? runtimeAsyncAwaitCallPlaceholder = (BoundAwaitableValuePlaceholder?)this.Visit(node.RuntimeAsyncAwaitCallPlaceholder); + return node.Update(awaitableInstancePlaceholder, node.IsDynamic, getAwaiter, isCompleted, getResult, runtimeAsyncAwaitCall, runtimeAsyncAwaitCallPlaceholder); } public override BoundNode? VisitAwaitExpression(BoundAwaitExpression node) { @@ -13108,7 +13120,9 @@ public NullabilityRewriter(ImmutableDictionary(obj), objInit, - _F.If( - _F.ObjectNotEqual( - _F.Local(obj), - _F.Null(obj.Type)), - rethrow)); + checkAndThrow(obj)); + + BoundStatement checkAndThrow(LocalSymbol obj) + { + BoundStatement rethrow = Rethrow(obj); + + BoundStatement checkAndThrow = _F.If( + _F.ObjectNotEqual( + _F.Local(obj), + _F.Null(obj.Type)), + rethrow); + return checkAndThrow; + } } private BoundStatement Rethrow(LocalSymbol obj) @@ -511,6 +564,13 @@ private BoundStatement RewriteFinalizedRegion(BoundTryStatement node) handlers.ToImmutableAndFree()), _F.HiddenSequencePoint(), _F.Label(handledLabel)); + + // It's possible that all catches end in rethrows, and the method ends after them. In such scenarios, + // after the above switch will be "reachable", but have no statements to execute. In practice such code + // in unreachable, but this is the halting problem. To ensure we have valid IL, we append a final throw + // to the method, and if further basic block optimization determines that it's unreachable, then it'll be + // trimmed. + _needsFinalThrow = true; } _currentAwaitCatchFrame = origAwaitCatchFrame; @@ -706,14 +766,24 @@ public override BoundNode VisitLambda(BoundLambda node) { var oldContainingSymbol = _F.CurrentFunction; var oldAwaitFinallyFrame = _currentAwaitFinallyFrame; + var oldNeedsFinalThrow = _needsFinalThrow; _F.CurrentFunction = node.Symbol; _currentAwaitFinallyFrame = new AwaitFinallyFrame(); + _needsFinalThrow = false; - var result = base.VisitLambda(node); + var result = (BoundLambda)base.VisitLambda(node); + result = result.Update( + result.UnboundLambda, + result.Symbol, + (BoundBlock)FinalizeMethodBody(result.Body), + node.Diagnostics, + node.Binder, + node.Type); _F.CurrentFunction = oldContainingSymbol; _currentAwaitFinallyFrame = oldAwaitFinallyFrame; + _needsFinalThrow = oldNeedsFinalThrow; return result; } @@ -722,14 +792,18 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen { var oldContainingSymbol = _F.CurrentFunction; var oldAwaitFinallyFrame = _currentAwaitFinallyFrame; + var oldNeedsFinalThrow = _needsFinalThrow; _F.CurrentFunction = node.Symbol; _currentAwaitFinallyFrame = new AwaitFinallyFrame(); + _needsFinalThrow = false; - var result = base.VisitLocalFunctionStatement(node); + var result = (BoundLocalFunctionStatement)base.VisitLocalFunctionStatement(node); + result = result.Update(node.Symbol, (BoundBlock)FinalizeMethodBody(result.Body), (BoundBlock)FinalizeMethodBody(result.ExpressionBody)); _F.CurrentFunction = oldContainingSymbol; _currentAwaitFinallyFrame = oldAwaitFinallyFrame; + _needsFinalThrow = oldNeedsFinalThrow; return result; } diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs index c387cbbaff1fa..2fbe2b81eacde 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs @@ -347,6 +347,7 @@ public sealed override BoundNode VisitBadExpression(BoundBadExpression node) private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace) { + Debug.Assert(node.AwaitableInfo.RuntimeAsyncAwaitCall is null); BoundStatement preamble = MakeAwaitPreamble(); var expression = (BoundExpression)Visit(node.Expression); diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs new file mode 100644 index 0000000000000..972b5f1407963 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs @@ -0,0 +1,164 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Symbols; + +namespace Microsoft.CodeAnalysis.CSharp; + +internal sealed class RuntimeAsyncRewriter : BoundTreeRewriterWithStackGuard +{ + public static BoundStatement Rewrite( + BoundStatement node, + MethodSymbol method, + TypeCompilationState compilationState, + BindingDiagnosticBag diagnostics) + { + if (!method.IsAsync) + { + return node; + } + + // https://github.com/dotnet/roslyn/issues/79763: struct lifting + var variablesToHoist = IteratorAndAsyncCaptureWalker.Analyze(compilationState.Compilation, method, node, isRuntimeAsync: true, diagnostics.DiagnosticBag); + + if (variablesToHoist.Count > 0) + { + foreach (var variable in variablesToHoist) + { + // Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + diagnostics.Add(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, variable.GetFirstLocation(), method); + } + } + + var rewriter = new RuntimeAsyncRewriter(new SyntheticBoundNodeFactory(method, node.Syntax, compilationState, diagnostics)); + var result = (BoundStatement)rewriter.Visit(node); + return SpillSequenceSpiller.Rewrite(result, method, compilationState, diagnostics); + } + + private readonly SyntheticBoundNodeFactory _factory; + private readonly Dictionary _placeholderMap; + + private RuntimeAsyncRewriter(SyntheticBoundNodeFactory factory) + { + _factory = factory; + _placeholderMap = []; + } + + [return: NotNullIfNotNull(nameof(node))] + public BoundExpression? VisitExpression(BoundExpression? node) + { + var result = Visit(node); + return (BoundExpression?)result; + } + + public override BoundNode? VisitAwaitExpression(BoundAwaitExpression node) + { + var nodeType = node.Expression.Type; + Debug.Assert(nodeType is not null); + + var awaitableInfo = node.AwaitableInfo; + + if (awaitableInfo.IsDynamic) + { + // https://github.com/dotnet/roslyn/issues/79762: await dynamic will need runtime checks, see AsyncMethodToStateMachine.GenerateAwaitOnCompletedDynamic + Debug.Assert(_factory.CurrentFunction is not null); + // Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + _factory.Diagnostics.Add(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, + node.Syntax.Location, + _factory.CurrentFunction); + return node; + } + + var runtimeAsyncAwaitCall = awaitableInfo.RuntimeAsyncAwaitCall; + Debug.Assert(runtimeAsyncAwaitCall is not null); + Debug.Assert(awaitableInfo.RuntimeAsyncAwaitCallPlaceholder is not null); + var runtimeAsyncAwaitMethod = runtimeAsyncAwaitCall.Method; + Debug.Assert(runtimeAsyncAwaitMethod is not null); + Debug.Assert(ReferenceEquals( + runtimeAsyncAwaitMethod.ContainingType.OriginalDefinition, + _factory.Compilation.GetSpecialType(InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers))); + Debug.Assert(runtimeAsyncAwaitMethod.Name is "Await" or "UnsafeAwaitAwaiter" or "AwaitAwaiter"); + + if (runtimeAsyncAwaitMethod.Name == "Await") + { + // This is the direct await case, with no need for the full pattern. + // System.Runtime.CompilerServices.RuntimeHelpers.Await(awaitedExpression) + var expr = VisitExpression(node.Expression); + _placeholderMap.Add(awaitableInfo.RuntimeAsyncAwaitCallPlaceholder, expr); + var call = Visit(awaitableInfo.RuntimeAsyncAwaitCall); + _placeholderMap.Remove(awaitableInfo.RuntimeAsyncAwaitCallPlaceholder); + return call; + } + else + { + return RewriteCustomAwaiterAwait(node); + } + } + + private BoundExpression RewriteCustomAwaiterAwait(BoundAwaitExpression node) + { + // await expr + // becomes + // var _tmp = expr.GetAwaiter(); + // if (!_tmp.IsCompleted) + // UnsafeAwaitAwaiter(_tmp) OR AwaitAwaiter(_tmp); + // _tmp.GetResult() + + var expr = VisitExpression(node.Expression); + + var awaitableInfo = node.AwaitableInfo; + var awaitablePlaceholder = awaitableInfo.AwaitableInstancePlaceholder; + if (awaitablePlaceholder is not null) + { + _placeholderMap.Add(awaitablePlaceholder, expr); + } + + // expr.GetAwaiter() + var getAwaiter = VisitExpression(awaitableInfo.GetAwaiter); + Debug.Assert(getAwaiter is not null); + + if (awaitablePlaceholder is not null) + { + _placeholderMap.Remove(awaitablePlaceholder); + } + + // var _tmp = expr.GetAwaiter(); + var tmp = _factory.StoreToTemp(getAwaiter, out BoundAssignmentOperator store, kind: SynthesizedLocalKind.Awaiter); + + // _tmp.IsCompleted + Debug.Assert(awaitableInfo.IsCompleted is not null); + var isCompletedMethod = awaitableInfo.IsCompleted.GetMethod; + Debug.Assert(isCompletedMethod is not null); + var isCompletedCall = _factory.Call(tmp, isCompletedMethod); + + // UnsafeAwaitAwaiter(_tmp) OR AwaitAwaiter(_tmp) + Debug.Assert(awaitableInfo.RuntimeAsyncAwaitCall is not null); + Debug.Assert(awaitableInfo.RuntimeAsyncAwaitCallPlaceholder is not null); + _placeholderMap.Add(awaitableInfo.RuntimeAsyncAwaitCallPlaceholder, tmp); + var awaitCall = (BoundCall)Visit(awaitableInfo.RuntimeAsyncAwaitCall); + _placeholderMap.Remove(awaitableInfo.RuntimeAsyncAwaitCallPlaceholder); + + // if (!_tmp.IsCompleted) awaitCall + var ifNotCompleted = _factory.If(_factory.Not(isCompletedCall), _factory.ExpressionStatement(awaitCall)); + + // _tmp.GetResult() + var getResultMethod = awaitableInfo.GetResult; + Debug.Assert(getResultMethod is not null); + var getResultCall = _factory.Call(tmp, getResultMethod); + + // final sequence + return _factory.SpillSequence( + locals: [tmp.LocalSymbol], + sideEffects: [_factory.ExpressionStatement(store), ifNotCompleted], + result: getResultCall); + } + + public override BoundNode VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) + { + return _placeholderMap[node]; + } +} diff --git a/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs index 921fdca645e3f..07fd38f7fef3f 100644 --- a/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs @@ -24,7 +24,7 @@ internal abstract class BoundTreeToDifferentEnclosingContextRewriter : BoundTree private readonly Dictionary localMap = new Dictionary(); //to handle type changes (e.g. type parameters) we need to update placeholders - private readonly Dictionary _placeholderMap = new Dictionary(); + private readonly Dictionary _placeholderMap = new Dictionary(); // A mapping for types in the original method to types in its replacement. This is mainly necessary // when the original method was generic, as type parameters in the original method are mapping into @@ -134,7 +134,22 @@ public override BoundNode VisitAwaitableInfo(BoundAwaitableInfo node) _placeholderMap.Remove(awaitablePlaceholder); - return node.Update(rewrittenPlaceholder, node.IsDynamic, getAwaiter, isCompleted, getResult); + BoundCall? runtimeAsyncAwaitCall = null; + var runtimeAsyncAwaitCallPlaceholder = node.RuntimeAsyncAwaitCallPlaceholder; + var rewrittenRuntimeAsyncAwaitCallPlaceholder = runtimeAsyncAwaitCallPlaceholder; + if (rewrittenRuntimeAsyncAwaitCallPlaceholder is not null) + { + rewrittenRuntimeAsyncAwaitCallPlaceholder = runtimeAsyncAwaitCallPlaceholder!.Update(VisitType(runtimeAsyncAwaitCallPlaceholder.Type)); + _placeholderMap.Add(runtimeAsyncAwaitCallPlaceholder, rewrittenRuntimeAsyncAwaitCallPlaceholder); + runtimeAsyncAwaitCall = (BoundCall?)this.Visit(node.RuntimeAsyncAwaitCall); + _placeholderMap.Remove(runtimeAsyncAwaitCallPlaceholder); + } + else + { + Debug.Assert(node.RuntimeAsyncAwaitCall is null); + } + + return node.Update(rewrittenPlaceholder, node.IsDynamic, getAwaiter, isCompleted, getResult, runtimeAsyncAwaitCall, rewrittenRuntimeAsyncAwaitCallPlaceholder); } public override BoundNode VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) diff --git a/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs index 1f4211f2cc6b4..67ecd02f6da0c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs @@ -183,7 +183,7 @@ method.OriginalDefinition is ErrorMethodSymbol || method.ContainingAssembly.GetSpecialTypeMember(SpecialMember.System_Nullable_T_get_HasValue) == (object)method.OriginalDefinition, { Name: nameof(VisitUserDefinedConditionalLogicalOperator) } => !method.IsExtensionMethod, // Expression tree context. At the moment an operator cannot be an extension method { Name: nameof(VisitCollectionElementInitializer) } => !method.IsExtensionMethod, // Expression tree context. At the moment an extension method cannot be used in expression tree here. - { Name: nameof(VisitAwaitableInfo) } => method is { Name: "GetResult", IsExtensionMethod: false }, // Cannot be an extension method + { Name: nameof(VisitAwaitableInfo) } => method is { Name: "GetResult" or "Await" or "AwaitAwaiter" or "UnsafeAwaitAwaiter", IsExtensionMethod: false }, // Cannot be an extension method { Name: nameof(VisitMethodSymbolWithExtensionRewrite), DeclaringType: { } declaringType } => declaringType == typeof(ExtensionMethodReferenceRewriter), _ => false }); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 0c90604e3f347..5542064b2998b 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -1049,6 +1049,11 @@ internal static bool CanBePassedByReference(BoundExpression expr) // Used for Length or Count properties only which are effectively readonly. return true; + case BoundKind.AwaitableValuePlaceholder: + // AwaitableValuePlaceholder that makes it here is always a parameter to a runtime async AsyncHelper method, + // and are always passed by value. + return false; + case BoundKind.EventAccess: var eventAccess = (BoundEventAccess)expr; if (eventAccess.IsUsableAsField) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs index b8c0079381658..44fe74506b1b0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs @@ -224,14 +224,14 @@ private BoundStatement RewriteForEachEnumerator( var disposalFinallyBlock = GetDisposalFinallyBlock(forEachSyntax, enumeratorInfo, enumeratorType, boundEnumeratorVar, out var hasAsyncDisposal); if (isAsync) { - Debug.Assert(awaitableInfo is { GetResult: { } }); + Debug.Assert(awaitableInfo is { GetResult: not null } or { RuntimeAsyncAwaitCall: not null }); // We need to be sure that when the disposal isn't async we reserve an unused state machine state number for it, // so that await foreach always produces 2 state machine states: one for MoveNextAsync and the other for DisposeAsync. // Otherwise, EnC wouldn't be able to map states when the disposal changes from having async dispose to not, or vice versa. var debugInfo = new BoundAwaitExpressionDebugInfo(s_moveNextAsyncAwaitId, ReservedStateMachineCount: (byte)(hasAsyncDisposal ? 0 : 1)); - rewrittenCondition = RewriteAwaitExpression(forEachSyntax, rewrittenCondition, awaitableInfo, awaitableInfo.GetResult.ReturnType, debugInfo, used: true); + rewrittenCondition = RewriteAwaitExpression(forEachSyntax, rewrittenCondition, awaitableInfo, (awaitableInfo.GetResult ?? awaitableInfo.RuntimeAsyncAwaitCall!.Method)!.ReturnType, debugInfo, used: true); } BoundStatement whileLoop = RewriteWhileStatement( diff --git a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs index 9607380fe911c..9e04140a4e6ba 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs @@ -705,15 +705,24 @@ public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement no return UpdateStatement(builder, node.Update(expression)); } +#nullable enable public override BoundNode VisitCatchBlock(BoundCatchBlock node) { - BoundExpression exceptionSourceOpt = (BoundExpression)this.Visit(node.ExceptionSourceOpt); + BoundExpression? exceptionSourceOpt = (BoundExpression?)this.Visit(node.ExceptionSourceOpt); var locals = node.Locals; var exceptionFilterPrologueOpt = node.ExceptionFilterPrologueOpt; - Debug.Assert(exceptionFilterPrologueOpt is null); // it is introduced by this pass - BoundSpillSequenceBuilder builder = null; + if (exceptionFilterPrologueOpt is not null) + { + exceptionFilterPrologueOpt = (BoundStatementList?)VisitStatementList(exceptionFilterPrologueOpt); + } + BoundSpillSequenceBuilder? builder = null; + var exceptionFilterOpt = VisitExpression(ref builder, node.ExceptionFilterOpt); + Debug.Assert(exceptionFilterPrologueOpt is null || builder is null, + "You are exercising SpillSequenceSpiller in a new fashion, causing a spill in an exception filter after LocalRewriting is complete. This is not someting " + + "that this builder supports today, so please update this rewrite to include the statements from exceptionFilterPrologueOpt with the appropriate " + + "syntax node and tracking."); if (builder is { }) { Debug.Assert(builder.Value is null); @@ -722,9 +731,10 @@ public override BoundNode VisitCatchBlock(BoundCatchBlock node) } BoundBlock body = (BoundBlock)this.Visit(node.Body); - TypeSymbol exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt); + TypeSymbol? exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt); return node.Update(locals, exceptionSourceOpt, exceptionTypeOpt, exceptionFilterPrologueOpt, exceptionFilterOpt, body, node.IsSynthesizedAsyncCatchAll); } +#nullable disable #if DEBUG public override BoundNode DefaultVisit(BoundNode node) diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs index 26b8010c98131..4c6d0df5f96df 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs @@ -29,6 +29,10 @@ internal sealed class IteratorAndAsyncCaptureWalker : DefiniteAssignmentPass // This set will contain such variables after the bound tree is visited. private readonly OrderedSet _variablesToHoist = new OrderedSet(); + // We have a smaller set of rules when runtime async is enabled: we only need to hoist by-refs across + // async boundaries in this case + private readonly bool _isRuntimeAsync; + // Contains variables that are captured but can't be hoisted since their type can't be allocated on heap. // The value is a list of all uses of each such variable. private MultiDictionary _lazyDisallowedCaptures; @@ -39,7 +43,7 @@ internal sealed class IteratorAndAsyncCaptureWalker : DefiniteAssignmentPass // variables in its initializing expression will need to be hoisted too. private readonly Dictionary _boundRefLocalInitializers = new Dictionary(); - private IteratorAndAsyncCaptureWalker(CSharpCompilation compilation, MethodSymbol method, BoundNode node, HashSet initiallyAssignedVariables) + private IteratorAndAsyncCaptureWalker(CSharpCompilation compilation, MethodSymbol method, BoundNode node, HashSet initiallyAssignedVariables, bool isRuntimeAsync) : base(compilation, method, node, @@ -47,13 +51,14 @@ private IteratorAndAsyncCaptureWalker(CSharpCompilation compilation, MethodSymbo trackUnassignments: true, initiallyAssignedVariables: initiallyAssignedVariables) { + _isRuntimeAsync = isRuntimeAsync; } // Returns deterministically ordered list of variables that ought to be hoisted. - public static OrderedSet Analyze(CSharpCompilation compilation, MethodSymbol method, BoundNode node, DiagnosticBag diagnostics) + public static OrderedSet Analyze(CSharpCompilation compilation, MethodSymbol method, BoundNode node, bool isRuntimeAsync, DiagnosticBag diagnostics) { var initiallyAssignedVariables = UnassignedVariablesWalker.Analyze(compilation, method, node, convertInsufficientExecutionStackExceptionToCancelledByStackGuardException: true); - var walker = new IteratorAndAsyncCaptureWalker(compilation, method, node, initiallyAssignedVariables); + var walker = new IteratorAndAsyncCaptureWalker(compilation, method, node, initiallyAssignedVariables, isRuntimeAsync); walker._convertInsufficientExecutionStackExceptionToCancelledByStackGuardException = true; @@ -61,7 +66,9 @@ public static OrderedSet Analyze(CSharpCompilation compilation, MethodSy walker.Analyze(ref badRegion); Debug.Assert(!badRegion); - if (!method.IsStatic && method.ContainingType.TypeKind == TypeKind.Struct) + // When runtime async is enabled, we don't want to blindly hoist `this`. We'll only hoist if the walker + // actually encounters it as a ref that should be hoisted + if (!method.IsStatic && method.ContainingType.TypeKind == TypeKind.Struct && !isRuntimeAsync) { // It is possible that the enclosing method only *writes* to the enclosing struct, but in that // case it should be considered captured anyway so that we have a proxy for it to write to. @@ -111,7 +118,7 @@ public static OrderedSet Analyze(CSharpCompilation compilation, MethodSy Debug.Assert(!allVariables.Any((s, method) => s.Symbol is ParameterSymbol { ContainingSymbol: var container } && container != method && container is not SynthesizedPrimaryConstructor, method)); var variablesToHoist = new OrderedSet(); - if (compilation.Options.OptimizationLevel != OptimizationLevel.Release) + if (compilation.Options.OptimizationLevel != OptimizationLevel.Release && !isRuntimeAsync) { // In debug build we hoist long-lived locals and parameters foreach (var v in allVariables) @@ -208,6 +215,18 @@ protected override ImmutableArray Scan(ref bool badRegion) private void CaptureVariable(Symbol variable, SyntaxNode syntax) { + if (_isRuntimeAsync) + { + switch (variable) + { + case ParameterSymbol { RefKind: RefKind.None }: + case LocalSymbol { RefKind: RefKind.None }: + case FieldSymbol { RefKind: RefKind.None }: + // Runtime async only needs to preserve by-ref captures + return; + } + } + var type = (variable.Kind == SymbolKind.Local) ? ((LocalSymbol)variable).Type : ((ParameterSymbol)variable).Type; if (type.IsRestrictedType() || (variable is LocalSymbol { RefKind: not RefKind.None } refLocal && !canRefLocalBeHoisted(refLocal))) diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs index 1d30a0ecd01ed..6c5fc05a07aff 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs @@ -115,7 +115,7 @@ protected BoundStatement Rewrite() } // fields for the captured variables of the method - var variablesToHoist = IteratorAndAsyncCaptureWalker.Analyze(F.Compilation, method, body, diagnostics.DiagnosticBag); + var variablesToHoist = IteratorAndAsyncCaptureWalker.Analyze(F.Compilation, method, body, isRuntimeAsync: false, diagnostics.DiagnosticBag); if (diagnostics.HasAnyErrors()) { diff --git a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs index b9b6dc93256bc..52fc241a7ebc0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs @@ -429,6 +429,8 @@ public bool SupportsRuntimeCapability(RuntimeCapability capability) return this.RuntimeSupportsInlineArrayTypes; case RuntimeCapability.ByRefLikeGenerics: return this.RuntimeSupportsByRefLikeGenerics; + case RuntimeCapability.RuntimeAsyncMethods: + return this.RuntimeSupportsAsyncMethods; } return false; @@ -494,6 +496,12 @@ internal bool RuntimeSupportsByRefLikeGenerics } } +#nullable enable + // Keep in sync with VB's AssemblySymbol.RuntimeSupportsAsyncMethods + internal bool RuntimeSupportsAsyncMethods + => GetSpecialType(InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers) is { TypeKind: TypeKind.Class, IsStatic: true }; +#nullable disable + protected bool RuntimeSupportsFeature(SpecialMember feature) { // Keep in sync with VB's AssemblySymbol.RuntimeSupportsFeature diff --git a/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/MethodWellKnownAttributeData.cs b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/MethodWellKnownAttributeData.cs index e95f295b21e87..c9410bc2f831c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/MethodWellKnownAttributeData.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/MethodWellKnownAttributeData.cs @@ -149,5 +149,21 @@ public UnmanagedCallersOnlyAttributeData? UnmanagedCallersOnlyAttributeData SetDataStored(); } } + + private ThreeState _runtimeAsyncMethodGenerationSetting; + public ThreeState RuntimeAsyncMethodGenerationSetting + { + get + { + VerifySealed(expected: true); + return _runtimeAsyncMethodGenerationSetting; + } + set + { + VerifySealed(expected: false); + _runtimeAsyncMethodGenerationSetting = value; + SetDataStored(); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index 7d58c0829aeb2..4e98b8cef33e4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -651,6 +651,13 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut arguments.AttributeSyntaxOpt); } } + else if (attribute.IsTargetAttribute(AttributeDescription.RuntimeAsyncMethodGenerationAttribute)) + { + arguments.GetOrCreateData().RuntimeAsyncMethodGenerationSetting = + attribute.CommonConstructorArguments[0].DecodeValue(SpecialType.System_Boolean) + ? ThreeState.True + : ThreeState.False; + } else { var compilation = this.DeclaringCompilation; @@ -661,6 +668,9 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut } } + internal ThreeState IsRuntimeAsyncEnabledInMethod + => GetDecodedWellKnownAttributeData()?.RuntimeAsyncMethodGenerationSetting ?? ThreeState.Unknown; + internal override ImmutableArray NotNullMembers => GetDecodedWellKnownAttributeData()?.NotNullMembers ?? ImmutableArray.Empty; @@ -1746,6 +1756,14 @@ internal override System.Reflection.MethodImplAttributes ImplementationAttribute result |= (System.Reflection.MethodImplAttributes.Runtime | System.Reflection.MethodImplAttributes.InternalCall); } + if (this.IsAsync && this.DeclaringCompilation.IsRuntimeAsyncEnabledIn(this)) + { + // https://github.com/dotnet/roslyn/issues/79792: Use real value from MethodImplAttributes when available + // When a method is emitted using runtime async, we add MethodImplAttributes.Async to indicate to the + // runtime to generate the state machine + result |= (System.Reflection.MethodImplAttributes)0x2000; + } + return result; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index 79823fc75481c..20595933253b8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -638,7 +638,24 @@ internal sealed override bool CallsAreOmitted(SyntaxTree syntaxTree) return base.CallsAreOmitted(syntaxTree); } - internal sealed override bool GenerateDebugInfo => !IsAsync && !IsIterator; + internal sealed override bool GenerateDebugInfo + { + get + { + if (IsIterator) + { + return false; + } + + if (IsAsync) + { + // https://github.com/dotnet/roslyn/issues/79793: Need more dedicated debug information testing when runtime async is enabled. + return DeclaringCompilation.IsRuntimeAsyncEnabledIn(this); + } + + return true; + } + } #nullable enable protected override void MethodChecks(BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index 13ef54295a2b0..4d1cd2f4ccec6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -373,11 +373,12 @@ internal AsyncForwardEntryPoint(CSharpCompilation compilation, NamedTypeSymbol c { WasCompilerGenerated = true }; // The diagnostics that would be produced here will already have been captured and returned. - var success = binder.GetAwaitableExpressionInfo(userMainInvocation, out _getAwaiterGetResultCall!, _userMainReturnTypeSyntax, BindingDiagnosticBag.Discarded); + var success = binder.GetAwaitableExpressionInfo(userMainInvocation, out _getAwaiterGetResultCall!, runtimeAsyncAwaitCall: out _, _userMainReturnTypeSyntax, BindingDiagnosticBag.Discarded); Debug.Assert( ReturnType.IsVoidType() || ReturnType.SpecialType == SpecialType.System_Int32); + Debug.Assert(!compilation.IsRuntimeAsyncEnabledIn(this)); } internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) @@ -491,7 +492,8 @@ internal override BoundBlock CreateBody(BindingDiagnosticBag diagnostics) Debug.Assert(!initializer.ReturnType.IsDynamic()); var initializeCall = CreateParameterlessCall(syntax, scriptLocal, receiverIsSubjectToCloning: ThreeState.False, initializer); BoundExpression getAwaiterGetResultCall; - if (!binder.GetAwaitableExpressionInfo(initializeCall, out getAwaiterGetResultCall, syntax, diagnostics)) + Debug.Assert(!compilation.IsRuntimeAsyncEnabledIn(this)); + if (!binder.GetAwaitableExpressionInfo(initializeCall, out getAwaiterGetResultCall, runtimeAsyncAwaitCall: out _, syntax, diagnostics)) { return new BoundBlock( syntax: syntax, diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index f66be9efb693a..f9ccea2fe2cd9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2572,6 +2572,11 @@ {0} vyžaduje funkci kompilátoru {1}, což tato verze kompilátoru C# nepodporuje. + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member Uvnitř členu instance nejde použít parametr primárního konstruktoru {0} typu odkaz, výstup nebo vstup. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index f75b8302c2c1d..afa4b2ad46cf9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2572,6 +2572,11 @@ '{0}' erfordert die Compilerfunktion '{1}', die von dieser Version des C#-Compilers nicht unterstützt wird. + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member Der ref-, out- oder in-primäre Konstruktorparameter „{0}“ kann nicht innerhalb eines Instanzmembers verwendet werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index b5a44b8cd7ad5..bcf103b3f23ed 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2572,6 +2572,11 @@ '{0}' requiere la característica del compilador '{1}', que no es compatible con esta versión del compilador de C#. + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member No se puede usar ref, out o en el parámetro de constructor principal '{0}' dentro de un miembro de instancia diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index b058907c8c9cd..f08487e03d14a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2572,6 +2572,11 @@ '{0}' nécessite la fonctionnalité de compilateur '{1}', qui n’est pas prise en charge par cette version du compilateur C#. + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member Impossible d’utiliser le paramètre de constructeur principal '{0}' avec ref, out ou in à l’intérieur d’un membre d’instance. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 329743b6bc9a4..f1a5d461b6bae 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2572,6 +2572,11 @@ '{0}' richiede la funzionalità del compilatore '{1}', che non è supportata da questa versione del compilatore C#. + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member Non è possibile usare il parametro ref, out o in del costruttore primario '{0}' all'interno di un membro di istanza diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 56658fcfa59b3..5be570201e715 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2572,6 +2572,11 @@ '{0}' にはコンパイラ機能 '{1}' が必要ですが、このバージョンのC## コンパイラではサポートされていません。 + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member インスタンス メンバー内のプライマリ コンストラクター パラメーター '{0}' では ref、out、in を使用できません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 582740d1bbc1d..44c6c9402a21a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2572,6 +2572,11 @@ '{0}'에는 이 버전의 C # 컴파일러에서 지원되지 않는 컴파일러 기능 '{1}'이(가) 필요합니다. + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member 인스턴스 멤버 내에서 ref, out 또는 기본 생성자 '{0}' 매개 변수를 사용할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 68253770718df..87108cb4fb5c4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2572,6 +2572,11 @@ „{0}” wymaga funkcji kompilatora „{1}”, która nie jest obsługiwana przez tę wersję kompilatora języka C#. + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member Nie można użyć ref, out lub w podstawowym parametrze konstruktora '{0}' wewnątrz elementu członkowskiego wystąpienia diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 79713c96f8f19..ffbaa8fd35b63 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2572,6 +2572,11 @@ '{0}' requer o recurso de compilador '{1}', o que não é suportado por esta versão do compilador de C#. + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member Não é possível usar ref, out ou no parâmetro de construtor primário "{0}" dentro de um membro da instância diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index da887dfccffc0..c518be882a931 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2572,6 +2572,11 @@ Для "{0}" требуется функция компилятора "{1}", которая не поддерживается в этой версии компилятора C#. + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member Невозможно использовать параметр основного конструктора "{0}" с модификаторами ref, out или in внутри элемента экземпляра diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 15f5b1f31e427..05e5fb7b38237 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2572,6 +2572,11 @@ '{0}', C# derleyicisinin bu sürümü tarafından desteklenmeyen '{1}' derleyici özelliğini gerektirir. + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member Bir örnek üye içinde ref, out veya in '{0}' birincil oluşturucu parametresi kullanılamaz diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index d96c3efb09388..7233c518c9ecb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2572,6 +2572,11 @@ '{0}' 需要编译器功能 '{1}',此版本的 C# 编译器不支持此功能。 + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member 无法在实例成员内的主构造函数参数 “{0}” 中使用 ref、out 或 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 34b891a3b5687..19725b044279f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2572,6 +2572,11 @@ '{0}' 需要編譯器功能 '{1}',此版本的 C# 編譯器不支援此功能。 + + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + + Cannot use ref, out, or in primary constructor parameter '{0}' inside an instance member 無法在執行個體成員內使用 ref、out 或 in 主要建立建構函式參數 '{0}' diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs index 3078e2a20345e..dbc131a409f15 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs @@ -143,6 +143,14 @@ public static void Main() }"; var expected = @""; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics( + // (3,1): hidden CS8019: Unnecessary using directive. + // using System.Diagnostics; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Diagnostics;").WithLocation(3, 1) + ); } [WorkItem(14878, "https://github.com/dotnet/roslyn/issues/14878")] @@ -570,6 +578,16 @@ .locals init (int V_0, IL_0332: ret } "); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [G]: Unexpected type on the stack. { Offset = 0x104, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact, WorkItem(855080, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/855080")] @@ -577,9 +595,6 @@ public void GenericCatchVariableInAsyncMethod() { var source = @" using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { @@ -612,6 +627,16 @@ public static async Task Goo() where T : Exception } "; CompileAndVerify(source, expectedOutput: "3"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("3", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Bar]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Goo]: Unexpected type on the stack. { Offset = 0x15, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact] @@ -651,6 +676,22 @@ public static void Main() -1 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify( + comp, + expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [G]: Unexpected type on the stack. { Offset = 0x13, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); } [Fact] @@ -689,6 +730,19 @@ public static void Main() exception "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [H]: Unexpected type on the stack. { Offset = 0xa, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); } [Fact] @@ -839,9 +893,56 @@ .locals init (int V_0, IL_00d1: ret } "); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x29, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); + + verifier.VerifyIL("Test.G()", """ + { + // Code size 42 (0x2a) + .maxstack 3 + .locals init (object V_0) + IL_0000: ldnull + IL_0001: stloc.0 + .try + { + IL_0002: leave.s IL_0007 + } + catch object + { + IL_0004: stloc.0 + IL_0005: leave.s IL_0007 + } + IL_0007: call "System.Threading.Tasks.Task Test.F()" + IL_000c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0011: ldloc.0 + IL_0012: brfalse.s IL_0029 + IL_0014: ldloc.0 + IL_0015: isinst "System.Exception" + IL_001a: dup + IL_001b: brtrue.s IL_001f + IL_001d: ldloc.0 + IL_001e: throw + IL_001f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0024: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0029: ret + } + """); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.TestExecutionNeedsWindowsTypes)] + [Fact] public void AsyncInFinally002() { var source = @" @@ -893,12 +994,71 @@ public static void Main() } } }"; - var expected = @"FOne or more errors occurred. -"; + var expected = ExecutionConditionUtil.IsWindowsDesktop + ? @"FOne or more errors occurred." + : @"FOne or more errors occurred. (hello)"; + CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0xb, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); + + verifier.VerifyIL("Test.G()", """ + { + // Code size 59 (0x3b) + .maxstack 2 + .locals init (int V_0, //x + object V_1, + int V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldnull + IL_0003: stloc.1 + .try + { + IL_0004: ldstr "hello" + IL_0009: newobj "System.Exception..ctor(string)" + IL_000e: throw + } + catch object + { + IL_000f: stloc.1 + IL_0010: leave.s IL_0012 + } + IL_0012: ldloc.0 + IL_0013: call "System.Threading.Tasks.Task Test.F()" + IL_0018: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001d: stloc.2 + IL_001e: ldloc.2 + IL_001f: add + IL_0020: stloc.0 + IL_0021: ldloc.1 + IL_0022: brfalse.s IL_0039 + IL_0024: ldloc.1 + IL_0025: isinst "System.Exception" + IL_002a: dup + IL_002b: brtrue.s IL_002f + IL_002d: ldloc.1 + IL_002e: throw + IL_002f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0034: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0039: ldnull + IL_003a: throw + } + """); } - [ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void AsyncInFinally003() { var source = @" @@ -954,7 +1114,10 @@ public static void Main() }, module.GetFieldNames("Test.d__1")); }); - v.VerifyPdb("Test.G", @" + // Native PDBs require desktop + if (ExecutionConditionUtil.IsWindowsDesktop) + { + v.VerifyPdb("Test.G", @" @@ -982,6 +1145,7 @@ public static void Main() "); + } v.VerifyIL("Test.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"{ @@ -1168,7 +1332,8 @@ .locals init (int V_0, IL_0194: ldarg.0 IL_0195: ldnull IL_0196: stfld ""object Test.d__1.<>s__2"" - IL_019b: leave.s IL_01b7 + IL_019b: ldnull + IL_019c: throw } catch System.Exception { @@ -1193,6 +1358,78 @@ .locals init (int V_0, IL_01cb: nop IL_01cc: ret }", sequencePoints: "Test+d__1.MoveNext"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); + + verifier.VerifyIL("Test.G()", """ + { + // Code size 75 (0x4b) + .maxstack 2 + .locals init (int V_0, //x + object V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldnull + IL_0003: stloc.1 + IL_0004: ldc.i4.0 + IL_0005: stloc.2 + .try + { + IL_0006: call "System.Threading.Tasks.Task Test.F()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: stloc.3 + IL_0013: ldc.i4.1 + IL_0014: stloc.2 + IL_0015: leave.s IL_001a + } + catch object + { + IL_0017: stloc.1 + IL_0018: leave.s IL_001a + } + IL_001a: ldloc.0 + IL_001b: call "System.Threading.Tasks.Task Test.F()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.s V_4 + IL_0027: ldloc.s V_4 + IL_0029: add + IL_002a: stloc.0 + IL_002b: ldloc.1 + IL_002c: brfalse.s IL_0043 + IL_002e: ldloc.1 + IL_002f: isinst "System.Exception" + IL_0034: dup + IL_0035: brtrue.s IL_0039 + IL_0037: ldloc.1 + IL_0038: throw + IL_0039: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0043: ldloc.2 + IL_0044: ldc.i4.1 + IL_0045: bne.un.s IL_0049 + IL_0047: ldloc.3 + IL_0048: ret + IL_0049: ldnull + IL_004a: throw + } + """); } [Fact] @@ -1241,6 +1478,970 @@ public static void Main() 2 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x3e, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); + + verifier.VerifyIL("Test.G()", """ + { + // Code size 63 (0x3f) + .maxstack 2 + .locals init (int V_0, //x + object V_1, + int V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldnull + IL_0003: stloc.1 + .try + { + IL_0004: newobj "System.Exception..ctor()" + IL_0009: throw + } + catch object + { + IL_000a: stloc.1 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: call "System.Threading.Tasks.Task Test.F()" + IL_0013: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0018: stloc.2 + IL_0019: ldloc.2 + IL_001a: add + IL_001b: stloc.0 + IL_001c: ldloc.1 + IL_001d: brfalse.s IL_0034 + IL_001f: ldloc.1 + IL_0020: isinst "System.Exception" + IL_0025: dup + IL_0026: brtrue.s IL_002a + IL_0028: ldloc.1 + IL_0029: throw + IL_002a: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_002f: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0034: leave.s IL_003b + } + catch object + { + IL_0036: pop + IL_0037: ldloc.0 + IL_0038: stloc.2 + IL_0039: leave.s IL_003d + } + IL_003b: ldnull + IL_003c: throw + IL_003d: ldloc.2 + IL_003e: ret + } + """); + } + + [Fact] + public void AsyncInFinally005() + { + var source = @" +using System; +using System.Threading.Tasks; +class Test +{ + static async Task F() + { + return 2; + } + static async Task G() + { + int x = 0; + try + { + x = await F(); + throw new Exception(x.ToString()); + } + finally + { + x += await F(); + } + } + public static void Main() + { + System.Globalization.CultureInfo saveUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; + System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; + Task t2 = G(); + try + { + t2.Wait(1000 * 60); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + System.Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture; + } + } +}"; + var expected = "One or more errors occurred."; + if (!ExecutionConditionUtil.IsDesktop) + { + expected += " (2)"; + } + var verifier = CompileAndVerify(source, expectedOutput: expected); + verifier.VerifyIL("Test.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 353 (0x161) + .maxstack 3 + .locals init (int V_0, + int V_1, + int V_2, + System.Runtime.CompilerServices.TaskAwaiter V_3, + object V_4, + System.Exception V_5) + IL_0000: ldarg.0 + IL_0001: ldfld "int Test.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0026 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00e9 + IL_0011: ldarg.0 + IL_0012: ldc.i4.0 + IL_0013: stfld "int Test.d__1.5__2" + IL_0018: ldarg.0 + IL_0019: ldnull + IL_001a: stfld "object Test.d__1.<>7__wrap2" + IL_001f: ldarg.0 + IL_0020: ldc.i4.0 + IL_0021: stfld "int Test.d__1.<>7__wrap3" + IL_0026: nop + .try + { + IL_0027: ldloc.0 + IL_0028: brfalse.s IL_0061 + IL_002a: call "System.Threading.Tasks.Task Test.F()" + IL_002f: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0034: stloc.3 + IL_0035: ldloca.s V_3 + IL_0037: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_003c: brtrue.s IL_007d + IL_003e: ldarg.0 + IL_003f: ldc.i4.0 + IL_0040: dup + IL_0041: stloc.0 + IL_0042: stfld "int Test.d__1.<>1__state" + IL_0047: ldarg.0 + IL_0048: ldloc.3 + IL_0049: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_004e: ldarg.0 + IL_004f: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Test.d__1.<>t__builder" + IL_0054: ldloca.s V_3 + IL_0056: ldarg.0 + IL_0057: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_005c: leave IL_0160 + IL_0061: ldarg.0 + IL_0062: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_0067: stloc.3 + IL_0068: ldarg.0 + IL_0069: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_006e: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0074: ldarg.0 + IL_0075: ldc.i4.m1 + IL_0076: dup + IL_0077: stloc.0 + IL_0078: stfld "int Test.d__1.<>1__state" + IL_007d: ldloca.s V_3 + IL_007f: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0084: stloc.2 + IL_0085: ldarg.0 + IL_0086: ldloc.2 + IL_0087: stfld "int Test.d__1.5__2" + IL_008c: ldarg.0 + IL_008d: ldflda "int Test.d__1.5__2" + IL_0092: call "string int.ToString()" + IL_0097: newobj "System.Exception..ctor(string)" + IL_009c: throw + } + catch object + { + IL_009d: stloc.s V_4 + IL_009f: ldarg.0 + IL_00a0: ldloc.s V_4 + IL_00a2: stfld "object Test.d__1.<>7__wrap2" + IL_00a7: leave.s IL_00a9 + } + IL_00a9: ldarg.0 + IL_00aa: ldarg.0 + IL_00ab: ldfld "int Test.d__1.5__2" + IL_00b0: stfld "int Test.d__1.<>7__wrap4" + IL_00b5: call "System.Threading.Tasks.Task Test.F()" + IL_00ba: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_00bf: stloc.3 + IL_00c0: ldloca.s V_3 + IL_00c2: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00c7: brtrue.s IL_0105 + IL_00c9: ldarg.0 + IL_00ca: ldc.i4.1 + IL_00cb: dup + IL_00cc: stloc.0 + IL_00cd: stfld "int Test.d__1.<>1__state" + IL_00d2: ldarg.0 + IL_00d3: ldloc.3 + IL_00d4: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00d9: ldarg.0 + IL_00da: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Test.d__1.<>t__builder" + IL_00df: ldloca.s V_3 + IL_00e1: ldarg.0 + IL_00e2: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_00e7: leave.s IL_0160 + IL_00e9: ldarg.0 + IL_00ea: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00ef: stloc.3 + IL_00f0: ldarg.0 + IL_00f1: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00f6: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00fc: ldarg.0 + IL_00fd: ldc.i4.m1 + IL_00fe: dup + IL_00ff: stloc.0 + IL_0100: stfld "int Test.d__1.<>1__state" + IL_0105: ldloca.s V_3 + IL_0107: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_010c: stloc.2 + IL_010d: ldarg.0 + IL_010e: ldarg.0 + IL_010f: ldfld "int Test.d__1.<>7__wrap4" + IL_0114: ldloc.2 + IL_0115: add + IL_0116: stfld "int Test.d__1.5__2" + IL_011b: ldarg.0 + IL_011c: ldfld "object Test.d__1.<>7__wrap2" + IL_0121: stloc.s V_4 + IL_0123: ldloc.s V_4 + IL_0125: brfalse.s IL_013e + IL_0127: ldloc.s V_4 + IL_0129: isinst "System.Exception" + IL_012e: dup + IL_012f: brtrue.s IL_0134 + IL_0131: ldloc.s V_4 + IL_0133: throw + IL_0134: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0139: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_013e: ldarg.0 + IL_013f: ldnull + IL_0140: stfld "object Test.d__1.<>7__wrap2" + IL_0145: ldnull + IL_0146: throw + } + catch System.Exception + { + IL_0147: stloc.s V_5 + IL_0149: ldarg.0 + IL_014a: ldc.i4.s -2 + IL_014c: stfld "int Test.d__1.<>1__state" + IL_0151: ldarg.0 + IL_0152: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Test.d__1.<>t__builder" + IL_0157: ldloc.s V_5 + IL_0159: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_015e: leave.s IL_0160 + } + IL_0160: ret + } + """); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (6,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(6, 28) + ); + verifier.VerifyIL("Test.G()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (int V_0, //x + object V_1, + int V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldnull + IL_0003: stloc.1 + .try + { + IL_0004: call "System.Threading.Tasks.Task Test.F()" + IL_0009: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000e: stloc.0 + IL_000f: ldloca.s V_0 + IL_0011: call "string int.ToString()" + IL_0016: newobj "System.Exception..ctor(string)" + IL_001b: throw + } + catch object + { + IL_001c: stloc.1 + IL_001d: leave.s IL_001f + } + IL_001f: ldloc.0 + IL_0020: call "System.Threading.Tasks.Task Test.F()" + IL_0025: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002a: stloc.2 + IL_002b: ldloc.2 + IL_002c: add + IL_002d: stloc.0 + IL_002e: ldloc.1 + IL_002f: brfalse.s IL_0046 + IL_0031: ldloc.1 + IL_0032: isinst "System.Exception" + IL_0037: dup + IL_0038: brtrue.s IL_003c + IL_003a: ldloc.1 + IL_003b: throw + IL_003c: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0041: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0046: ldnull + IL_0047: throw + } + """); + } + + [Fact] + public void AsyncInFinally006_AsyncVoid_01() + { + var source = """ + using System; + using System.Threading; + using System.Threading.Tasks; + class Test + { + static async Task F() + { + return 2; + } + static async void G(SemaphoreSlim semaphore) + { + int x = 0; + try + { + x = await F(); + } + finally + { + x += await F(); + Console.WriteLine(x); + semaphore.Release(); + } + } + public static void Main() + { + System.Globalization.CultureInfo saveUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; + System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; + try + { + var semaphore = new SemaphoreSlim(0, 1); + G(semaphore); + semaphore.Wait(1000 * 60); + } + finally + { + System.Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture; + } + } + } + """; + var expected = "4"; + var verifier = CompileAndVerify(source, expectedOutput: expected); + verifier.VerifyIL("Test.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 377 (0x179) + .maxstack 3 + .locals init (int V_0, + int V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + object V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int Test.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0026 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00db + IL_0011: ldarg.0 + IL_0012: ldc.i4.0 + IL_0013: stfld "int Test.d__1.5__2" + IL_0018: ldarg.0 + IL_0019: ldnull + IL_001a: stfld "object Test.d__1.<>7__wrap2" + IL_001f: ldarg.0 + IL_0020: ldc.i4.0 + IL_0021: stfld "int Test.d__1.<>7__wrap3" + IL_0026: nop + .try + { + IL_0027: ldloc.0 + IL_0028: brfalse.s IL_0061 + IL_002a: call "System.Threading.Tasks.Task Test.F()" + IL_002f: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0034: stloc.2 + IL_0035: ldloca.s V_2 + IL_0037: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_003c: brtrue.s IL_007d + IL_003e: ldarg.0 + IL_003f: ldc.i4.0 + IL_0040: dup + IL_0041: stloc.0 + IL_0042: stfld "int Test.d__1.<>1__state" + IL_0047: ldarg.0 + IL_0048: ldloc.2 + IL_0049: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_004e: ldarg.0 + IL_004f: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0054: ldloca.s V_2 + IL_0056: ldarg.0 + IL_0057: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_005c: leave IL_0178 + IL_0061: ldarg.0 + IL_0062: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_0067: stloc.2 + IL_0068: ldarg.0 + IL_0069: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_006e: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0074: ldarg.0 + IL_0075: ldc.i4.m1 + IL_0076: dup + IL_0077: stloc.0 + IL_0078: stfld "int Test.d__1.<>1__state" + IL_007d: ldloca.s V_2 + IL_007f: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0084: stloc.1 + IL_0085: ldarg.0 + IL_0086: ldloc.1 + IL_0087: stfld "int Test.d__1.5__2" + IL_008c: leave.s IL_0098 + } + catch object + { + IL_008e: stloc.3 + IL_008f: ldarg.0 + IL_0090: ldloc.3 + IL_0091: stfld "object Test.d__1.<>7__wrap2" + IL_0096: leave.s IL_0098 + } + IL_0098: ldarg.0 + IL_0099: ldarg.0 + IL_009a: ldfld "int Test.d__1.5__2" + IL_009f: stfld "int Test.d__1.<>7__wrap4" + IL_00a4: call "System.Threading.Tasks.Task Test.F()" + IL_00a9: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_00ae: stloc.2 + IL_00af: ldloca.s V_2 + IL_00b1: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00b6: brtrue.s IL_00f7 + IL_00b8: ldarg.0 + IL_00b9: ldc.i4.1 + IL_00ba: dup + IL_00bb: stloc.0 + IL_00bc: stfld "int Test.d__1.<>1__state" + IL_00c1: ldarg.0 + IL_00c2: ldloc.2 + IL_00c3: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00c8: ldarg.0 + IL_00c9: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_00ce: ldloca.s V_2 + IL_00d0: ldarg.0 + IL_00d1: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_00d6: leave IL_0178 + IL_00db: ldarg.0 + IL_00dc: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00e1: stloc.2 + IL_00e2: ldarg.0 + IL_00e3: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00e8: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00ee: ldarg.0 + IL_00ef: ldc.i4.m1 + IL_00f0: dup + IL_00f1: stloc.0 + IL_00f2: stfld "int Test.d__1.<>1__state" + IL_00f7: ldloca.s V_2 + IL_00f9: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00fe: stloc.1 + IL_00ff: ldarg.0 + IL_0100: ldarg.0 + IL_0101: ldfld "int Test.d__1.<>7__wrap4" + IL_0106: ldloc.1 + IL_0107: add + IL_0108: stfld "int Test.d__1.5__2" + IL_010d: ldarg.0 + IL_010e: ldfld "int Test.d__1.5__2" + IL_0113: call "void System.Console.WriteLine(int)" + IL_0118: ldarg.0 + IL_0119: ldfld "System.Threading.SemaphoreSlim Test.d__1.semaphore" + IL_011e: callvirt "int System.Threading.SemaphoreSlim.Release()" + IL_0123: pop + IL_0124: ldarg.0 + IL_0125: ldfld "object Test.d__1.<>7__wrap2" + IL_012a: stloc.3 + IL_012b: ldloc.3 + IL_012c: brfalse.s IL_0143 + IL_012e: ldloc.3 + IL_012f: isinst "System.Exception" + IL_0134: dup + IL_0135: brtrue.s IL_0139 + IL_0137: ldloc.3 + IL_0138: throw + IL_0139: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_013e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0143: ldarg.0 + IL_0144: ldnull + IL_0145: stfld "object Test.d__1.<>7__wrap2" + IL_014a: leave.s IL_0165 + } + catch System.Exception + { + IL_014c: stloc.s V_4 + IL_014e: ldarg.0 + IL_014f: ldc.i4.s -2 + IL_0151: stfld "int Test.d__1.<>1__state" + IL_0156: ldarg.0 + IL_0157: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_015c: ldloc.s V_4 + IL_015e: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException(System.Exception)" + IL_0163: leave.s IL_0178 + } + IL_0165: ldarg.0 + IL_0166: ldc.i4.s -2 + IL_0168: stfld "int Test.d__1.<>1__state" + IL_016d: ldarg.0 + IL_016e: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0173: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetResult()" + IL_0178: ret + } + """); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (6,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(6, 28) + ); + verifier.VerifyIL("Test.G(System.Threading.SemaphoreSlim)", """ + { + // Code size 43 (0x2b) + .maxstack 2 + .locals init (Test.d__1 V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncVoidMethodBuilder System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldarg.0 + IL_000f: stfld "System.Threading.SemaphoreSlim Test.d__1.semaphore" + IL_0014: ldloca.s V_0 + IL_0016: ldc.i4.m1 + IL_0017: stfld "int Test.d__1.<>1__state" + IL_001c: ldloca.s V_0 + IL_001e: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0023: ldloca.s V_0 + IL_0025: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Startd__1>(ref Test.d__1)" + IL_002a: ret + } + """); + } + + [Fact] + public void AsyncInFinally006_AsyncVoid_02() + { + var source = """ + using System; + using System.Threading; + using System.Threading.Tasks; + class Test + { + static async Task F() + { + return 2; + } + static async void G(SemaphoreSlim semaphore) + { + int x = 0; + try + { + x = await F(); + } + finally + { + x += await F(); + } + + Console.WriteLine(x); + semaphore.Release(); + } + public static void Main() + { + System.Globalization.CultureInfo saveUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; + System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; + try + { + var semaphore = new SemaphoreSlim(0, 1); + G(semaphore); + semaphore.Wait(1000 * 60); + } + finally + { + System.Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture; + } + } + } + """; + var expected = "4"; + var verifier = CompileAndVerify(source, expectedOutput: expected); + verifier.VerifyIL("Test.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 377 (0x179) + .maxstack 3 + .locals init (int V_0, + int V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + object V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int Test.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0026 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00db + IL_0011: ldarg.0 + IL_0012: ldc.i4.0 + IL_0013: stfld "int Test.d__1.5__2" + IL_0018: ldarg.0 + IL_0019: ldnull + IL_001a: stfld "object Test.d__1.<>7__wrap2" + IL_001f: ldarg.0 + IL_0020: ldc.i4.0 + IL_0021: stfld "int Test.d__1.<>7__wrap3" + IL_0026: nop + .try + { + IL_0027: ldloc.0 + IL_0028: brfalse.s IL_0061 + IL_002a: call "System.Threading.Tasks.Task Test.F()" + IL_002f: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0034: stloc.2 + IL_0035: ldloca.s V_2 + IL_0037: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_003c: brtrue.s IL_007d + IL_003e: ldarg.0 + IL_003f: ldc.i4.0 + IL_0040: dup + IL_0041: stloc.0 + IL_0042: stfld "int Test.d__1.<>1__state" + IL_0047: ldarg.0 + IL_0048: ldloc.2 + IL_0049: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_004e: ldarg.0 + IL_004f: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0054: ldloca.s V_2 + IL_0056: ldarg.0 + IL_0057: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_005c: leave IL_0178 + IL_0061: ldarg.0 + IL_0062: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_0067: stloc.2 + IL_0068: ldarg.0 + IL_0069: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_006e: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0074: ldarg.0 + IL_0075: ldc.i4.m1 + IL_0076: dup + IL_0077: stloc.0 + IL_0078: stfld "int Test.d__1.<>1__state" + IL_007d: ldloca.s V_2 + IL_007f: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0084: stloc.1 + IL_0085: ldarg.0 + IL_0086: ldloc.1 + IL_0087: stfld "int Test.d__1.5__2" + IL_008c: leave.s IL_0098 + } + catch object + { + IL_008e: stloc.3 + IL_008f: ldarg.0 + IL_0090: ldloc.3 + IL_0091: stfld "object Test.d__1.<>7__wrap2" + IL_0096: leave.s IL_0098 + } + IL_0098: ldarg.0 + IL_0099: ldarg.0 + IL_009a: ldfld "int Test.d__1.5__2" + IL_009f: stfld "int Test.d__1.<>7__wrap4" + IL_00a4: call "System.Threading.Tasks.Task Test.F()" + IL_00a9: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_00ae: stloc.2 + IL_00af: ldloca.s V_2 + IL_00b1: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00b6: brtrue.s IL_00f7 + IL_00b8: ldarg.0 + IL_00b9: ldc.i4.1 + IL_00ba: dup + IL_00bb: stloc.0 + IL_00bc: stfld "int Test.d__1.<>1__state" + IL_00c1: ldarg.0 + IL_00c2: ldloc.2 + IL_00c3: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00c8: ldarg.0 + IL_00c9: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_00ce: ldloca.s V_2 + IL_00d0: ldarg.0 + IL_00d1: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_00d6: leave IL_0178 + IL_00db: ldarg.0 + IL_00dc: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00e1: stloc.2 + IL_00e2: ldarg.0 + IL_00e3: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00e8: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00ee: ldarg.0 + IL_00ef: ldc.i4.m1 + IL_00f0: dup + IL_00f1: stloc.0 + IL_00f2: stfld "int Test.d__1.<>1__state" + IL_00f7: ldloca.s V_2 + IL_00f9: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00fe: stloc.1 + IL_00ff: ldarg.0 + IL_0100: ldarg.0 + IL_0101: ldfld "int Test.d__1.<>7__wrap4" + IL_0106: ldloc.1 + IL_0107: add + IL_0108: stfld "int Test.d__1.5__2" + IL_010d: ldarg.0 + IL_010e: ldfld "object Test.d__1.<>7__wrap2" + IL_0113: stloc.3 + IL_0114: ldloc.3 + IL_0115: brfalse.s IL_012c + IL_0117: ldloc.3 + IL_0118: isinst "System.Exception" + IL_011d: dup + IL_011e: brtrue.s IL_0122 + IL_0120: ldloc.3 + IL_0121: throw + IL_0122: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0127: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_012c: ldarg.0 + IL_012d: ldnull + IL_012e: stfld "object Test.d__1.<>7__wrap2" + IL_0133: ldarg.0 + IL_0134: ldfld "int Test.d__1.5__2" + IL_0139: call "void System.Console.WriteLine(int)" + IL_013e: ldarg.0 + IL_013f: ldfld "System.Threading.SemaphoreSlim Test.d__1.semaphore" + IL_0144: callvirt "int System.Threading.SemaphoreSlim.Release()" + IL_0149: pop + IL_014a: leave.s IL_0165 + } + catch System.Exception + { + IL_014c: stloc.s V_4 + IL_014e: ldarg.0 + IL_014f: ldc.i4.s -2 + IL_0151: stfld "int Test.d__1.<>1__state" + IL_0156: ldarg.0 + IL_0157: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_015c: ldloc.s V_4 + IL_015e: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException(System.Exception)" + IL_0163: leave.s IL_0178 + } + IL_0165: ldarg.0 + IL_0166: ldc.i4.s -2 + IL_0168: stfld "int Test.d__1.<>1__state" + IL_016d: ldarg.0 + IL_016e: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0173: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetResult()" + IL_0178: ret + } + """); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (6,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(6, 28) + ); + verifier.VerifyIL("Test.G(System.Threading.SemaphoreSlim)", """ + { + // Code size 43 (0x2b) + .maxstack 2 + .locals init (Test.d__1 V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncVoidMethodBuilder System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldarg.0 + IL_000f: stfld "System.Threading.SemaphoreSlim Test.d__1.semaphore" + IL_0014: ldloca.s V_0 + IL_0016: ldc.i4.m1 + IL_0017: stfld "int Test.d__1.<>1__state" + IL_001c: ldloca.s V_0 + IL_001e: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0023: ldloca.s V_0 + IL_0025: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Startd__1>(ref Test.d__1)" + IL_002a: ret + } + """); + } + + [Fact] + public void AsyncInFinallyWithGotos() + { + var source = """ + using System; + using System.Threading.Tasks; + class Test + { + static async Task F() + { + return 2; + } + static async Task G() + { + int x = 0; + bool loop = true; + goto afterLabel; + label: + loop = false; + afterLabel: + try + { + x = await F(); + } + finally + { + x += await F(); + } + if (loop) + { + goto label; + } + return x; + } + public static void Main() + { + Task t2 = G(); + t2.Wait(1000 * 60); + Console.WriteLine(t2.Result); + } + } + """; + + var expected = "4"; + CompileAndVerify(source, expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x45, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (5,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(5, 28) + ); + verifier.VerifyIL("Test.G()", """ + { + // Code size 70 (0x46) + .maxstack 2 + .locals init (int V_0, //x + bool V_1, //loop + object V_2, + int V_3) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.1 + IL_0003: stloc.1 + IL_0004: br.s IL_0008 + IL_0006: ldc.i4.0 + IL_0007: stloc.1 + IL_0008: ldnull + IL_0009: stloc.2 + .try + { + IL_000a: call "System.Threading.Tasks.Task Test.F()" + IL_000f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0014: stloc.0 + IL_0015: leave.s IL_001a + } + catch object + { + IL_0017: stloc.2 + IL_0018: leave.s IL_001a + } + IL_001a: ldloc.0 + IL_001b: call "System.Threading.Tasks.Task Test.F()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.3 + IL_0026: ldloc.3 + IL_0027: add + IL_0028: stloc.0 + IL_0029: ldloc.2 + IL_002a: brfalse.s IL_0041 + IL_002c: ldloc.2 + IL_002d: isinst "System.Exception" + IL_0032: dup + IL_0033: brtrue.s IL_0037 + IL_0035: ldloc.2 + IL_0036: throw + IL_0037: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0041: ldloc.1 + IL_0042: brtrue.s IL_0006 + IL_0044: ldloc.0 + IL_0045: ret + } + """); } [Fact] @@ -1302,6 +2503,24 @@ public static void Main() }"; var expected = @"15"; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0xc1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (23,17): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(23, 17), + // (42,9): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(42, 9) + ); } [Fact] @@ -1372,6 +2591,27 @@ public static void Main() var expected = @"hello 15"; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0xc7, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (25,21): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(25, 21), + // (32,17): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(32, 17), + // (46,13): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(46, 13) + ); } [Fact] @@ -1443,6 +2683,27 @@ public static void Main() var expected = @"bye 15"; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x96, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (25,21): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(25, 21), + // (32,17): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(32, 17), + // (47,13): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(47, 13) + ); } [Fact] @@ -1585,6 +2846,59 @@ .locals init (int V_0, IL_00a9: ret } "); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify( + comp, + expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x1f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); + + verifier.VerifyIL("Test.G()", """ + { + // Code size 32 (0x20) + .maxstack 2 + .locals init (int V_0, //x + int V_1) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: stloc.1 + .try + { + IL_0004: ldloc.0 + IL_0005: ldloc.0 + IL_0006: div + IL_0007: stloc.0 + IL_0008: leave.s IL_000f + } + catch object + { + IL_000a: pop + IL_000b: ldc.i4.1 + IL_000c: stloc.1 + IL_000d: leave.s IL_000f + } + IL_000f: ldloc.1 + IL_0010: ldc.i4.1 + IL_0011: bne.un.s IL_001e + IL_0013: call "System.Threading.Tasks.Task Test.F()" + IL_0018: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001d: stloc.0 + IL_001e: ldloc.0 + IL_001f: ret + } + """); } [Fact] @@ -1649,6 +2963,17 @@ Attempted to divide by zero. 2 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x5f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [WorkItem(74, "https://github.com/dotnet/roslyn/issues/1334")] @@ -1718,6 +3043,17 @@ Attempted to divide by zero. 2 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x58, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [WorkItem(74, "https://github.com/dotnet/roslyn/issues/1334")] @@ -1792,6 +3128,17 @@ Attempted to divide by zero. 4 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x9d, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact] @@ -1846,6 +3193,130 @@ public static void Main() 2 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0xa4, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Test.G", """ + { + // Code size 165 (0xa5) + .maxstack 2 + .locals init (int V_0, //x + int V_1, + System.Exception V_2, //ex + object V_3, + int V_4, + object V_5, + System.Exception V_6) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: stloc.1 + .try + { + IL_0004: ldc.i4.0 + IL_0005: stloc.s V_4 + .try + { + IL_0007: ldloc.0 + IL_0008: ldloc.0 + IL_0009: div + IL_000a: stloc.0 + IL_000b: leave.s IL_002d + } + filter + { + IL_000d: isinst "object" + IL_0012: dup + IL_0013: brtrue.s IL_0019 + IL_0015: pop + IL_0016: ldc.i4.0 + IL_0017: br.s IL_0025 + IL_0019: stloc.s V_5 + IL_001b: ldloc.s V_5 + IL_001d: stloc.3 + IL_001e: ldloc.0 + IL_001f: ldc.i4.0 + IL_0020: cgt.un + IL_0022: ldc.i4.0 + IL_0023: cgt.un + IL_0025: endfilter + } // end filter + { // handler + IL_0027: pop + IL_0028: ldc.i4.1 + IL_0029: stloc.s V_4 + IL_002b: leave.s IL_002d + } + IL_002d: ldloc.s V_4 + IL_002f: ldc.i4.1 + IL_0030: bne.un.s IL_0052 + IL_0032: call "System.Threading.Tasks.Task Test.F()" + IL_0037: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003c: stloc.0 + IL_003d: ldloc.3 + IL_003e: isinst "System.Exception" + IL_0043: dup + IL_0044: brtrue.s IL_0048 + IL_0046: ldloc.3 + IL_0047: throw + IL_0048: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0052: leave.s IL_0089 + } + filter + { + IL_0054: isinst "System.Exception" + IL_0059: dup + IL_005a: brtrue.s IL_0060 + IL_005c: pop + IL_005d: ldc.i4.0 + IL_005e: br.s IL_0082 + IL_0060: stloc.s V_6 + IL_0062: ldloc.s V_6 + IL_0064: castclass "System.Exception" + IL_0069: stloc.2 + IL_006a: ldloc.0 + IL_006b: brtrue.s IL_007e + IL_006d: ldstr "hello" + IL_0072: newobj "System.Exception..ctor(string)" + IL_0077: dup + IL_0078: stloc.2 + IL_0079: ldnull + IL_007a: cgt.un + IL_007c: br.s IL_007f + IL_007e: ldc.i4.0 + IL_007f: ldc.i4.0 + IL_0080: cgt.un + IL_0082: endfilter + } // end filter + { // handler + IL_0084: pop + IL_0085: ldc.i4.1 + IL_0086: stloc.1 + IL_0087: leave.s IL_0089 + } + IL_0089: ldloc.1 + IL_008a: ldc.i4.1 + IL_008b: bne.un.s IL_00a3 + IL_008d: call "System.Threading.Tasks.Task Test.F()" + IL_0092: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0097: stloc.0 + IL_0098: ldloc.2 + IL_0099: callvirt "string System.Exception.Message.get" + IL_009e: call "void System.Console.WriteLine(string)" + IL_00a3: ldloc.0 + IL_00a4: ret + } + """); } [Fact] @@ -1902,6 +3373,17 @@ public static void Main() 2 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0xcf, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact] @@ -1983,6 +3465,17 @@ public static void Main() 42 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x16e, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact] @@ -2067,6 +3560,17 @@ public static void Main() 42 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [b__0]: Unexpected type on the stack. { Offset = 0x1a9, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact] @@ -2103,6 +3607,18 @@ static void Main() 1 "; CompileAndVerify(source, expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify( + comp, + expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Return value missing on the stack. { Offset = 0x3d } + """ + }); + verifier.VerifyDiagnostics(); } [Fact, WorkItem(67091, "https://github.com/dotnet/roslyn/issues/67091")] @@ -2169,6 +3685,20 @@ async Task M2(string caller, Exception ex) CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2a } + [M1]: Return value missing on the stack. { Offset = 0x6d } + [M1]: Return value missing on the stack. { Offset = 0xaa } + [M1]: Return value missing on the stack. { Offset = 0x7f } + [M2]: Return value missing on the stack. { Offset = 0x3f } + """ + }); + verifier.VerifyDiagnostics(); } [Fact, WorkItem(67091, "https://github.com/dotnet/roslyn/issues/67091")] @@ -2266,6 +3796,22 @@ async Task M2(string caller, Exception ex) CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa3 } + [M1]: Return value missing on the stack. { Offset = 0x6d } + [M1]: Return value missing on the stack. { Offset = 0xf8 } + [M1]: Return value missing on the stack. { Offset = 0x159 } + [M1]: Return value missing on the stack. { Offset = 0x11c } + [M1]: Return value missing on the stack. { Offset = 0x7f } + [M2]: Return value missing on the stack. { Offset = 0x3f } + """ + }); + verifier.VerifyDiagnostics(); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/70483")] @@ -2334,6 +3880,24 @@ async Task M2(string caller, Exception ex) CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var ilVerifyMessage = awaitInTry2 + ? """ + [Main]: Return value missing on the stack. { Offset = 0x2a } + [M1]: Return value missing on the stack. { Offset = 0x6b } + [M1]: Return value missing on the stack. { Offset = 0xc1 } + [M1]: Return value missing on the stack. { Offset = 0x7d } + [M2]: Return value missing on the stack. { Offset = 0x3f } + """ + : """ + [Main]: Return value missing on the stack. { Offset = 0x2a } + [M1]: Return value missing on the stack. { Offset = 0x88 } + [M2]: Return value missing on the stack. { Offset = 0x3f } + """; + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = ilVerifyMessage }); + verifier.VerifyDiagnostics(); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/71569")] @@ -2414,6 +3978,34 @@ class Exception2 : Exception { } targetFramework: TargetFramework.Mscorlib46).VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput, targetFramework: TargetFramework.Mscorlib46).VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var ilVerifyMessage = (await1, await2) switch + { + (true, true) => """ + [
$]: Return value missing on the stack. { Offset = 0x3b } + [<
$>g__Test1|0_0]: Return value missing on the stack. { Offset = 0x67 } + [<
$>g__Test2|0_1]: Return value missing on the stack. { Offset = 0x59 } + """, + (false, true) => """ + [
$]: Return value missing on the stack. { Offset = 0x3b } + [<
$>g__Test1|0_0]: Return value missing on the stack. { Offset = 0x26 } + [<
$>g__Test2|0_1]: Return value missing on the stack. { Offset = 0x59 } + """, + (true, false) => """ + [
$]: Return value missing on the stack. { Offset = 0x3b } + [<
$>g__Test1|0_0]: Return value missing on the stack. { Offset = 0x67 } + [<
$>g__Test2|0_1]: Return value missing on the stack. { Offset = 0x24 } + """, + (false, false) => """ + [
$]: Return value missing on the stack. { Offset = 0x3b } + [<
$>g__Test1|0_0]: Return value missing on the stack. { Offset = 0x26 } + [<
$>g__Test2|0_1]: Return value missing on the stack. { Offset = 0x24 } + """ + }; + + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = ilVerifyMessage }); + verifier.VerifyDiagnostics(); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/71569")] @@ -2471,6 +4063,650 @@ class Exception3 : Exception { } CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = "[
$]: Return value missing on the stack. { Offset = 0x1d }" + }); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<
$>g__Run|0_0()", getIL()); + + string getIL() => (await1, await2, await3) switch + { + (false, false, false) => """ + { + // Code size 23 (0x17) + .maxstack 1 + .try + { + IL_0000: newobj "Exception1..ctor()" + IL_0005: throw + } + catch Exception1 + { + IL_0006: pop + .try + { + IL_0007: newobj "Exception2..ctor()" + IL_000c: throw + } + catch Exception2 + { + IL_000d: pop + .try + { + IL_000e: newobj "Exception3..ctor()" + IL_0013: throw + } + catch Exception3 + { + IL_0014: pop + IL_0015: rethrow + } + } + } + } + """, + (true, false, false) => """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un.s IL_0046 + IL_0011: nop + .try + { + IL_0012: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0017: stloc.2 + IL_0018: ldloca.s V_2 + IL_001a: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_001f: stloc.1 + IL_0020: ldloca.s V_1 + IL_0022: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0027: brtrue.s IL_002f + IL_0029: ldloc.1 + IL_002a: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_002f: ldloca.s V_1 + IL_0031: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0036: newobj "Exception2..ctor()" + IL_003b: throw + } + catch Exception2 + { + IL_003c: pop + .try + { + IL_003d: newobj "Exception3..ctor()" + IL_0042: throw + } + catch Exception3 + { + IL_0043: pop + IL_0044: rethrow + } + } + IL_0046: ldnull + IL_0047: throw + } + """, + (false, true, false) => """ + { + // Code size 82 (0x52) + .maxstack 2 + .locals init (int V_0, + int V_1, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, + System.Runtime.CompilerServices.YieldAwaitable V_3) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un.s IL_0050 + IL_0011: ldc.i4.0 + IL_0012: stloc.1 + .try + { + IL_0013: newobj "Exception2..ctor()" + IL_0018: throw + } + catch Exception2 + { + IL_0019: pop + IL_001a: ldc.i4.1 + IL_001b: stloc.1 + IL_001c: leave.s IL_001e + } + IL_001e: ldloc.1 + IL_001f: ldc.i4.1 + IL_0020: bne.un.s IL_0050 + IL_0022: nop + .try + { + IL_0023: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0028: stloc.3 + IL_0029: ldloca.s V_3 + IL_002b: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0030: stloc.2 + IL_0031: ldloca.s V_2 + IL_0033: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0038: brtrue.s IL_0040 + IL_003a: ldloc.2 + IL_003b: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0040: ldloca.s V_2 + IL_0042: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0047: newobj "Exception3..ctor()" + IL_004c: throw + } + catch Exception3 + { + IL_004d: pop + IL_004e: rethrow + } + IL_0050: ldnull + IL_0051: throw + } + """, + (false, false, true) => """ + { + // Code size 113 (0x71) + .maxstack 2 + .locals init (int V_0, + int V_1, + object V_2, + int V_3, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_4, + System.Runtime.CompilerServices.YieldAwaitable V_5) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un.s IL_006f + IL_0011: ldc.i4.0 + IL_0012: stloc.1 + .try + { + IL_0013: newobj "Exception2..ctor()" + IL_0018: throw + } + catch Exception2 + { + IL_0019: pop + IL_001a: ldc.i4.1 + IL_001b: stloc.1 + IL_001c: leave.s IL_001e + } + IL_001e: ldloc.1 + IL_001f: ldc.i4.1 + IL_0020: bne.un.s IL_006f + IL_0022: ldc.i4.0 + IL_0023: stloc.3 + .try + { + IL_0024: newobj "Exception3..ctor()" + IL_0029: throw + } + catch Exception3 + { + IL_002a: stloc.2 + IL_002b: ldc.i4.1 + IL_002c: stloc.3 + IL_002d: leave.s IL_002f + } + IL_002f: ldloc.3 + IL_0030: ldc.i4.1 + IL_0031: bne.un.s IL_006f + IL_0033: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0038: stloc.s V_5 + IL_003a: ldloca.s V_5 + IL_003c: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0041: stloc.s V_4 + IL_0043: ldloca.s V_4 + IL_0045: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_004a: brtrue.s IL_0053 + IL_004c: ldloc.s V_4 + IL_004e: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0053: ldloca.s V_4 + IL_0055: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_005a: ldloc.2 + IL_005b: isinst "System.Exception" + IL_0060: dup + IL_0061: brtrue.s IL_0065 + IL_0063: ldloc.2 + IL_0064: throw + IL_0065: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_006a: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_006f: ldnull + IL_0070: throw + } + """, + (true, true, false) => """ + { + // Code size 118 (0x76) + .maxstack 2 + .locals init (int V_0, + int V_1, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, + System.Runtime.CompilerServices.YieldAwaitable V_3) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un.s IL_0074 + IL_0011: ldc.i4.0 + IL_0012: stloc.1 + .try + { + IL_0013: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0018: stloc.3 + IL_0019: ldloca.s V_3 + IL_001b: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0020: stloc.2 + IL_0021: ldloca.s V_2 + IL_0023: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0028: brtrue.s IL_0030 + IL_002a: ldloc.2 + IL_002b: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0030: ldloca.s V_2 + IL_0032: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0037: newobj "Exception2..ctor()" + IL_003c: throw + } + catch Exception2 + { + IL_003d: pop + IL_003e: ldc.i4.1 + IL_003f: stloc.1 + IL_0040: leave.s IL_0042 + } + IL_0042: ldloc.1 + IL_0043: ldc.i4.1 + IL_0044: bne.un.s IL_0074 + IL_0046: nop + .try + { + IL_0047: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_004c: stloc.3 + IL_004d: ldloca.s V_3 + IL_004f: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0054: stloc.2 + IL_0055: ldloca.s V_2 + IL_0057: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_005c: brtrue.s IL_0064 + IL_005e: ldloc.2 + IL_005f: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0064: ldloca.s V_2 + IL_0066: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_006b: newobj "Exception3..ctor()" + IL_0070: throw + } + catch Exception3 + { + IL_0071: pop + IL_0072: rethrow + } + IL_0074: ldnull + IL_0075: throw + } + """, + (true, false, true) => """ + { + // Code size 155 (0x9b) + .maxstack 2 + .locals init (int V_0, + int V_1, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, + System.Runtime.CompilerServices.YieldAwaitable V_3, + object V_4, + int V_5) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un IL_0099 + IL_0014: ldc.i4.0 + IL_0015: stloc.1 + .try + { + IL_0016: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_001b: stloc.3 + IL_001c: ldloca.s V_3 + IL_001e: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0023: stloc.2 + IL_0024: ldloca.s V_2 + IL_0026: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_002b: brtrue.s IL_0033 + IL_002d: ldloc.2 + IL_002e: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0033: ldloca.s V_2 + IL_0035: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_003a: newobj "Exception2..ctor()" + IL_003f: throw + } + catch Exception2 + { + IL_0040: pop + IL_0041: ldc.i4.1 + IL_0042: stloc.1 + IL_0043: leave.s IL_0045 + } + IL_0045: ldloc.1 + IL_0046: ldc.i4.1 + IL_0047: bne.un.s IL_0099 + IL_0049: ldc.i4.0 + IL_004a: stloc.s V_5 + .try + { + IL_004c: newobj "Exception3..ctor()" + IL_0051: throw + } + catch Exception3 + { + IL_0052: stloc.s V_4 + IL_0054: ldc.i4.1 + IL_0055: stloc.s V_5 + IL_0057: leave.s IL_0059 + } + IL_0059: ldloc.s V_5 + IL_005b: ldc.i4.1 + IL_005c: bne.un.s IL_0099 + IL_005e: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0063: stloc.3 + IL_0064: ldloca.s V_3 + IL_0066: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_006b: stloc.2 + IL_006c: ldloca.s V_2 + IL_006e: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0073: brtrue.s IL_007b + IL_0075: ldloc.2 + IL_0076: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_007b: ldloca.s V_2 + IL_007d: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0082: ldloc.s V_4 + IL_0084: isinst "System.Exception" + IL_0089: dup + IL_008a: brtrue.s IL_008f + IL_008c: ldloc.s V_4 + IL_008e: throw + IL_008f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0094: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0099: ldnull + IL_009a: throw + } + """, + (false, true, true) => """ + { + // Code size 155 (0x9b) + .maxstack 2 + .locals init (int V_0, + int V_1, + object V_2, + int V_3, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_4, + System.Runtime.CompilerServices.YieldAwaitable V_5) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un IL_0099 + IL_0014: ldc.i4.0 + IL_0015: stloc.1 + .try + { + IL_0016: newobj "Exception2..ctor()" + IL_001b: throw + } + catch Exception2 + { + IL_001c: pop + IL_001d: ldc.i4.1 + IL_001e: stloc.1 + IL_001f: leave.s IL_0021 + } + IL_0021: ldloc.1 + IL_0022: ldc.i4.1 + IL_0023: bne.un.s IL_0099 + IL_0025: ldc.i4.0 + IL_0026: stloc.3 + .try + { + IL_0027: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_002c: stloc.s V_5 + IL_002e: ldloca.s V_5 + IL_0030: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0035: stloc.s V_4 + IL_0037: ldloca.s V_4 + IL_0039: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_003e: brtrue.s IL_0047 + IL_0040: ldloc.s V_4 + IL_0042: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0047: ldloca.s V_4 + IL_0049: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_004e: newobj "Exception3..ctor()" + IL_0053: throw + } + catch Exception3 + { + IL_0054: stloc.2 + IL_0055: ldc.i4.1 + IL_0056: stloc.3 + IL_0057: leave.s IL_0059 + } + IL_0059: ldloc.3 + IL_005a: ldc.i4.1 + IL_005b: bne.un.s IL_0099 + IL_005d: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0062: stloc.s V_5 + IL_0064: ldloca.s V_5 + IL_0066: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_006b: stloc.s V_4 + IL_006d: ldloca.s V_4 + IL_006f: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0074: brtrue.s IL_007d + IL_0076: ldloc.s V_4 + IL_0078: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_007d: ldloca.s V_4 + IL_007f: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0084: ldloc.2 + IL_0085: isinst "System.Exception" + IL_008a: dup + IL_008b: brtrue.s IL_008f + IL_008d: ldloc.2 + IL_008e: throw + IL_008f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0094: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0099: ldnull + IL_009a: throw + } + """, + (true, true, true) => """ + { + // Code size 191 (0xbf) + .maxstack 2 + .locals init (int V_0, + int V_1, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, + System.Runtime.CompilerServices.YieldAwaitable V_3, + object V_4, + int V_5) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un IL_00bd + IL_0014: ldc.i4.0 + IL_0015: stloc.1 + .try + { + IL_0016: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_001b: stloc.3 + IL_001c: ldloca.s V_3 + IL_001e: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0023: stloc.2 + IL_0024: ldloca.s V_2 + IL_0026: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_002b: brtrue.s IL_0033 + IL_002d: ldloc.2 + IL_002e: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0033: ldloca.s V_2 + IL_0035: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_003a: newobj "Exception2..ctor()" + IL_003f: throw + } + catch Exception2 + { + IL_0040: pop + IL_0041: ldc.i4.1 + IL_0042: stloc.1 + IL_0043: leave.s IL_0045 + } + IL_0045: ldloc.1 + IL_0046: ldc.i4.1 + IL_0047: bne.un.s IL_00bd + IL_0049: ldc.i4.0 + IL_004a: stloc.s V_5 + .try + { + IL_004c: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0051: stloc.3 + IL_0052: ldloca.s V_3 + IL_0054: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0059: stloc.2 + IL_005a: ldloca.s V_2 + IL_005c: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0061: brtrue.s IL_0069 + IL_0063: ldloc.2 + IL_0064: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0069: ldloca.s V_2 + IL_006b: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0070: newobj "Exception3..ctor()" + IL_0075: throw + } + catch Exception3 + { + IL_0076: stloc.s V_4 + IL_0078: ldc.i4.1 + IL_0079: stloc.s V_5 + IL_007b: leave.s IL_007d + } + IL_007d: ldloc.s V_5 + IL_007f: ldc.i4.1 + IL_0080: bne.un.s IL_00bd + IL_0082: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0087: stloc.3 + IL_0088: ldloca.s V_3 + IL_008a: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_008f: stloc.2 + IL_0090: ldloca.s V_2 + IL_0092: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0097: brtrue.s IL_009f + IL_0099: ldloc.2 + IL_009a: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_009f: ldloca.s V_2 + IL_00a1: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_00a6: ldloc.s V_4 + IL_00a8: isinst "System.Exception" + IL_00ad: dup + IL_00ae: brtrue.s IL_00b3 + IL_00b0: ldloc.s V_4 + IL_00b2: throw + IL_00b3: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00b8: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00bd: ldnull + IL_00be: throw + } + """, + }; } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/71569")] @@ -2542,6 +4778,247 @@ public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken ct) CompileAndVerify(CreateCompilationWithTasksExtensions(sources, options: TestOptions.DebugExe), expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(CreateCompilationWithTasksExtensions(sources, options: TestOptions.ReleaseExe), expectedOutput: expectedOutput).VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.<
$>g__Run|0_0()", getIL(statement)); + + static string getIL(string statement) + { + if (statement.Contains("using")) + { + return """ + { + // Code size 84 (0x54) + .maxstack 2 + .locals init (int V_0, + C V_1, //c + object V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un.s IL_0052 + IL_0011: newobj "C..ctor()" + IL_0016: stloc.1 + IL_0017: ldnull + IL_0018: stloc.2 + .try + { + .try + { + IL_0019: newobj "Exception2..ctor()" + IL_001e: throw + } + catch Exception2 + { + IL_001f: pop + .try + { + IL_0020: newobj "Exception3..ctor()" + IL_0025: throw + } + catch Exception3 + { + IL_0026: pop + IL_0027: rethrow + } + } + } + catch object + { + IL_0029: stloc.2 + IL_002a: leave.s IL_002c + } + IL_002c: ldloc.1 + IL_002d: brfalse.s IL_003a + IL_002f: ldloc.1 + IL_0030: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0035: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003a: ldloc.2 + IL_003b: brfalse.s IL_0052 + IL_003d: ldloc.2 + IL_003e: isinst "System.Exception" + IL_0043: dup + IL_0044: brtrue.s IL_0048 + IL_0046: ldloc.2 + IL_0047: throw + IL_0048: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0052: ldnull + IL_0053: throw + } + """; + } + else if (statement.Contains("foreach")) + { + return """ + { + // Code size 123 (0x7b) + .maxstack 2 + .locals init (int V_0, + System.Collections.Generic.IAsyncEnumerator V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un.s IL_0079 + IL_0011: newobj "C..ctor()" + IL_0016: ldloca.s V_2 + IL_0018: initobj "System.Threading.CancellationToken" + IL_001e: ldloc.2 + IL_001f: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0024: stloc.1 + IL_0025: ldnull + IL_0026: stloc.3 + .try + { + IL_0027: br.s IL_0030 + IL_0029: ldloc.1 + IL_002a: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_002f: pop + IL_0030: ldloc.1 + IL_0031: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0036: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003b: brtrue.s IL_0029 + IL_003d: leave.s IL_0042 + } + catch object + { + IL_003f: stloc.3 + IL_0040: leave.s IL_0042 + } + IL_0042: ldloc.1 + IL_0043: brfalse.s IL_0050 + IL_0045: ldloc.1 + IL_0046: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_004b: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0050: ldloc.3 + IL_0051: brfalse.s IL_0068 + IL_0053: ldloc.3 + IL_0054: isinst "System.Exception" + IL_0059: dup + IL_005a: brtrue.s IL_005e + IL_005c: ldloc.3 + IL_005d: throw + IL_005e: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0063: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0068: nop + .try + { + IL_0069: newobj "Exception2..ctor()" + IL_006e: throw + } + catch Exception2 + { + IL_006f: pop + .try + { + IL_0070: newobj "Exception3..ctor()" + IL_0075: throw + } + catch Exception3 + { + IL_0076: pop + IL_0077: rethrow + } + } + IL_0079: ldnull + IL_007a: throw + } + """; + } + else + { + return """ + { + // Code size 71 (0x47) + .maxstack 2 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un.s IL_0045 + IL_0011: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0016: stloc.2 + IL_0017: ldloca.s V_2 + IL_0019: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0026: brtrue.s IL_002e + IL_0028: ldloc.1 + IL_0029: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_002e: ldloca.s V_1 + IL_0030: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + .try + { + IL_0035: newobj "Exception2..ctor()" + IL_003a: throw + } + catch Exception2 + { + IL_003b: pop + .try + { + IL_003c: newobj "Exception3..ctor()" + IL_0041: throw + } + catch Exception3 + { + IL_0042: pop + IL_0043: rethrow + } + } + IL_0045: ldnull + IL_0046: throw + } + """; + } + } } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index e678c33d82008..6b49e879285a3 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -500,7 +500,7 @@ public async System.Threading.Tasks.Task GetTemperatureAsync() CompileAndVerify(comp); } - [ConditionalFact(typeof(DesktopOnly))] + [Fact] [WorkItem(30566, "https://github.com/dotnet/roslyn/issues/30566")] public void YieldReturnAwait1() { @@ -529,7 +529,7 @@ public static async Task Main(string[] args) 8"); } - [ConditionalFact(typeof(DesktopOnly))] + [Fact] [WorkItem(30566, "https://github.com/dotnet/roslyn/issues/30566")] public void YieldReturnAwait2() { @@ -2620,7 +2620,7 @@ static async System.Threading.Tasks.Task Main() CompileAndVerify(comp, expectedOutput: "Stream1:1 Finally Stream2:1 Stream2:2 Finally Done"); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void AsyncIteratorWithAwaitCompletedAndYield() { string source = @" @@ -3189,7 +3189,7 @@ .locals init (int V_0, } } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void AsyncIteratorWithAwaitCompletedAndYield_WithEnumeratorCancellation() { string source = @" @@ -3454,7 +3454,7 @@ .locals init (int V_0, }", sequencePoints: "C+d__0.MoveNext", source: source); } - [ConditionalTheory(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Theory] [InlineData("[EnumeratorCancellation] ", "")] [InlineData("", "[EnumeratorCancellation] ")] public void AsyncIteratorWithAwaitCompletedAndYield_WithEnumeratorCancellation_ExtendedPartialMethod(string definitionAttributes, string implementationAttributes) @@ -3722,7 +3722,7 @@ .locals init (int V_0, }", sequencePoints: "C+d__0.MoveNext", source: source); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void AsyncIteratorWithAwaitCompletedAndYield_WithEnumeratorCancellation_LocalFunction() { string source = @" @@ -3991,7 +3991,7 @@ .locals init (int V_0, }", sequencePoints: "C+<g__local|0_0>d.MoveNext", source: source); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void AsyncIteratorWithAwaitCompletedAndYield_WithEnumeratorCancellation_NoUsage() { string source = @" @@ -4459,7 +4459,7 @@ static async System.Threading.Tasks.Task Main() CompileAndVerify(comp, expectedOutput: "0 1 2 3 Done"); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 2 END DISPOSAL DONE")] [InlineData(10, "1 2 END DISPOSAL DONE")] @@ -4661,7 +4661,7 @@ static async System.Threading.Tasks.Task Main() CompileAndVerify(comp, expectedOutput: "0 1 2 3 4 Done"); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "0 DISPOSAL DONE")] [InlineData(2, "0 1 DISPOSAL Finally DONE")] [InlineData(3, "0 1 Finally 2 DISPOSAL DONE")] @@ -4696,7 +4696,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(2, "1 Break Throw Caught Finally END DISPOSAL DONE")] public void TryFinally_DisposeIAsyncEnumeratorMethod(int iterations, string expectedOutput) { @@ -4744,7 +4744,7 @@ public async System.Collections.Generic.IAsyncEnumerator GetAsyncEnumerator CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(2, "1 Break Throw Caught Finally END DISPOSAL DONE")] public void TryFinally_YieldBreakInDisposeMode(int iterations, string expectedOutput) { @@ -4782,10 +4782,219 @@ public static async System.Collections.Generic.IAsyncEnumerable M() }"; var comp = CreateCompilationWithAsyncIterator(new[] { Run(iterations), source }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: expectedOutput); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, + verify: Verification.FailsILVerify with + { + ILVerifyMessage = "[MoveNext]: Leave into try block. { Offset = 0x11a }" + }); + + verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ + { + // Code size 414 (0x19e) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.TaskAwaiter V_1, + C.d__0 V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -4 + IL_000a: sub + IL_000b: switch ( + IL_0026, + IL_0028, + IL_002c, + IL_002c, + IL_002a) + IL_0024: br.s IL_002c + IL_0026: br.s IL_0059 + IL_0028: br.s IL_002c + IL_002a: br.s IL_00aa + IL_002c: ldarg.0 + IL_002d: ldfld "bool C.d__0.<>w__disposeMode" + IL_0032: brfalse.s IL_0039 + IL_0034: leave IL_0167 + IL_0039: ldarg.0 + IL_003a: ldc.i4.m1 + IL_003b: dup + IL_003c: stloc.0 + IL_003d: stfld "int C.d__0.<>1__state" + IL_0042: nop + IL_0043: ldarg.0 + IL_0044: ldc.i4.1 + IL_0045: stfld "int C.d__0.<>2__current" + IL_004a: ldarg.0 + IL_004b: ldc.i4.s -4 + IL_004d: dup + IL_004e: stloc.0 + IL_004f: stfld "int C.d__0.<>1__state" + IL_0054: leave IL_0190 + IL_0059: ldarg.0 + IL_005a: ldc.i4.m1 + IL_005b: dup + IL_005c: stloc.0 + IL_005d: stfld "int C.d__0.<>1__state" + IL_0062: ldarg.0 + IL_0063: ldfld "bool C.d__0.<>w__disposeMode" + IL_0068: brfalse.s IL_006f + IL_006a: leave IL_0167 + IL_006f: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0074: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0079: stloc.1 + IL_007a: ldloca.s V_1 + IL_007c: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0081: brtrue.s IL_00c6 + IL_0083: ldarg.0 + IL_0084: ldc.i4.0 + IL_0085: dup + IL_0086: stloc.0 + IL_0087: stfld "int C.d__0.<>1__state" + IL_008c: ldarg.0 + IL_008d: ldloc.1 + IL_008e: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0093: ldarg.0 + IL_0094: stloc.2 + IL_0095: ldarg.0 + IL_0096: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_009b: ldloca.s V_1 + IL_009d: ldloca.s V_2 + IL_009f: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_00a4: nop + IL_00a5: leave IL_019d + IL_00aa: ldarg.0 + IL_00ab: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00b0: stloc.1 + IL_00b1: ldarg.0 + IL_00b2: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00b7: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00bd: ldarg.0 + IL_00be: ldc.i4.m1 + IL_00bf: dup + IL_00c0: stloc.0 + IL_00c1: stfld "int C.d__0.<>1__state" + IL_00c6: ldloca.s V_1 + IL_00c8: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00cd: nop + .try + { + .try + { + IL_00ce: nop + .try + { + IL_00cf: nop + IL_00d0: ldstr "Break " + IL_00d5: call "void System.Console.Write(string)" + IL_00da: nop + IL_00db: ldarg.0 + IL_00dc: ldc.i4.1 + IL_00dd: stfld "bool C.d__0.<>w__disposeMode" + IL_00e2: br.s IL_00e4 + IL_00e4: leave.s IL_00f9 + } + finally + { + IL_00e6: ldloc.0 + IL_00e7: ldc.i4.m1 + IL_00e8: bne.un.s IL_00f8 + IL_00ea: nop + IL_00eb: ldstr "Throw " + IL_00f0: call "void System.Console.Write(string)" + IL_00f5: nop + IL_00f6: ldnull + IL_00f7: throw + IL_00f8: endfinally + } + IL_00f9: ldarg.0 + IL_00fa: ldfld "bool C.d__0.<>w__disposeMode" + IL_00ff: brfalse.s IL_0103 + IL_0101: br.s IL_0104 + IL_0103: nop + IL_0104: leave.s IL_011c + } + catch object + { + IL_0106: pop + IL_0107: nop + IL_0108: ldstr "Caught " + IL_010d: call "void System.Console.Write(string)" + IL_0112: nop + IL_0113: ldarg.0 + IL_0114: ldc.i4.1 + IL_0115: stfld "bool C.d__0.<>w__disposeMode" + IL_011a: leave.s IL_0104 + } + IL_011c: leave.s IL_0130 + } + finally + { + IL_011e: ldloc.0 + IL_011f: ldc.i4.m1 + IL_0120: bne.un.s IL_012f + IL_0122: nop + IL_0123: ldstr "Finally " + IL_0128: call "void System.Console.Write(string)" + IL_012d: nop + IL_012e: nop + IL_012f: endfinally + } + IL_0130: ldarg.0 + IL_0131: ldfld "bool C.d__0.<>w__disposeMode" + IL_0136: brfalse.s IL_013a + IL_0138: leave.s IL_0167 + IL_013a: leave.s IL_0167 + } + catch System.Exception + { + IL_013c: stloc.3 + IL_013d: ldarg.0 + IL_013e: ldc.i4.s -2 + IL_0140: stfld "int C.d__0.<>1__state" + IL_0145: ldarg.0 + IL_0146: ldc.i4.0 + IL_0147: stfld "int C.d__0.<>2__current" + IL_014c: ldarg.0 + IL_014d: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_0152: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0157: nop + IL_0158: ldarg.0 + IL_0159: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_015e: ldloc.3 + IL_015f: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_0164: nop + IL_0165: leave.s IL_019d + } + IL_0167: ldarg.0 + IL_0168: ldc.i4.s -2 + IL_016a: stfld "int C.d__0.<>1__state" + IL_016f: ldarg.0 + IL_0170: ldc.i4.0 + IL_0171: stfld "int C.d__0.<>2__current" + IL_0176: ldarg.0 + IL_0177: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_017c: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0181: nop + IL_0182: ldarg.0 + IL_0183: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0188: ldc.i4.0 + IL_0189: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_018e: nop + IL_018f: ret + IL_0190: ldarg.0 + IL_0191: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0196: ldc.i4.1 + IL_0197: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_019c: nop + IL_019d: ret + } + """); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 2 DISPOSAL Dispose Finally Throw Dispose CAUGHT2 DONE")] [InlineData(3, "1 2 Try Dispose Finally Throw Dispose CAUGHT DISPOSAL DONE")] @@ -4831,7 +5040,7 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "Try 1 DISPOSAL Finally Item1 Item2 Throw CAUGHT2 DONE")] [InlineData(2, "Try 1 2 DISPOSAL Finally Item1 Item2 Throw CAUGHT2 DONE")] [InlineData(3, "Try 1 2 Finally Item1 Item2 Throw CAUGHT DISPOSAL DONE")] @@ -4880,7 +5089,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M2() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(2, "1 Throw Caught Throw2 Dispose CAUGHT DISPOSAL DONE")] public void TryFinally_AwaitUsingInCatch(int iterations, string expectedOutput) { @@ -4924,7 +5133,7 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "Try Item1 Item2 Throw1 Finally Item1 Item2 Throw2 CAUGHT DISPOSAL DONE")] public void TryFinally_AwaitForeachInCatch(int iterations, string expectedOutput) { @@ -4978,7 +5187,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M2() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Caught Finally END DISPOSAL DONE")] public void TryFinally_YieldBreakInCatch(int iterations, string expectedOutput) @@ -5022,7 +5231,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Caught Finally END DISPOSAL DONE")] public void TryFinally_YieldBreakInCatch_WithAwaits(int iterations, string expectedOutput) @@ -5068,7 +5277,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL Finally2 DONE")] [InlineData(2, "1 Throw Caught Break Finally Finally2 END DISPOSAL DONE")] public void TryFinally_YieldBreakInCatch_Nested(int iterations, string expectedOutput) @@ -5121,7 +5330,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(2, "1 Break Finally END DISPOSAL DONE")] @@ -5154,7 +5363,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(2, "1 2 DISPOSAL Finally DONE")] @@ -5185,7 +5394,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally1 Finally2 Finally5 Finally6 DONE")] [InlineData(2, "1 2 DISPOSAL Finally1 Finally2 Finally5 Finally6 DONE")] @@ -5250,7 +5459,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(2, "1 Throw Finally CAUGHT DISPOSAL DONE")] @@ -5284,7 +5493,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(2, "1 2 DISPOSAL Finally DONE")] @@ -5315,7 +5524,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(10, "1 Throw Finally CAUGHT DISPOSAL DONE")] public void TryFinally_WithYieldsOnly_WithThrow(int iterations, string expectedOutput) @@ -5347,7 +5556,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Try Finally 2 DISPOSAL DONE")] [InlineData(3, "1 Try Finally 2 END DISPOSAL DONE")] @@ -5382,7 +5591,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Finally CAUGHT DISPOSAL DONE")] public void TryFinally_WithAwaitsOnly_WithThrow(int iterations, string expectedOutput) @@ -5416,7 +5625,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw1 Throw2 Finally CAUGHT DISPOSAL DONE")] public void TryFinally_WithAwaitsOnly_WithSlowThrowInAwait(int iterations, string expectedOutput) @@ -5455,7 +5664,7 @@ static async System.Threading.Tasks.Task SlowThrowAsync() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Finally CAUGHT DISPOSAL DONE")] public void TryFinally_WithAwaitsOnly_WithFastThrowInAwait(int iterations, string expectedOutput) @@ -5495,7 +5704,7 @@ static async System.Threading.Tasks.Task FastThrowAsync() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 Throw Finally1 Caught")] [InlineData(2, "1 2 Throw Finally2 Finally1 Caught")] [InlineData(3, "1 2 Finally2 3 Throw Finally3 Finally1 Caught")] @@ -5573,7 +5782,7 @@ static async System.Threading.Tasks.Task Main() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "100 1 Throw Finally1 Caught")] [InlineData(2, "100 1 Throw Finally1 Caught")] [InlineData(3, "100 1 2 Throw Finally2 Finally1 Caught")] @@ -5666,7 +5875,7 @@ static async System.Threading.Tasks.Task Main() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Try Caught1 Caught2 After END DISPOSAL DONE")] public void TryFinally_AwaitAndCatch(int iterations, string expectedOutput) @@ -5701,7 +5910,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Caught END DISPOSAL DONE")] public void TryFinally_AwaitInCatch(int iterations, string expectedOutput) @@ -5731,7 +5940,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Caught END DISPOSAL DONE")] public void TryFinally_AwaitAndYieldBreakInCatch(int iterations, string expectedOutput) @@ -5762,7 +5971,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(2, "1 Try 2 DISPOSAL Finally DONE")] @@ -6147,7 +6356,7 @@ static async System.Collections.Generic.IAsyncEnumerable M() ); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "0 DISPOSAL Finally1 DONE")] [InlineData(2, "0 Finally1 Again 2 DISPOSAL Finally3 DONE")] @@ -6187,7 +6396,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally CAUGHT2 DONE")] [InlineData(2, "1 Finally CAUGHT DISPOSAL DONE")] @@ -6221,7 +6430,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally1 Finally2 CAUGHT2 DONE")] [InlineData(2, "1 Finally1 Finally2 CAUGHT DISPOSAL DONE")] @@ -6262,7 +6471,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Try1 Try2 Caught Finally1 Finally2 END DISPOSAL DONE")] @@ -6301,7 +6510,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "Try1 1 DISPOSAL Finally1 Finally2 DONE")] [InlineData(2, "Try1 1 Throw Finally1 Finally2 CAUGHT DISPOSAL DONE")] [InlineData(10, "Try1 1 Throw Finally1 Finally2 CAUGHT DISPOSAL DONE")] @@ -6337,7 +6546,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "Try1 1 DISPOSAL Finally1 Finally2 DONE")] [InlineData(2, "Try1 1 Try2 Finally1 Finally2 END DISPOSAL DONE")] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs index caaf685ab21be..692feeba2f018 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs @@ -260,7 +260,7 @@ public static async Task M(int x, int y, int z) }); } - [ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void SynthesizedVariables1() { var source = @@ -334,7 +334,9 @@ public async Task M(IDisposable disposable) }, module.GetFieldNames("C.d__3")); }); - vd.VerifyPdb("C.M", @" + if (ExecutionConditionUtil.IsWindows) + { + vd.VerifyPdb("C.M", @" @@ -374,6 +376,7 @@ public async Task M(IDisposable disposable) ", options: PdbValidationOptions.ExcludeDocuments); + } } [Fact] @@ -477,6 +480,13 @@ public static void Main() 42 "; CompileAndVerify(source, expectedOutput: expected, references: new[] { CSharpRef }); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,16): error CS9328: Method 'Test.F(dynamic)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // return await t; + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await t").WithArguments("Test.F(dynamic)").WithLocation(9, 16) + ); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs index 999bf6667331d..5549c0841fe87 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs @@ -63,7 +63,7 @@ public static void Main() Console.WriteLine(H(true, true)); } }"; - var expected = @" + var expectedOutput = @" F(2) F(10) 10 @@ -77,7 +77,50 @@ public static void Main() F(5) 5 "; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [G]: Unexpected type on the stack. { Offset = 0x35, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G(bool, bool)", """ + { + // Code size 54 (0x36) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldc.i4.0 + IL_0001: ldarg.0 + IL_0002: brfalse.s IL_0008 + IL_0004: ldc.i4.1 + IL_0005: stloc.0 + IL_0006: br.s IL_0014 + IL_0008: ldc.i4.2 + IL_0009: call "System.Threading.Tasks.Task Test.F(int)" + IL_000e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0013: stloc.0 + IL_0014: ldloc.0 + IL_0015: add + IL_0016: ldarg.1 + IL_0017: brfalse.s IL_0027 + IL_0019: ldc.i4.4 + IL_001a: call "System.Threading.Tasks.Task Test.F(int)" + IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: stloc.0 + IL_0025: br.s IL_0029 + IL_0027: ldc.i4.8 + IL_0028: stloc.0 + IL_0029: ldloc.0 + IL_002a: add + IL_002b: call "System.Threading.Tasks.Task Test.F(int)" + IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0035: ret + } + """); } [Fact] @@ -121,13 +164,102 @@ public static void Main() Console.WriteLine(H(false, true)); } }"; - var expected = @" + var expectedOutput = @" F(True) F(False) F(4) 4 "; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [G]: Unexpected type on the stack. { Offset = 0x83, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G(bool, bool)", """ + { + // Code size 132 (0x84) + .maxstack 3 + .locals init (bool V_0, //x1 + bool V_1, //x2 + bool V_2, //x3 + int V_3, //c + bool V_4) + IL_0000: ldarg.0 + IL_0001: stloc.s V_4 + IL_0003: ldloc.s V_4 + IL_0005: brfalse.s IL_0014 + IL_0007: ldc.i4.1 + IL_0008: call "System.Threading.Tasks.Task Test.F(bool)" + IL_000d: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0012: stloc.s V_4 + IL_0014: ldloc.s V_4 + IL_0016: stloc.0 + IL_0017: ldarg.0 + IL_0018: stloc.s V_4 + IL_001a: ldloc.s V_4 + IL_001c: brfalse.s IL_002b + IL_001e: ldc.i4.0 + IL_001f: call "System.Threading.Tasks.Task Test.F(bool)" + IL_0024: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0029: stloc.s V_4 + IL_002b: ldloc.s V_4 + IL_002d: stloc.1 + IL_002e: ldarg.1 + IL_002f: stloc.s V_4 + IL_0031: ldloc.s V_4 + IL_0033: brfalse.s IL_0042 + IL_0035: ldc.i4.1 + IL_0036: call "System.Threading.Tasks.Task Test.F(bool)" + IL_003b: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0040: stloc.s V_4 + IL_0042: ldloc.s V_4 + IL_0044: stloc.2 + IL_0045: ldarg.1 + IL_0046: stloc.s V_4 + IL_0048: ldloc.s V_4 + IL_004a: brfalse.s IL_0059 + IL_004c: ldc.i4.0 + IL_004d: call "System.Threading.Tasks.Task Test.F(bool)" + IL_0052: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0057: stloc.s V_4 + IL_0059: ldloc.s V_4 + IL_005b: ldc.i4.0 + IL_005c: stloc.3 + IL_005d: ldloc.0 + IL_005e: brfalse.s IL_0064 + IL_0060: ldloc.3 + IL_0061: ldc.i4.1 + IL_0062: add + IL_0063: stloc.3 + IL_0064: ldloc.1 + IL_0065: brfalse.s IL_006b + IL_0067: ldloc.3 + IL_0068: ldc.i4.2 + IL_0069: add + IL_006a: stloc.3 + IL_006b: ldloc.2 + IL_006c: brfalse.s IL_0072 + IL_006e: ldloc.3 + IL_006f: ldc.i4.4 + IL_0070: add + IL_0071: stloc.3 + IL_0072: brfalse.s IL_0078 + IL_0074: ldloc.3 + IL_0075: ldc.i4.8 + IL_0076: add + IL_0077: stloc.3 + IL_0078: ldloc.3 + IL_0079: call "System.Threading.Tasks.Task Test.F(int)" + IL_007e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0083: ret + } + """); } [Fact] @@ -171,13 +303,102 @@ public static void Main() Console.WriteLine(H(false, true)); } }"; - var expected = @" + var expectedOutput = @" F(True) F(False) F(13) 13 "; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [G]: Unexpected type on the stack. { Offset = 0x83, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G(bool, bool)", """ + { + // Code size 132 (0x84) + .maxstack 3 + .locals init (bool V_0, //x1 + bool V_1, //x2 + bool V_2, //x3 + int V_3, //c + bool V_4) + IL_0000: ldarg.0 + IL_0001: stloc.s V_4 + IL_0003: ldloc.s V_4 + IL_0005: brtrue.s IL_0014 + IL_0007: ldc.i4.1 + IL_0008: call "System.Threading.Tasks.Task Test.F(bool)" + IL_000d: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0012: stloc.s V_4 + IL_0014: ldloc.s V_4 + IL_0016: stloc.0 + IL_0017: ldarg.0 + IL_0018: stloc.s V_4 + IL_001a: ldloc.s V_4 + IL_001c: brtrue.s IL_002b + IL_001e: ldc.i4.0 + IL_001f: call "System.Threading.Tasks.Task Test.F(bool)" + IL_0024: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0029: stloc.s V_4 + IL_002b: ldloc.s V_4 + IL_002d: stloc.1 + IL_002e: ldarg.1 + IL_002f: stloc.s V_4 + IL_0031: ldloc.s V_4 + IL_0033: brtrue.s IL_0042 + IL_0035: ldc.i4.1 + IL_0036: call "System.Threading.Tasks.Task Test.F(bool)" + IL_003b: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0040: stloc.s V_4 + IL_0042: ldloc.s V_4 + IL_0044: stloc.2 + IL_0045: ldarg.1 + IL_0046: stloc.s V_4 + IL_0048: ldloc.s V_4 + IL_004a: brtrue.s IL_0059 + IL_004c: ldc.i4.0 + IL_004d: call "System.Threading.Tasks.Task Test.F(bool)" + IL_0052: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0057: stloc.s V_4 + IL_0059: ldloc.s V_4 + IL_005b: ldc.i4.0 + IL_005c: stloc.3 + IL_005d: ldloc.0 + IL_005e: brfalse.s IL_0064 + IL_0060: ldloc.3 + IL_0061: ldc.i4.1 + IL_0062: add + IL_0063: stloc.3 + IL_0064: ldloc.1 + IL_0065: brfalse.s IL_006b + IL_0067: ldloc.3 + IL_0068: ldc.i4.2 + IL_0069: add + IL_006a: stloc.3 + IL_006b: ldloc.2 + IL_006c: brfalse.s IL_0072 + IL_006e: ldloc.3 + IL_006f: ldc.i4.4 + IL_0070: add + IL_0071: stloc.3 + IL_0072: brfalse.s IL_0078 + IL_0074: ldloc.3 + IL_0075: ldc.i4.8 + IL_0076: add + IL_0077: stloc.3 + IL_0078: ldloc.3 + IL_0079: call "System.Threading.Tasks.Task Test.F(int)" + IL_007e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0083: ret + } + """); } [Fact] @@ -217,7 +438,7 @@ public static void Main() H(""c"", ""d""); } }"; - var expected = @" + var expectedOutput = @" F(null) F(null) null @@ -229,7 +450,47 @@ public static void Main() F(c) c "; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [G]: Unexpected type on the stack. { Offset = 0x37, Found = ref 'string', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G(string, string)", """ + { + // Code size 56 (0x38) + .maxstack 3 + .locals init (string V_0, //result + string V_1) + IL_0000: ldarg.0 + IL_0001: call "System.Threading.Tasks.Task Test.F(string)" + IL_0006: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000b: stloc.1 + IL_000c: ldloc.1 + IL_000d: brtrue.s IL_001b + IL_000f: ldarg.1 + IL_0010: call "System.Threading.Tasks.Task Test.F(string)" + IL_0015: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001a: stloc.1 + IL_001b: ldloc.1 + IL_001c: stloc.0 + IL_001d: ldstr " " + IL_0022: ldloc.0 + IL_0023: dup + IL_0024: brtrue.s IL_002c + IL_0026: pop + IL_0027: ldstr "null" + IL_002c: call "string string.Concat(string, string)" + IL_0031: call "void System.Console.WriteLine(string)" + IL_0036: ldloc.0 + IL_0037: ret + } + """); } [Fact] @@ -260,16 +521,39 @@ public static void Main() Console.WriteLine(t.Result); } }"; - var expected = @" + var expectedOutput = @" 42 "; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x2e, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0xd, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G()", """ + { + // Code size 14 (0xe) + .maxstack 2 + IL_0000: call "System.Threading.Tasks.Task Test.F()" + IL_0005: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ldc.i4.s 21 + IL_000c: add + IL_000d: ret + } + """); } [Fact] public void SpillNestedUnary() { var source = @" +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously using System; using System.Threading.Tasks; @@ -308,12 +592,62 @@ public static void Main() WaitAndPrint(G3()); } }"; - var expected = @" + var expectedOutput = @" -1 1 -1 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G1]: Unexpected type on the stack. { Offset = 0xb, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G2]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G3]: Unexpected type on the stack. { Offset = 0xd, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G1()", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.Task Test.F()" + IL_0005: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: neg + IL_000b: ret + } + """); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G2()", """ + { + // Code size 13 (0xd) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.Task Test.F()" + IL_0005: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: neg + IL_000b: neg + IL_000c: ret + } + """); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G3()", """ + { + // Code size 14 (0xe) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.Task Test.F()" + IL_0005: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: neg + IL_000b: neg + IL_000c: neg + IL_000d: ret + } + """); } [Fact] @@ -345,7 +679,7 @@ public static void Main() Console.WriteLine(t.Result); } }"; - var expected = @" + var expectedOutput = @" 42 "; // When the local 'c' gets hoisted, the statement: @@ -369,7 +703,41 @@ public static void Main() // So this case actually requires stack spilling, which is not yet implemented. This has the unfortunate // consequence of preventing await expressions from being assigned to hoisted locals. // - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x28, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x1f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G(int)", """ + { + // Code size 32 (0x20) + .maxstack 2 + .locals init (int V_0) //c + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldarg.0 + IL_0003: call "System.Threading.Tasks.Task Test.F(int)" + IL_0008: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000d: ldloc.0 + IL_000e: add + IL_000f: stloc.0 + IL_0010: ldarg.0 + IL_0011: call "System.Threading.Tasks.Task Test.F(int)" + IL_0016: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001b: ldloc.0 + IL_001c: add + IL_001d: stloc.0 + IL_001e: ldloc.0 + IL_001f: ret + } + """); } [Fact] @@ -412,7 +780,7 @@ public static void Main() t.Wait(); } }"; - var expected = @" + var expectedOutput = @" > 111 > 222 > 333 @@ -424,7 +792,47 @@ public static void Main() 444 555 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x28, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Return value missing on the stack. { Offset = 0x44 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G()", """ + { + // Code size 69 (0x45) + .maxstack 5 + .locals init (int V_0, + int V_1, + int V_2) + IL_0000: ldc.i4.s 111 + IL_0002: call "int Test.Get(int)" + IL_0007: ldc.i4 0xde + IL_000c: call "int Test.Get(int)" + IL_0011: stloc.0 + IL_0012: ldc.i4 0x14d + IL_0017: call "int Test.Get(int)" + IL_001c: stloc.1 + IL_001d: ldc.i4 0x1bc + IL_0022: call "int Test.Get(int)" + IL_0027: call "System.Threading.Tasks.Task Test.F(int)" + IL_002c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0031: stloc.2 + IL_0032: ldloc.0 + IL_0033: ldloc.1 + IL_0034: ldloc.2 + IL_0035: ldc.i4 0x22b + IL_003a: call "int Test.Get(int)" + IL_003f: call "void Test.Printer(int, int, int, int, int)" + IL_0044: ret + } + """); } [Fact] @@ -467,7 +875,7 @@ public static void Main() t.Wait(); } }"; - var expected = @" + var expectedOutput = @" > 111 > 222 > 333 @@ -479,7 +887,49 @@ public static void Main() 444 555 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x28, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Return value missing on the stack. { Offset = 0x4e } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G()", """ + { + // Code size 79 (0x4f) + .maxstack 5 + .locals init (int V_0, + int V_1, + int V_2) + IL_0000: ldc.i4.s 111 + IL_0002: call "int Test.Get(int)" + IL_0007: ldc.i4 0xde + IL_000c: call "int Test.Get(int)" + IL_0011: call "System.Threading.Tasks.Task Test.F(int)" + IL_0016: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001b: stloc.0 + IL_001c: ldc.i4 0x14d + IL_0021: call "int Test.Get(int)" + IL_0026: stloc.1 + IL_0027: ldc.i4 0x1bc + IL_002c: call "int Test.Get(int)" + IL_0031: call "System.Threading.Tasks.Task Test.F(int)" + IL_0036: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003b: stloc.2 + IL_003c: ldloc.0 + IL_003d: ldloc.1 + IL_003e: ldloc.2 + IL_003f: ldc.i4 0x22b + IL_0044: call "int Test.Get(int)" + IL_0049: call "void Test.Printer(int, int, int, int, int)" + IL_004e: ret + } + """); } [Fact] @@ -516,7 +966,7 @@ public static void Main() t.Wait(); } }"; - var expected = @" + var expectedOutput = @" 1 2 3 @@ -524,7 +974,53 @@ public static void Main() 5 6 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x28, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Return value missing on the stack. { Offset = 0x4d } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G()", """ + { + // Code size 78 (0x4e) + .maxstack 6 + .locals init (int V_0, + int V_1, + int V_2) + IL_0000: ldc.i4.2 + IL_0001: call "System.Threading.Tasks.Task Test.F(int)" + IL_0006: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000b: stloc.0 + IL_000c: ldc.i4.4 + IL_000d: call "System.Threading.Tasks.Task Test.F(int)" + IL_0012: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0017: call "System.Threading.Tasks.Task Test.F(int)" + IL_001c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: call "System.Threading.Tasks.Task Test.F(int)" + IL_0026: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002b: call "System.Threading.Tasks.Task Test.F(int)" + IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0035: stloc.1 + IL_0036: ldc.i4.5 + IL_0037: call "System.Threading.Tasks.Task Test.F(int)" + IL_003c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0041: stloc.2 + IL_0042: ldc.i4.1 + IL_0043: ldloc.0 + IL_0044: ldc.i4.3 + IL_0045: ldloc.1 + IL_0046: ldloc.2 + IL_0047: ldc.i4.6 + IL_0048: call "void Test.Printer(int, int, int, int, int, int)" + IL_004d: ret + } + """); } [Fact] @@ -561,11 +1057,39 @@ public static void Main() t.Wait(); } }"; - var expected = @" + var expectedOutput = @" 1 2 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x28, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Return value missing on the stack. { Offset = 0x1d } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G()", """ + { + // Code size 30 (0x1e) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldc.i4.2 + IL_0001: call "System.Threading.Tasks.Task Test.F(int)" + IL_0006: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000b: call "System.Threading.Tasks.Task Test.F(int)" + IL_0010: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0015: stloc.0 + IL_0016: ldc.i4.1 + IL_0017: ldloc.0 + IL_0018: call "void Test.Printer(int, int)" + IL_001d: ret + } + """); } [Fact] @@ -729,6 +1253,66 @@ .locals init (int V_0, IL_010f: nop IL_0110: ret }", sequencePoints: "Test+d__2.MoveNext"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (18,38): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // H(array[1] += 2, array[3] += await G(), 4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(18, 38) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [F]: Unexpected type on the stack. { Offset = 0x35, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("Test.F(int[])", """ + // { + // // Code size 54 (0x36) + // .maxstack 4 + // .locals init (int& V_0, + // int V_1, + // int V_2, + // int V_3) + // IL_0000: ldarg.0 + // IL_0001: ldc.i4.1 + // IL_0002: ldelema "int" + // IL_0007: dup + // IL_0008: ldind.i4 + // IL_0009: ldc.i4.2 + // IL_000a: add + // IL_000b: dup + // IL_000c: stloc.3 + // IL_000d: stind.i4 + // IL_000e: ldloc.3 + // IL_000f: ldarg.0 + // IL_0010: ldc.i4.3 + // IL_0011: ldelema "int" + // IL_0016: stloc.0 + // IL_0017: ldloc.0 + // IL_0018: ldind.i4 + // IL_0019: stloc.1 + // IL_001a: call "System.Threading.Tasks.Task Test.G()" + // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0024: stloc.2 + // IL_0025: ldloc.0 + // IL_0026: ldloc.1 + // IL_0027: ldloc.2 + // IL_0028: add + // IL_0029: dup + // IL_002a: stloc.3 + // IL_002b: stind.i4 + // IL_002c: ldloc.3 + // IL_002d: ldc.i4.4 + // IL_002e: call "int Test.H(int, int, int)" + // IL_0033: pop + // IL_0034: ldc.i4.1 + // IL_0035: ret + // } + // """); } [Fact] @@ -882,6 +1466,66 @@ .locals init (int V_0, IL_00f5: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult(int)"" IL_00fa: ret }", sequencePoints: "Test+d__2.MoveNext"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (18,38): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // H(array[1] += 2, array[3] += await G(), 4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(18, 38) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [F]: Unexpected type on the stack. { Offset = 0x35, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("Test.F(int[])", """ + // { + // // Code size 54 (0x36) + // .maxstack 4 + // .locals init (int& V_0, + // int V_1, + // int V_2, + // int V_3) + // IL_0000: ldarg.0 + // IL_0001: ldc.i4.1 + // IL_0002: ldelema "int" + // IL_0007: dup + // IL_0008: ldind.i4 + // IL_0009: ldc.i4.2 + // IL_000a: add + // IL_000b: dup + // IL_000c: stloc.3 + // IL_000d: stind.i4 + // IL_000e: ldloc.3 + // IL_000f: ldarg.0 + // IL_0010: ldc.i4.3 + // IL_0011: ldelema "int" + // IL_0016: stloc.0 + // IL_0017: ldloc.0 + // IL_0018: ldind.i4 + // IL_0019: stloc.1 + // IL_001a: call "System.Threading.Tasks.Task Test.G()" + // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0024: stloc.2 + // IL_0025: ldloc.0 + // IL_0026: ldloc.1 + // IL_0027: ldloc.2 + // IL_0028: add + // IL_0029: dup + // IL_002a: stloc.3 + // IL_002b: stind.i4 + // IL_002c: ldloc.3 + // IL_002d: ldc.i4.4 + // IL_002e: call "int Test.H(int, int, int)" + // IL_0033: pop + // IL_0034: ldc.i4.1 + // IL_0035: ret + // } + // """); } [Fact] @@ -910,6 +1554,66 @@ public static async Task F(int[] array) } "; CompileAndVerify(source, options: TestOptions.DebugDll); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (18,45): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // H(0, (1 == await G()) ? array[3] += await G() : 1, 4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(18, 45) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [F]: Unexpected type on the stack. { Offset = 0x3c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("Test.F(int[])", """ + // { + // // Code size 61 (0x3d) + // .maxstack 3 + // .locals init (int V_0, + // int V_1, + // int V_2, + // int V_3, + // int V_4) + // IL_0000: call "System.Threading.Tasks.Task Test.G()" + // IL_0005: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_000a: stloc.0 + // IL_000b: ldc.i4.1 + // IL_000c: ldloc.0 + // IL_000d: bne.un.s IL_0030 + // IL_000f: ldarg.0 + // IL_0010: ldc.i4.3 + // IL_0011: ldelema "int" + // IL_0016: dup + // IL_0017: ldind.i4 + // IL_0018: stloc.2 + // IL_0019: call "System.Threading.Tasks.Task Test.G()" + // IL_001e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0023: stloc.3 + // IL_0024: ldloc.2 + // IL_0025: ldloc.3 + // IL_0026: add + // IL_0027: dup + // IL_0028: stloc.s V_4 + // IL_002a: stind.i4 + // IL_002b: ldloc.s V_4 + // IL_002d: stloc.1 + // IL_002e: br.s IL_0032 + // IL_0030: ldc.i4.1 + // IL_0031: stloc.1 + // IL_0032: ldc.i4.0 + // IL_0033: ldloc.1 + // IL_0034: ldc.i4.4 + // IL_0035: call "int Test.H(int, int, int)" + // IL_003a: pop + // IL_003b: ldc.i4.1 + // IL_003c: ret + // } + // """); } [Fact] @@ -975,6 +1679,82 @@ public static async Task F(int[] array) "<>s__8" }, module.GetFieldNames("C.d__3")); }); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (23,28): error CS9328: Method 'C.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // H(0, O(array[0] += await G()) ?? (array[1] += await G()), 4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("C.F(int[])").WithLocation(23, 28), + // (23,55): error CS9328: Method 'C.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // H(0, O(array[0] += await G()) ?? (array[1] += await G()), 4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("C.F(int[])").WithLocation(23, 55) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [F]: Unexpected type on the stack. { Offset = 0x55, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("C.F(int[])", """ + // { + // // Code size 86 (0x56) + // .maxstack 3 + // .locals init (int V_0, + // int V_1, + // object V_2, + // int V_3, + // int V_4, + // int V_5) + // IL_0000: ldarg.0 + // IL_0001: ldc.i4.0 + // IL_0002: ldelema "int" + // IL_0007: dup + // IL_0008: ldind.i4 + // IL_0009: stloc.0 + // IL_000a: call "System.Threading.Tasks.Task C.G()" + // IL_000f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0014: stloc.1 + // IL_0015: ldloc.0 + // IL_0016: ldloc.1 + // IL_0017: add + // IL_0018: dup + // IL_0019: stloc.3 + // IL_001a: stind.i4 + // IL_001b: ldloc.3 + // IL_001c: call "object C.O(int)" + // IL_0021: stloc.2 + // IL_0022: ldloc.2 + // IL_0023: brtrue.s IL_004b + // IL_0025: ldarg.0 + // IL_0026: ldc.i4.1 + // IL_0027: ldelema "int" + // IL_002c: dup + // IL_002d: ldind.i4 + // IL_002e: stloc.3 + // IL_002f: call "System.Threading.Tasks.Task C.G()" + // IL_0034: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0039: stloc.s V_4 + // IL_003b: ldloc.3 + // IL_003c: ldloc.s V_4 + // IL_003e: add + // IL_003f: dup + // IL_0040: stloc.s V_5 + // IL_0042: stind.i4 + // IL_0043: ldloc.s V_5 + // IL_0045: box "int" + // IL_004a: stloc.2 + // IL_004b: ldc.i4.0 + // IL_004c: ldloc.2 + // IL_004d: ldc.i4.4 + // IL_004e: call "int C.H(int, object, int)" + // IL_0053: pop + // IL_0054: ldc.i4.1 + // IL_0055: ret + // } + // """); } [WorkItem(4628, "https://github.com/dotnet/roslyn/issues/4628")] @@ -999,11 +1779,43 @@ static void Main(string[] args) { } } }"; - var expected = @" + var expectedOutput = @" False True "; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [AsyncMethod]: Return value missing on the stack. { Offset = 0x27 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("AsyncConditionalBug.Program.AsyncMethod()", """ + { + // Code size 40 (0x28) + .maxstack 1 + .locals init (bool V_0) + IL_0000: ldarg.0 + IL_0001: ldfld "bool AsyncConditionalBug.Program.b" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0016 + IL_000a: ldc.i4.0 + IL_000b: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(bool)" + IL_0010: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0015: stloc.0 + IL_0016: ldloc.0 + IL_0017: call "void System.Console.WriteLine(bool)" + IL_001c: ldarg.0 + IL_001d: ldfld "bool AsyncConditionalBug.Program.b" + IL_0022: call "void System.Console.WriteLine(bool)" + IL_0027: ret + } + """); } [WorkItem(4628, "https://github.com/dotnet/roslyn/issues/4628")] @@ -1028,11 +1840,41 @@ static void Main(string[] args) { } } }"; - var expected = @" + var expectedOutput = @" False True "; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [AsyncMethod]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("AsyncConditionalBug.Program.AsyncMethod()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (bool V_0) + IL_0000: ldsfld "bool AsyncConditionalBug.Program.b" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: brfalse.s IL_0015 + IL_0009: ldc.i4.0 + IL_000a: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(bool)" + IL_000f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0014: stloc.0 + IL_0015: ldloc.0 + IL_0016: call "void System.Console.WriteLine(bool)" + IL_001b: ldsfld "bool AsyncConditionalBug.Program.b" + IL_0020: call "void System.Console.WriteLine(bool)" + IL_0025: ret + } + """); } [WorkItem(4628, "https://github.com/dotnet/roslyn/issues/4628")] @@ -1061,10 +1903,42 @@ static void Main(string[] args) } } }"; - var expected = @" + var expectedOutput = @" hello "; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [AsyncMethod]: Return value missing on the stack. { Offset = 0x2b } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("AsyncConditionalBug.Program.AsyncMethod()", """ + { + // Code size 44 (0x2c) + .maxstack 1 + .locals init (string V_0) + IL_0000: ldarg.0 + IL_0001: ldfld "string AsyncConditionalBug.Program.NULL" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brtrue.s IL_001a + IL_000a: ldstr "hello" + IL_000f: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(string)" + IL_0014: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0019: stloc.0 + IL_001a: ldloc.0 + IL_001b: call "void System.Console.WriteLine(string)" + IL_0020: ldarg.0 + IL_0021: ldfld "string AsyncConditionalBug.Program.NULL" + IL_0026: call "void System.Console.WriteLine(string)" + IL_002b: ret + } + """); } [WorkItem(4638, "https://github.com/dotnet/roslyn/issues/4638")] @@ -1105,10 +1979,53 @@ private static async Task IsValid(Guid id) } } }"; - var expected = @" + var expectedOutput = @" Not Valid! "; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [DoSomething]: Return value missing on the stack. { Offset = 0x2b } + [IsValid]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics( + // (23,17): warning CS8073: The result of the expression is always 'true' since a value of type 'Guid' is never equal to 'null' of type 'Guid?' + // if (item.Item2 != null || await IsValid(item.Item2)) + Diagnostic(ErrorCode.WRN_NubExprIsConstBool2, "item.Item2 != null").WithArguments("true", "System.Guid", "System.Guid?").WithLocation(23, 17), + // (29,41): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // private static async Task IsValid(Guid id) + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "IsValid").WithLocation(29, 41) + ); + verifier.VerifyIL("AsyncConditionalBug.Program.DoSomething(System.Tuple)", """ + { + // Code size 44 (0x2c) + .maxstack 1 + .locals init (bool V_0) + IL_0000: ldarg.0 + IL_0001: callvirt "System.Guid System.Tuple.Item2.get" + IL_0006: pop + IL_0007: ldc.i4.1 + IL_0008: stloc.0 + IL_0009: ldloc.0 + IL_000a: brtrue.s IL_001d + IL_000c: ldarg.0 + IL_000d: callvirt "System.Guid System.Tuple.Item2.get" + IL_0012: call "System.Threading.Tasks.Task AsyncConditionalBug.Program.IsValid(System.Guid)" + IL_0017: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001c: stloc.0 + IL_001d: ldloc.0 + IL_001e: brfalse.s IL_002b + IL_0020: ldstr "Not Valid!" + IL_0025: newobj "System.Exception..ctor(string)" + IL_002a: throw + IL_002b: ret + } + """); } [Fact] @@ -1142,6 +2059,82 @@ public static async Task F(int[] array) } "; CompileAndVerify(source, options: TestOptions.DebugDll); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (23,28): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // H(0, B(array[0] += await G()) || B(array[1] += await G()), 4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(23, 28), + // (23,56): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // H(0, B(array[0] += await G()) || B(array[1] += await G()), 4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(23, 56) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [F]: Unexpected type on the stack. { Offset = 0x55, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("Test.F(int[])", """ + // { + // // Code size 86 (0x56) + // .maxstack 3 + // .locals init (int V_0, + // int V_1, + // bool V_2, + // int V_3, + // int V_4, + // int V_5) + // IL_0000: ldarg.0 + // IL_0001: ldc.i4.0 + // IL_0002: ldelema "int" + // IL_0007: dup + // IL_0008: ldind.i4 + // IL_0009: stloc.0 + // IL_000a: call "System.Threading.Tasks.Task Test.G()" + // IL_000f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0014: stloc.1 + // IL_0015: ldloc.0 + // IL_0016: ldloc.1 + // IL_0017: add + // IL_0018: dup + // IL_0019: stloc.3 + // IL_001a: stind.i4 + // IL_001b: ldloc.3 + // IL_001c: call "bool Test.B(int)" + // IL_0021: stloc.2 + // IL_0022: ldloc.2 + // IL_0023: brtrue.s IL_004b + // IL_0025: ldarg.0 + // IL_0026: ldc.i4.1 + // IL_0027: ldelema "int" + // IL_002c: dup + // IL_002d: ldind.i4 + // IL_002e: stloc.3 + // IL_002f: call "System.Threading.Tasks.Task Test.G()" + // IL_0034: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0039: stloc.s V_4 + // IL_003b: ldloc.3 + // IL_003c: ldloc.s V_4 + // IL_003e: add + // IL_003f: dup + // IL_0040: stloc.s V_5 + // IL_0042: stind.i4 + // IL_0043: ldloc.s V_5 + // IL_0045: call "bool Test.B(int)" + // IL_004a: stloc.2 + // IL_004b: ldc.i4.0 + // IL_004c: ldloc.2 + // IL_004d: ldc.i4.4 + // IL_004e: call "int Test.H(int, bool, int)" + // IL_0053: pop + // IL_0054: ldc.i4.1 + // IL_0055: ret + // } + // """); } [Fact] @@ -1218,14 +2211,14 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - var expected = @" + var expectedOutput = @" 0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); } [Fact] - public void SpillArray02_1() + public void SpillArray01WithTaskAndRuntimeAsync() { var source = @" using System; @@ -1240,7 +2233,7 @@ public async Task GetVal(T t) return t; } - public async void Run(T t) where T : struct + public async Task Run(T t) where T : struct { int tests = 0; try @@ -1249,6 +2242,28 @@ public async void Run(T t) where T : struct int[] arr = new int[await GetVal(4)]; if (arr.Length == 4) Driver.Count++; + + //multidimensional + tests++; + decimal[,] arr2 = new decimal[await GetVal(4), await GetVal(4)]; + if (arr2.Rank == 2 && arr2.Length == 16) + Driver.Count++; + + arr2 = new decimal[4, await GetVal(4)]; + if (arr2.Rank == 2 && arr2.Length == 16) + Driver.Count++; + + tests++; + arr2 = new decimal[await GetVal(4), 4]; + if (arr2.Rank == 2 && arr2.Length == 16) + Driver.Count++; + + + //jagged array + tests++; + decimal?[][] arr3 = new decimal?[await GetVal(4)][]; + if (arr3.Rank == 2 && arr3.Length == 4) + Driver.Count++; } finally { @@ -1276,14 +2291,169 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - var expected = @" + var expectedOutput = @" 0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x120 } + """ + }); + + verifier.VerifyDiagnostics( + // (63,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. + // t.Run(6); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "t.Run(6)").WithLocation(63, 9) + ); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 289 (0x121) + .maxstack 3 + .locals init (int V_0, //tests + decimal[,] V_1, //arr2 + decimal?[][] V_2, //arr3 + int V_3) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldloc.0 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.0 + IL_0006: ldarg.0 + IL_0007: ldc.i4.4 + IL_0008: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_000d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0012: newarr "int" + IL_0017: ldlen + IL_0018: conv.i4 + IL_0019: ldc.i4.4 + IL_001a: bne.un.s IL_0028 + IL_001c: ldsfld "int Driver.Count" + IL_0021: ldc.i4.1 + IL_0022: add + IL_0023: stsfld "int Driver.Count" + IL_0028: ldloc.0 + IL_0029: ldc.i4.1 + IL_002a: add + IL_002b: stloc.0 + IL_002c: ldarg.0 + IL_002d: ldc.i4.4 + IL_002e: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0033: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0038: ldarg.0 + IL_0039: ldc.i4.4 + IL_003a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_003f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0044: stloc.3 + IL_0045: ldloc.3 + IL_0046: newobj "decimal[*,*]..ctor" + IL_004b: stloc.1 + IL_004c: ldloc.1 + IL_004d: callvirt "int System.Array.Rank.get" + IL_0052: ldc.i4.2 + IL_0053: bne.un.s IL_006b + IL_0055: ldloc.1 + IL_0056: callvirt "int System.Array.Length.get" + IL_005b: ldc.i4.s 16 + IL_005d: bne.un.s IL_006b + IL_005f: ldsfld "int Driver.Count" + IL_0064: ldc.i4.1 + IL_0065: add + IL_0066: stsfld "int Driver.Count" + IL_006b: ldarg.0 + IL_006c: ldc.i4.4 + IL_006d: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0072: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0077: stloc.3 + IL_0078: ldc.i4.4 + IL_0079: ldloc.3 + IL_007a: newobj "decimal[*,*]..ctor" + IL_007f: stloc.1 + IL_0080: ldloc.1 + IL_0081: callvirt "int System.Array.Rank.get" + IL_0086: ldc.i4.2 + IL_0087: bne.un.s IL_009f + IL_0089: ldloc.1 + IL_008a: callvirt "int System.Array.Length.get" + IL_008f: ldc.i4.s 16 + IL_0091: bne.un.s IL_009f + IL_0093: ldsfld "int Driver.Count" + IL_0098: ldc.i4.1 + IL_0099: add + IL_009a: stsfld "int Driver.Count" + IL_009f: ldloc.0 + IL_00a0: ldc.i4.1 + IL_00a1: add + IL_00a2: stloc.0 + IL_00a3: ldarg.0 + IL_00a4: ldc.i4.4 + IL_00a5: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_00aa: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00af: ldc.i4.4 + IL_00b0: newobj "decimal[*,*]..ctor" + IL_00b5: stloc.1 + IL_00b6: ldloc.1 + IL_00b7: callvirt "int System.Array.Rank.get" + IL_00bc: ldc.i4.2 + IL_00bd: bne.un.s IL_00d5 + IL_00bf: ldloc.1 + IL_00c0: callvirt "int System.Array.Length.get" + IL_00c5: ldc.i4.s 16 + IL_00c7: bne.un.s IL_00d5 + IL_00c9: ldsfld "int Driver.Count" + IL_00ce: ldc.i4.1 + IL_00cf: add + IL_00d0: stsfld "int Driver.Count" + IL_00d5: ldloc.0 + IL_00d6: ldc.i4.1 + IL_00d7: add + IL_00d8: stloc.0 + IL_00d9: ldarg.0 + IL_00da: ldc.i4.4 + IL_00db: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_00e0: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00e5: newarr "decimal?[]" + IL_00ea: stloc.2 + IL_00eb: ldloc.2 + IL_00ec: callvirt "int System.Array.Rank.get" + IL_00f1: ldc.i4.2 + IL_00f2: bne.un.s IL_0106 + IL_00f4: ldloc.2 + IL_00f5: ldlen + IL_00f6: conv.i4 + IL_00f7: ldc.i4.4 + IL_00f8: bne.un.s IL_0106 + IL_00fa: ldsfld "int Driver.Count" + IL_00ff: ldc.i4.1 + IL_0100: add + IL_0101: stsfld "int Driver.Count" + IL_0106: leave.s IL_0120 + } + finally + { + IL_0108: ldsfld "int Driver.Count" + IL_010d: ldloc.0 + IL_010e: sub + IL_010f: stsfld "int Driver.Result" + IL_0114: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0119: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_011e: pop + IL_011f: endfinally + } + IL_0120: ret + } + """); } [Fact] - public void SpillArray02_2() + public void SpillArray02_1() { var source = @" using System; @@ -1303,11 +2473,9 @@ public async void Run(T t) where T : struct int tests = 0; try { - int[] arr = new int[4]; - tests++; - arr[0] = await GetVal(4); - if (arr[0] == 4) + int[] arr = new int[await GetVal(4)]; + if (arr.Length == 4) Driver.Count++; } finally @@ -1336,14 +2504,14 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - var expected = @" + var expectedOutput = @" 0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); } [Fact] - public void SpillArray02_3() + public void SpillArray02_1WithTaskAndRuntimeAsync() { var source = @" using System; @@ -1358,17 +2526,14 @@ public async Task GetVal(T t) return t; } - public async void Run(T t) where T : struct + public async Task Run(T t) where T : struct { int tests = 0; try { - int[] arr = new int[4]; - arr[0] = 4; - tests++; - arr[0] += await GetVal(4); - if (arr[0] == 8) + int[] arr = new int[await GetVal(4)]; + if (arr.Length == 4) Driver.Count++; } finally @@ -1388,7 +2553,9 @@ class Driver static void Main() { var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed t.Run(6); +#pragma warning restore CS4014 CompletedSignal.WaitOne(); // 0 - success @@ -1397,14 +2564,67 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - var expected = @" + var expectedOutput = @" 0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x42 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 67 (0x43) + .maxstack 2 + .locals init (int V_0) //tests + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldloc.0 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.0 + IL_0006: ldarg.0 + IL_0007: ldc.i4.4 + IL_0008: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_000d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0012: newarr "int" + IL_0017: ldlen + IL_0018: conv.i4 + IL_0019: ldc.i4.4 + IL_001a: bne.un.s IL_0028 + IL_001c: ldsfld "int Driver.Count" + IL_0021: ldc.i4.1 + IL_0022: add + IL_0023: stsfld "int Driver.Count" + IL_0028: leave.s IL_0042 + } + finally + { + IL_002a: ldsfld "int Driver.Count" + IL_002f: ldloc.0 + IL_0030: sub + IL_0031: stsfld "int Driver.Result" + IL_0036: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_003b: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0040: pop + IL_0041: endfinally + } + IL_0042: ret + } + """); } [Fact] - public void SpillArray02_4() + public void SpillArray02_2() { var source = @" using System; @@ -1424,11 +2644,11 @@ public async void Run(T t) where T : struct int tests = 0; try { - int[] arr = new int[] { 8, 0, 0, 0 }; - + int[] arr = new int[4]; + tests++; - arr[1] += await (GetVal(arr[0])); - if (arr[1] == 8) + arr[0] = await GetVal(4); + if (arr[0] == 4) Driver.Count++; } finally @@ -1457,14 +2677,14 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - var expected = @" + var expectedOutput = @" 0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); } [Fact] - public void SpillArray02_5() + public void SpillArray02_2WithTaskAndRuntimeAsync() { var source = @" using System; @@ -1479,21 +2699,16 @@ public async Task GetVal(T t) return t; } - public async void Run(T t) where T : struct + public async Task Run(T t) where T : struct { int tests = 0; try { - int[] arr = new int[] { 8, 8, 0, 0 }; - - tests++; - arr[1] += await (GetVal(arr[await GetVal(0)])); - if (arr[1] == 16) - Driver.Count++; - + int[] arr = new int[4]; + tests++; - arr[await GetVal(2)]++; - if (arr[2] == 1) + arr[0] = await GetVal(4); + if (arr[0] == 4) Driver.Count++; } finally @@ -1513,7 +2728,9 @@ class Driver static void Main() { var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed t.Run(6); +#pragma warning restore CS4014 CompletedSignal.WaitOne(); // 0 - success @@ -1522,14 +2739,74 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - var expected = @" + var expectedOutput = @" 0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x48 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 73 (0x49) + .maxstack 4 + .locals init (int V_0, //tests + int V_1) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldc.i4.4 + IL_0003: newarr "int" + IL_0008: ldloc.0 + IL_0009: ldc.i4.1 + IL_000a: add + IL_000b: stloc.0 + IL_000c: dup + IL_000d: ldarg.0 + IL_000e: ldc.i4.4 + IL_000f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0014: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0019: stloc.1 + IL_001a: ldc.i4.0 + IL_001b: ldloc.1 + IL_001c: stelem.i4 + IL_001d: ldc.i4.0 + IL_001e: ldelem.i4 + IL_001f: ldc.i4.4 + IL_0020: bne.un.s IL_002e + IL_0022: ldsfld "int Driver.Count" + IL_0027: ldc.i4.1 + IL_0028: add + IL_0029: stsfld "int Driver.Count" + IL_002e: leave.s IL_0048 + } + finally + { + IL_0030: ldsfld "int Driver.Count" + IL_0035: ldloc.0 + IL_0036: sub + IL_0037: stsfld "int Driver.Result" + IL_003c: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0041: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0046: pop + IL_0047: endfinally + } + IL_0048: ret + } + """); } [Fact] - public void SpillArray02_6() + public void SpillArray02_3() { var source = @" using System; @@ -1549,11 +2826,12 @@ public async void Run(T t) where T : struct int tests = 0; try { - int[] arr = new int[4]; - + int[] arr = new int[4]; + arr[0] = 4; + tests++; - arr[await GetVal(2)]++; - if (arr[2] == 1) + arr[0] += await GetVal(4); + if (arr[0] == 8) Driver.Count++; } finally @@ -1582,14 +2860,14 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - var expected = @" + var expectedOutput = @" 0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); } [Fact] - public void SpillArray03() + public void SpillArray02_3WithTaskAndRuntimeAsync() { var source = @" using System; @@ -1604,36 +2882,17 @@ public async Task GetVal(T t) return t; } - public async void Run(T t) where T : struct + public async Task Run(T t) where T : struct { int tests = 0; try { - int[,] arr = new int[await GetVal(4), await GetVal(4)]; - - tests++; - arr[0, 0] = await GetVal(4); - if (arr[0, await (GetVal(0))] == 4) - Driver.Count++; - - tests++; - arr[0, 0] += await GetVal(4); - if (arr[0, 0] == 8) - Driver.Count++; - - tests++; - arr[1, 1] += await (GetVal(arr[0, 0])); - if (arr[1, 1] == 8) - Driver.Count++; - - tests++; - arr[1, 1] += await (GetVal(arr[0, await GetVal(0)])); - if (arr[1, 1] == 16) - Driver.Count++; - + int[] arr = new int[4]; + arr[0] = 4; + tests++; - arr[2, await GetVal(2)]++; - if (arr[2, 2] == 1) + arr[0] += await GetVal(4); + if (arr[0] == 8) Driver.Count++; } finally @@ -1653,7 +2912,9 @@ class Driver static void Main() { var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed t.Run(6); +#pragma warning restore CS4014 CompletedSignal.WaitOne(); // 0 - success @@ -1662,173 +2923,295 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - var expected = @" + var expectedOutput = @" 0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (23,23): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[0] += await GetVal(4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run(T)").WithLocation(23, 23) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // [Run]: Return value missing on the stack. { Offset = 0x56 } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("TestCase.Run(T)", """ + // { + // // Code size 87 (0x57) + // .maxstack 4 + // .locals init (int V_0, //tests + // int V_1, + // int V_2) + // IL_0000: ldc.i4.0 + // IL_0001: stloc.0 + // .try + // { + // IL_0002: ldc.i4.4 + // IL_0003: newarr "int" + // IL_0008: dup + // IL_0009: ldc.i4.0 + // IL_000a: ldc.i4.4 + // IL_000b: stelem.i4 + // IL_000c: ldloc.0 + // IL_000d: ldc.i4.1 + // IL_000e: add + // IL_000f: stloc.0 + // IL_0010: dup + // IL_0011: ldc.i4.0 + // IL_0012: ldelema "int" + // IL_0017: dup + // IL_0018: ldind.i4 + // IL_0019: stloc.1 + // IL_001a: ldarg.0 + // IL_001b: ldc.i4.4 + // IL_001c: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0021: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0026: stloc.2 + // IL_0027: ldloc.1 + // IL_0028: ldloc.2 + // IL_0029: add + // IL_002a: stind.i4 + // IL_002b: ldc.i4.0 + // IL_002c: ldelem.i4 + // IL_002d: ldc.i4.8 + // IL_002e: bne.un.s IL_003c + // IL_0030: ldsfld "int Driver.Count" + // IL_0035: ldc.i4.1 + // IL_0036: add + // IL_0037: stsfld "int Driver.Count" + // IL_003c: leave.s IL_0056 + // } + // finally + // { + // IL_003e: ldsfld "int Driver.Count" + // IL_0043: ldloc.0 + // IL_0044: sub + // IL_0045: stsfld "int Driver.Result" + // IL_004a: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + // IL_004f: callvirt "bool System.Threading.EventWaitHandle.Set()" + // IL_0054: pop + // IL_0055: endfinally + // } + // IL_0056: ret + // } + // """); } [Fact] - public void SpillArray04() + public void SpillArray02_4() { var source = @" +using System; using System.Threading; using System.Threading.Tasks; -using System; -struct MyStruct +class TestCase { - T t { get; set; } - public T this[T index] + public async Task GetVal(T t) { - get - { - return t; - } - set - { - t = value; - } + await Task.Delay(1); + return t; } -} -struct TestCase -{ - public async void Run() + public async void Run(T t) where T : struct { + int tests = 0; try { - MyStruct ms = new MyStruct(); - var x = ms[index: await Goo()]; + int[] arr = new int[] { 8, 0, 0, 0 }; + + tests++; + arr[1] += await (GetVal(arr[0])); + if (arr[1] == 8) + Driver.Count++; } finally { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. Driver.CompletedSignal.Set(); } } - public async Task Goo() - { - await Task.Delay(1); - return 1; - } } class Driver { + public static int Result = -1; + public static int Count = 0; public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); static void Main() { var t = new TestCase(); - t.Run(); + t.Run(6); + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); } }"; - CompileAndVerify(source, ""); + var expectedOutput = @" +0 +"; + CompileAndVerify(source, expectedOutput); } [Fact] - public void SpillArrayAssign() + public void SpillArray02_4WithTaskAndRuntimeAsync() { var source = @" using System; +using System.Threading; using System.Threading.Tasks; class TestCase { - static int[] arr = new int[4]; - - static async Task Run() - { - arr[0] = await Task.Factory.StartNew(() => 42); - } - - static void Main() + public async Task GetVal(T t) { - Task task = Run(); - task.Wait(); - Console.WriteLine(arr[0]); + await Task.Delay(1); + return t; } -}"; - var expected = @" -42 -"; - CompileAndVerify(source, expected); - } - [WorkItem(19609, "https://github.com/dotnet/roslyn/issues/19609")] - [Fact] - public void SpillArrayAssign2() - { - var source = @" -using System.Threading.Tasks; - -class Program -{ - static int[] array = new int[5]; - - static void Main(string[] args) + public async Task Run(T t) where T : struct { + int tests = 0; try { - System.Console.WriteLine(""test not awaited""); - TestNotAwaited().Wait(); - } - catch - { - System.Console.WriteLine(""exception thrown""); - } - - System.Console.WriteLine(); + int[] arr = new int[] { 8, 0, 0, 0 }; - try - { - System.Console.WriteLine(""test awaited""); - TestAwaited().Wait(); + tests++; + arr[1] += await (GetVal(arr[0])); + if (arr[1] == 8) + Driver.Count++; } - catch + finally { - System.Console.WriteLine(""exception thrown""); + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); } - - } - - static async Task TestNotAwaited() - { - array[6] = Moo1(); - } - - static async Task TestAwaited() - { - array[6] = await Moo(); } +} - static int Moo1() +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() { - System.Console.WriteLine(""hello""); - return 123; - } + var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + t.Run(6); +#pragma warning restore CS4014 - static async Task Moo() - { - System.Console.WriteLine(""hello""); - return 123; + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); } }"; - - var expected = @" -test not awaited -hello -exception thrown - -test awaited -hello -exception thrown + var expectedOutput = @" +0 "; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (22,23): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[1] += await (GetVal(arr[0])); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[0]))").WithArguments("TestCase.Run(T)").WithLocation(22, 23) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // [Run]: Return value missing on the stack. { Offset = 0x5a } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("TestCase.Run(T)", """ + // { + // // Code size 91 (0x5b) + // .maxstack 4 + // .locals init (int V_0, //tests + // int[] V_1, //arr + // int V_2, + // int V_3) + // IL_0000: ldc.i4.0 + // IL_0001: stloc.0 + // .try + // { + // IL_0002: ldc.i4.4 + // IL_0003: newarr "int" + // IL_0008: dup + // IL_0009: ldc.i4.0 + // IL_000a: ldc.i4.8 + // IL_000b: stelem.i4 + // IL_000c: stloc.1 + // IL_000d: ldloc.0 + // IL_000e: ldc.i4.1 + // IL_000f: add + // IL_0010: stloc.0 + // IL_0011: ldloc.1 + // IL_0012: ldc.i4.1 + // IL_0013: ldelema "int" + // IL_0018: dup + // IL_0019: ldind.i4 + // IL_001a: stloc.2 + // IL_001b: ldarg.0 + // IL_001c: ldloc.1 + // IL_001d: ldc.i4.0 + // IL_001e: ldelem.i4 + // IL_001f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0024: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0029: stloc.3 + // IL_002a: ldloc.2 + // IL_002b: ldloc.3 + // IL_002c: add + // IL_002d: stind.i4 + // IL_002e: ldloc.1 + // IL_002f: ldc.i4.1 + // IL_0030: ldelem.i4 + // IL_0031: ldc.i4.8 + // IL_0032: bne.un.s IL_0040 + // IL_0034: ldsfld "int Driver.Count" + // IL_0039: ldc.i4.1 + // IL_003a: add + // IL_003b: stsfld "int Driver.Count" + // IL_0040: leave.s IL_005a + // } + // finally + // { + // IL_0042: ldsfld "int Driver.Count" + // IL_0047: ldloc.0 + // IL_0048: sub + // IL_0049: stsfld "int Driver.Result" + // IL_004e: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + // IL_0053: callvirt "bool System.Threading.EventWaitHandle.Set()" + // IL_0058: pop + // IL_0059: endfinally + // } + // IL_005a: ret + // } + // """); } [Fact] - public void SpillArrayLocal() + public void SpillArray02_5() { var source = @" using System; @@ -1845,14 +3228,19 @@ public async Task GetVal(T t) public async void Run(T t) where T : struct { - int[] arr = new int[2] { -1, 42 }; - int tests = 0; try { + int[] arr = new int[] { 8, 8, 0, 0 }; + tests++; - int t1 = arr[await GetVal(1)]; - if (t1 == 42) + arr[1] += await (GetVal(arr[await GetVal(0)])); + if (arr[1] == 16) + Driver.Count++; + + tests++; + arr[await GetVal(2)]++; + if (arr[2] == 1) Driver.Count++; } finally @@ -1881,154 +3269,393 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - var expected = @" + var expectedOutput = @" 0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); } [Fact] - public void SpillArrayCompoundAssignmentLValue() + public void SpillArray02_5WithTaskAndRuntimeAsync() { var source = @" using System; using System.Threading; using System.Threading.Tasks; -class Driver +class TestCase { - static int[] arr; + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } - static async Task Run() + public async Task Run(T t) where T : struct { - arr = new int[1]; - arr[0] += await Task.Factory.StartNew(() => 42); + int tests = 0; + try + { + int[] arr = new int[] { 8, 8, 0, 0 }; + + tests++; + arr[1] += await (GetVal(arr[await GetVal(0)])); + if (arr[1] == 16) + Driver.Count++; + + tests++; + arr[await GetVal(2)]++; + if (arr[2] == 1) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } } +} +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); static void Main() { - Run().Wait(); - Console.WriteLine(arr[0]); + var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + t.Run(6); +#pragma warning restore CS4014 + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); } }"; - var expected = @" -42 + var expectedOutput = @" +0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (22,23): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[1] += await (GetVal(arr[await GetVal(0)])); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[await GetVal(0)]))").WithArguments("TestCase.Run(T)").WithLocation(22, 23) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // [Run]: Return value missing on the stack. { Offset = 0xa3 } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("TestCase.Run(T)", """ + // { + // // Code size 164 (0xa4) + // .maxstack 4 + // .locals init (int V_0, //tests + // int& V_1, + // int V_2, + // int V_3, + // int[] V_4, + // int V_5) + // IL_0000: ldc.i4.0 + // IL_0001: stloc.0 + // .try + // { + // IL_0002: ldc.i4.4 + // IL_0003: newarr "int" + // IL_0008: dup + // IL_0009: ldc.i4.0 + // IL_000a: ldc.i4.8 + // IL_000b: stelem.i4 + // IL_000c: dup + // IL_000d: ldc.i4.1 + // IL_000e: ldc.i4.8 + // IL_000f: stelem.i4 + // IL_0010: ldloc.0 + // IL_0011: ldc.i4.1 + // IL_0012: add + // IL_0013: stloc.0 + // IL_0014: dup + // IL_0015: ldc.i4.1 + // IL_0016: ldelema "int" + // IL_001b: stloc.1 + // IL_001c: ldloc.1 + // IL_001d: ldind.i4 + // IL_001e: stloc.2 + // IL_001f: dup + // IL_0020: stloc.s V_4 + // IL_0022: ldarg.0 + // IL_0023: ldc.i4.0 + // IL_0024: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0029: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_002e: stloc.s V_5 + // IL_0030: ldarg.0 + // IL_0031: ldloc.s V_4 + // IL_0033: ldloc.s V_5 + // IL_0035: ldelem.i4 + // IL_0036: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_003b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0040: stloc.3 + // IL_0041: ldloc.1 + // IL_0042: ldloc.2 + // IL_0043: ldloc.3 + // IL_0044: add + // IL_0045: stind.i4 + // IL_0046: dup + // IL_0047: ldc.i4.1 + // IL_0048: ldelem.i4 + // IL_0049: ldc.i4.s 16 + // IL_004b: bne.un.s IL_0059 + // IL_004d: ldsfld "int Driver.Count" + // IL_0052: ldc.i4.1 + // IL_0053: add + // IL_0054: stsfld "int Driver.Count" + // IL_0059: ldloc.0 + // IL_005a: ldc.i4.1 + // IL_005b: add + // IL_005c: stloc.0 + // IL_005d: dup + // IL_005e: ldarg.0 + // IL_005f: ldc.i4.2 + // IL_0060: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0065: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_006a: stloc.3 + // IL_006b: ldloc.3 + // IL_006c: ldelema "int" + // IL_0071: dup + // IL_0072: ldind.i4 + // IL_0073: stloc.2 + // IL_0074: ldloc.2 + // IL_0075: ldc.i4.1 + // IL_0076: add + // IL_0077: stind.i4 + // IL_0078: ldc.i4.2 + // IL_0079: ldelem.i4 + // IL_007a: ldc.i4.1 + // IL_007b: bne.un.s IL_0089 + // IL_007d: ldsfld "int Driver.Count" + // IL_0082: ldc.i4.1 + // IL_0083: add + // IL_0084: stsfld "int Driver.Count" + // IL_0089: leave.s IL_00a3 + // } + // finally + // { + // IL_008b: ldsfld "int Driver.Count" + // IL_0090: ldloc.0 + // IL_0091: sub + // IL_0092: stsfld "int Driver.Result" + // IL_0097: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + // IL_009c: callvirt "bool System.Threading.EventWaitHandle.Set()" + // IL_00a1: pop + // IL_00a2: endfinally + // } + // IL_00a3: ret + // } + // """); } [Fact] - public void SpillArrayCompoundAssignmentLValueAwait() + public void SpillArray02_6() { var source = @" using System; using System.Threading; using System.Threading.Tasks; -class Driver +class TestCase { - static int[] arr; + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } - static async Task Run() + public async void Run(T t) where T : struct { - arr = new int[1]; - arr[await Task.Factory.StartNew(() => 0)] += await Task.Factory.StartNew(() => 42); + int tests = 0; + try + { + int[] arr = new int[4]; + + tests++; + arr[await GetVal(2)]++; + if (arr[2] == 1) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } } +} +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); static void Main() { - Run().Wait(); - Console.WriteLine(arr[0]); + var t = new TestCase(); + t.Run(6); + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); } }"; - var expected = @" -42 + var expectedOutput = @" +0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); } [Fact] - public void SpillArrayCompoundAssignmentLValueAwait2() + public void SpillArray02_6WithTaskAndRuntimeAsync() { var source = @" using System; using System.Threading; using System.Threading.Tasks; -struct S1 +class TestCase { - public int x; -} + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } -struct S2 -{ - public S1 s1; -} - -class Driver -{ - static async Task Run() + public async Task Run(T t) where T : struct { - var arr = new S2[1]; - arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42); - return arr[await Task.Factory.StartNew(() => 0)].s1.x; - } + int tests = 0; + try + { + int[] arr = new int[4]; - static void Main() - { - var t = Run(); - t.Wait(); - Console.WriteLine(t.Result); - } -}"; - var expected = @" -42 -"; - CompileAndVerify(source, expected); + tests++; + arr[await GetVal(2)]++; + if (arr[2] == 1) + Driver.Count++; } - - [Fact] - public void DoubleSpillArrayCompoundAssignment() + finally { - var source = @" -using System; -using System.Threading; -using System.Threading.Tasks; - -struct S1 -{ - public int x; -} - -struct S2 -{ - public S1 s1; + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } } class Driver { - static async Task Run() - { - var arr = new S2[1]; - arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); - return arr[await Task.Factory.StartNew(() => 0)].s1.x; - } - + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); static void Main() { - var t = Run(); - t.Wait(); - Console.WriteLine(t.Result); + var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + t.Run(6); +#pragma warning restore CS4014 + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); } }"; - var expected = @" -42 + var expectedOutput = @" +0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x52 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 83 (0x53) + .maxstack 4 + .locals init (int V_0, //tests + int V_1, + int V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldc.i4.4 + IL_0003: newarr "int" + IL_0008: ldloc.0 + IL_0009: ldc.i4.1 + IL_000a: add + IL_000b: stloc.0 + IL_000c: dup + IL_000d: ldarg.0 + IL_000e: ldc.i4.2 + IL_000f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0014: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0019: stloc.1 + IL_001a: ldloc.1 + IL_001b: ldelema "int" + IL_0020: dup + IL_0021: ldind.i4 + IL_0022: stloc.2 + IL_0023: ldloc.2 + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: stind.i4 + IL_0027: ldc.i4.2 + IL_0028: ldelem.i4 + IL_0029: ldc.i4.1 + IL_002a: bne.un.s IL_0038 + IL_002c: ldsfld "int Driver.Count" + IL_0031: ldc.i4.1 + IL_0032: add + IL_0033: stsfld "int Driver.Count" + IL_0038: leave.s IL_0052 + } + finally + { + IL_003a: ldsfld "int Driver.Count" + IL_003f: ldloc.0 + IL_0040: sub + IL_0041: stsfld "int Driver.Result" + IL_0046: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_004b: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0050: pop + IL_0051: endfinally + } + IL_0052: ret + } + """); } [Fact] - public void SpillArrayInitializers1() + public void SpillArray03() { var source = @" using System; @@ -2043,28 +3670,36 @@ public async Task GetVal(T t) return t; } - public async void Run() + public async void Run(T t) where T : struct { int tests = 0; try { - //jagged array + int[,] arr = new int[await GetVal(4), await GetVal(4)]; + tests++; - int[][] arr1 = new[] - { - new []{await GetVal(2),await GetVal(3)}, - new []{4,await GetVal(5),await GetVal(6)} - }; - if (arr1[0][1] == 3 && arr1[1][1] == 5 && arr1[1][2] == 6) + arr[0, 0] = await GetVal(4); + if (arr[0, await (GetVal(0))] == 4) Driver.Count++; tests++; - int[][] arr2 = new[] - { - new []{await GetVal(2),await GetVal(3)}, - await Goo() - }; - if (arr2[0][1] == 3 && arr2[1][1] == 2) + arr[0, 0] += await GetVal(4); + if (arr[0, 0] == 8) + Driver.Count++; + + tests++; + arr[1, 1] += await (GetVal(arr[0, 0])); + if (arr[1, 1] == 8) + Driver.Count++; + + tests++; + arr[1, 1] += await (GetVal(arr[0, await GetVal(0)])); + if (arr[1, 1] == 16) + Driver.Count++; + + tests++; + arr[2, await GetVal(2)]++; + if (arr[2, 2] == 1) Driver.Count++; } finally @@ -2074,12 +3709,6 @@ await Goo() Driver.CompletedSignal.Set(); } } - - public async Task Goo() - { - await Task.Delay(1); - return new int[] { 1, 2, 3 }; - } } class Driver @@ -2090,7 +3719,7 @@ class Driver static void Main() { var t = new TestCase(); - t.Run(); + t.Run(6); CompletedSignal.WaitOne(); // 0 - success @@ -2099,14 +3728,14 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - var expected = @" + var expectedOutput = @" 0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); } [Fact] - public void SpillArrayInitializers2() + public void SpillArray03WithTaskAndRuntimeAsync() { var source = @" using System; @@ -2121,28 +3750,36 @@ public async Task GetVal(T t) return t; } - public async void Run() + public async Task Run(T t) where T : struct { int tests = 0; try { - //jagged array + int[,] arr = new int[await GetVal(4), await GetVal(4)]; + tests++; - int[,] arr1 = - { - {await GetVal(2),await GetVal(3)}, - {await GetVal(5),await GetVal(6)} - }; - if (arr1[0, 1] == 3 && arr1[1, 0] == 5 && arr1[1, 1] == 6) + arr[0, 0] = await GetVal(4); + if (arr[0, await (GetVal(0))] == 4) Driver.Count++; tests++; - int[,] arr2 = - { - {await GetVal(2),3}, - {4,await GetVal(5)} - }; - if (arr2[0, 1] == 3 && arr2[1, 1] == 5) + arr[0, 0] += await GetVal(4); + if (arr[0, 0] == 8) + Driver.Count++; + + tests++; + arr[1, 1] += await (GetVal(arr[0, 0])); + if (arr[1, 1] == 8) + Driver.Count++; + + tests++; + arr[1, 1] += await (GetVal(arr[0, await GetVal(0)])); + if (arr[1, 1] == 16) + Driver.Count++; + + tests++; + arr[2, await GetVal(2)]++; + if (arr[2, 2] == 1) Driver.Count++; } finally @@ -2162,422 +3799,3071 @@ class Driver static void Main() { var t = new TestCase(); - t.Run(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + t.Run(6); +#pragma warning restore CS4014 CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) Console.WriteLine(Driver.Result); } }"; - var expected = @" + var expectedOutput = @" 0 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (27,26): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[0, 0] += await GetVal(4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run(T)").WithLocation(27, 26), + // (32,26): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[1, 1] += await (GetVal(arr[0, 0])); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[0, 0]))").WithArguments("TestCase.Run(T)").WithLocation(32, 26), + // (37,26): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[1, 1] += await (GetVal(arr[0, await GetVal(0)])); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[0, await GetVal(0)]))").WithArguments("TestCase.Run(T)").WithLocation(37, 26) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // [Run]: Return value missing on the stack. { Offset = 0x178 } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("TestCase.Run(T)", """ + // { + // // Code size 377 (0x179) + // .maxstack 5 + // .locals init (int V_0, //tests + // int[,] V_1, //arr + // int V_2, + // int V_3, + // int[,] V_4, + // int V_5) + // IL_0000: ldc.i4.0 + // IL_0001: stloc.0 + // .try + // { + // IL_0002: ldarg.0 + // IL_0003: ldc.i4.4 + // IL_0004: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0009: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_000e: ldarg.0 + // IL_000f: ldc.i4.4 + // IL_0010: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0015: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_001a: stloc.2 + // IL_001b: ldloc.2 + // IL_001c: newobj "int[*,*]..ctor" + // IL_0021: stloc.1 + // IL_0022: ldloc.0 + // IL_0023: ldc.i4.1 + // IL_0024: add + // IL_0025: stloc.0 + // IL_0026: ldloc.1 + // IL_0027: ldarg.0 + // IL_0028: ldc.i4.4 + // IL_0029: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_002e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0033: stloc.2 + // IL_0034: ldc.i4.0 + // IL_0035: ldc.i4.0 + // IL_0036: ldloc.2 + // IL_0037: call "int[*,*].Set" + // IL_003c: ldloc.1 + // IL_003d: ldarg.0 + // IL_003e: ldc.i4.0 + // IL_003f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0044: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0049: stloc.2 + // IL_004a: ldc.i4.0 + // IL_004b: ldloc.2 + // IL_004c: call "int[*,*].Get" + // IL_0051: ldc.i4.4 + // IL_0052: bne.un.s IL_0060 + // IL_0054: ldsfld "int Driver.Count" + // IL_0059: ldc.i4.1 + // IL_005a: add + // IL_005b: stsfld "int Driver.Count" + // IL_0060: ldloc.0 + // IL_0061: ldc.i4.1 + // IL_0062: add + // IL_0063: stloc.0 + // IL_0064: ldloc.1 + // IL_0065: ldc.i4.0 + // IL_0066: ldc.i4.0 + // IL_0067: call "int[*,*].Address" + // IL_006c: dup + // IL_006d: ldind.i4 + // IL_006e: stloc.2 + // IL_006f: ldarg.0 + // IL_0070: ldc.i4.4 + // IL_0071: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0076: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_007b: stloc.3 + // IL_007c: ldloc.2 + // IL_007d: ldloc.3 + // IL_007e: add + // IL_007f: stind.i4 + // IL_0080: ldloc.1 + // IL_0081: ldc.i4.0 + // IL_0082: ldc.i4.0 + // IL_0083: call "int[*,*].Get" + // IL_0088: ldc.i4.8 + // IL_0089: bne.un.s IL_0097 + // IL_008b: ldsfld "int Driver.Count" + // IL_0090: ldc.i4.1 + // IL_0091: add + // IL_0092: stsfld "int Driver.Count" + // IL_0097: ldloc.0 + // IL_0098: ldc.i4.1 + // IL_0099: add + // IL_009a: stloc.0 + // IL_009b: ldloc.1 + // IL_009c: ldc.i4.1 + // IL_009d: ldc.i4.1 + // IL_009e: call "int[*,*].Address" + // IL_00a3: dup + // IL_00a4: ldind.i4 + // IL_00a5: stloc.3 + // IL_00a6: ldarg.0 + // IL_00a7: ldloc.1 + // IL_00a8: ldc.i4.0 + // IL_00a9: ldc.i4.0 + // IL_00aa: call "int[*,*].Get" + // IL_00af: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_00b4: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_00b9: stloc.2 + // IL_00ba: ldloc.3 + // IL_00bb: ldloc.2 + // IL_00bc: add + // IL_00bd: stind.i4 + // IL_00be: ldloc.1 + // IL_00bf: ldc.i4.1 + // IL_00c0: ldc.i4.1 + // IL_00c1: call "int[*,*].Get" + // IL_00c6: ldc.i4.8 + // IL_00c7: bne.un.s IL_00d5 + // IL_00c9: ldsfld "int Driver.Count" + // IL_00ce: ldc.i4.1 + // IL_00cf: add + // IL_00d0: stsfld "int Driver.Count" + // IL_00d5: ldloc.0 + // IL_00d6: ldc.i4.1 + // IL_00d7: add + // IL_00d8: stloc.0 + // IL_00d9: ldloc.1 + // IL_00da: ldc.i4.1 + // IL_00db: ldc.i4.1 + // IL_00dc: call "int[*,*].Address" + // IL_00e1: dup + // IL_00e2: ldind.i4 + // IL_00e3: stloc.2 + // IL_00e4: ldloc.1 + // IL_00e5: stloc.s V_4 + // IL_00e7: ldarg.0 + // IL_00e8: ldc.i4.0 + // IL_00e9: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_00ee: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_00f3: stloc.s V_5 + // IL_00f5: ldarg.0 + // IL_00f6: ldloc.s V_4 + // IL_00f8: ldc.i4.0 + // IL_00f9: ldloc.s V_5 + // IL_00fb: call "int[*,*].Get" + // IL_0100: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0105: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_010a: stloc.3 + // IL_010b: ldloc.2 + // IL_010c: ldloc.3 + // IL_010d: add + // IL_010e: stind.i4 + // IL_010f: ldloc.1 + // IL_0110: ldc.i4.1 + // IL_0111: ldc.i4.1 + // IL_0112: call "int[*,*].Get" + // IL_0117: ldc.i4.s 16 + // IL_0119: bne.un.s IL_0127 + // IL_011b: ldsfld "int Driver.Count" + // IL_0120: ldc.i4.1 + // IL_0121: add + // IL_0122: stsfld "int Driver.Count" + // IL_0127: ldloc.0 + // IL_0128: ldc.i4.1 + // IL_0129: add + // IL_012a: stloc.0 + // IL_012b: ldloc.1 + // IL_012c: ldarg.0 + // IL_012d: ldc.i4.2 + // IL_012e: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0133: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0138: stloc.3 + // IL_0139: ldc.i4.2 + // IL_013a: ldloc.3 + // IL_013b: call "int[*,*].Address" + // IL_0140: dup + // IL_0141: ldind.i4 + // IL_0142: stloc.2 + // IL_0143: ldloc.2 + // IL_0144: ldc.i4.1 + // IL_0145: add + // IL_0146: stind.i4 + // IL_0147: ldloc.1 + // IL_0148: ldc.i4.2 + // IL_0149: ldc.i4.2 + // IL_014a: call "int[*,*].Get" + // IL_014f: ldc.i4.1 + // IL_0150: bne.un.s IL_015e + // IL_0152: ldsfld "int Driver.Count" + // IL_0157: ldc.i4.1 + // IL_0158: add + // IL_0159: stsfld "int Driver.Count" + // IL_015e: leave.s IL_0178 + // } + // finally + // { + // IL_0160: ldsfld "int Driver.Count" + // IL_0165: ldloc.0 + // IL_0166: sub + // IL_0167: stsfld "int Driver.Result" + // IL_016c: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + // IL_0171: callvirt "bool System.Threading.EventWaitHandle.Set()" + // IL_0176: pop + // IL_0177: endfinally + // } + // IL_0178: ret + // } + // """); } [Fact] - public void SpillArrayInitializers3() + public void SpillArray04() { var source = @" -using System; using System.Threading; using System.Threading.Tasks; -class TestCase +struct MyStruct { - public async Task GetVal(T t) + T t { get; set; } + public T this[T index] { - await Task.Delay(1); - return t; + get + { + return t; + } + set + { + t = value; + } } +} +struct TestCase +{ public async void Run() { - int tests = 0; try { - //jagged array - tests++; - int[][] arr1 = new[] - { - new []{await GetVal(2),await Task.Run(async()=>{await Task.Delay(1);return 3;})}, - new []{await GetVal(5),4,await Task.Run(async()=>{await Task.Delay(1);return 6;})} - }; - if (arr1[0][1] == 3 && arr1[1][1] == 4 && arr1[1][2] == 6) - Driver.Count++; - - tests++; - dynamic arr2 = new[] - { - new []{await GetVal(2),3}, - await Goo() - }; - if (arr2[0][1] == 3 && arr2[1][1] == 2) - Driver.Count++; + MyStruct ms = new MyStruct(); + var x = ms[index: await Goo()]; } finally { - Driver.Result = Driver.Count - tests; - //When test complete, set the flag. Driver.CompletedSignal.Set(); } } - - public async Task Goo() + public async Task Goo() { await Task.Delay(1); - return new int[] { 1, 2, 3 }; + return 1; } } class Driver { - public static int Result = -1; - public static int Count = 0; public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); static void Main() { var t = new TestCase(); t.Run(); - CompletedSignal.WaitOne(); - // 0 - success - // 1 - failed (test completed) - // -1 - failed (test incomplete - deadlock, etc) - Console.WriteLine(Driver.Result); } }"; - var expected = @" -0 -"; - CompileAndVerify(source, expected, references: new[] { CSharpRef }); + CompileAndVerify(source, ""); } [Fact] - public void SpillNestedExpressionInArrayInitializer() + public void SpillArray04WithTaskAndRuntimeAsync() { var source = @" -using System; +using System.Threading; using System.Threading.Tasks; -class Test +struct MyStruct { - public static async Task Run() + T t { get; set; } + public T this[T index] { - return new int[,] { - {1, 2, 21 + (await Task.Factory.StartNew(() => 21)) }, - }; + get + { + return t; + } + set + { + t = value; + } } +} - public static void Main() +struct TestCase +{ + public async Task Run() { - var t = Run(); - t.Wait(); - foreach (var xs in t.Result) + try { - Console.WriteLine(xs); + MyStruct ms = new MyStruct(); + var x = ms[index: await Goo()]; + } + finally + { + Driver.CompletedSignal.Set(); } } + public async Task Goo() + { + await Task.Delay(1); + return 1; + } +} + +class Driver +{ + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + t.Run(); +#pragma warning restore CS4014 + CompletedSignal.WaitOne(); + } }"; - var expected = @" -1 -2 + CompileAndVerify(source, ""); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (23,23): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // public async Task Run() + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Run").WithArguments("TestCase.Run()").WithLocation(23, 23) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("", isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [Run]: Return value missing on the stack. { Offset = 0x2b } + // [Goo]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("TestCase.Run()", """ + // { + // // Code size 44 (0x2c) + // .maxstack 2 + // .locals init (MyStruct V_0, //ms + // int V_1) + // .try + // { + // IL_0000: ldloca.s V_0 + // IL_0002: initobj "MyStruct" + // IL_0008: ldarg.0 + // IL_0009: call "System.Threading.Tasks.Task TestCase.Goo()" + // IL_000e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0013: stloc.1 + // IL_0014: ldloca.s V_0 + // IL_0016: ldloc.1 + // IL_0017: call "int MyStruct.this[int].get" + // IL_001c: pop + // IL_001d: leave.s IL_002b + // } + // finally + // { + // IL_001f: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + // IL_0024: callvirt "bool System.Threading.EventWaitHandle.Set()" + // IL_0029: pop + // IL_002a: endfinally + // } + // IL_002b: ret + // } + // """); + } + + [Fact] + public void SpillArrayAssign() + { + var source = @" +using System; +using System.Threading.Tasks; + +class TestCase +{ + static int[] arr = new int[4]; + + static async Task Run() + { + arr[0] = await Task.Factory.StartNew(() => 42); + } + + static void Main() + { + Task task = Run(); + task.Wait(); + Console.WriteLine(arr[0]); + } +}"; + var expectedOutput = @" 42 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Return value missing on the stack. { Offset = 0x37 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 56 (0x38) + .maxstack 4 + .locals init (int V_0) + IL_0000: ldsfld "int[] TestCase.arr" + IL_0005: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_000a: ldsfld "System.Func TestCase.<>c.<>9__1_0" + IL_000f: dup + IL_0010: brtrue.s IL_0029 + IL_0012: pop + IL_0013: ldsfld "TestCase.<>c TestCase.<>c.<>9" + IL_0018: ldftn "int TestCase.<>c.b__1_0()" + IL_001e: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0023: dup + IL_0024: stsfld "System.Func TestCase.<>c.<>9__1_0" + IL_0029: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_002e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0033: stloc.0 + IL_0034: ldc.i4.0 + IL_0035: ldloc.0 + IL_0036: stelem.i4 + IL_0037: ret + } + """); } + [WorkItem(19609, "https://github.com/dotnet/roslyn/issues/19609")] [Fact] - public void SpillConditionalAccess() + public void SpillArrayAssign2() { var source = @" -using System; -using System.Collections.Generic; +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously using System.Threading.Tasks; -class Test +class Program { + static int[] array = new int[5]; - class C1 + static void Main(string[] args) { - public int M(int x) + try { - return x; + System.Console.WriteLine(""test not awaited""); + TestNotAwaited().Wait(); + } + catch + { + System.Console.WriteLine(""exception thrown""); + } + + System.Console.WriteLine(); + + try + { + System.Console.WriteLine(""test awaited""); + TestAwaited().Wait(); + } + catch + { + System.Console.WriteLine(""exception thrown""); } + } - public static int Get(int x) + static async Task TestNotAwaited() { - Console.WriteLine(""> "" + x); - return x; + array[6] = Moo1(); } - public static async Task F(int x) + static async Task TestAwaited() { - return await Task.Factory.StartNew(() => x); + array[6] = await Moo(); } - public static async Task G() + static int Moo1() { - var c = new C1(); - return c?.M(await F(Get(42))); + System.Console.WriteLine(""hello""); + return 123; } - public static void Main() + static async Task Moo() { - var t = G(); - System.Console.WriteLine(t.Result); + System.Console.WriteLine(""hello""); + return 123; } }"; - var expected = @" -> 42 -42"; - CompileAndVerify(source, expected); + + var expectedOutput = @" +test not awaited +hello +exception thrown + +test awaited +hello +exception thrown +"; + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [TestNotAwaited]: Return value missing on the stack. { Offset = 0xc } + [TestAwaited]: Return value missing on the stack. { Offset = 0x13 } + [Moo]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.TestNotAwaited()", """ + { + // Code size 13 (0xd) + .maxstack 3 + IL_0000: ldsfld "int[] Program.array" + IL_0005: ldc.i4.6 + IL_0006: call "int Program.Moo1()" + IL_000b: stelem.i4 + IL_000c: ret + } + """); + verifier.VerifyIL("Program.TestAwaited()", """ + { + // Code size 20 (0x14) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldsfld "int[] Program.array" + IL_0005: call "System.Threading.Tasks.Task Program.Moo()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: ldc.i4.6 + IL_0011: ldloc.0 + IL_0012: stelem.i4 + IL_0013: ret + } + """); } [Fact] - public void AssignToAwait() + public void SpillArrayLocal() { var source = @" using System; using System.Threading; using System.Threading.Tasks; -class S +class TestCase { - public int x = -1; + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } + + public async void Run(T t) where T : struct + { + int[] arr = new int[2] { -1, 42 }; + + int tests = 0; + try + { + tests++; + int t1 = arr[await GetVal(1)]; + if (t1 == 42) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } } -class Test +class Driver { - static S _s = new S(); + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); + t.Run(6); - public static async Task GetS() + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + var expectedOutput = @" +0 +"; + CompileAndVerify(source, expectedOutput); + } + + [Fact] + public void SpillArrayLocalWithTaskAndRuntimeAsync() + { + var source = @" +using System; +using System.Threading; +using System.Threading.Tasks; + +class TestCase +{ + public async Task GetVal(T t) { - return await Task.Factory.StartNew(() => _s); + await Task.Delay(1); + return t; } - public static async Task Run() + public async Task Run(T t) where T : struct { - (await GetS()).x = 42; - Console.WriteLine(_s.x); + int[] arr = new int[2] { -1, 42 }; + + int tests = 0; + try + { + tests++; + int t1 = arr[await GetVal(1)]; + if (t1 == 42) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } } } class Driver { + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); static void Main() { - Test.Run().Wait(); + var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + t.Run(6); +#pragma warning restore CS4014 + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + var expectedOutput = @" +0 +"; + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x50 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 81 (0x51) + .maxstack 4 + .locals init (int[] V_0, //arr + int V_1, //tests + int V_2) + IL_0000: ldc.i4.2 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.m1 + IL_0009: stelem.i4 + IL_000a: dup + IL_000b: ldc.i4.1 + IL_000c: ldc.i4.s 42 + IL_000e: stelem.i4 + IL_000f: stloc.0 + IL_0010: ldc.i4.0 + IL_0011: stloc.1 + .try + { + IL_0012: ldloc.1 + IL_0013: ldc.i4.1 + IL_0014: add + IL_0015: stloc.1 + IL_0016: ldloc.0 + IL_0017: ldarg.0 + IL_0018: ldc.i4.1 + IL_0019: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_001e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: stloc.2 + IL_0024: ldloc.2 + IL_0025: ldelem.i4 + IL_0026: ldc.i4.s 42 + IL_0028: bne.un.s IL_0036 + IL_002a: ldsfld "int Driver.Count" + IL_002f: ldc.i4.1 + IL_0030: add + IL_0031: stsfld "int Driver.Count" + IL_0036: leave.s IL_0050 + } + finally + { + IL_0038: ldsfld "int Driver.Count" + IL_003d: ldloc.1 + IL_003e: sub + IL_003f: stsfld "int Driver.Result" + IL_0044: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0049: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_004e: pop + IL_004f: endfinally + } + IL_0050: ret + } + """); + } + + [Fact] + public void SpillArrayCompoundAssignmentLValue() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Driver +{ + static int[] arr; + + static async Task Run() + { + arr = new int[1]; + arr[0] += await Task.Factory.StartNew(() => 42); + } + + static void Main() + { + Run().Wait(); + Console.WriteLine(arr[0]); + } +}"; + var expectedOutput = @" +42 +"; + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (12,19): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[0] += await Task.Factory.StartNew(() => 42); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 42)").WithArguments("Driver.Run()").WithLocation(12, 19) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [Run]: Return value missing on the stack. { Offset = 0x4c } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("Driver.Run()", """ + // { + // // Code size 77 (0x4d) + // .maxstack 4 + // .locals init (int V_0, + // int V_1) + // IL_0000: ldc.i4.1 + // IL_0001: newarr "int" + // IL_0006: stsfld "int[] Driver.arr" + // IL_000b: ldsfld "int[] Driver.arr" + // IL_0010: ldc.i4.0 + // IL_0011: ldelema "int" + // IL_0016: dup + // IL_0017: ldind.i4 + // IL_0018: stloc.0 + // IL_0019: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + // IL_001e: ldsfld "System.Func Driver.<>c.<>9__1_0" + // IL_0023: dup + // IL_0024: brtrue.s IL_003d + // IL_0026: pop + // IL_0027: ldsfld "Driver.<>c Driver.<>c.<>9" + // IL_002c: ldftn "int Driver.<>c.b__1_0()" + // IL_0032: newobj "System.Func..ctor(object, System.IntPtr)" + // IL_0037: dup + // IL_0038: stsfld "System.Func Driver.<>c.<>9__1_0" + // IL_003d: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + // IL_0042: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0047: stloc.1 + // IL_0048: ldloc.0 + // IL_0049: ldloc.1 + // IL_004a: add + // IL_004b: stind.i4 + // IL_004c: ret + // } + // """); + } + + [Fact] + public void SpillArrayCompoundAssignmentLValueAwait() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Driver +{ + static int[] arr; + + static async Task Run() + { + arr = new int[1]; + arr[await Task.Factory.StartNew(() => 0)] += await Task.Factory.StartNew(() => 42); + } + + static void Main() + { + Run().Wait(); + Console.WriteLine(arr[0]); + } +}"; + var expectedOutput = @" +42 +"; + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (12,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[await Task.Factory.StartNew(() => 0)] += await Task.Factory.StartNew(() => 42); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(12, 13) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [Run]: Return value missing on the stack. { Offset = 0x7b } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("Driver.Run()", """ + // { + // // Code size 124 (0x7c) + // .maxstack 4 + // .locals init (int V_0, + // int V_1, + // int V_2) + // IL_0000: ldc.i4.1 + // IL_0001: newarr "int" + // IL_0006: stsfld "int[] Driver.arr" + // IL_000b: ldsfld "int[] Driver.arr" + // IL_0010: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + // IL_0015: ldsfld "System.Func Driver.<>c.<>9__1_0" + // IL_001a: dup + // IL_001b: brtrue.s IL_0034 + // IL_001d: pop + // IL_001e: ldsfld "Driver.<>c Driver.<>c.<>9" + // IL_0023: ldftn "int Driver.<>c.b__1_0()" + // IL_0029: newobj "System.Func..ctor(object, System.IntPtr)" + // IL_002e: dup + // IL_002f: stsfld "System.Func Driver.<>c.<>9__1_0" + // IL_0034: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + // IL_0039: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_003e: stloc.0 + // IL_003f: ldloc.0 + // IL_0040: ldelema "int" + // IL_0045: dup + // IL_0046: ldind.i4 + // IL_0047: stloc.1 + // IL_0048: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + // IL_004d: ldsfld "System.Func Driver.<>c.<>9__1_1" + // IL_0052: dup + // IL_0053: brtrue.s IL_006c + // IL_0055: pop + // IL_0056: ldsfld "Driver.<>c Driver.<>c.<>9" + // IL_005b: ldftn "int Driver.<>c.b__1_1()" + // IL_0061: newobj "System.Func..ctor(object, System.IntPtr)" + // IL_0066: dup + // IL_0067: stsfld "System.Func Driver.<>c.<>9__1_1" + // IL_006c: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + // IL_0071: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0076: stloc.2 + // IL_0077: ldloc.1 + // IL_0078: ldloc.2 + // IL_0079: add + // IL_007a: stind.i4 + // IL_007b: ret + // } + // """); + } + + [Fact] + public void SpillArrayCompoundAssignmentLValueAwait2() + { + var source = @" +using System; +using System.Threading.Tasks; + +struct S1 +{ + public int x; +} + +struct S2 +{ + public S1 s1; +} + +class Driver +{ + static async Task Run() + { + var arr = new S2[1]; + arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42); + return arr[await Task.Factory.StartNew(() => 0)].s1.x; + } + + static void Main() + { + var t = Run(); + t.Wait(); + Console.WriteLine(t.Result); + } +}"; + var expectedOutput = @" +42 +"; + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13), + // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13) + ); + // https://github.com/dotnet/roslyn/issues/79763 - support struct lifting + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [Run]: Unexpected type on the stack. { Offset = 0xbb, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("Driver.Run()", """ + // { + // // Code size 188 (0xbc) + // .maxstack 5 + // .locals init (int V_0, + // int V_1, + // int V_2) + // IL_0000: ldc.i4.1 + // IL_0001: newarr "S2" + // IL_0006: dup + // IL_0007: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + // IL_000c: ldsfld "System.Func Driver.<>c.<>9__0_0" + // IL_0011: dup + // IL_0012: brtrue.s IL_002b + // IL_0014: pop + // IL_0015: ldsfld "Driver.<>c Driver.<>c.<>9" + // IL_001a: ldftn "int Driver.<>c.b__0_0()" + // IL_0020: newobj "System.Func..ctor(object, System.IntPtr)" + // IL_0025: dup + // IL_0026: stsfld "System.Func Driver.<>c.<>9__0_0" + // IL_002b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + // IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0035: stloc.0 + // IL_0036: ldloc.0 + // IL_0037: ldelema "S2" + // IL_003c: ldflda "S1 S2.s1" + // IL_0041: ldflda "int S1.x" + // IL_0046: dup + // IL_0047: ldind.i4 + // IL_0048: stloc.1 + // IL_0049: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + // IL_004e: ldsfld "System.Func Driver.<>c.<>9__0_1" + // IL_0053: dup + // IL_0054: brtrue.s IL_006d + // IL_0056: pop + // IL_0057: ldsfld "Driver.<>c Driver.<>c.<>9" + // IL_005c: ldftn "int Driver.<>c.b__0_1()" + // IL_0062: newobj "System.Func..ctor(object, System.IntPtr)" + // IL_0067: dup + // IL_0068: stsfld "System.Func Driver.<>c.<>9__0_1" + // IL_006d: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + // IL_0072: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0077: stloc.2 + // IL_0078: ldloc.1 + // IL_0079: ldloc.2 + // IL_007a: add + // IL_007b: stind.i4 + // IL_007c: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + // IL_0081: ldsfld "System.Func Driver.<>c.<>9__0_2" + // IL_0086: dup + // IL_0087: brtrue.s IL_00a0 + // IL_0089: pop + // IL_008a: ldsfld "Driver.<>c Driver.<>c.<>9" + // IL_008f: ldftn "int Driver.<>c.b__0_2()" + // IL_0095: newobj "System.Func..ctor(object, System.IntPtr)" + // IL_009a: dup + // IL_009b: stsfld "System.Func Driver.<>c.<>9__0_2" + // IL_00a0: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + // IL_00a5: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_00aa: stloc.2 + // IL_00ab: ldloc.2 + // IL_00ac: ldelema "S2" + // IL_00b1: ldflda "S1 S2.s1" + // IL_00b6: ldfld "int S1.x" + // IL_00bb: ret + // } + // """); + } + + [Fact] + public void DoubleSpillArrayCompoundAssignment() + { + var source = @" +using System; +using System.Threading.Tasks; + +struct S1 +{ + public int x; +} + +struct S2 +{ + public S1 s1; +} + +class Driver +{ + static async Task Run() + { + var arr = new S2[1]; + arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); + return arr[await Task.Factory.StartNew(() => 0)].s1.x; + } + + static void Main() + { + var t = Run(); + t.Wait(); + Console.WriteLine(t.Result); + } +}"; + var expectedOutput = @" +42 +"; + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (20,64): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 64), + // (20,64): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 64), + // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13), + // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [Run]: Unexpected type on the stack. { Offset = 0x113, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("Driver.Run()", """ + // { + // // Code size 276 (0x114) + // .maxstack 6 + // .locals init (int V_0, + // int& V_1, + // int V_2, + // int V_3, + // int& V_4, + // int V_5, + // int V_6, + // int V_7) + // IL_0000: ldc.i4.1 + // IL_0001: newarr "S2" + // IL_0006: dup + // IL_0007: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + // IL_000c: ldsfld "System.Func Driver.<>c.<>9__0_0" + // IL_0011: dup + // IL_0012: brtrue.s IL_002b + // IL_0014: pop + // IL_0015: ldsfld "Driver.<>c Driver.<>c.<>9" + // IL_001a: ldftn "int Driver.<>c.b__0_0()" + // IL_0020: newobj "System.Func..ctor(object, System.IntPtr)" + // IL_0025: dup + // IL_0026: stsfld "System.Func Driver.<>c.<>9__0_0" + // IL_002b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + // IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0035: stloc.0 + // IL_0036: ldloc.0 + // IL_0037: ldelema "S2" + // IL_003c: ldflda "S1 S2.s1" + // IL_0041: ldflda "int S1.x" + // IL_0046: stloc.1 + // IL_0047: ldloc.1 + // IL_0048: ldind.i4 + // IL_0049: stloc.2 + // IL_004a: dup + // IL_004b: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + // IL_0050: ldsfld "System.Func Driver.<>c.<>9__0_1" + // IL_0055: dup + // IL_0056: brtrue.s IL_006f + // IL_0058: pop + // IL_0059: ldsfld "Driver.<>c Driver.<>c.<>9" + // IL_005e: ldftn "int Driver.<>c.b__0_1()" + // IL_0064: newobj "System.Func..ctor(object, System.IntPtr)" + // IL_0069: dup + // IL_006a: stsfld "System.Func Driver.<>c.<>9__0_1" + // IL_006f: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + // IL_0074: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0079: stloc.3 + // IL_007a: ldloc.3 + // IL_007b: ldelema "S2" + // IL_0080: ldflda "S1 S2.s1" + // IL_0085: ldflda "int S1.x" + // IL_008a: stloc.s V_4 + // IL_008c: ldloc.s V_4 + // IL_008e: ldind.i4 + // IL_008f: stloc.s V_5 + // IL_0091: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + // IL_0096: ldsfld "System.Func Driver.<>c.<>9__0_2" + // IL_009b: dup + // IL_009c: brtrue.s IL_00b5 + // IL_009e: pop + // IL_009f: ldsfld "Driver.<>c Driver.<>c.<>9" + // IL_00a4: ldftn "int Driver.<>c.b__0_2()" + // IL_00aa: newobj "System.Func..ctor(object, System.IntPtr)" + // IL_00af: dup + // IL_00b0: stsfld "System.Func Driver.<>c.<>9__0_2" + // IL_00b5: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + // IL_00ba: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_00bf: stloc.s V_6 + // IL_00c1: ldloc.1 + // IL_00c2: ldloc.2 + // IL_00c3: ldloc.s V_4 + // IL_00c5: ldloc.s V_5 + // IL_00c7: ldloc.s V_6 + // IL_00c9: add + // IL_00ca: dup + // IL_00cb: stloc.s V_7 + // IL_00cd: stind.i4 + // IL_00ce: ldloc.s V_7 + // IL_00d0: add + // IL_00d1: stind.i4 + // IL_00d2: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + // IL_00d7: ldsfld "System.Func Driver.<>c.<>9__0_3" + // IL_00dc: dup + // IL_00dd: brtrue.s IL_00f6 + // IL_00df: pop + // IL_00e0: ldsfld "Driver.<>c Driver.<>c.<>9" + // IL_00e5: ldftn "int Driver.<>c.b__0_3()" + // IL_00eb: newobj "System.Func..ctor(object, System.IntPtr)" + // IL_00f0: dup + // IL_00f1: stsfld "System.Func Driver.<>c.<>9__0_3" + // IL_00f6: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + // IL_00fb: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0100: stloc.s V_6 + // IL_0102: ldloc.s V_6 + // IL_0104: ldelema "S2" + // IL_0109: ldflda "S1 S2.s1" + // IL_010e: ldfld "int S1.x" + // IL_0113: ret + // } + // """); + } + + [Fact] + public void SpillArrayInitializers1() + { + var source = @" +using System; +using System.Threading; +using System.Threading.Tasks; + +class TestCase +{ + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } + + public async void Run() + { + int tests = 0; + try + { + //jagged array + tests++; + int[][] arr1 = new[] + { + new []{await GetVal(2),await GetVal(3)}, + new []{4,await GetVal(5),await GetVal(6)} + }; + if (arr1[0][1] == 3 && arr1[1][1] == 5 && arr1[1][2] == 6) + Driver.Count++; + + tests++; + int[][] arr2 = new[] + { + new []{await GetVal(2),await GetVal(3)}, + await Goo() + }; + if (arr2[0][1] == 3 && arr2[1][1] == 2) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } + + public async Task Goo() + { + await Task.Delay(1); + return new int[] { 1, 2, 3 }; + } +} + +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); + t.Run(); + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + var expectedOutput = @" +0 +"; + CompileAndVerify(source, expectedOutput); + } + + [Fact] + public void SpillArrayInitializers1WithTaskAndRuntimeAsync() + { + var source = @" +using System; +using System.Threading; +using System.Threading.Tasks; + +class TestCase +{ + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } + + public async Task Run() + { + int tests = 0; + try + { + //jagged array + tests++; + int[][] arr1 = new[] + { + new []{await GetVal(2),await GetVal(3)}, + new []{4,await GetVal(5),await GetVal(6)} + }; + if (arr1[0][1] == 3 && arr1[1][1] == 5 && arr1[1][2] == 6) + Driver.Count++; + + tests++; + int[][] arr2 = new[] + { + new []{await GetVal(2),await GetVal(3)}, + await Goo() + }; + if (arr2[0][1] == 3 && arr2[1][1] == 2) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } + + public async Task Goo() + { + await Task.Delay(1); + return new int[] { 1, 2, 3 }; + } +} + +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + t.Run(); +#pragma warning restore CS4014 + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + var expectedOutput = @" +0 +"; + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x11b } + [Goo]: Unexpected type on the stack. { Offset = 0x1c, Found = ref 'int32[]', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 284 (0x11c) + .maxstack 7 + .locals init (int V_0, //tests + int[][] V_1, //arr1 + int[][] V_2, //arr2 + int V_3, + int V_4, + int[] V_5, + int V_6, + int V_7, + int[] V_8) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldloc.0 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.0 + IL_0006: ldarg.0 + IL_0007: ldc.i4.2 + IL_0008: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_000d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0012: stloc.3 + IL_0013: ldarg.0 + IL_0014: ldc.i4.3 + IL_0015: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_001a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: stloc.s V_4 + IL_0021: ldc.i4.2 + IL_0022: newarr "int" + IL_0027: dup + IL_0028: ldc.i4.0 + IL_0029: ldloc.3 + IL_002a: stelem.i4 + IL_002b: dup + IL_002c: ldc.i4.1 + IL_002d: ldloc.s V_4 + IL_002f: stelem.i4 + IL_0030: stloc.s V_5 + IL_0032: ldarg.0 + IL_0033: ldc.i4.5 + IL_0034: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0039: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003e: stloc.s V_6 + IL_0040: ldarg.0 + IL_0041: ldc.i4.6 + IL_0042: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0047: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_004c: stloc.s V_7 + IL_004e: ldc.i4.2 + IL_004f: newarr "int[]" + IL_0054: dup + IL_0055: ldc.i4.0 + IL_0056: ldloc.s V_5 + IL_0058: stelem.ref + IL_0059: dup + IL_005a: ldc.i4.1 + IL_005b: ldc.i4.3 + IL_005c: newarr "int" + IL_0061: dup + IL_0062: ldc.i4.0 + IL_0063: ldc.i4.4 + IL_0064: stelem.i4 + IL_0065: dup + IL_0066: ldc.i4.1 + IL_0067: ldloc.s V_6 + IL_0069: stelem.i4 + IL_006a: dup + IL_006b: ldc.i4.2 + IL_006c: ldloc.s V_7 + IL_006e: stelem.i4 + IL_006f: stelem.ref + IL_0070: stloc.1 + IL_0071: ldloc.1 + IL_0072: ldc.i4.0 + IL_0073: ldelem.ref + IL_0074: ldc.i4.1 + IL_0075: ldelem.i4 + IL_0076: ldc.i4.3 + IL_0077: bne.un.s IL_0095 + IL_0079: ldloc.1 + IL_007a: ldc.i4.1 + IL_007b: ldelem.ref + IL_007c: ldc.i4.1 + IL_007d: ldelem.i4 + IL_007e: ldc.i4.5 + IL_007f: bne.un.s IL_0095 + IL_0081: ldloc.1 + IL_0082: ldc.i4.1 + IL_0083: ldelem.ref + IL_0084: ldc.i4.2 + IL_0085: ldelem.i4 + IL_0086: ldc.i4.6 + IL_0087: bne.un.s IL_0095 + IL_0089: ldsfld "int Driver.Count" + IL_008e: ldc.i4.1 + IL_008f: add + IL_0090: stsfld "int Driver.Count" + IL_0095: ldloc.0 + IL_0096: ldc.i4.1 + IL_0097: add + IL_0098: stloc.0 + IL_0099: ldarg.0 + IL_009a: ldc.i4.2 + IL_009b: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_00a0: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00a5: stloc.s V_7 + IL_00a7: ldarg.0 + IL_00a8: ldc.i4.3 + IL_00a9: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_00ae: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00b3: stloc.s V_6 + IL_00b5: ldc.i4.2 + IL_00b6: newarr "int" + IL_00bb: dup + IL_00bc: ldc.i4.0 + IL_00bd: ldloc.s V_7 + IL_00bf: stelem.i4 + IL_00c0: dup + IL_00c1: ldc.i4.1 + IL_00c2: ldloc.s V_6 + IL_00c4: stelem.i4 + IL_00c5: stloc.s V_5 + IL_00c7: ldarg.0 + IL_00c8: call "System.Threading.Tasks.Task TestCase.Goo()" + IL_00cd: call "int[] System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00d2: stloc.s V_8 + IL_00d4: ldc.i4.2 + IL_00d5: newarr "int[]" + IL_00da: dup + IL_00db: ldc.i4.0 + IL_00dc: ldloc.s V_5 + IL_00de: stelem.ref + IL_00df: dup + IL_00e0: ldc.i4.1 + IL_00e1: ldloc.s V_8 + IL_00e3: stelem.ref + IL_00e4: stloc.2 + IL_00e5: ldloc.2 + IL_00e6: ldc.i4.0 + IL_00e7: ldelem.ref + IL_00e8: ldc.i4.1 + IL_00e9: ldelem.i4 + IL_00ea: ldc.i4.3 + IL_00eb: bne.un.s IL_0101 + IL_00ed: ldloc.2 + IL_00ee: ldc.i4.1 + IL_00ef: ldelem.ref + IL_00f0: ldc.i4.1 + IL_00f1: ldelem.i4 + IL_00f2: ldc.i4.2 + IL_00f3: bne.un.s IL_0101 + IL_00f5: ldsfld "int Driver.Count" + IL_00fa: ldc.i4.1 + IL_00fb: add + IL_00fc: stsfld "int Driver.Count" + IL_0101: leave.s IL_011b + } + finally + { + IL_0103: ldsfld "int Driver.Count" + IL_0108: ldloc.0 + IL_0109: sub + IL_010a: stsfld "int Driver.Result" + IL_010f: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0114: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0119: pop + IL_011a: endfinally + } + IL_011b: ret + } + """); + } + + [Fact] + public void SpillArrayInitializers2() + { + var source = @" +using System; +using System.Threading; +using System.Threading.Tasks; + +class TestCase +{ + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } + + public async void Run() + { + int tests = 0; + try + { + //jagged array + tests++; + int[,] arr1 = + { + {await GetVal(2),await GetVal(3)}, + {await GetVal(5),await GetVal(6)} + }; + if (arr1[0, 1] == 3 && arr1[1, 0] == 5 && arr1[1, 1] == 6) + Driver.Count++; + + tests++; + int[,] arr2 = + { + {await GetVal(2),3}, + {4,await GetVal(5)} + }; + if (arr2[0, 1] == 3 && arr2[1, 1] == 5) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } +} + +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); + t.Run(); + + CompletedSignal.WaitOne(); + Console.WriteLine(Driver.Result); + } +}"; + var expectedOutput = @" +0 +"; + CompileAndVerify(source, expectedOutput); + } + + [Fact] + public void SpillArrayInitializers2WithTaskAndRuntimeAsync() + { + var source = @" +using System; +using System.Threading; +using System.Threading.Tasks; + +class TestCase +{ + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } + + public async Task Run() + { + int tests = 0; + try + { + //jagged array + tests++; + int[,] arr1 = + { + {await GetVal(2),await GetVal(3)}, + {await GetVal(5),await GetVal(6)} + }; + if (arr1[0, 1] == 3 && arr1[1, 0] == 5 && arr1[1, 1] == 6) + Driver.Count++; + + tests++; + int[,] arr2 = + { + {await GetVal(2),3}, + {4,await GetVal(5)} + }; + if (arr2[0, 1] == 3 && arr2[1, 1] == 5) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } +} + +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + t.Run(); +#pragma warning restore CS4014 + + CompletedSignal.WaitOne(); + Console.WriteLine(Driver.Result); + } +}"; + var expectedOutput = @" +0 +"; + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x123 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 292 (0x124) + .maxstack 5 + .locals init (int V_0, //tests + int[,] V_1, //arr1 + int[,] V_2, //arr2 + int V_3, + int V_4, + int V_5, + int V_6) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldloc.0 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.0 + IL_0006: ldarg.0 + IL_0007: ldc.i4.2 + IL_0008: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_000d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0012: stloc.3 + IL_0013: ldarg.0 + IL_0014: ldc.i4.3 + IL_0015: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_001a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: stloc.s V_4 + IL_0021: ldarg.0 + IL_0022: ldc.i4.5 + IL_0023: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0028: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002d: stloc.s V_5 + IL_002f: ldarg.0 + IL_0030: ldc.i4.6 + IL_0031: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0036: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003b: stloc.s V_6 + IL_003d: ldc.i4.2 + IL_003e: ldc.i4.2 + IL_003f: newobj "int[*,*]..ctor" + IL_0044: dup + IL_0045: ldc.i4.0 + IL_0046: ldc.i4.0 + IL_0047: ldloc.3 + IL_0048: call "int[*,*].Set" + IL_004d: dup + IL_004e: ldc.i4.0 + IL_004f: ldc.i4.1 + IL_0050: ldloc.s V_4 + IL_0052: call "int[*,*].Set" + IL_0057: dup + IL_0058: ldc.i4.1 + IL_0059: ldc.i4.0 + IL_005a: ldloc.s V_5 + IL_005c: call "int[*,*].Set" + IL_0061: dup + IL_0062: ldc.i4.1 + IL_0063: ldc.i4.1 + IL_0064: ldloc.s V_6 + IL_0066: call "int[*,*].Set" + IL_006b: stloc.1 + IL_006c: ldloc.1 + IL_006d: ldc.i4.0 + IL_006e: ldc.i4.1 + IL_006f: call "int[*,*].Get" + IL_0074: ldc.i4.3 + IL_0075: bne.un.s IL_0099 + IL_0077: ldloc.1 + IL_0078: ldc.i4.1 + IL_0079: ldc.i4.0 + IL_007a: call "int[*,*].Get" + IL_007f: ldc.i4.5 + IL_0080: bne.un.s IL_0099 + IL_0082: ldloc.1 + IL_0083: ldc.i4.1 + IL_0084: ldc.i4.1 + IL_0085: call "int[*,*].Get" + IL_008a: ldc.i4.6 + IL_008b: bne.un.s IL_0099 + IL_008d: ldsfld "int Driver.Count" + IL_0092: ldc.i4.1 + IL_0093: add + IL_0094: stsfld "int Driver.Count" + IL_0099: ldloc.0 + IL_009a: ldc.i4.1 + IL_009b: add + IL_009c: stloc.0 + IL_009d: ldarg.0 + IL_009e: ldc.i4.2 + IL_009f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_00a4: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00a9: stloc.s V_6 + IL_00ab: ldarg.0 + IL_00ac: ldc.i4.5 + IL_00ad: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_00b2: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00b7: stloc.s V_5 + IL_00b9: ldc.i4.2 + IL_00ba: ldc.i4.2 + IL_00bb: newobj "int[*,*]..ctor" + IL_00c0: dup + IL_00c1: ldc.i4.0 + IL_00c2: ldc.i4.0 + IL_00c3: ldloc.s V_6 + IL_00c5: call "int[*,*].Set" + IL_00ca: dup + IL_00cb: ldc.i4.0 + IL_00cc: ldc.i4.1 + IL_00cd: ldc.i4.3 + IL_00ce: call "int[*,*].Set" + IL_00d3: dup + IL_00d4: ldc.i4.1 + IL_00d5: ldc.i4.0 + IL_00d6: ldc.i4.4 + IL_00d7: call "int[*,*].Set" + IL_00dc: dup + IL_00dd: ldc.i4.1 + IL_00de: ldc.i4.1 + IL_00df: ldloc.s V_5 + IL_00e1: call "int[*,*].Set" + IL_00e6: stloc.2 + IL_00e7: ldloc.2 + IL_00e8: ldc.i4.0 + IL_00e9: ldc.i4.1 + IL_00ea: call "int[*,*].Get" + IL_00ef: ldc.i4.3 + IL_00f0: bne.un.s IL_0109 + IL_00f2: ldloc.2 + IL_00f3: ldc.i4.1 + IL_00f4: ldc.i4.1 + IL_00f5: call "int[*,*].Get" + IL_00fa: ldc.i4.5 + IL_00fb: bne.un.s IL_0109 + IL_00fd: ldsfld "int Driver.Count" + IL_0102: ldc.i4.1 + IL_0103: add + IL_0104: stsfld "int Driver.Count" + IL_0109: leave.s IL_0123 + } + finally + { + IL_010b: ldsfld "int Driver.Count" + IL_0110: ldloc.0 + IL_0111: sub + IL_0112: stsfld "int Driver.Result" + IL_0117: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_011c: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0121: pop + IL_0122: endfinally + } + IL_0123: ret + } + """); + } + + [Fact] + public void SpillArrayInitializers3() + { + var source = @" +using System; +using System.Threading; +using System.Threading.Tasks; + +class TestCase +{ + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } + + public async void Run() + { + int tests = 0; + try + { + //jagged array + tests++; + int[][] arr1 = new[] + { + new []{await GetVal(2),await Task.Run(async()=>{await Task.Delay(1);return 3;})}, + new []{await GetVal(5),4,await Task.Run(async()=>{await Task.Delay(1);return 6;})} + }; + if (arr1[0][1] == 3 && arr1[1][1] == 4 && arr1[1][2] == 6) + Driver.Count++; + + tests++; + dynamic arr2 = new[] + { + new []{await GetVal(2),3}, + await Goo() + }; + if (arr2[0][1] == 3 && arr2[1][1] == 2) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } + + public async Task Goo() + { + await Task.Delay(1); + return new int[] { 1, 2, 3 }; + } +} + +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); + t.Run(); + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + var expectedOutput = @" +0 +"; + CompileAndVerify(source, expectedOutput, references: new[] { CSharpRef }); + } + + [Fact] + public void SpillArrayInitializers3WithTaskAndRuntimeAsync() + { + var source = @" +using System; +using System.Threading; +using System.Threading.Tasks; + +class TestCase +{ + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } + + public async Task Run() + { + int tests = 0; + try + { + //jagged array + tests++; + int[][] arr1 = new[] + { + new []{await GetVal(2),await Task.Run(async()=>{await Task.Delay(1);return 3;})}, + new []{await GetVal(5),4,await Task.Run(async()=>{await Task.Delay(1);return 6;})} + }; + if (arr1[0][1] == 3 && arr1[1][1] == 4 && arr1[1][2] == 6) + Driver.Count++; + + tests++; + dynamic arr2 = new[] + { + new []{await GetVal(2),3}, + await Goo() + }; + if (arr2[0][1] == 3 && arr2[1][1] == 2) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } + + public async Task Goo() + { + await Task.Delay(1); + return new int[] { 1, 2, 3 }; + } +} + +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); + t.Run(); + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + var expectedOutput = @" +0 +"; + CompileAndVerify(source, expectedOutput, references: new[] { CSharpRef }); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x40b } + [Goo]: Unexpected type on the stack. { Offset = 0x1c, Found = ref 'int32[]', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [b__1_0]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [b__1_1]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics( + // (61,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. + // t.Run(); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "t.Run()").WithLocation(61, 9) + ); + } + + [Fact] + public void SpillNestedExpressionInArrayInitializer() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Test +{ + public static async Task Run() + { + return new int[,] { + {1, 2, 21 + (await Task.Factory.StartNew(() => 21)) }, + }; + } + + public static void Main() + { + var t = Run(); + t.Wait(); + foreach (var xs in t.Result) + { + Console.WriteLine(xs); + } + } +}"; + var expectedOutput = @" +1 +2 +42 +"; + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Unexpected type on the stack. { Offset = 0x54, Found = ref 'int32[,]', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.Run()", """ + { + // Code size 85 (0x55) + .maxstack 6 + .locals init (int V_0) + IL_0000: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_0005: ldsfld "System.Func Test.<>c.<>9__0_0" + IL_000a: dup + IL_000b: brtrue.s IL_0024 + IL_000d: pop + IL_000e: ldsfld "Test.<>c Test.<>c.<>9" + IL_0013: ldftn "int Test.<>c.b__0_0()" + IL_0019: newobj "System.Func..ctor(object, System.IntPtr)" + IL_001e: dup + IL_001f: stsfld "System.Func Test.<>c.<>9__0_0" + IL_0024: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0029: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002e: stloc.0 + IL_002f: ldc.i4.1 + IL_0030: ldc.i4.3 + IL_0031: newobj "int[*,*]..ctor" + IL_0036: dup + IL_0037: ldc.i4.0 + IL_0038: ldc.i4.0 + IL_0039: ldc.i4.1 + IL_003a: call "int[*,*].Set" + IL_003f: dup + IL_0040: ldc.i4.0 + IL_0041: ldc.i4.1 + IL_0042: ldc.i4.2 + IL_0043: call "int[*,*].Set" + IL_0048: dup + IL_0049: ldc.i4.0 + IL_004a: ldc.i4.2 + IL_004b: ldc.i4.s 21 + IL_004d: ldloc.0 + IL_004e: add + IL_004f: call "int[*,*].Set" + IL_0054: ret + } + """); + } + + [Fact] + public void SpillConditionalAccess() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Test +{ + + class C1 + { + public int M(int x) + { + return x; + } + } + + public static int Get(int x) + { + Console.WriteLine(""> "" + x); + return x; + } + + public static async Task F(int x) + { + return await Task.Factory.StartNew(() => x); + } + + public static async Task G() + { + var c = new C1(); + return c?.M(await F(Get(42))); + } + + public static void Main() + { + var t = G(); + System.Console.WriteLine(t.Result); + } +}"; + var expectedOutput = @" +> 42 +42"; + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x28, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x34, Found = value '[System.Runtime]System.Nullable`1', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1>' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.G()", """ + { + // Code size 53 (0x35) + .maxstack 3 + .locals init (Test.C1 V_0, + int? V_1, + int V_2) + IL_0000: newobj "Test.C1..ctor()" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: brfalse.s IL_002b + IL_0009: ldc.i4.s 42 + IL_000b: call "int Test.Get(int)" + IL_0010: call "System.Threading.Tasks.Task Test.F(int)" + IL_0015: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001a: stloc.2 + IL_001b: ldloca.s V_1 + IL_001d: ldloc.0 + IL_001e: ldloc.2 + IL_001f: callvirt "int Test.C1.M(int)" + IL_0024: call "int?..ctor(int)" + IL_0029: br.s IL_0033 + IL_002b: ldloca.s V_1 + IL_002d: initobj "int?" + IL_0033: ldloc.1 + IL_0034: ret + } + """); + } + + [Fact] + public void AssignToAwait() + { + var source = @" +using System; +using System.Threading.Tasks; + +class S +{ + public int x = -1; +} + +class Test +{ + static S _s = new S(); + + public static async Task GetS() + { + return await Task.Factory.StartNew(() => _s); + } + + public static async Task Run() + { + (await GetS()).x = 42; + Console.WriteLine(_s.x); + } +} + +class Driver +{ + static void Main() + { + Test.Run().Wait(); + } +}"; + var expectedOutput = @" +42 +"; + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetS]: Unexpected type on the stack. { Offset = 0x2e, Found = ref 'S', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x20 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.Run()", """ + { + // Code size 33 (0x21) + .maxstack 2 + IL_0000: call "System.Threading.Tasks.Task Test.GetS()" + IL_0005: call "S System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ldc.i4.s 42 + IL_000c: stfld "int S.x" + IL_0011: ldsfld "S Test._s" + IL_0016: ldfld "int S.x" + IL_001b: call "void System.Console.WriteLine(int)" + IL_0020: ret + } + """); + } + + [Fact] + public void AssignAwaitToAwait() + { + var source = @" +using System; +using System.Threading.Tasks; + +class S +{ + public int x = -1; +} + +class Test +{ + static S _s = new S(); + + public static async Task GetS() + { + return await Task.Factory.StartNew(() => _s); + } + + public static async Task Run() + { + (await GetS()).x = await Task.Factory.StartNew(() => 42); + Console.WriteLine(_s.x); + } +} + +class Driver +{ + static void Main() + { + Test.Run().Wait(); + } +}"; + var expectedOutput = @" +42 +"; + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetS]: Unexpected type on the stack. { Offset = 0x2e, Found = ref 'S', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x4e } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.Run()", """ + { + // Code size 79 (0x4f) + .maxstack 4 + .locals init (int V_0) + IL_0000: call "System.Threading.Tasks.Task Test.GetS()" + IL_0005: call "S System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_000f: ldsfld "System.Func Test.<>c.<>9__2_0" + IL_0014: dup + IL_0015: brtrue.s IL_002e + IL_0017: pop + IL_0018: ldsfld "Test.<>c Test.<>c.<>9" + IL_001d: ldftn "int Test.<>c.b__2_0()" + IL_0023: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0028: dup + IL_0029: stsfld "System.Func Test.<>c.<>9__2_0" + IL_002e: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0033: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0038: stloc.0 + IL_0039: ldloc.0 + IL_003a: stfld "int S.x" + IL_003f: ldsfld "S Test._s" + IL_0044: ldfld "int S.x" + IL_0049: call "void System.Console.WriteLine(int)" + IL_004e: ret + } + """); + } + + [Fact] + public void SpillArglist() + { + var source = @" +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +class TestCase +{ + static StringBuilder sb = new StringBuilder(); + public async Task Run() + { + try + { + Bar(__arglist(One(), await Two())); + if (sb.ToString() == ""OneTwo"") + Driver.Result = 0; + } + finally + { + Driver.CompleteSignal.Set(); + } + } + int One() + { + sb.Append(""One""); + return 1; + } + async Task Two() + { + await Task.Delay(1); + sb.Append(""Two""); + return 2; + } + void Bar(__arglist) + { + var ai = new ArgIterator(__arglist); + while (ai.GetRemainingCount() > 0) + Console.WriteLine( __refvalue(ai.GetNextArg(), int)); + } +} +class Driver +{ + static public AutoResetEvent CompleteSignal = new AutoResetEvent(false); + public static int Result = -1; + public static void Main() + { + TestCase tc = new TestCase(); + tc.Run(); + CompleteSignal.WaitOne(); + + Console.WriteLine(Result); + } +}"; + var expectedOutput = ExecutionConditionUtil.IsDesktop ? @" +1 +2 +0 +" : null; + CompileAndVerify(source, targetFramework: TargetFramework.NetFramework, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics( + // (14,17): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // Bar(__arglist(One(), await Two())); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "__arglist(One(), await Two())").WithArguments("TestCase.Run()").WithLocation(14, 17), + // (48,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. + // tc.Run(); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "tc.Run()").WithLocation(48, 9) + ); + } + + [Fact] + public void SpillObjectInitializer1() + { + var source = @" +using System; +using System.Collections; +using System.Threading; +using System.Threading.Tasks; + + +struct TestCase : IEnumerable +{ + int X; + public async Task Run() + { + int test = 0; + int count = 0; + try + { + test++; + var x = new TestCase { X = await Bar() }; + if (x.X == 1) + count++; + } + finally + { + Driver.Result = test - count; + Driver.CompleteSignal.Set(); + } + } + async Task Bar() + { + await Task.Delay(1); + return 1; + } + + public IEnumerator GetEnumerator() + { + throw new System.NotImplementedException(); + } +} +class Driver +{ + static public AutoResetEvent CompleteSignal = new AutoResetEvent(false); + public static int Result = -1; + public static void Main() + { + TestCase tc = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + tc.Run(); +#pragma warning restore CS4014 + CompleteSignal.WaitOne(); + + Console.WriteLine(Result); + } +}"; + var expectedOutput = @" +0 +"; + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (11,23): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // public async Task Run() + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Run").WithArguments("TestCase.Run()").WithLocation(11, 23) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [Run]: Return value missing on the stack. { Offset = 0x47 } + // [Bar]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("TestCase.Run()", """ + // { + // // Code size 72 (0x48) + // .maxstack 2 + // .locals init (int V_0, //test + // int V_1, //count + // int V_2, + // TestCase V_3) + // IL_0000: ldc.i4.0 + // IL_0001: stloc.0 + // IL_0002: ldc.i4.0 + // IL_0003: stloc.1 + // .try + // { + // IL_0004: ldloc.0 + // IL_0005: ldc.i4.1 + // IL_0006: add + // IL_0007: stloc.0 + // IL_0008: ldloca.s V_3 + // IL_000a: initobj "TestCase" + // IL_0010: ldarg.0 + // IL_0011: call "System.Threading.Tasks.Task TestCase.Bar()" + // IL_0016: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_001b: stloc.2 + // IL_001c: ldloca.s V_3 + // IL_001e: ldloc.2 + // IL_001f: stfld "int TestCase.X" + // IL_0024: ldloc.3 + // IL_0025: ldfld "int TestCase.X" + // IL_002a: ldc.i4.1 + // IL_002b: bne.un.s IL_0031 + // IL_002d: ldloc.1 + // IL_002e: ldc.i4.1 + // IL_002f: add + // IL_0030: stloc.1 + // IL_0031: leave.s IL_0047 + // } + // finally + // { + // IL_0033: ldloc.0 + // IL_0034: ldloc.1 + // IL_0035: sub + // IL_0036: stsfld "int Driver.Result" + // IL_003b: ldsfld "System.Threading.AutoResetEvent Driver.CompleteSignal" + // IL_0040: callvirt "bool System.Threading.EventWaitHandle.Set()" + // IL_0045: pop + // IL_0046: endfinally + // } + // IL_0047: ret + // } + // """); + } + + [Fact] + public void SpillWithByRefArguments01() + { + var source = @" +using System; +using System.Threading; +using System.Threading.Tasks; + +class BaseTestCase +{ + public void GooRef(ref decimal d, int x, out decimal od) + { + od = d; + d++; + } + + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } +} + +class TestCase : BaseTestCase +{ + public async void Run() + { + int tests = 0; + try + { + decimal d = 1; + decimal od; + + tests++; + base.GooRef(ref d, await base.GetVal(4), out od); + if (d == 2 && od == 1) Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } +} + +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); + t.Run(); + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + CompileAndVerify(source, "0"); + } + + [Fact] + public void SpillWithByRefArguments01WithTaskAndRuntimeAsync() + { + var source = @" +using System; +using System.Threading; +using System.Threading.Tasks; + +class BaseTestCase +{ + public void GooRef(ref decimal d, int x, out decimal od) + { + od = d; + d++; + } + + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } +} + +class TestCase : BaseTestCase +{ + public async Task Run() + { + int tests = 0; + try + { + decimal d = 1; + decimal od; + + tests++; + base.GooRef(ref d, await base.GetVal(4), out od); + if (d == 2 && od == 1) Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } +} + +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); + t.Run(); + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); } }"; - var expected = @" -42 -"; - CompileAndVerify(source, expected); + CompileAndVerify(source, "0"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x65 } + """ + }); + + verifier.VerifyDiagnostics( + // (52,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. + // t.Run(); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "t.Run()").WithLocation(52, 9) + ); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 102 (0x66) + .maxstack 4 + .locals init (int V_0, //tests + decimal V_1, //d + decimal V_2, //od + int V_3) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldsfld "decimal decimal.One" + IL_0007: stloc.1 + IL_0008: ldloc.0 + IL_0009: ldc.i4.1 + IL_000a: add + IL_000b: stloc.0 + IL_000c: ldarg.0 + IL_000d: ldc.i4.4 + IL_000e: call "System.Threading.Tasks.Task BaseTestCase.GetVal(int)" + IL_0013: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0018: stloc.3 + IL_0019: ldarg.0 + IL_001a: ldloca.s V_1 + IL_001c: ldloc.3 + IL_001d: ldloca.s V_2 + IL_001f: call "void BaseTestCase.GooRef(ref decimal, int, out decimal)" + IL_0024: ldloc.1 + IL_0025: ldc.i4.2 + IL_0026: newobj "decimal..ctor(int)" + IL_002b: call "bool decimal.op_Equality(decimal, decimal)" + IL_0030: brfalse.s IL_004b + IL_0032: ldloc.2 + IL_0033: ldsfld "decimal decimal.One" + IL_0038: call "bool decimal.op_Equality(decimal, decimal)" + IL_003d: brfalse.s IL_004b + IL_003f: ldsfld "int Driver.Count" + IL_0044: ldc.i4.1 + IL_0045: add + IL_0046: stsfld "int Driver.Count" + IL_004b: leave.s IL_0065 + } + finally + { + IL_004d: ldsfld "int Driver.Count" + IL_0052: ldloc.0 + IL_0053: sub + IL_0054: stsfld "int Driver.Result" + IL_0059: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_005e: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0063: pop + IL_0064: endfinally + } + IL_0065: ret + } + """); } [Fact] - public void AssignAwaitToAwait() + public void SpillOperator_Compound1() { var source = @" using System; +using System.Threading; using System.Threading.Tasks; -class S -{ - public int x = -1; -} - -class Test +class TestCase { - static S _s = new S(); - - public static async Task GetS() + public async Task GetVal(T t) { - return await Task.Factory.StartNew(() => _s); + await Task.Delay(1); + return t; } - public static async Task Run() + public async void Run() { - (await GetS()).x = await Task.Factory.StartNew(() => 42); - Console.WriteLine(_s.x); + int tests = 0; + try + { + tests++; + int[] x = new int[] { 1, 2, 3, 4 }; + x[await GetVal(0)] += await GetVal(4); + if (x[0] == 5) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } } } class Driver { + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); static void Main() { - Test.Run().Wait(); + var t = new TestCase(); + t.Run(); + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); } }"; - var expected = @" -42 -"; - CompileAndVerify(source, expected); + CompileAndVerify(source, "0"); } - [ConditionalFact(typeof(DesktopOnly))] - public void SpillArglist() + [Fact] + public void SpillOperator_Compound1WithTaskAndRuntimeAsync() { var source = @" using System; -using System.Runtime.InteropServices; -using System.Text; using System.Threading; using System.Threading.Tasks; class TestCase { - static StringBuilder sb = new StringBuilder(); + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } + public async Task Run() { + int tests = 0; try { - Bar(__arglist(One(), await Two())); - if (sb.ToString() == ""OneTwo"") - Driver.Result = 0; + tests++; + int[] x = new int[] { 1, 2, 3, 4 }; + x[await GetVal(0)] += await GetVal(4); + if (x[0] == 5) + Driver.Count++; } finally { - Driver.CompleteSignal.Set(); + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); } } - int One() - { - sb.Append(""One""); - return 1; - } - async Task Two() - { - await Task.Delay(1); - sb.Append(""Two""); - return 2; - } - void Bar(__arglist) - { - var ai = new ArgIterator(__arglist); - while (ai.GetRemainingCount() > 0) - Console.WriteLine( __refvalue(ai.GetNextArg(), int)); - } } + class Driver { - static public AutoResetEvent CompleteSignal = new AutoResetEvent(false); public static int Result = -1; - public static void Main() + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() { - TestCase tc = new TestCase(); - tc.Run(); - CompleteSignal.WaitOne(); + var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + t.Run(); +#pragma warning restore CS4014 - Console.WriteLine(Result); + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); } }"; - var expected = @" -1 -2 -0 -"; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, "0"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (21,15): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // x[await GetVal(0)] += await GetVal(4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(0)").WithArguments("TestCase.Run()").WithLocation(21, 15) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // [Run]: Return value missing on the stack. { Offset = 0x6a } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("TestCase.Run()", """ + // { + // // Code size 107 (0x6b) + // .maxstack 4 + // .locals init (int V_0, //tests + // int V_1, + // int V_2, + // int V_3) + // IL_0000: ldc.i4.0 + // IL_0001: stloc.0 + // .try + // { + // IL_0002: ldloc.0 + // IL_0003: ldc.i4.1 + // IL_0004: add + // IL_0005: stloc.0 + // IL_0006: ldc.i4.4 + // IL_0007: newarr "int" + // IL_000c: dup + // IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" + // IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + // IL_0017: dup + // IL_0018: ldarg.0 + // IL_0019: ldc.i4.0 + // IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0024: stloc.1 + // IL_0025: ldloc.1 + // IL_0026: ldelema "int" + // IL_002b: dup + // IL_002c: ldind.i4 + // IL_002d: stloc.2 + // IL_002e: ldarg.0 + // IL_002f: ldc.i4.4 + // IL_0030: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0035: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_003a: stloc.3 + // IL_003b: ldloc.2 + // IL_003c: ldloc.3 + // IL_003d: add + // IL_003e: stind.i4 + // IL_003f: ldc.i4.0 + // IL_0040: ldelem.i4 + // IL_0041: ldc.i4.5 + // IL_0042: bne.un.s IL_0050 + // IL_0044: ldsfld "int Driver.Count" + // IL_0049: ldc.i4.1 + // IL_004a: add + // IL_004b: stsfld "int Driver.Count" + // IL_0050: leave.s IL_006a + // } + // finally + // { + // IL_0052: ldsfld "int Driver.Count" + // IL_0057: ldloc.0 + // IL_0058: sub + // IL_0059: stsfld "int Driver.Result" + // IL_005e: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + // IL_0063: callvirt "bool System.Threading.EventWaitHandle.Set()" + // IL_0068: pop + // IL_0069: endfinally + // } + // IL_006a: ret + // } + // """); } [Fact] - public void SpillObjectInitializer1() + public void SpillOperator_Compound2() { var source = @" using System; -using System.Collections; using System.Threading; using System.Threading.Tasks; - -struct TestCase : IEnumerable +class TestCase { - int X; - public async Task Run() + public async Task GetVal(T t) { - int test = 0; - int count = 0; + await Task.Delay(1); + return t; + } + + public async void Run() + { + int tests = 0; try { - test++; - var x = new TestCase { X = await Bar() }; - if (x.X == 1) - count++; + tests++; + int[] x = new int[] { 1, 2, 3, 4 }; + x[await GetVal(0)] += await GetVal(4); + if (x[0] == 5) + Driver.Count++; } finally { - Driver.Result = test - count; - Driver.CompleteSignal.Set(); + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); } } - async Task Bar() - { - await Task.Delay(1); - return 1; - } - - public IEnumerator GetEnumerator() - { - throw new System.NotImplementedException(); - } } + class Driver { - static public AutoResetEvent CompleteSignal = new AutoResetEvent(false); public static int Result = -1; - public static void Main() + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() { - TestCase tc = new TestCase(); - tc.Run(); - CompleteSignal.WaitOne(); + var t = new TestCase(); + t.Run(); - Console.WriteLine(Result); + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); } }"; - var expected = @" -0 -"; - CompileAndVerify(source, expectedOutput: expected); + CompileAndVerify(source, "0"); } [Fact] - public void SpillWithByRefArguments01() + public void SpillOperator_Compound2WithRuntimeAsync() { var source = @" using System; using System.Threading; using System.Threading.Tasks; -class BaseTestCase +class TestCase { - public void GooRef(ref decimal d, int x, out decimal od) - { - od = d; - d++; - } - public async Task GetVal(T t) { await Task.Delay(1); return t; } -} -class TestCase : BaseTestCase -{ - public async void Run() + public async Task Run() { int tests = 0; try { - decimal d = 1; - decimal od; - tests++; - base.GooRef(ref d, await base.GetVal(4), out od); - if (d == 2 && od == 1) Driver.Count++; + int[] x = new int[] { 1, 2, 3, 4 }; + x[await GetVal(0)] += await GetVal(4); + if (x[0] == 5) + Driver.Count++; } finally { @@ -2596,7 +6882,9 @@ class Driver static void Main() { var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed t.Run(); +#pragma warning restore CS4014 CompletedSignal.WaitOne(); // 0 - success @@ -2606,18 +6894,138 @@ static void Main() } }"; CompileAndVerify(source, "0"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (21,15): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // x[await GetVal(0)] += await GetVal(4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(0)").WithArguments("TestCase.Run()").WithLocation(21, 15) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // [Run]: Return value missing on the stack. { Offset = 0x6a } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("TestCase.Run()", """ + // { + // // Code size 107 (0x6b) + // .maxstack 4 + // .locals init (int V_0, //tests + // int V_1, + // int V_2, + // int V_3) + // IL_0000: ldc.i4.0 + // IL_0001: stloc.0 + // .try + // { + // IL_0002: ldloc.0 + // IL_0003: ldc.i4.1 + // IL_0004: add + // IL_0005: stloc.0 + // IL_0006: ldc.i4.4 + // IL_0007: newarr "int" + // IL_000c: dup + // IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" + // IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + // IL_0017: dup + // IL_0018: ldarg.0 + // IL_0019: ldc.i4.0 + // IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0024: stloc.1 + // IL_0025: ldloc.1 + // IL_0026: ldelema "int" + // IL_002b: dup + // IL_002c: ldind.i4 + // IL_002d: stloc.2 + // IL_002e: ldarg.0 + // IL_002f: ldc.i4.4 + // IL_0030: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0035: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_003a: stloc.3 + // IL_003b: ldloc.2 + // IL_003c: ldloc.3 + // IL_003d: add + // IL_003e: stind.i4 + // IL_003f: ldc.i4.0 + // IL_0040: ldelem.i4 + // IL_0041: ldc.i4.5 + // IL_0042: bne.un.s IL_0050 + // IL_0044: ldsfld "int Driver.Count" + // IL_0049: ldc.i4.1 + // IL_004a: add + // IL_004b: stsfld "int Driver.Count" + // IL_0050: leave.s IL_006a + // } + // finally + // { + // IL_0052: ldsfld "int Driver.Count" + // IL_0057: ldloc.0 + // IL_0058: sub + // IL_0059: stsfld "int Driver.Result" + // IL_005e: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + // IL_0063: callvirt "bool System.Threading.EventWaitHandle.Set()" + // IL_0068: pop + // IL_0069: endfinally + // } + // IL_006a: ret + // } + // """); } [Fact] - public void SpillOperator_Compound1() + public void Async_StackSpill_Argument_Generic04() + { + var source = @" +using System; +using System.Threading.Tasks; +public class MC +{ + async public System.Threading.Tasks.Task Goo(T t, V u) { await Task.Delay(1); return u; } +} + +class Test +{ + static async Task Goo() + { + dynamic mc = new MC(); + var rez = await mc.Goo(null, await ((Func>)(async () => { await Task.Delay(1); return ""Test""; }))()); + if (rez == ""Test"") + return 0; + return 1; + } + + static void Main() + { + Console.WriteLine(Goo().Result); + } +}"; + CompileAndVerify(source, "0", references: new[] { CSharpRef }); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (14,19): error CS9328: Method 'Test.Goo()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // var rez = await mc.Goo(null, await ((Func>)(async () => { await Task.Delay(1); return "Test"; }))()); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, @"await mc.Goo(null, await ((Func>)(async () => { await Task.Delay(1); return ""Test""; }))())").WithArguments("Test.Goo()").WithLocation(14, 19) + ); + } + + [Fact] + public void AsyncStackSpill_assign01() { var source = @" using System; using System.Threading; using System.Threading.Tasks; -class TestCase +struct TestCase { + private int val; public async Task GetVal(T t) { await Task.Delay(1); @@ -2627,12 +7035,13 @@ public async Task GetVal(T t) public async void Run() { int tests = 0; + try { tests++; int[] x = new int[] { 1, 2, 3, 4 }; - x[await GetVal(0)] += await GetVal(4); - if (x[0] == 5) + val = x[await GetVal(0)] += await GetVal(4); + if (x[0] == 5 && val == await GetVal(5)) Driver.Count++; } finally @@ -2665,30 +7074,32 @@ static void Main() } [Fact] - public void SpillOperator_Compound2() + public void AsyncStackSpill_assign01WithTaskAndRuntimeAsync() { var source = @" using System; using System.Threading; using System.Threading.Tasks; -class TestCase +struct TestCase { + private int val; public async Task GetVal(T t) { await Task.Delay(1); return t; } - public async void Run() + public async Task Run() { int tests = 0; + try { tests++; int[] x = new int[] { 1, 2, 3, 4 }; - x[await GetVal(0)] += await GetVal(4); - if (x[0] == 5) + val = x[await GetVal(0)] += await GetVal(4); + if (x[0] == 5 && val == await GetVal(5)) Driver.Count++; } finally @@ -2708,7 +7119,9 @@ class Driver static void Main() { var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed t.Run(); +#pragma warning restore CS4014 CompletedSignal.WaitOne(); // 0 - success @@ -2718,53 +7131,150 @@ static void Main() } }"; CompileAndVerify(source, "0"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (15,23): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // public async Task Run() + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Run").WithArguments("TestCase.Run()").WithLocation(15, 23), + // (23,21): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // val = x[await GetVal(0)] += await GetVal(4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(0)").WithArguments("TestCase.Run()").WithLocation(23, 21) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // [Run]: Return value missing on the stack. { Offset = 0x9d } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("TestCase.Run()", """ + // { + // // Code size 158 (0x9e) + // .maxstack 5 + // .locals init (int V_0, //tests + // int V_1, + // int& V_2, + // int V_3, + // int V_4, + // int V_5, + // bool V_6) + // IL_0000: ldc.i4.0 + // IL_0001: stloc.0 + // .try + // { + // IL_0002: ldloc.0 + // IL_0003: ldc.i4.1 + // IL_0004: add + // IL_0005: stloc.0 + // IL_0006: ldc.i4.4 + // IL_0007: newarr "int" + // IL_000c: dup + // IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" + // IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + // IL_0017: dup + // IL_0018: ldarg.0 + // IL_0019: ldc.i4.0 + // IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0024: stloc.1 + // IL_0025: ldloc.1 + // IL_0026: ldelema "int" + // IL_002b: stloc.2 + // IL_002c: ldloc.2 + // IL_002d: ldind.i4 + // IL_002e: stloc.3 + // IL_002f: ldarg.0 + // IL_0030: ldc.i4.4 + // IL_0031: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0036: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_003b: stloc.s V_4 + // IL_003d: ldarg.0 + // IL_003e: ldloc.2 + // IL_003f: ldloc.3 + // IL_0040: ldloc.s V_4 + // IL_0042: add + // IL_0043: dup + // IL_0044: stloc.s V_5 + // IL_0046: stind.i4 + // IL_0047: ldloc.s V_5 + // IL_0049: stfld "int TestCase.val" + // IL_004e: ldc.i4.0 + // IL_004f: ldelem.i4 + // IL_0050: ldc.i4.5 + // IL_0051: ceq + // IL_0053: stloc.s V_6 + // IL_0055: ldloc.s V_6 + // IL_0057: brfalse.s IL_0073 + // IL_0059: ldarg.0 + // IL_005a: ldfld "int TestCase.val" + // IL_005f: ldarg.0 + // IL_0060: ldc.i4.5 + // IL_0061: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0066: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_006b: stloc.s V_4 + // IL_006d: ldloc.s V_4 + // IL_006f: ceq + // IL_0071: stloc.s V_6 + // IL_0073: ldloc.s V_6 + // IL_0075: brfalse.s IL_0083 + // IL_0077: ldsfld "int Driver.Count" + // IL_007c: ldc.i4.1 + // IL_007d: add + // IL_007e: stsfld "int Driver.Count" + // IL_0083: leave.s IL_009d + // } + // finally + // { + // IL_0085: ldsfld "int Driver.Count" + // IL_008a: ldloc.0 + // IL_008b: sub + // IL_008c: stsfld "int Driver.Result" + // IL_0091: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + // IL_0096: callvirt "bool System.Threading.EventWaitHandle.Set()" + // IL_009b: pop + // IL_009c: endfinally + // } + // IL_009d: ret + // } + // """); } [Fact] - public void Async_StackSpill_Argument_Generic04() + public void SpillCollectionInitializer() { var source = @" using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; -public class mc -{ - async public System.Threading.Tasks.Task Goo(T t, V u) { await Task.Delay(1); return u; } -} -class Test +struct PrivateCollection : IEnumerable { - static async Task Goo() + public List lst; //public so we can check the values + public void Add(int x) { - dynamic mc = new mc(); - var rez = await mc.Goo(null, await ((Func>)(async () => { await Task.Delay(1); return ""Test""; }))()); - if (rez == ""Test"") - return 0; - return 1; + if (lst == null) + lst = new List(); + lst.Add(x); } - static void Main() + public IEnumerator GetEnumerator() { - Console.WriteLine(Goo().Result); + return lst as IEnumerator; } -}"; - CompileAndVerify(source, "0", references: new[] { CSharpRef }); - } - - [Fact] - public void AsyncStackSpill_assign01() - { - var source = @" -using System; -using System.Threading; -using System.Threading.Tasks; +} -struct TestCase +class TestCase { - private int val; - public async Task GetVal(T t) + public async Task GetValue(T x) { await Task.Delay(1); - return t; + return x; } public async void Run() @@ -2774,18 +7284,22 @@ public async void Run() try { tests++; - int[] x = new int[] { 1, 2, 3, 4 }; - val = x[await GetVal(0)] += await GetVal(4); - if (x[0] == 5 && val == await GetVal(5)) + var myCol = new PrivateCollection() { + await GetValue(1), + await GetValue(2) + }; + if (myCol.lst[0] == 1 && myCol.lst[1] == 2) Driver.Count++; } finally { Driver.Result = Driver.Count - tests; - //When test complete, set the flag. + //When test completes, set the flag. Driver.CompletedSignal.Set(); } } + + public int Goo { get; set; } } class Driver @@ -2809,7 +7323,7 @@ static void Main() } [Fact] - public void SpillCollectionInitializer() + public void SpillCollectionInitializerWithRuntimeAsync() { var source = @" using System; @@ -2842,7 +7356,7 @@ public async Task GetValue(T x) return x; } - public async void Run() + public async Task Run() { int tests = 0; @@ -2885,6 +7399,90 @@ static void Main() } }"; CompileAndVerify(source, "0"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetValue]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x7f } + """ + }); + + verifier.VerifyDiagnostics( + // (65,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. + // t.Run(); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "t.Run()").WithLocation(65, 9) + ); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 128 (0x80) + .maxstack 2 + .locals init (int V_0, //tests + PrivateCollection V_1, //myCol + int V_2, + int V_3, + PrivateCollection V_4) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldloc.0 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.0 + IL_0006: ldloca.s V_4 + IL_0008: initobj "PrivateCollection" + IL_000e: ldarg.0 + IL_000f: ldc.i4.1 + IL_0010: call "System.Threading.Tasks.Task TestCase.GetValue(int)" + IL_0015: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001a: stloc.2 + IL_001b: ldloca.s V_4 + IL_001d: ldloc.2 + IL_001e: call "void PrivateCollection.Add(int)" + IL_0023: ldarg.0 + IL_0024: ldc.i4.2 + IL_0025: call "System.Threading.Tasks.Task TestCase.GetValue(int)" + IL_002a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002f: stloc.3 + IL_0030: ldloca.s V_4 + IL_0032: ldloc.3 + IL_0033: call "void PrivateCollection.Add(int)" + IL_0038: ldloc.s V_4 + IL_003a: stloc.1 + IL_003b: ldloc.1 + IL_003c: ldfld "System.Collections.Generic.List PrivateCollection.lst" + IL_0041: ldc.i4.0 + IL_0042: callvirt "int System.Collections.Generic.List.this[int].get" + IL_0047: ldc.i4.1 + IL_0048: bne.un.s IL_0065 + IL_004a: ldloc.1 + IL_004b: ldfld "System.Collections.Generic.List PrivateCollection.lst" + IL_0050: ldc.i4.1 + IL_0051: callvirt "int System.Collections.Generic.List.this[int].get" + IL_0056: ldc.i4.2 + IL_0057: bne.un.s IL_0065 + IL_0059: ldsfld "int Driver.Count" + IL_005e: ldc.i4.1 + IL_005f: add + IL_0060: stsfld "int Driver.Count" + IL_0065: leave.s IL_007f + } + finally + { + IL_0067: ldsfld "int Driver.Count" + IL_006c: ldloc.0 + IL_006d: sub + IL_006e: stsfld "int Driver.Result" + IL_0073: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0078: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_007d: pop + IL_007e: endfinally + } + IL_007f: ret + } + """); } [Fact] @@ -2924,6 +7522,44 @@ static void Main() } }"; CompileAndVerify(source, "42"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Unexpected type on the stack. { Offset = 0x47, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 72 (0x48) + .maxstack 4 + .locals init (int V_0) + IL_0000: newobj "MyClass..ctor()" + IL_0005: dup + IL_0006: ldc.i4.s 21 + IL_0008: stfld "int MyClass.Field" + IL_000d: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_0012: ldsfld "System.Func TestCase.<>c.<>9__1_0" + IL_0017: dup + IL_0018: brtrue.s IL_0031 + IL_001a: pop + IL_001b: ldsfld "TestCase.<>c TestCase.<>c.<>9" + IL_0020: ldftn "int TestCase.<>c.b__1_0()" + IL_0026: newobj "System.Func..ctor(object, System.IntPtr)" + IL_002b: dup + IL_002c: stsfld "System.Func TestCase.<>c.<>9__1_0" + IL_0031: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0036: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003b: stloc.0 + IL_003c: ldflda "int MyClass.Field" + IL_0041: ldloc.0 + IL_0042: call "int TestCase.Goo(ref int, int)" + IL_0047: ret + } + """); } [Fact] @@ -3010,6 +7646,288 @@ static void Main() CompileAndVerify(source, "0"); } + [Fact] + public void SpillManagedPointerAssign03WithTaskAndRuntimeAsync() + { + var source = @" +using System; +using System.Threading; +using System.Threading.Tasks; + +class TestCase +{ + public async Task GetVal(T t) + { + await Task.Delay(1); + return t; + } + + class PrivClass + { + internal struct ValueT + { + public int Field; + } + + internal ValueT[] arr = new ValueT[3]; + } + + private PrivClass myClass; + + public async Task Run() + { + int tests = 0; + this.myClass = new PrivClass(); + + try + { + tests++; + this.myClass.arr[0].Field = await GetVal(4); + if (myClass.arr[0].Field == 4) + Driver.Count++; + + tests++; + this.myClass.arr[0].Field += await GetVal(4); + if (myClass.arr[0].Field == 8) + Driver.Count++; + + tests++; + this.myClass.arr[await GetVal(1)].Field += await GetVal(4); + if (myClass.arr[1].Field == 4) + Driver.Count++; + + tests++; + this.myClass.arr[await GetVal(1)].Field++; + if (myClass.arr[1].Field == 5) + Driver.Count++; + } + finally + { + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } +} + +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + t.Run(); +#pragma warning restore CS4014 + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + CompileAndVerify(source, "0"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (39,42): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // this.myClass.arr[0].Field += await GetVal(4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run()").WithLocation(39, 42), + // (39,42): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // this.myClass.arr[0].Field += await GetVal(4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run()").WithLocation(39, 42), + // (44,30): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // this.myClass.arr[await GetVal(1)].Field += await GetVal(4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(1)").WithArguments("TestCase.Run()").WithLocation(44, 30), + // (44,30): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // this.myClass.arr[await GetVal(1)].Field += await GetVal(4); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(1)").WithArguments("TestCase.Run()").WithLocation(44, 30) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // [Run]: Return value missing on the stack. { Offset = 0x182 } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("TestCase.Run()", """ + // { + // // Code size 387 (0x183) + // .maxstack 3 + // .locals init (int V_0, //tests + // int V_1, + // int V_2, + // int V_3) + // IL_0000: ldc.i4.0 + // IL_0001: stloc.0 + // IL_0002: ldarg.0 + // IL_0003: newobj "TestCase.PrivClass..ctor()" + // IL_0008: stfld "TestCase.PrivClass TestCase.myClass" + // .try + // { + // IL_000d: ldloc.0 + // IL_000e: ldc.i4.1 + // IL_000f: add + // IL_0010: stloc.0 + // IL_0011: ldarg.0 + // IL_0012: ldfld "TestCase.PrivClass TestCase.myClass" + // IL_0017: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + // IL_001c: dup + // IL_001d: ldc.i4.0 + // IL_001e: ldelema "TestCase.PrivClass.ValueT" + // IL_0023: pop + // IL_0024: ldarg.0 + // IL_0025: ldc.i4.4 + // IL_0026: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_002b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0030: stloc.1 + // IL_0031: ldc.i4.0 + // IL_0032: ldelema "TestCase.PrivClass.ValueT" + // IL_0037: ldloc.1 + // IL_0038: stfld "int TestCase.PrivClass.ValueT.Field" + // IL_003d: ldarg.0 + // IL_003e: ldfld "TestCase.PrivClass TestCase.myClass" + // IL_0043: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + // IL_0048: ldc.i4.0 + // IL_0049: ldelema "TestCase.PrivClass.ValueT" + // IL_004e: ldfld "int TestCase.PrivClass.ValueT.Field" + // IL_0053: ldc.i4.4 + // IL_0054: bne.un.s IL_0062 + // IL_0056: ldsfld "int Driver.Count" + // IL_005b: ldc.i4.1 + // IL_005c: add + // IL_005d: stsfld "int Driver.Count" + // IL_0062: ldloc.0 + // IL_0063: ldc.i4.1 + // IL_0064: add + // IL_0065: stloc.0 + // IL_0066: ldarg.0 + // IL_0067: ldfld "TestCase.PrivClass TestCase.myClass" + // IL_006c: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + // IL_0071: ldc.i4.0 + // IL_0072: ldelema "TestCase.PrivClass.ValueT" + // IL_0077: ldflda "int TestCase.PrivClass.ValueT.Field" + // IL_007c: dup + // IL_007d: ldind.i4 + // IL_007e: stloc.1 + // IL_007f: ldarg.0 + // IL_0080: ldc.i4.4 + // IL_0081: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_0086: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_008b: stloc.2 + // IL_008c: ldloc.1 + // IL_008d: ldloc.2 + // IL_008e: add + // IL_008f: stind.i4 + // IL_0090: ldarg.0 + // IL_0091: ldfld "TestCase.PrivClass TestCase.myClass" + // IL_0096: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + // IL_009b: ldc.i4.0 + // IL_009c: ldelema "TestCase.PrivClass.ValueT" + // IL_00a1: ldfld "int TestCase.PrivClass.ValueT.Field" + // IL_00a6: ldc.i4.8 + // IL_00a7: bne.un.s IL_00b5 + // IL_00a9: ldsfld "int Driver.Count" + // IL_00ae: ldc.i4.1 + // IL_00af: add + // IL_00b0: stsfld "int Driver.Count" + // IL_00b5: ldloc.0 + // IL_00b6: ldc.i4.1 + // IL_00b7: add + // IL_00b8: stloc.0 + // IL_00b9: ldarg.0 + // IL_00ba: ldfld "TestCase.PrivClass TestCase.myClass" + // IL_00bf: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + // IL_00c4: ldarg.0 + // IL_00c5: ldc.i4.1 + // IL_00c6: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_00cb: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_00d0: stloc.2 + // IL_00d1: ldloc.2 + // IL_00d2: ldelema "TestCase.PrivClass.ValueT" + // IL_00d7: ldflda "int TestCase.PrivClass.ValueT.Field" + // IL_00dc: dup + // IL_00dd: ldind.i4 + // IL_00de: stloc.1 + // IL_00df: ldarg.0 + // IL_00e0: ldc.i4.4 + // IL_00e1: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_00e6: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_00eb: stloc.3 + // IL_00ec: ldloc.1 + // IL_00ed: ldloc.3 + // IL_00ee: add + // IL_00ef: stind.i4 + // IL_00f0: ldarg.0 + // IL_00f1: ldfld "TestCase.PrivClass TestCase.myClass" + // IL_00f6: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + // IL_00fb: ldc.i4.1 + // IL_00fc: ldelema "TestCase.PrivClass.ValueT" + // IL_0101: ldfld "int TestCase.PrivClass.ValueT.Field" + // IL_0106: ldc.i4.4 + // IL_0107: bne.un.s IL_0115 + // IL_0109: ldsfld "int Driver.Count" + // IL_010e: ldc.i4.1 + // IL_010f: add + // IL_0110: stsfld "int Driver.Count" + // IL_0115: ldloc.0 + // IL_0116: ldc.i4.1 + // IL_0117: add + // IL_0118: stloc.0 + // IL_0119: ldarg.0 + // IL_011a: ldfld "TestCase.PrivClass TestCase.myClass" + // IL_011f: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + // IL_0124: ldarg.0 + // IL_0125: ldc.i4.1 + // IL_0126: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + // IL_012b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0130: stloc.3 + // IL_0131: ldloc.3 + // IL_0132: ldelema "TestCase.PrivClass.ValueT" + // IL_0137: ldflda "int TestCase.PrivClass.ValueT.Field" + // IL_013c: dup + // IL_013d: ldind.i4 + // IL_013e: stloc.1 + // IL_013f: ldloc.1 + // IL_0140: ldc.i4.1 + // IL_0141: add + // IL_0142: stind.i4 + // IL_0143: ldarg.0 + // IL_0144: ldfld "TestCase.PrivClass TestCase.myClass" + // IL_0149: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + // IL_014e: ldc.i4.1 + // IL_014f: ldelema "TestCase.PrivClass.ValueT" + // IL_0154: ldfld "int TestCase.PrivClass.ValueT.Field" + // IL_0159: ldc.i4.5 + // IL_015a: bne.un.s IL_0168 + // IL_015c: ldsfld "int Driver.Count" + // IL_0161: ldc.i4.1 + // IL_0162: add + // IL_0163: stsfld "int Driver.Count" + // IL_0168: leave.s IL_0182 + // } + // finally + // { + // IL_016a: ldsfld "int Driver.Count" + // IL_016f: ldloc.0 + // IL_0170: sub + // IL_0171: stsfld "int Driver.Result" + // IL_0176: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + // IL_017b: callvirt "bool System.Threading.EventWaitHandle.Set()" + // IL_0180: pop + // IL_0181: endfinally + // } + // IL_0182: ret + // } + // """); + } + [Fact, WorkItem(36443, "https://github.com/dotnet/roslyn/issues/36443")] public void SpillCompoundAssignmentToNullableMemberOfLocal_01() { @@ -3028,8 +7946,68 @@ static async Task Main() static Task GetInt() => Task.FromResult((int?)1); }"; - CompileAndVerify(source, expectedOutput: "", options: TestOptions.ReleaseExe); - CompileAndVerify(source, expectedOutput: "", options: TestOptions.DebugExe); + var expectedOutput = ""; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (11,34): error CS9328: Method 'S.Main()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // Console.WriteLine(s.i += await GetInt()); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetInt()").WithArguments("S.Main()").WithLocation(11, 34) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [Main]: Return value missing on the stack. { Offset = 0x63 } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("S.Main()", """ + // { + // // Code size 100 (0x64) + // .maxstack 3 + // .locals init (S V_0, //s + // int? V_1, + // int? V_2, + // int? V_3) + // IL_0000: ldloca.s V_0 + // IL_0002: initobj "S" + // IL_0008: ldloca.s V_0 + // IL_000a: ldflda "int? S.i" + // IL_000f: dup + // IL_0010: ldobj "int?" + // IL_0015: stloc.1 + // IL_0016: call "System.Threading.Tasks.Task S.GetInt()" + // IL_001b: call "int? System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0020: stloc.2 + // IL_0021: ldloca.s V_1 + // IL_0023: call "readonly bool int?.HasValue.get" + // IL_0028: ldloca.s V_2 + // IL_002a: call "readonly bool int?.HasValue.get" + // IL_002f: and + // IL_0030: brtrue.s IL_003d + // IL_0032: ldloca.s V_3 + // IL_0034: initobj "int?" + // IL_003a: ldloc.3 + // IL_003b: br.s IL_0051 + // IL_003d: ldloca.s V_1 + // IL_003f: call "readonly int int?.GetValueOrDefault()" + // IL_0044: ldloca.s V_2 + // IL_0046: call "readonly int int?.GetValueOrDefault()" + // IL_004b: add + // IL_004c: newobj "int?..ctor(int)" + // IL_0051: dup + // IL_0052: stloc.3 + // IL_0053: stobj "int?" + // IL_0058: ldloc.3 + // IL_0059: box "int?" + // IL_005e: call "void System.Console.WriteLine(object)" + // IL_0063: ret + // } + // """); } [Fact, WorkItem(36443, "https://github.com/dotnet/roslyn/issues/36443")] @@ -3057,8 +8035,45 @@ async System.Threading.Tasks.Task M2() } } "; - CompileAndVerify(source, expectedOutput: "43", options: TestOptions.DebugExe); - CompileAndVerify(source, expectedOutput: "43", options: TestOptions.ReleaseExe); + var expectedOutput = "43"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xf } + [M]: Return value missing on the stack. { Offset = 0x27 } + [M2]: Unexpected type on the stack. { Offset = 0x26, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M()", """ + { + // Code size 40 (0x28) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.field" + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: call "System.Threading.Tasks.Task C.M2()" + IL_000d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0012: stloc.1 + IL_0013: ldarg.0 + IL_0014: ldloc.0 + IL_0015: ldloc.1 + IL_0016: add + IL_0017: stfld "int C.field" + IL_001c: ldarg.0 + IL_001d: ldfld "int C.field" + IL_0022: call "void System.Console.Write(int)" + IL_0027: ret + } + """); } [Fact, WorkItem(36443, "https://github.com/dotnet/roslyn/issues/36443")] @@ -3086,8 +8101,60 @@ async System.Threading.Tasks.Task M() } } "; - CompileAndVerify(source, expectedOutput: "43", options: TestOptions.ReleaseExe); - CompileAndVerify(source, expectedOutput: "43", options: TestOptions.DebugExe); + var expectedOutput = "43"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xf } + [M]: Return value missing on the stack. { Offset = 0x59 } + [M2]: Unexpected type on the stack. { Offset = 0x2b, Found = value '[System.Runtime]System.Nullable`1', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1>' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M()", """ + { + // Code size 90 (0x5a) + .maxstack 3 + .locals init (int? V_0, + int? V_1, + int? V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int? C.field" + IL_0006: stloc.0 + IL_0007: ldarg.0 + IL_0008: call "System.Threading.Tasks.Task C.M2()" + IL_000d: call "int? System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0012: stloc.1 + IL_0013: ldarg.0 + IL_0014: ldloca.s V_0 + IL_0016: call "readonly bool int?.HasValue.get" + IL_001b: ldloca.s V_1 + IL_001d: call "readonly bool int?.HasValue.get" + IL_0022: and + IL_0023: brtrue.s IL_0030 + IL_0025: ldloca.s V_2 + IL_0027: initobj "int?" + IL_002d: ldloc.2 + IL_002e: br.s IL_0044 + IL_0030: ldloca.s V_0 + IL_0032: call "readonly int int?.GetValueOrDefault()" + IL_0037: ldloca.s V_1 + IL_0039: call "readonly int int?.GetValueOrDefault()" + IL_003e: add + IL_003f: newobj "int?..ctor(int)" + IL_0044: stfld "int? C.field" + IL_0049: ldarg.0 + IL_004a: ldfld "int? C.field" + IL_004f: box "int?" + IL_0054: call "void System.Console.Write(object)" + IL_0059: ret + } + """); } [Fact, WorkItem(36443, "https://github.com/dotnet/roslyn/issues/36443")] @@ -3108,64 +8175,282 @@ static async Task M(S s = default) static async Task Main() { - M(); + M(); + } + + static Task GetInt() => Task.FromResult((int?)1); +}"; + var expectedOutput = ""; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (11,34): error CS9328: Method 'S.M(S)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // Console.WriteLine(s.i += await GetInt()); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetInt()").WithArguments("S.M(S)").WithLocation(11, 34), + // (16,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. + // M(); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "M()").WithLocation(16, 9), + // (14,23): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task Main() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(14, 23) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [M]: Return value missing on the stack. { Offset = 0x63 } + // [Main]: Return value missing on the stack. { Offset = 0xf } + // """ + // }); + + // verifier.VerifyDiagnostics( + // // (14,23): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // // static async Task Main() + // Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(14, 23), + // // (16,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. + // // M(); + // Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "M()").WithLocation(16, 9) + // ); + // verifier.VerifyIL("S.M(S)", """ + // { + // // Code size 100 (0x64) + // .maxstack 3 + // .locals init (int? V_0, + // int? V_1, + // int? V_2) + // IL_0000: ldarga.s V_0 + // IL_0002: initobj "S" + // IL_0008: ldarga.s V_0 + // IL_000a: ldflda "int? S.i" + // IL_000f: dup + // IL_0010: ldobj "int?" + // IL_0015: stloc.0 + // IL_0016: call "System.Threading.Tasks.Task S.GetInt()" + // IL_001b: call "int? System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0020: stloc.1 + // IL_0021: ldloca.s V_0 + // IL_0023: call "readonly bool int?.HasValue.get" + // IL_0028: ldloca.s V_1 + // IL_002a: call "readonly bool int?.HasValue.get" + // IL_002f: and + // IL_0030: brtrue.s IL_003d + // IL_0032: ldloca.s V_2 + // IL_0034: initobj "int?" + // IL_003a: ldloc.2 + // IL_003b: br.s IL_0051 + // IL_003d: ldloca.s V_0 + // IL_003f: call "readonly int int?.GetValueOrDefault()" + // IL_0044: ldloca.s V_1 + // IL_0046: call "readonly int int?.GetValueOrDefault()" + // IL_004b: add + // IL_004c: newobj "int?..ctor(int)" + // IL_0051: dup + // IL_0052: stloc.2 + // IL_0053: stobj "int?" + // IL_0058: ldloc.2 + // IL_0059: box "int?" + // IL_005e: call "void System.Console.WriteLine(object)" + // IL_0063: ret + // } + // """); + } + + [Fact] + public void SpillSacrificialRead() + { + var source = @" +using System; +using System.Threading.Tasks; + +class C +{ + static void F1(ref int x, int y, int z) + { + x += y + z; + } + + static int F0() + { + Console.WriteLine(-1); + return 0; + } + + static async Task F2() + { + int[] x = new int[1] { 21 }; + x = null; + F1(ref x[0], F0(), await Task.Factory.StartNew(() => 21)); + return x[0]; + } + + public static void Main() + { + var t = F2(); + try + { + t.Wait(); + } + catch(Exception) + { + Console.WriteLine(0); + return; + } + + Console.WriteLine(-1); + } +}"; + CompileAndVerify(source, "0"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (22,28): error CS9328: Method 'C.F2()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // F1(ref x[0], F0(), await Task.Factory.StartNew(() => 21)); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 21)").WithArguments("C.F2()").WithLocation(22, 28) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [F2]: Unexpected type on the stack. { Offset = 0x52, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("C.F2()", """ + // { + // // Code size 83 (0x53) + // .maxstack 5 + // .locals init (int V_0, + // int V_1) + // IL_0000: ldc.i4.1 + // IL_0001: newarr "int" + // IL_0006: dup + // IL_0007: ldc.i4.0 + // IL_0008: ldc.i4.s 21 + // IL_000a: stelem.i4 + // IL_000b: pop + // IL_000c: ldnull + // IL_000d: dup + // IL_000e: ldc.i4.0 + // IL_000f: ldelema "int" + // IL_0014: call "int C.F0()" + // IL_0019: stloc.0 + // IL_001a: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + // IL_001f: ldsfld "System.Func C.<>c.<>9__2_0" + // IL_0024: dup + // IL_0025: brtrue.s IL_003e + // IL_0027: pop + // IL_0028: ldsfld "C.<>c C.<>c.<>9" + // IL_002d: ldftn "int C.<>c.b__2_0()" + // IL_0033: newobj "System.Func..ctor(object, System.IntPtr)" + // IL_0038: dup + // IL_0039: stsfld "System.Func C.<>c.<>9__2_0" + // IL_003e: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + // IL_0043: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0048: stloc.1 + // IL_0049: ldloc.0 + // IL_004a: ldloc.1 + // IL_004b: call "void C.F1(ref int, int, int)" + // IL_0050: ldc.i4.0 + // IL_0051: ldelem.i4 + // IL_0052: ret + // } + // """); + } + + [Fact] + public void SpillRefThisStruct() + { + var source = @" +using System; +using System.Threading.Tasks; + +struct s1 +{ + public int X; + + public async void Goo1() + { + Bar(ref this, await Task.FromResult(42)); + } + + public void Goo2() + { + Bar(ref this, 42); + } + + public void Bar(ref s1 x, int y) + { + x.X = 42; } +} - static Task GetInt() => Task.FromResult((int?)1); -}"; - CompileAndVerify(source, expectedOutput: "", options: TestOptions.ReleaseExe); - CompileAndVerify(source, expectedOutput: "", options: TestOptions.DebugExe); - } - - [Fact] - public void SpillSacrificialRead() - { - var source = @" -using System; -using System.Threading.Tasks; - -class C +class c1 { - static void F1(ref int x, int y, int z) + public int X; + + public async void Goo1() { - x += y + z; + Bar(this, await Task.FromResult(42)); } - static int F0() + public void Goo2() { - Console.WriteLine(-1); - return 0; + Bar(this, 42); } - static async Task F2() + public void Bar(c1 x, int y) { - int[] x = new int[1] { 21 }; - x = null; - F1(ref x[0], F0(), await Task.Factory.StartNew(() => 21)); - return x[0]; + x.X = 42; } +} +class C +{ public static void Main() { - var t = F2(); - try { - t.Wait(); + s1 s; + s.X = -1; + s.Goo1(); + Console.WriteLine(s.X); } - catch(Exception e) + { - Console.WriteLine(0); - return; + s1 s; + s.X = -1; + s.Goo2(); + Console.WriteLine(s.X); } - Console.WriteLine(-1); + { + c1 c = new c1(); + c.X = -1; + c.Goo1(); + Console.WriteLine(c.X); + } + + { + c1 c = new c1(); + c.X = -1; + c.Goo2(); + Console.WriteLine(c.X); + } } }"; - CompileAndVerify(source, "0"); + var expectedOutput = @" +-1 +42 +42 +42 +"; + CompileAndVerify(source, expectedOutput); } [Fact] - public void SpillRefThisStruct() + public void SpillRefThisStruct_WithTaskAndRuntimeAsync() { var source = @" using System; @@ -3175,7 +8460,7 @@ struct s1 { public int X; - public async void Goo1() + public async Task Goo1() { Bar(ref this, await Task.FromResult(42)); } @@ -3195,7 +8480,7 @@ class c1 { public int X; - public async void Goo1() + public async Task Goo1() { Bar(this, await Task.FromResult(42)); } @@ -3218,7 +8503,9 @@ public static void Main() { s1 s; s.X = -1; +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed s.Goo1(); +#pragma warning restore CS4014 Console.WriteLine(s.X); } @@ -3232,7 +8519,9 @@ public static void Main() { c1 c = new c1(); c.X = -1; +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed c.Goo1(); +#pragma warning restore CS4014 Console.WriteLine(c.X); } @@ -3244,13 +8533,63 @@ public static void Main() } } }"; - var expected = @" + var expectedOutput = @" -1 42 42 42 "; - CompileAndVerify(source, expected); + CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,23): error CS9328: Method 's1.Goo1()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // public async Task Goo1() + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Goo1").WithArguments("s1.Goo1()").WithLocation(9, 23) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [Goo1]: Return value missing on the stack. { Offset = 0x15 } + // [Goo1]: Return value missing on the stack. { Offset = 0x15 } + // """ + // }); + + // verifier.VerifyDiagnostics(); + // verifier.VerifyIL("s1.Goo1()", """ + // { + // // Code size 22 (0x16) + // .maxstack 3 + // .locals init (int V_0) + // IL_0000: ldc.i4.s 42 + // IL_0002: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" + // IL_0007: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_000c: stloc.0 + // IL_000d: ldarg.0 + // IL_000e: ldarg.0 + // IL_000f: ldloc.0 + // IL_0010: call "void s1.Bar(ref s1, int)" + // IL_0015: ret + // } + // """); + + // verifier.VerifyIL("c1.Goo1()", """ + // { + // // Code size 22 (0x16) + // .maxstack 3 + // .locals init (int V_0) + // IL_0000: ldc.i4.s 42 + // IL_0002: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" + // IL_0007: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_000c: stloc.0 + // IL_000d: ldarg.0 + // IL_000e: ldarg.0 + // IL_000f: ldloc.0 + // IL_0010: call "void c1.Bar(c1, int)" + // IL_0015: ret + // } + // """); } [Fact] @@ -3275,7 +8614,8 @@ public static async Task Boom() "; // See tracking issue https://github.com/dotnet/runtime/issues/96695 - var verifier = CompileAndVerify(source, expectedOutput: "System.Int32", + var expectedOutput = "System.Int32"; + var verifier = CompileAndVerify(source, expectedOutput: expectedOutput, verify: Verification.FailsILVerify with { ILVerifyMessage = "[MoveNext]: Unrecognized arguments for delegate .ctor. { Offset = 0x6d }" }); verifier.VerifyIL("AsyncBug.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ @@ -3354,6 +8694,32 @@ .locals init (int V_0, IL_00a8: ret } """); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Boom]: Unrecognized arguments for delegate .ctor. { Offset = 0x16 } + [Boom]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("AsyncBug.Boom()", """ + { + // Code size 38 (0x26) + .maxstack 2 + IL_0000: ldc.i4.1 + IL_0001: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" + IL_0006: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000b: box "int" + IL_0010: ldftn "System.Type object.GetType()" + IL_0016: newobj "System.Func..ctor(object, System.IntPtr)" + IL_001b: callvirt "System.Type System.Func.Invoke()" + IL_0020: call "void System.Console.WriteLine(object)" + IL_0025: ret + } + """); } [Fact] @@ -3397,8 +8763,40 @@ static void Main(string[] args) } } "; - var expected = new bool[] { false, true, false, true, false }.Aggregate("", (str, next) => str += $"{next}{Environment.NewLine}"); - var v = CompileAndVerify(source, expected); + var expectedOutput = new bool[] { false, true, false, true, false }.Aggregate("", (str, next) => str += $"{next}{Environment.NewLine}"); + var v = CompileAndVerify(source, expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Danger]: Unexpected type on the stack. { Offset = 0x29, Found = ref 'AsyncBug.Program+SomeClass', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Killer]: Unexpected type on the stack. { Offset = 0x2e, Found = ref '[System.Runtime]System.Collections.Generic.IEnumerable`1', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1>' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("AsyncBug.Program.Killer()", """ + { + // Code size 47 (0x2f) + .maxstack 3 + .locals init (AsyncBug.Program.SomeClass V_0) + IL_0000: ldc.i4.5 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldtoken ".__StaticArrayInitTypeSize=20 .4F6ADDC9659D6FB90FE94B6688A79F2A1FA8D36EC43F8F3E1D9B6528C448A384" + IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0011: ldarg.0 + IL_0012: call "System.Threading.Tasks.Task AsyncBug.Program.Danger()" + IL_0017: call "AsyncBug.Program.SomeClass System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001c: stloc.0 + IL_001d: ldloc.0 + IL_001e: ldftn "bool AsyncBug.Program.SomeClass.Method(int)" + IL_0024: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0029: call "System.Collections.Generic.IEnumerable System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable, System.Func)" + IL_002e: ret + } + """); } [Fact] @@ -3435,6 +8833,30 @@ public static void Main() "; var v = CompileAndVerify(source, "42"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x12 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M(System.Threading.Tasks.Task)", """ + { + // Code size 19 (0x13) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0006: stloc.0 + IL_0007: call "ref int C.P.get" + IL_000c: ldloc.0 + IL_000d: call "void C.Assign(ref int, int)" + IL_0012: ret + } + """); } [Fact] @@ -3502,30 +8924,61 @@ void C(in object obj, int length) {} [WorkItem(27831, "https://github.com/dotnet/roslyn/issues/27831")] public void AwaitWithInParameter_NoArgModifier() { - CompileAndVerify(@" -using System; -using System.Threading.Tasks; -class Foo -{ - static async Task Main() - { - await A(""test"", Task.FromResult(4)); - } - - static async Task A(string s, Task task) - { - B(s, await task); - } + var source = """ + using System; + using System.Threading.Tasks; + class Goo + { + static async Task Main() + { + await A("test", Task.FromResult(4)); + } + + static async Task A(string s, Task task) + { + B(s, await task); + } + + static void B(in object obj, int v) + { + Console.WriteLine(obj); + Console.WriteLine(v); + } + } + """; + var expectedOutput = """ + test + 4 + """; + CompileAndVerify(source, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x15 } + [A]: Return value missing on the stack. { Offset = 0x11 } + """ + }); - static void B(in object obj, int v) - { - Console.WriteLine(obj); - Console.WriteLine(v); - } -}", expectedOutput: @" -test -4 -"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Goo.A(string, System.Threading.Tasks.Task)", """ + { + // Code size 18 (0x12) + .maxstack 2 + .locals init (object V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldarg.1 + IL_0003: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0008: stloc.1 + IL_0009: ldloca.s V_0 + IL_000b: ldloc.1 + IL_000c: call "void Goo.B(in object, int)" + IL_0011: ret + } + """); } [Fact, WorkItem(36856, "https://github.com/dotnet/roslyn/issues/36856")] @@ -3550,32 +9003,34 @@ private static Task TestAsync() return null; } } - -namespace System +namespace System.Text.Json.Serialization { - public readonly ref struct ReadOnlySpan + public static class JsonSerializer { - public static implicit operator ReadOnlySpan(T[] array) + public static TValue Parse(ReadOnlySpan utf8Json, JsonSerializerOptions options = null) { throw null; } } + public sealed class JsonSerializerOptions + { + } } -namespace System.Text.Json.Serialization +"; + + var span = @" +namespace System { - public static class JsonSerializer + public readonly ref struct ReadOnlySpan { - public static TValue Parse(ReadOnlySpan utf8Json, JsonSerializerOptions options = null) + public static implicit operator ReadOnlySpan(T[] array) { throw null; } } - public sealed class JsonSerializerOptions - { - } } "; - var v = CompileAndVerify(source, options: TestOptions.DebugExe); + var v = CompileAndVerify(source + span, options: TestOptions.DebugExe); v.VerifyMethodBody("Program.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { @@ -3677,6 +9132,29 @@ .locals init (int V_0, IL_00b7: ret } "); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Serialize]: Return value missing on the stack. { Offset = 0x16 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Serialize()", """ + { + // Code size 23 (0x17) + .maxstack 2 + IL_0000: call "System.Threading.Tasks.Task Program.TestAsync()" + IL_0005: call "byte[] System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(byte[])" + IL_000f: ldnull + IL_0010: call "string System.Text.Json.Serialization.JsonSerializer.Parse(System.ReadOnlySpan, System.Text.Json.Serialization.JsonSerializerOptions)" + IL_0015: pop + IL_0016: ret + } + """); } [Fact, WorkItem(37461, "https://github.com/dotnet/roslyn/issues/37461")] @@ -3719,6 +9197,55 @@ static Task Async1(int k, int i) expectedOutput: expectedOutput, verify: Verification.Fails // localloc is not verifiable. ); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Instruction cannot be verified. { Offset = 0xf } + [Main]: Instruction cannot be verified. { Offset = 0x2a } + [Main]: Return value missing on the stack. { Offset = 0x45 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("P.Main()", """ + { + // Code size 70 (0x46) + .maxstack 4 + .locals init (int V_0, + int V_1, + System.Span V_2, + System.ReadOnlySpan V_3) + IL_0000: call "int P.F1()" + IL_0005: stloc.0 + IL_0006: call "int P.F2()" + IL_000b: stloc.1 + IL_000c: ldc.i4.s 12 + IL_000e: conv.u + IL_000f: localloc + IL_0011: dup + IL_0012: ldtoken ".__StaticArrayInitTypeSize=12_Align=4 .358467287387D6976FA2AD2813A4D1AE4F0A0865C5125FB87D822D9432AA423D4" + IL_0017: call "System.ReadOnlySpan System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan(System.RuntimeFieldHandle)" + IL_001c: stloc.3 + IL_001d: ldloca.s V_3 + IL_001f: ldc.i4.0 + IL_0020: call "ref readonly int System.ReadOnlySpan.this[int].get" + IL_0025: ldc.i4.s 12 + IL_0027: unaligned. 4 + IL_002a: cpblk + IL_002c: ldc.i4.3 + IL_002d: newobj "System.Span..ctor(void*, int)" + IL_0032: stloc.2 + IL_0033: ldloc.0 + IL_0034: ldloc.1 + IL_0035: ldloc.2 + IL_0036: call "int P.G(int, System.Span)" + IL_003b: call "System.Threading.Tasks.Task P.Async1(int, int)" + IL_0040: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0045: ret + } + """); } [Fact, WorkItem(37461, "https://github.com/dotnet/roslyn/issues/37461")] @@ -3761,6 +9288,64 @@ static Task Async1(int k, int i) expectedOutput: expectedOutput, verify: Verification.Fails // localloc is not verifiable. ); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Instruction cannot be verified. { Offset = 0x1f } + [Main]: Expected ByRef on the stack. { Offset = 0x24, Found = Native Int } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("P.Main()", """ + { + // Code size 80 (0x50) + .maxstack 4 + .locals init (int V_0, + int V_1, + System.Span V_2, + int V_3) + IL_0000: call "int P.F1()" + IL_0005: stloc.0 + IL_0006: call "int P.F2()" + IL_000b: stloc.1 + IL_000c: ldc.i4 0x1f4 + IL_0011: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" + IL_0016: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001b: stloc.3 + IL_001c: ldc.i4.s 12 + IL_001e: conv.u + IL_001f: localloc + IL_0021: dup + IL_0022: ldc.i4.s 40 + IL_0024: stind.i4 + IL_0025: dup + IL_0026: ldc.i4.4 + IL_0027: add + IL_0028: ldloc.3 + IL_0029: stind.i4 + IL_002a: dup + IL_002b: ldc.i4.2 + IL_002c: conv.i + IL_002d: ldc.i4.4 + IL_002e: mul + IL_002f: add + IL_0030: ldc.i4 0x1770 + IL_0035: stind.i4 + IL_0036: ldc.i4.3 + IL_0037: newobj "System.Span..ctor(void*, int)" + IL_003c: stloc.2 + IL_003d: ldloc.0 + IL_003e: ldloc.1 + IL_003f: ldloc.2 + IL_0040: call "int P.G(int, System.Span)" + IL_0045: call "System.Threading.Tasks.Task P.Async1(int, int)" + IL_004a: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_004f: ret + } + """); } [Fact, WorkItem(37461, "https://github.com/dotnet/roslyn/issues/37461")] @@ -3841,8 +9426,57 @@ public F(bool result) public bool P2 => _result; } "; - CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "2"); - CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: "2"); + var expectedOutput = "2"; + CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput); + CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M1]: Unexpected type on the stack. { Offset = 0x38, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [SF]: Unexpected type on the stack. { Offset = 0xd, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M1(object, System.Threading.Tasks.Task)", """ + { + // Code size 57 (0x39) + .maxstack 1 + .locals init (int V_0, + Q V_1, + F V_2) + IL_0000: ldarg.0 + IL_0001: isinst "Q" + IL_0006: stloc.1 + IL_0007: ldloc.1 + IL_0008: brfalse.s IL_0035 + IL_000a: ldloc.1 + IL_000b: callvirt "F Q.F.get" + IL_0010: stloc.2 + IL_0011: ldloca.s V_2 + IL_0013: call "bool F.P1.get" + IL_0018: brtrue.s IL_0025 + IL_001a: ldloca.s V_2 + IL_001c: call "bool F.P2.get" + IL_0021: brtrue.s IL_0031 + IL_0023: br.s IL_0035 + IL_0025: ldarg.1 + IL_0026: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002b: brfalse.s IL_001a + IL_002d: ldc.i4.1 + IL_002e: stloc.0 + IL_002f: br.s IL_0037 + IL_0031: ldc.i4.2 + IL_0032: stloc.0 + IL_0033: br.s IL_0037 + IL_0035: ldc.i4.3 + IL_0036: stloc.0 + IL_0037: ldloc.0 + IL_0038: ret + } + """); } [Fact] @@ -3939,8 +9573,57 @@ class Box public T Value; } "; - CompileAndVerify(source, expectedOutput: "42", options: TestOptions.DebugExe); - CompileAndVerify(source, expectedOutput: "42", options: TestOptions.ReleaseExe); + var expectedOutput = "42"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x77 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Main()", """ + { + // Code size 120 (0x78) + .maxstack 7 + .locals init (Program.<>c__DisplayClass0_0 V_0) //CS$<>8__locals0 + IL_0000: newobj "Program.<>c__DisplayClass0_0..ctor()" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldc.i4.s 42 + IL_0009: stfld "int Program.<>c__DisplayClass0_0.value" + IL_000e: ldtoken "Box" + IL_0013: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_0018: call "System.Linq.Expressions.NewExpression System.Linq.Expressions.Expression.New(System.Type)" + IL_001d: ldc.i4.1 + IL_001e: newarr "System.Linq.Expressions.MemberBinding" + IL_0023: dup + IL_0024: ldc.i4.0 + IL_0025: ldtoken "int Box.Value" + IL_002a: ldtoken "Box" + IL_002f: call "System.Reflection.FieldInfo System.Reflection.FieldInfo.GetFieldFromHandle(System.RuntimeFieldHandle, System.RuntimeTypeHandle)" + IL_0034: ldloc.0 + IL_0035: ldtoken "Program.<>c__DisplayClass0_0" + IL_003a: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_003f: call "System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)" + IL_0044: ldtoken "int Program.<>c__DisplayClass0_0.value" + IL_0049: call "System.Reflection.FieldInfo System.Reflection.FieldInfo.GetFieldFromHandle(System.RuntimeFieldHandle)" + IL_004e: call "System.Linq.Expressions.MemberExpression System.Linq.Expressions.Expression.Field(System.Linq.Expressions.Expression, System.Reflection.FieldInfo)" + IL_0053: call "System.Linq.Expressions.MemberAssignment System.Linq.Expressions.Expression.Bind(System.Reflection.MemberInfo, System.Linq.Expressions.Expression)" + IL_0058: stelem.ref + IL_0059: call "System.Linq.Expressions.MemberInitExpression System.Linq.Expressions.Expression.MemberInit(System.Linq.Expressions.NewExpression, params System.Linq.Expressions.MemberBinding[])" + IL_005e: call "System.Linq.Expressions.ParameterExpression[] System.Array.Empty()" + IL_0063: call "System.Linq.Expressions.Expression>> System.Linq.Expressions.Expression.Lambda>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])" + IL_0068: call "System.Threading.Tasks.Task Program.M(System.Linq.Expressions.Expression>>)" + IL_006d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0072: call "void System.Console.WriteLine(int)" + IL_0077: ret + } + """); } [Fact] @@ -3977,8 +9660,18 @@ class AltBoolean } } "; - CompileAndVerify(source, expectedOutput: "True", options: TestOptions.DebugExe); - CompileAndVerify(source, expectedOutput: "True", options: TestOptions.ReleaseExe); + var expectedOutput = "True"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xb } + [MainAsync]: Return value missing on the stack. { Offset = 0x4b } + """ + }); } [Fact] @@ -4063,17 +9756,20 @@ class B public int x; }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"TestPropertyAccessThrows -Before Assignment -Caught NullReferenceException -TestFieldAccessThrows -Before Assignment -RHS -Caught NullReferenceException -TestPropertyAccessSucceeds -Before Assignment a.B.x is: 0 -RHS -After Assignment a.B.x is: 42") + var expectedOutput = """ + TestPropertyAccessThrows + Before Assignment + Caught NullReferenceException + TestFieldAccessThrows + Before Assignment + RHS + Caught NullReferenceException + TestPropertyAccessSucceeds + Before Assignment a.B.x is: 0 + RHS + After Assignment a.B.x is: 42 + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 184 (0xb8) @@ -4158,6 +9854,37 @@ .locals init (int V_0, IL_00b2: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00b7: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x1c } + [Main]: Return value missing on the stack. { Offset = 0x1e } + [TestPropertyAccessThrows]: Return value missing on the stack. { Offset = 0x30 } + [TestFieldAccessThrows]: Return value missing on the stack. { Offset = 0x34 } + [TestPropertyAccessSucceeds]: Return value missing on the stack. { Offset = 0x64 } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign(A)", """ + { + // Code size 29 (0x1d) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: callvirt "B A.B.get" + IL_0006: ldstr "RHS" + IL_000b: call "System.Threading.Tasks.Task Program.Write(string)" + IL_0010: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0015: stloc.0 + IL_0016: ldloc.0 + IL_0017: stfld "int B.x" + IL_001c: ret + } + """); } [Fact] @@ -4287,31 +10014,34 @@ class A public bool y; }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"TestIndexerThrows -Before Assignment -Caught IndexOutOfRangeException -TestAssignmentThrows -Before Assignment -RHS -Caught NullReferenceException -TestIndexerSucceeds -Before Assignment arr[0].x is: 0 -RHS -After Assignment arr[0].x is: 42 -TestReassignsArrayAndIndexerDuringAwait -Before Assignment arr.Length is: 1 -Before Assignment a.x is: 0 -RHS -After Assignment arr.Length is: 0 -After Assignment a.x is: 42 -TestReassignsTargetDuringAwait -Before Assignment arr[0].x is: 0 -Before Assignment arr[0].y is: False -Before Assignment a.x is: 0 -RHS -After Assignment arr[0].x is: 0 -After Assignment arr[0].y is: True -After Assignment a.x is: 42") + var expectedOutput = """ + TestIndexerThrows + Before Assignment + Caught IndexOutOfRangeException + TestAssignmentThrows + Before Assignment + RHS + Caught NullReferenceException + TestIndexerSucceeds + Before Assignment arr[0].x is: 0 + RHS + After Assignment arr[0].x is: 42 + TestReassignsArrayAndIndexerDuringAwait + Before Assignment arr.Length is: 1 + Before Assignment a.x is: 0 + RHS + After Assignment arr.Length is: 0 + After Assignment a.x is: 42 + TestReassignsTargetDuringAwait + Before Assignment arr[0].x is: 0 + Before Assignment arr[0].y is: False + Before Assignment a.x is: 0 + RHS + After Assignment arr[0].x is: 0 + After Assignment arr[0].y is: True + After Assignment a.x is: 42 + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 181 (0xb5) @@ -4397,6 +10127,42 @@ .locals init (int V_0, IL_00af: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00b4: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x19 } + [Main]: Return value missing on the stack. { Offset = 0x32 } + [TestIndexerThrows]: Return value missing on the stack. { Offset = 0x35 } + [TestAssignmentThrows]: Return value missing on the stack. { Offset = 0x35 } + [TestIndexerSucceeds]: Return value missing on the stack. { Offset = 0x5c } + [TestReassignsArrayAndIndexerDuringAwait]: Return value missing on the stack. { Offset = 0xc3 } + [TestReassignsTargetDuringAwait]: Return value missing on the stack. { Offset = 0xfd } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x3f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x40, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign(A[])", """ + { + // Code size 26 (0x1a) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: ldelem.ref + IL_0003: ldstr "RHS" + IL_0008: call "System.Threading.Tasks.Task Program.Write(string)" + IL_000d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0012: stloc.0 + IL_0013: ldloc.0 + IL_0014: stfld "int A.x" + IL_0019: ret + } + """); } [Fact] @@ -4506,25 +10272,28 @@ struct A public bool y; }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"TestIndexerThrows -Before Assignment -Caught IndexOutOfRangeException -TestIndexerSucceeds -Before Assignment arr[0].x is: 0 -RHS -After Assignment arr[0].x is: 42 -TestReassignsArrayAndIndexerDuringAwait -Before Assignment arr.Length is: 1 -Before Assignment arrCopy[0].x is: 0 -RHS -After Assignment arr.Length is: 0 -After Assignment arrCopy[0].x is: 42 -TestReassignsTargetDuringAwait -Before Assignment arr[0].x is: 0 -Before Assignment arr[0].y is: False -RHS -After Assignment arr[0].x is: 42 -Before Assignment arr[0].y is: True") + var expectedOutput = """ + TestIndexerThrows + Before Assignment + Caught IndexOutOfRangeException + TestIndexerSucceeds + Before Assignment arr[0].x is: 0 + RHS + After Assignment arr[0].x is: 42 + TestReassignsArrayAndIndexerDuringAwait + Before Assignment arr.Length is: 1 + Before Assignment arrCopy[0].x is: 0 + RHS + After Assignment arr.Length is: 0 + After Assignment arrCopy[0].x is: 42 + TestReassignsTargetDuringAwait + Before Assignment arr[0].x is: 0 + Before Assignment arr[0].y is: False + RHS + After Assignment arr[0].x is: 42 + Before Assignment arr[0].y is: True + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 198 (0xc6) @@ -4615,6 +10384,45 @@ .locals init (int V_0, IL_00c0: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00c5: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x25 } + [Main]: Return value missing on the stack. { Offset = 0x28 } + [TestIndexerThrows]: Return value missing on the stack. { Offset = 0x35 } + [TestIndexerSucceeds]: Return value missing on the stack. { Offset = 0x5c } + [TestReassignsArrayAndIndexerDuringAwait]: Return value missing on the stack. { Offset = 0xda } + [TestReassignsTargetDuringAwait]: Return value missing on the stack. { Offset = 0xdb } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x3f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x49, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign(A[])", """ + { + // Code size 38 (0x26) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: ldc.i4.0 + IL_0003: ldelema "A" + IL_0008: pop + IL_0009: ldstr "RHS" + IL_000e: call "System.Threading.Tasks.Task Program.Write(string)" + IL_0013: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0018: stloc.0 + IL_0019: ldc.i4.0 + IL_001a: ldelema "A" + IL_001f: ldloc.0 + IL_0020: stfld "int A.x" + IL_0025: ret + } + """); } [Fact] @@ -4696,20 +10504,23 @@ static async Task Write(string s) } }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"TestIndexerThrows -Before Assignment -RHS -Caught IndexOutOfRangeException -TestIndexerSucceeds -Before Assignment arr[0] is: 0 -RHS -After Assignment arr[0] is: 42 -TestReassignsArrayAndIndexerDuringAwait -Before Assignment arr.Length is: 1 -Before Assignment arrCopy[0] is: 0 -RHS -After Assignment arr.Length is: 0 -After Assignment arrCopy[0] is: 42") + var expectedOutput = """ + TestIndexerThrows + Before Assignment + RHS + Caught IndexOutOfRangeException + TestIndexerSucceeds + Before Assignment arr[0] is: 0 + RHS + After Assignment arr[0] is: 42 + TestReassignsArrayAndIndexerDuringAwait + Before Assignment arr.Length is: 1 + Before Assignment arrCopy[0] is: 0 + RHS + After Assignment arr.Length is: 0 + After Assignment arrCopy[0] is: 42 + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 176 (0xb0) @@ -4794,6 +10605,38 @@ .locals init (int V_0, IL_00aa: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00af: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x14 } + [Main]: Return value missing on the stack. { Offset = 0x1e } + [TestIndexerThrows]: Return value missing on the stack. { Offset = 0x35 } + [TestIndexerSucceeds]: Return value missing on the stack. { Offset = 0x52 } + [TestReassignsArrayAndIndexerDuringAwait]: Return value missing on the stack. { Offset = 0xbf } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x3f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign(int[])", """ + { + // Code size 21 (0x15) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldstr "RHS" + IL_0006: call "System.Threading.Tasks.Task Program.Write(string)" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.0 + IL_0011: ldc.i4.0 + IL_0012: ldloc.0 + IL_0013: stelem.i4 + IL_0014: ret + } + """); } [Fact] @@ -4888,19 +10731,22 @@ struct C public int x; }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"TestAIsNull -Before Assignment -Caught NullReferenceException -TestAIsNotNull -Before Assignment a.b.c.x is: 0 -RHS -After Assignment a.b.c.x is: 42 -ReassignADuringAssignment -Before Assignment a is null == False -Before Assignment aCopy.b.c.x is: 0 -RHS -After Assignment a is null == True -After Assignment aCopy.b.c.x is: 42") + var expectedOutput = """ + TestAIsNull + Before Assignment + Caught NullReferenceException + TestAIsNotNull + Before Assignment a.b.c.x is: 0 + RHS + After Assignment a.b.c.x is: 42 + ReassignADuringAssignment + Before Assignment a is null == False + Before Assignment aCopy.b.c.x is: 0 + RHS + After Assignment a is null == True + After Assignment aCopy.b.c.x is: 42 + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 201 (0xc9) @@ -4990,6 +10836,42 @@ .locals init (int V_0, IL_00c3: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00c8: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x28 } + [Main]: Return value missing on the stack. { Offset = 0x1e } + [TestAIsNull]: Return value missing on the stack. { Offset = 0x30 } + [TestAIsNotNull]: Return value missing on the stack. { Offset = 0x63 } + [ReassignADuringAssignment]: Return value missing on the stack. { Offset = 0xd8 } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x33, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign(A)", """ + { + // Code size 41 (0x29) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: ldfld "B A.b" + IL_0007: pop + IL_0008: ldstr "RHS" + IL_000d: call "System.Threading.Tasks.Task Program.Write(string)" + IL_0012: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0017: stloc.0 + IL_0018: ldflda "B A.b" + IL_001d: ldflda "C B.c" + IL_0022: ldloc.0 + IL_0023: stfld "int C.x" + IL_0028: ret + } + """); } [Fact] @@ -5081,23 +10963,26 @@ class B public int x { get { Console.WriteLine(""GetX""); return _x; } set { Console.WriteLine(""SetX""); _x = value; } } }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"TestAIsNull -Before Assignment -Caught NullReferenceException -TestAIsNotNull -Before Assignment a._b._x is: 0 -GetB -RHS -SetX -After Assignment a._b._x is: 42 -ReassignADuringAssignment -Before Assignment a is null == False -Before Assignment aCopy._b._x is: 0 -GetB -RHS -SetX -After Assignment a is null == True -After Assignment aCopy._b._x is: 42") + var expectedOutput = """ + TestAIsNull + Before Assignment + Caught NullReferenceException + TestAIsNotNull + Before Assignment a._b._x is: 0 + GetB + RHS + SetX + After Assignment a._b._x is: 42 + ReassignADuringAssignment + Before Assignment a is null == False + Before Assignment aCopy._b._x is: 0 + GetB + RHS + SetX + After Assignment a is null == True + After Assignment aCopy._b._x is: 42 + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 184 (0xb8) @@ -5182,6 +11067,38 @@ .locals init (int V_0, IL_00b2: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00b7: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x1c } + [Main]: Return value missing on the stack. { Offset = 0x1e } + [TestAIsNull]: Return value missing on the stack. { Offset = 0x30 } + [TestAIsNotNull]: Return value missing on the stack. { Offset = 0x64 } + [ReassignADuringAssignment]: Return value missing on the stack. { Offset = 0xcd } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x33, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign(A)", """ + { + // Code size 29 (0x1d) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: callvirt "B A.b.get" + IL_0006: ldstr "RHS" + IL_000b: call "System.Threading.Tasks.Task Program.Write(string)" + IL_0010: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0015: stloc.0 + IL_0016: ldloc.0 + IL_0017: callvirt "void B.x.set" + IL_001c: ret + } + """); } [WorkItem(19609, "https://github.com/dotnet/roslyn/issues/19609")] @@ -5266,20 +11183,23 @@ class A public int x; }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"TestAIsNull -Before Assignment -RHS -Caught NullReferenceException -TestAIsNotNull -Before Assignment a.x is: 0 -RHS -After Assignment a.x is: 42 -ReassignADuringAssignment -Before Assignment a is null == False -Before Assignment aCopy.x is: 0 -RHS -After Assignment a is null == True -After Assignment aCopy.x is: 42") + var expectedOutput = """ + TestAIsNull + Before Assignment + RHS + Caught NullReferenceException + TestAIsNotNull + Before Assignment a.x is: 0 + RHS + After Assignment a.x is: 42 + ReassignADuringAssignment + Before Assignment a is null == False + Before Assignment aCopy.x is: 0 + RHS + After Assignment a is null == True + After Assignment aCopy.x is: 42 + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 179 (0xb3) @@ -5363,6 +11283,37 @@ .locals init (int V_0, IL_00ad: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00b2: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x17 } + [Main]: Return value missing on the stack. { Offset = 0x1e } + [TestAIsNull]: Return value missing on the stack. { Offset = 0x30 } + [TestAIsNotNull]: Return value missing on the stack. { Offset = 0x4f } + [ReassignADuringAssignment]: Return value missing on the stack. { Offset = 0xb3 } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x33, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign(A)", """ + { + // Code size 24 (0x18) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldstr "RHS" + IL_0006: call "System.Threading.Tasks.Task Program.Write(string)" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: stfld "int A.x" + IL_0017: ret + } + """); } [Fact] @@ -5466,23 +11417,26 @@ class A public int x; }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"TestAIsNull -Before Assignment -Caught NullReferenceException -TestAIsNotNull -Before Assignment a.x is: 1 -RHS -After Assignment a.x is: 43 -ReassignADuringAssignment -Before Assignment a is null == False -Before Assignment aCopy.x is: 1 -RHS -After Assignment a is null == True -After Assignment aCopy.x is: 43 -ReassignXDuringAssignment -Before Assignment a.x is: 1 -RHS -After Assignment a.x is: 43") + var expectedOutput = """ + TestAIsNull + Before Assignment + Caught NullReferenceException + TestAIsNotNull + Before Assignment a.x is: 1 + RHS + After Assignment a.x is: 43 + ReassignADuringAssignment + Before Assignment a is null == False + Before Assignment aCopy.x is: 1 + RHS + After Assignment a is null == True + After Assignment aCopy.x is: 43 + ReassignXDuringAssignment + Before Assignment a.x is: 1 + RHS + After Assignment a.x is: 43 + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 202 (0xca) @@ -5576,6 +11530,48 @@ .locals init (int V_0, IL_00c4: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00c9: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x22 } + [Main]: Return value missing on the stack. { Offset = 0x28 } + [TestAIsNull]: Return value missing on the stack. { Offset = 0x30 } + [TestAIsNotNull]: Return value missing on the stack. { Offset = 0x56 } + [ReassignADuringAssignment]: Return value missing on the stack. { Offset = 0xc9 } + [ReassignXDuringAssignment]: Return value missing on the stack. { Offset = 0x88 } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x33, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x39, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign(A)", """ + { + // Code size 35 (0x23) + .maxstack 3 + .locals init (A V_0, + int V_1, + int V_2) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: stloc.0 + IL_0003: ldfld "int A.x" + IL_0008: stloc.1 + IL_0009: ldstr "RHS" + IL_000e: call "System.Threading.Tasks.Task Program.Write(string)" + IL_0013: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0018: stloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: add + IL_001d: stfld "int A.x" + IL_0022: ret + } + """); } [Fact] @@ -5680,29 +11676,32 @@ class A public int x { get { Console.WriteLine(""GetX""); return _x; } set { Console.WriteLine(""SetX""); _x = value; } } }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"TestAIsNull -Before Assignment -Caught NullReferenceException -TestAIsNotNull -Before Assignment a._x is: 1 -GetX -RHS -SetX -After Assignment a._x is: 43 -ReassignADuringAssignment -Before Assignment a is null == False -Before Assignment aCopy._x is: 1 -GetX -RHS -SetX -After Assignment a is null == True -After Assignment aCopy._x is: 43 -ReassignXDuringAssignment -Before Assignment a._x is: 1 -GetX -RHS -SetX -After Assignment a._x is: 43") + var expectedOutput = """ + TestAIsNull + Before Assignment + Caught NullReferenceException + TestAIsNotNull + Before Assignment a._x is: 1 + GetX + RHS + SetX + After Assignment a._x is: 43 + ReassignADuringAssignment + Before Assignment a is null == False + Before Assignment aCopy._x is: 1 + GetX + RHS + SetX + After Assignment a is null == True + After Assignment aCopy._x is: 43 + ReassignXDuringAssignment + Before Assignment a._x is: 1 + GetX + RHS + SetX + After Assignment a._x is: 43 + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 202 (0xca) @@ -5796,6 +11795,48 @@ .locals init (int V_0, IL_00c4: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00c9: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x22 } + [Main]: Return value missing on the stack. { Offset = 0x28 } + [TestAIsNull]: Return value missing on the stack. { Offset = 0x30 } + [TestAIsNotNull]: Return value missing on the stack. { Offset = 0x56 } + [ReassignADuringAssignment]: Return value missing on the stack. { Offset = 0xc9 } + [ReassignXDuringAssignment]: Return value missing on the stack. { Offset = 0x88 } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x33, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [g__WriteAndReassign|0]: Unexpected type on the stack. { Offset = 0x39, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign(A)", """ + { + // Code size 35 (0x23) + .maxstack 3 + .locals init (A V_0, + int V_1, + int V_2) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: stloc.0 + IL_0003: callvirt "int A.x.get" + IL_0008: stloc.1 + IL_0009: ldstr "RHS" + IL_000e: call "System.Threading.Tasks.Task Program.Write(string)" + IL_0013: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0018: stloc.2 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: add + IL_001d: callvirt "void A.x.set" + IL_0022: ret + } + """); } [WorkItem(19609, "https://github.com/dotnet/roslyn/issues/19609")] @@ -5921,26 +11962,29 @@ class B public int x; }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"TestAIsNullBIsNull -Before Assignment -Caught NullReferenceException -TestAIsNullBIsNotNull -Before Assignment -Caught NullReferenceException -TestAIsNotNullBIsNull -Before Assignment -RHS -Caught NullReferenceException -TestADotBIsNullBIsNotNull -Before Assignment -RHS -Caught NullReferenceException -TestADotBIsNotNullBIsNotNull -Before Assignment a.b.x is: 0 -Before Assignment b.x is: 0 -RHS -After Assignment a.b.x is: 42 -After Assignment b.x is: 42") + var expectedOutput = """ + TestAIsNullBIsNull + Before Assignment + Caught NullReferenceException + TestAIsNullBIsNotNull + Before Assignment + Caught NullReferenceException + TestAIsNotNullBIsNull + Before Assignment + RHS + Caught NullReferenceException + TestADotBIsNullBIsNotNull + Before Assignment + RHS + Caught NullReferenceException + TestADotBIsNotNullBIsNotNull + Before Assignment a.b.x is: 0 + Before Assignment b.x is: 0 + RHS + After Assignment a.b.x is: 42 + After Assignment b.x is: 42 + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 219 (0xdb) @@ -6039,6 +12083,48 @@ .locals init (int V_0, IL_00d5: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00da: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x27 } + [Main]: Return value missing on the stack. { Offset = 0x32 } + [TestAIsNullBIsNull]: Return value missing on the stack. { Offset = 0x33 } + [TestAIsNullBIsNotNull]: Return value missing on the stack. { Offset = 0x37 } + [TestAIsNotNullBIsNull]: Return value missing on the stack. { Offset = 0x42 } + [TestADotBIsNullBIsNotNull]: Return value missing on the stack. { Offset = 0x3b } + [TestADotBIsNotNullBIsNotNull]: Return value missing on the stack. { Offset = 0x9f } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign(A, B)", """ + { + // Code size 40 (0x28) + .maxstack 4 + .locals init (B V_0, + int V_1, + int V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "B A.b" + IL_0006: ldarg.1 + IL_0007: stloc.0 + IL_0008: ldstr "RHS" + IL_000d: call "System.Threading.Tasks.Task Program.Write(string)" + IL_0012: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0017: stloc.1 + IL_0018: ldloc.0 + IL_0019: ldloc.1 + IL_001a: dup + IL_001b: stloc.2 + IL_001c: stfld "int B.x" + IL_0021: ldloc.2 + IL_0022: stfld "int B.x" + IL_0027: ret + } + """); } [WorkItem(19609, "https://github.com/dotnet/roslyn/issues/19609")] @@ -6166,32 +12252,35 @@ class B public int x { get { Console.WriteLine(""GetX""); return _x; } set { Console.WriteLine(""SetX""); _x = value; } } }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"TestAIsNullBIsNull -Before Assignment -Caught NullReferenceException -TestAIsNullBIsNotNull -Before Assignment -Caught NullReferenceException -TestAIsNotNullBIsNull -Before Assignment -GetB -RHS -Caught NullReferenceException -TestADotBIsNullBIsNotNull -Before Assignment -GetB -RHS -SetX -Caught NullReferenceException -TestADotBIsNotNullBIsNotNull -Before Assignment a._b._x is: 0 -Before Assignment b._x is: 0 -GetB -RHS -SetX -SetX -After Assignment a._b._x is: 42 -After Assignment b._x is: 42") + var expectedOutput = """ + TestAIsNullBIsNull + Before Assignment + Caught NullReferenceException + TestAIsNullBIsNotNull + Before Assignment + Caught NullReferenceException + TestAIsNotNullBIsNull + Before Assignment + GetB + RHS + Caught NullReferenceException + TestADotBIsNullBIsNotNull + Before Assignment + GetB + RHS + SetX + Caught NullReferenceException + TestADotBIsNotNullBIsNotNull + Before Assignment a._b._x is: 0 + Before Assignment b._x is: 0 + GetB + RHS + SetX + SetX + After Assignment a._b._x is: 42 + After Assignment b._x is: 42 + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 219 (0xdb) @@ -6290,6 +12379,45 @@ .locals init (int V_0, IL_00d5: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00da: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x25 } + [Main]: Return value missing on the stack. { Offset = 0x32 } + [TestAIsNullBIsNull]: Return value missing on the stack. { Offset = 0x33 } + [TestAIsNullBIsNotNull]: Return value missing on the stack. { Offset = 0x37 } + [TestAIsNotNullBIsNull]: Return value missing on the stack. { Offset = 0x42 } + [TestADotBIsNullBIsNotNull]: Return value missing on the stack. { Offset = 0x3b } + [TestADotBIsNotNullBIsNotNull]: Return value missing on the stack. { Offset = 0x9f } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign(A, B)", """ + { + // Code size 38 (0x26) + .maxstack 4 + .locals init (int V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: callvirt "B A.b.get" + IL_0006: ldarg.1 + IL_0007: ldstr "RHS" + IL_000c: call "System.Threading.Tasks.Task Program.Write(string)" + IL_0011: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0016: stloc.0 + IL_0017: ldloc.0 + IL_0018: dup + IL_0019: stloc.1 + IL_001a: callvirt "void B.x.set" + IL_001f: ldloc.1 + IL_0020: callvirt "void B.x.set" + IL_0025: ret + } + """); } [Fact] @@ -6332,9 +12460,12 @@ struct B public int x; }"; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: @"Before Assignment A.b.x is: 0 -RHS -After Assignment A.b.x is: 42") + var expectedOutput = """ + Before Assignment A.b.x is: 0 + RHS + After Assignment A.b.x is: 42 + """; + CompileAndVerify(comp, expectedOutput: expectedOutput) .VerifyIL("Program.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { // Code size 159 (0x9f) @@ -6410,6 +12541,33 @@ .locals init (int V_0, IL_0099: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_009e: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Assign]: Return value missing on the stack. { Offset = 0x1b } + [Write]: Unexpected type on the stack. { Offset = 0x2c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Main]: Return value missing on the stack. { Offset = 0x46 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Assign()", """ + { + // Code size 28 (0x1c) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldstr "RHS" + IL_0005: call "System.Threading.Tasks.Task Program.Write(string)" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: ldsflda "B A.b" + IL_0015: ldloc.0 + IL_0016: stfld "int B.x" + IL_001b: ret + } + """); } [Fact, WorkItem(47191, "https://github.com/dotnet/roslyn/issues/47191")] @@ -6438,8 +12596,34 @@ static async Task Main() Console.Write(s1.Field); } }"; - var verifier = CompileAndVerify(source, expectedOutput: "1"); + var expectedOutput = "1"; + var verifier = CompileAndVerify(source, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x12 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M(System.Threading.Tasks.Task)", """ + { + // Code size 19 (0x13) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0006: stloc.0 + IL_0007: ldsflda "S1 C.s1" + IL_000c: ldloc.0 + IL_000d: stfld "int S1.Field" + IL_0012: ret + } + """); } [Fact, WorkItem(47191, "https://github.com/dotnet/roslyn/issues/47191")] @@ -6474,8 +12658,34 @@ static async Task Main() } } "; - var verifier = CompileAndVerify(source, expectedOutput: "1"); + var expectedOutput = "1"; + var verifier = CompileAndVerify(source, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x12 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.M(System.Threading.Tasks.Task)", """ + { + // Code size 19 (0x13) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0006: stloc.0 + IL_0007: ldsflda "S1 C.s1" + IL_000c: ldloc.0 + IL_000d: stfld "int S1.Field" + IL_0012: ret + } + """); } [Fact, WorkItem(47191, "https://github.com/dotnet/roslyn/issues/47191")] @@ -6505,8 +12715,38 @@ static async Task Main() Console.Write(c.s1.Field); } }"; - var verifier = CompileAndVerify(source, expectedOutput: "1"); + var expectedOutput = "1"; + var verifier = CompileAndVerify(source, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x1a } + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M(System.Threading.Tasks.Task)", """ + { + // Code size 27 (0x1b) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld "S1 C.s1" + IL_0006: pop + IL_0007: ldarg.1 + IL_0008: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000d: stloc.0 + IL_000e: ldarg.0 + IL_000f: ldflda "S1 C.s1" + IL_0014: ldloc.0 + IL_0015: stfld "int S1.Field" + IL_001a: ret + } + """); } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs index 02805bebf64d5..ab7d55e1e5bea 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs @@ -6,21 +6,34 @@ using System.Collections.Generic; using System.Linq; +using System.Reflection; +using Basic.Reference.Assemblies; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; -using Basic.Reference.Assemblies; + +// https://github.com/dotnet/roslyn/issues/79791: Verify execution of runtime async methods +// https://github.com/dotnet/runtime/issues/118042: ILVerify for runtime async? namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { public class CodeGenAsyncTests : EmitMetadataTestBase { - internal static string ExpectedOutput(string output) + // https://github.com/dotnet/roslyn/issues/79792: Use the real value when possible + private const MethodImplAttributes MethodImplOptionsAsync = (MethodImplAttributes)0x2000; + private static CSharpParseOptions WithRuntimeAsync(CSharpParseOptions options) => options.WithFeature("runtime-async", "on"); + + internal static string ExpectedOutput(string output, bool isRuntimeAsync = false) { - return ExecutionConditionUtil.IsMonoOrCoreClr ? output : null; + return ExecutionConditionUtil.IsMonoOrCoreClr + ? isRuntimeAsync + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + ? null + : output + : null; } private static CSharpCompilation CreateCompilation(string source, IEnumerable references = null, CSharpCompilationOptions options = null) @@ -33,12 +46,27 @@ private static CSharpCompilation CreateCompilation(string source, IEnumerable references = null, CSharpCompilationOptions options = null, Verification verify = default) { var compilation = CreateCompilation(source, references: references, options: options); return base.CompileAndVerify(compilation, expectedOutput: expectedOutput, verify: verify); } + private static string ReturnValueMissing(string method, string offset) => $$"""[{{method}}]: Return value missing on the stack. { Offset = {{offset}} }"""; + [Fact] public void StructVsClass() { @@ -95,7 +123,6 @@ public void VoidReturningAsync() { var source = @" using System; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; @@ -130,6 +157,129 @@ public static void Main() 1 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); + verifier.VerifyIL("Test.F(System.Threading.AutoResetEvent)", """ + { + // Code size 43 (0x2b) + .maxstack 2 + .locals init (Test.d__1 V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncVoidMethodBuilder System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldarg.0 + IL_000f: stfld "System.Threading.AutoResetEvent Test.d__1.handle" + IL_0014: ldloca.s V_0 + IL_0016: ldc.i4.m1 + IL_0017: stfld "int Test.d__1.<>1__state" + IL_001c: ldloca.s V_0 + IL_001e: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0023: ldloca.s V_0 + IL_0025: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Startd__1>(ref Test.d__1)" + IL_002a: ret + } + """); + + verifier.VerifyIL("Test.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ + { + // Code size 200 (0xc8) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.TaskAwaiter V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int Test.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: pop + IL_0009: nop + .try + { + IL_000a: ldloc.0 + IL_000b: brfalse.s IL_0065 + IL_000d: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_0012: ldsfld "System.Action Test.<>c.<>9__1_0" + IL_0017: dup + IL_0018: brtrue.s IL_0031 + IL_001a: pop + IL_001b: ldsfld "Test.<>c Test.<>c.<>9" + IL_0020: ldftn "void Test.<>c.b__1_0()" + IL_0026: newobj "System.Action..ctor(object, System.IntPtr)" + IL_002b: dup + IL_002c: stsfld "System.Action Test.<>c.<>9__1_0" + IL_0031: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Action)" + IL_0036: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_003b: stloc.1 + IL_003c: ldloca.s V_1 + IL_003e: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0043: brtrue.s IL_0081 + IL_0045: ldarg.0 + IL_0046: ldc.i4.0 + IL_0047: dup + IL_0048: stloc.0 + IL_0049: stfld "int Test.d__1.<>1__state" + IL_004e: ldarg.0 + IL_004f: ldloc.1 + IL_0050: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_0055: ldarg.0 + IL_0056: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_005b: ldloca.s V_1 + IL_005d: ldarg.0 + IL_005e: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompletedd__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_0063: leave.s IL_00c7 + IL_0065: ldarg.0 + IL_0066: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_006b: stloc.1 + IL_006c: ldarg.0 + IL_006d: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_0072: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0078: ldarg.0 + IL_0079: ldc.i4.m1 + IL_007a: dup + IL_007b: stloc.0 + IL_007c: stfld "int Test.d__1.<>1__state" + IL_0081: ldloca.s V_1 + IL_0083: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0088: leave.s IL_009b + } + finally + { + IL_008a: ldloc.0 + IL_008b: ldc.i4.0 + IL_008c: bge.s IL_009a + IL_008e: ldarg.0 + IL_008f: ldfld "System.Threading.AutoResetEvent Test.d__1.handle" + IL_0094: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0099: pop + IL_009a: endfinally + } + IL_009b: leave.s IL_00b4 + } + catch System.Exception + { + IL_009d: stloc.2 + IL_009e: ldarg.0 + IL_009f: ldc.i4.s -2 + IL_00a1: stfld "int Test.d__1.<>1__state" + IL_00a6: ldarg.0 + IL_00a7: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_00ac: ldloc.2 + IL_00ad: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException(System.Exception)" + IL_00b2: leave.s IL_00c7 + } + IL_00b4: ldarg.0 + IL_00b5: ldc.i4.s -2 + IL_00b7: stfld "int Test.d__1.<>1__state" + IL_00bc: ldarg.0 + IL_00bd: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_00c2: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetResult()" + IL_00c7: ret + } + """); } [Fact] @@ -137,7 +287,6 @@ public void TaskReturningAsync() { var source = @" using System; -using System.Diagnostics; using System.Threading.Tasks; class Test @@ -163,6 +312,106 @@ public static void Main() 42 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CreateRuntimeAsyncCompilation(source); + + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ReturnValueMissing("F", "0x2e"), + }, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Test.F", """ + { + // Code size 47 (0x2f) + .maxstack 3 + IL_0000: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_0005: ldsfld "System.Action Test.<>c.<>9__1_0" + IL_000a: dup + IL_000b: brtrue.s IL_0024 + IL_000d: pop + IL_000e: ldsfld "Test.<>c Test.<>c.<>9" + IL_0013: ldftn "void Test.<>c.b__1_0()" + IL_0019: newobj "System.Action..ctor(object, System.IntPtr)" + IL_001e: dup + IL_001f: stsfld "System.Action Test.<>c.<>9__1_0" + IL_0024: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Action)" + IL_0029: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002e: ret + } + """); + + void verify(ModuleSymbol module) + { + var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); + var f = test.GetMethod("F"); + Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + AssertEx.Equal(["<>c"], test.GetTypeMembers().SelectAsArray(t => t.Name)); + } + } + + [Fact] + public void ValueTaskReturningAsync() + { + var source = @" +using System; +using System.Threading.Tasks; + +class Test +{ + static int i = 0; + + public static async ValueTask F() + { + await Impl(); + + async ValueTask Impl() + { + Test.i = 42; + } + } + + public static async Task Main() + { + await F(); + Console.WriteLine(Test.i); + } +}"; + + var expected = "42"; + var comp = CreateRuntimeAsyncCompilation(source); + + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = $""" + {ReturnValueMissing("F", "0xa")} + {ReturnValueMissing("Main", "0x14")} + {ReturnValueMissing("g__Impl|1_0", "0x7")} + """ + }, symbolValidator: verify); + verifier.VerifyDiagnostics( + // (13,25): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // async ValueTask Impl() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Impl").WithLocation(13, 25) + ); + + verifier.VerifyIL("Test.F", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.ValueTask Test.g__Impl|1_0()" + IL_0005: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_000a: ret + } + """); + + void verify(ModuleSymbol module) + { + var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); + var f = test.GetMethod("F"); + Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + Assert.Empty(test.GetTypeMembers()); + } } [Fact] @@ -170,7 +419,6 @@ public void GenericTaskReturningAsync() { var source = @" using System; -using System.Diagnostics; using System.Threading.Tasks; class Test @@ -191,234 +439,981 @@ public static void Main() O brave new world... "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CreateRuntimeAsyncCompilation(source); + + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = "[F]: Unexpected type on the stack. { Offset = 0x2e, Found = ref 'string', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' }", + }, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Test.F", """ + { + // Code size 47 (0x2f) + .maxstack 3 + IL_0000: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_0005: ldsfld "System.Func Test.<>c.<>9__0_0" + IL_000a: dup + IL_000b: brtrue.s IL_0024 + IL_000d: pop + IL_000e: ldsfld "Test.<>c Test.<>c.<>9" + IL_0013: ldftn "string Test.<>c.b__0_0()" + IL_0019: newobj "System.Func..ctor(object, System.IntPtr)" + IL_001e: dup + IL_001f: stsfld "System.Func Test.<>c.<>9__0_0" + IL_0024: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0029: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002e: ret + } + """); + + void verify(ModuleSymbol module) + { + var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); + var f = test.GetMethod("F"); + Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + AssertEx.Equal(["<>c"], test.GetTypeMembers().SelectAsArray(t => t.Name)); + } } [Fact] - public void Conformance_Awaiting_Methods_Generic01() + public void GenericValueTaskReturningAsync() { var source = @" using System; -using System.Runtime.CompilerServices; -using System.Threading; +using System.Threading.Tasks; -//Implementation of you own async pattern -public class MyTask +class Test { - public MyTaskAwaiter GetAwaiter() + public static async ValueTask F() { - return new MyTaskAwaiter(); + return await Impl(); + + ValueTask Impl() => ValueTask.FromResult(""O brave new world...""); } - public async void Run(U u) where U : MyTask, new() + public static async Task Main() { - try - { - int tests = 0; + string s = await F(); + Console.WriteLine(s); + } +}"; + var expected = @"O brave new world..."; + var comp = CreateRuntimeAsyncCompilation(source); - tests++; - var rez = await u; - if (rez == 0) - Driver.Count++; + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = $$""" + [F]: Unexpected type on the stack. { Offset = 0xa, Found = ref 'string', Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + {{ReturnValueMissing("Main", "0xf")}} + """, + }, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Test.F", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.ValueTask Test.g__Impl|0_0()" + IL_0005: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_000a: ret + } + """); - Driver.Result = Driver.Count - tests; + void verify(ModuleSymbol module) + { + var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); + var f = test.GetMethod("F"); + Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + AssertEx.Empty(test.GetTypeMembers()); + } } - finally + + [Theory] + [CombinatorialData] + public void ObjectCreation_ReturningAsync(bool useValueTask, bool useGeneric) { - //When test complete, set the flag. - Driver.CompletedSignal.Set(); - } - } -} -public class MyTaskAwaiter : INotifyCompletion -{ - public void OnCompleted(Action continuationAction) - { - } + string retType = useGeneric ? (useValueTask ? "ValueTask" : "Task") : (useValueTask ? "ValueTask" : "Task"); + string expr = useValueTask ? + (useGeneric ? """new ValueTask("42")""" : "new ValueTask()") : + (useGeneric ? """new Task(() => "42")""" : "new Task(null)"); - public T GetResult() - { - return default(T); - } + var source = $$""" + using System; + using System.Threading.Tasks; - public bool IsCompleted { get { return true; } } -} -//------------------------------------- + class Test + { + public static async {{retType}} F() + { + {{(useGeneric ? "return " : "")}}await {{expr}}; + } -class Driver -{ - public static int Result = -1; - public static int Count = 0; - public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); - static void Main() - { - new MyTask().Run>(new MyTask()); + public static async Task Main() + { + {{(useGeneric ? "string result = await F();" : "await F();")}} + Console.WriteLine({{(useGeneric ? "result" : "42")}}); + } + } + """; + var comp = CreateRuntimeAsyncCompilation(source); - CompletedSignal.WaitOne(); + var ilVerifyMessage = (useValueTask, useGeneric) switch + { + (false, false) => $$""" + {{ReturnValueMissing("F", "0xb")}} + {{ReturnValueMissing("Main", "0x11")}} + """, + (false, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0x29, Found = ref 'string', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + {{ReturnValueMissing("Main", "0xf")}} + """, + (true, false) => $$""" + {{ReturnValueMissing("F", "0xe")}} + {{ReturnValueMissing("Main", "0x11")}} + """, + (true, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0xf, Found = ref 'string', Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + {{ReturnValueMissing("Main", "0xf")}} + """, + }; - // 0 - success - // 1 - failed (test completed) - // -1 - failed (test incomplete - deadlock, etc) - Console.WriteLine(Driver.Result); - } -}"; - CompileAndVerify(source, "0"); + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ilVerifyMessage + }, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + var expectedIl = (useValueTask, useGeneric) switch + { + (false, false) => """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldnull + IL_0001: newobj "System.Threading.Tasks.Task..ctor(System.Action)" + IL_0006: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000b: ret + } + """, + (false, true) => """ + { + // Code size 42 (0x2a) + .maxstack 2 + IL_0000: ldsfld "System.Func Test.<>c.<>9__0_0" + IL_0005: dup + IL_0006: brtrue.s IL_001f + IL_0008: pop + IL_0009: ldsfld "Test.<>c Test.<>c.<>9" + IL_000e: ldftn "string Test.<>c.b__0_0()" + IL_0014: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0019: dup + IL_001a: stsfld "System.Func Test.<>c.<>9__0_0" + IL_001f: newobj "System.Threading.Tasks.Task..ctor(System.Func)" + IL_0024: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0029: ret + } + """, + (true, false) => """ + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (System.Threading.Tasks.ValueTask V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj "System.Threading.Tasks.ValueTask" + IL_0008: ldloc.0 + IL_0009: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_000e: ret + } + """, + (true, true) => """ + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: ldstr "42" + IL_0005: newobj "System.Threading.Tasks.ValueTask..ctor(string)" + IL_000a: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_000f: ret + } + """, + }; + + verifier.VerifyIL("Test.F", expectedIl); + + void verify(ModuleSymbol module) + { + var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); + var f = test.GetMethod("F"); + Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + if (!useValueTask && useGeneric) + { + AssertEx.Equal(["<>c"], test.GetTypeMembers().SelectAsArray(t => t.Name)); + } + else + { + AssertEx.Empty(test.GetTypeMembers()); + } + } } - [Fact] - public void Conformance_Awaiting_Methods_Method01() + [Theory] + [CombinatorialData] + public void Local_ReturningAsync(bool useValueTask, bool useGeneric) { - var source = @" -using System.Threading; -using System.Threading.Tasks; -using System; + string retType = useGeneric ? (useValueTask ? "ValueTask" : "Task") : (useValueTask ? "ValueTask" : "Task"); + string expr = useValueTask ? + (useGeneric ? "default(ValueTask)" : "default(ValueTask)") : + (useGeneric ? """default(Task)""" : "default(Task)"); + var source = $$""" + using System; + using System.Threading.Tasks; -public interface IExplicit -{ - Task Method(int x = 4); -} + class Test + { + public static async {{retType}} F() + { + var l = {{expr}}; + NoOp(); + {{(useGeneric ? "return " : "")}}await l; + } -class C1 : IExplicit -{ - Task IExplicit.Method(int x) - { - //This will fail until Run and RunEx are merged back together - return Task.Run(async () => - { - await Task.Delay(1); - Driver.Count++; - }); - } -} + private static void NoOp() { } -class TestCase -{ - public async void Run() - { - try - { - int tests = 0; - tests++; + public static async Task Main() + { + #pragma warning disable CS0219 // Unused assignment + string result = null; + try + { + {{(useGeneric ? "result = " : "")}}await F(); + } + catch (System.NullReferenceException) + { + } + Console.WriteLine({{(useValueTask && useGeneric ? "result" : "42")}}); + } + } + """; + var comp = CreateRuntimeAsyncCompilation(source); - C1 c = new C1(); - IExplicit e = (IExplicit)c; - await e.Method(); + var ilVerifyMessage = (useValueTask, useGeneric) switch + { + (false, false) => $$""" + {{ReturnValueMissing("F", "0xb")}} + {{ReturnValueMissing("Main", "0x16")}} + """, + (false, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0xb, Found = ref 'string', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + {{ReturnValueMissing("Main", "0x17")}} + """, + (true, false) => $$""" + {{ReturnValueMissing("F", "0x13")}} + {{ReturnValueMissing("Main", "0x16")}} + """, + (true, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0x13, Found = ref 'string', Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + {{ReturnValueMissing("Main", "0x18")}} + """, + }; - Driver.Result = Driver.Count - tests; - } - finally - { - //When test complete, set the flag. - Driver.CompletedSignal.Set(); - } - } -} + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ilVerifyMessage, + }, symbolValidator: verify); + verifier.VerifyDiagnostics(); -class Driver -{ - public static int Result = -1; - public static int Count = 0; - public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); - static void Main() - { - var t = new TestCase(); - t.Run(); + var expectedIl = (useValueTask, useGeneric) switch + { + (false, false) => """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldnull + IL_0001: call "void Test.NoOp()" + IL_0006: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000b: ret + } + """, + (false, true) => """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldnull + IL_0001: call "void Test.NoOp()" + IL_0006: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000b: ret + } + """, + (true, false) => """ + { + // Code size 20 (0x14) + .maxstack 1 + .locals init (System.Threading.Tasks.ValueTask V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj "System.Threading.Tasks.ValueTask" + IL_0008: ldloc.0 + IL_0009: call "void Test.NoOp()" + IL_000e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0013: ret + } + """, + (true, true) => """ + { + // Code size 20 (0x14) + .maxstack 1 + .locals init (System.Threading.Tasks.ValueTask V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj "System.Threading.Tasks.ValueTask" + IL_0008: ldloc.0 + IL_0009: call "void Test.NoOp()" + IL_000e: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0013: ret + } + """, + }; - CompletedSignal.WaitOne(); - // 0 - success - // 1 - failed (test completed) - // -1 - failed (test incomplete - deadlock, etc) - Console.WriteLine(Driver.Result); - } -}"; - CompileAndVerify(source, "0"); + verifier.VerifyIL("Test.F", expectedIl); + + void verify(ModuleSymbol module) + { + var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); + var f = test.GetMethod("F"); + Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + AssertEx.Empty(test.GetTypeMembers()); + } } - [Fact] - public void Conformance_Awaiting_Methods_Parameter003() + [Theory] + [CombinatorialData] + public void DefaultLiteral_ReturningAsync(bool useValueTask, bool useGeneric) { - var source = @" -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.Threading; + string retType = useGeneric ? (useValueTask ? "ValueTask" : "Task") : (useValueTask ? "ValueTask" : "Task"); + string expr = useValueTask ? + (useGeneric ? "default(ValueTask)" : "default(ValueTask)") : + (useGeneric ? """default(Task)""" : "default(Task)"); -class TestCase -{ - public static int Count = 0; - public static T Goo(T t) - { - return t; - } + var source = $$""" + using System; + using System.Threading.Tasks; - public async static Task Bar(T t) - { - await Task.Delay(1); - return t; - } + class Test + { + public static async {{retType}} F() + { + {{(useGeneric ? "return " : "")}}await {{expr}}; + } - public static async void Run() - { - try + public static async Task Main() + { + #pragma warning disable CS0219 // Unused assignment + string result = null; + try + { + {{(useGeneric ? "result = " : "")}}await F(); + } + catch (System.NullReferenceException) + { + } + Console.WriteLine({{(useValueTask && useGeneric ? "result" : "42")}}); + } + } + """; + var comp = CreateRuntimeAsyncCompilation(source); + + var ilVerifyMessage = (useValueTask, useGeneric) switch + { + (false, false) => $$""" + {{ReturnValueMissing("F", "0x6")}} + {{ReturnValueMissing("Main", "0x16")}} + """, + (false, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0x6, Found = ref 'string', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + {{ReturnValueMissing("Main", "0x17")}} + """, + (true, false) => $$""" + {{ReturnValueMissing("F", "0xe")}} + {{ReturnValueMissing("Main", "0x16")}} + """, + (true, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0xe, Found = ref 'string', Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + {{ReturnValueMissing("Main", "0x18")}} + """, + }; + + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ilVerifyMessage, + }, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + var expectedIl = (useValueTask, useGeneric) switch + { + (false, false) => """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldnull + IL_0001: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0006: ret + } + """, + (false, true) => """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldnull + IL_0001: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0006: ret + } + """, + (true, false) => """ + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (System.Threading.Tasks.ValueTask V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj "System.Threading.Tasks.ValueTask" + IL_0008: ldloc.0 + IL_0009: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_000e: ret + } + """, + (true, true) => """ + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (System.Threading.Tasks.ValueTask V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj "System.Threading.Tasks.ValueTask" + IL_0008: ldloc.0 + IL_0009: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_000e: ret + } + """, + }; + + verifier.VerifyIL("Test.F", expectedIl); + + void verify(ModuleSymbol module) + { + var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); + var f = test.GetMethod("F"); + Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + AssertEx.Empty(test.GetTypeMembers()); + } + } + + [Theory] + [CombinatorialData] + public void Conditional_ReturningAsync(bool useValueTask, bool useGeneric) { - int x1 = Goo(await Bar(4)); - Task t = Bar(5); - int x2 = Goo(await t); - if (x1 != 4) - Count++; - if (x2 != 5) - Count++; + string retType = useGeneric ? (useValueTask ? "ValueTask" : "Task") : (useValueTask ? "ValueTask" : "Task"); + string baseExpr = useValueTask ? + (useGeneric ? """new ValueTask("42")""" : "new ValueTask()") : + (useGeneric ? """Task.FromResult("42")""" : "Task.CompletedTask"); + + var source = $$""" + using System; + using System.Threading.Tasks; + + class Test + { + public static async {{retType}} F() + { + bool b = true; + {{(useGeneric ? "return " : "")}}await (b ? {{baseExpr}} : {{baseExpr}}); + } + + public static async Task Main() + { + {{(useGeneric ? "string result = " : "")}}await F(); + Console.WriteLine({{(useGeneric ? "result" : "42")}}); + } + } + """; + var comp = CreateRuntimeAsyncCompilation(source); + + var ilVerifyMessage = (useValueTask, useGeneric) switch + { + (false, false) => $$""" + {{ReturnValueMissing("F", "0x14")}} + {{ReturnValueMissing("Main", "0x11")}} + """, + (false, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0x1e, Found = ref 'string', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + {{ReturnValueMissing("Main", "0xf")}} + """, + (true, false) => $$""" + {{ReturnValueMissing("F", "0x1c")}} + {{ReturnValueMissing("Main", "0x11")}} + """, + (true, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0x1e, Found = ref 'string', Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + {{ReturnValueMissing("Main", "0xf")}} + """, + }; + + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ilVerifyMessage, + }, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + var expectedIl = (useValueTask, useGeneric) switch + { + (false, false) => """ + { + // Code size 21 (0x15) + .maxstack 1 + IL_0000: ldc.i4.1 + IL_0001: brtrue.s IL_000a + IL_0003: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0008: br.s IL_000f + IL_000a: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_000f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0014: ret + } + """, + (false, true) => """ + { + // Code size 31 (0x1f) + .maxstack 1 + IL_0000: ldc.i4.1 + IL_0001: brtrue.s IL_000f + IL_0003: ldstr "42" + IL_0008: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(string)" + IL_000d: br.s IL_0019 + IL_000f: ldstr "42" + IL_0014: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(string)" + IL_0019: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001e: ret + } + """, + (true, false) => """ + { + // Code size 29 (0x1d) + .maxstack 1 + .locals init (System.Threading.Tasks.ValueTask V_0) + IL_0000: ldc.i4.1 + IL_0001: brtrue.s IL_000e + IL_0003: ldloca.s V_0 + IL_0005: initobj "System.Threading.Tasks.ValueTask" + IL_000b: ldloc.0 + IL_000c: br.s IL_0017 + IL_000e: ldloca.s V_0 + IL_0010: initobj "System.Threading.Tasks.ValueTask" + IL_0016: ldloc.0 + IL_0017: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_001c: ret + } + """, + (true, true) => """ + { + // Code size 31 (0x1f) + .maxstack 1 + IL_0000: ldc.i4.1 + IL_0001: brtrue.s IL_000f + IL_0003: ldstr "42" + IL_0008: newobj "System.Threading.Tasks.ValueTask..ctor(string)" + IL_000d: br.s IL_0019 + IL_000f: ldstr "42" + IL_0014: newobj "System.Threading.Tasks.ValueTask..ctor(string)" + IL_0019: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_001e: ret + } + """, + }; + + verifier.VerifyIL("Test.F", expectedIl); + + void verify(ModuleSymbol module) + { + var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); + var f = test.GetMethod("F"); + Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + AssertEx.Empty(test.GetTypeMembers()); + } } - finally + + [Theory] + [CombinatorialData] + public void Cast_ReturningAsync(bool useValueTask, bool useGeneric) { - Driver.CompletedSignal.Set(); + string retType = useGeneric ? (useValueTask ? "ValueTask" : "Task") : (useValueTask ? "ValueTask" : "Task"); + string baseExpr = useValueTask ? + (useGeneric ? """new ValueTask("42")""" : "new ValueTask()") : + (useGeneric ? """Task.FromResult("42")""" : "Task.CompletedTask"); + + var source = $$""" + using System; + using System.Threading.Tasks; + + class Test + { + public static async {{retType}} F() + { + {{(useGeneric ? "return await " : "await ")}}({{retType}}){{baseExpr}}; + } + + public static async Task Main() + { + {{(useGeneric ? "string result = " : "")}}await F(); + Console.WriteLine({{(useGeneric ? "result" : "42")}}); + } + } + """; + var comp = CreateRuntimeAsyncCompilation(source); + + var ilVerifyMessage = (useValueTask, useGeneric) switch + { + (false, false) => $$""" + {{ReturnValueMissing("F", "0xa")}} + {{ReturnValueMissing("Main", "0x11")}} + """, + (false, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0xf, Found = ref 'string', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + {{ReturnValueMissing("Main", "0xf")}} + """, + (true, false) => $$""" + {{ReturnValueMissing("F", "0xe")}} + {{ReturnValueMissing("Main", "0x11")}} + """, + (true, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0xf, Found = ref 'string', Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + {{ReturnValueMissing("Main", "0xf")}} + """, + }; + + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ilVerifyMessage, + }, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + var expectedIl = (useValueTask, useGeneric) switch + { + (false, false) => """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0005: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ret + } + """, + (false, true) => """ + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: ldstr "42" + IL_0005: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(string)" + IL_000a: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: ret + } + """, + (true, false) => """ + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (System.Threading.Tasks.ValueTask V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj "System.Threading.Tasks.ValueTask" + IL_0008: ldloc.0 + IL_0009: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_000e: ret + } + """, + (true, true) => """ + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: ldstr "42" + IL_0005: newobj "System.Threading.Tasks.ValueTask..ctor(string)" + IL_000a: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_000f: ret + } + """, + }; + + verifier.VerifyIL("Test.F", expectedIl); + + void verify(ModuleSymbol module) + { + var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); + var f = test.GetMethod("F"); + Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + AssertEx.Empty(test.GetTypeMembers()); + } } - } -} -class Driver -{ - public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); - static void Main() - { - TestCase.Run(); + [Theory] + [CombinatorialData] + public void PropertyAccess_ReturningAsync(bool useValueTask, bool useGeneric) + { + string retType = useGeneric ? (useValueTask ? "ValueTask" : "Task") : (useValueTask ? "ValueTask" : "Task"); + string baseExpr = useValueTask ? + (useGeneric ? """new ValueTask("42")""" : "new ValueTask()") : + (useGeneric ? """Task.FromResult("42")""" : "Task.CompletedTask"); - CompletedSignal.WaitOne(); + var source = $$""" + using System; + using System.Threading.Tasks; - // 0 - success - Console.WriteLine(TestCase.Count); - } -}"; - CompileAndVerify(source, expectedOutput: "0"); + class Test + { + public static async {{retType}} F() + { + {{(useGeneric ? "return " : "")}}await Prop; + } + + public static async Task Main() + { + {{(useGeneric ? "string result = " : "")}}await F(); + Console.WriteLine({{(useGeneric ? "result" : "42")}}); + } + + public static {{retType}} Prop => {{baseExpr}}; + } + """; + var comp = CreateRuntimeAsyncCompilation(source); + + var ilVerifyMessage = (useValueTask, useGeneric) switch + { + (false, false) => $$""" + {{ReturnValueMissing("F", "0xa")}} + {{ReturnValueMissing("Main", "0x11")}} + """, + (false, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0xa, Found = ref 'string', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + {{ReturnValueMissing("Main", "0xf")}} + """, + (true, false) => $$""" + {{ReturnValueMissing("F", "0xa")}} + {{ReturnValueMissing("Main", "0x11")}} + """, + (true, true) => $$""" + [F]: Unexpected type on the stack. { Offset = 0xa, Found = ref 'string', Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + {{ReturnValueMissing("Main", "0xf")}} + """, + }; + + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ilVerifyMessage, + }, symbolValidator: verify); + verifier.VerifyDiagnostics(); + + var expectedIl = (useValueTask, useGeneric) switch + { + (false, false) => """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.Task Test.Prop.get" + IL_0005: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ret + } + """, + (false, true) => """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.Task Test.Prop.get" + IL_0005: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ret + } + """, + (true, false) => """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.ValueTask Test.Prop.get" + IL_0005: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_000a: ret + } + """, + (true, true) => """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.ValueTask Test.Prop.get" + IL_0005: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_000a: ret + } + """, + }; + + verifier.VerifyIL("Test.F", expectedIl); + + void verify(ModuleSymbol module) + { + var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); + var f = test.GetMethod("F"); + Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + AssertEx.Empty(test.GetTypeMembers()); + } + } + + [Theory] + [CombinatorialData] + public void Conformance_Awaiting_Methods_Generic01(bool useCritical) + { + var source = $$""" + using System; + using System.Runtime.CompilerServices; + using System.Threading; + + //Implementation of you own async pattern + public class MyTask + { + public MyTaskAwaiter GetAwaiter() + { + return new MyTaskAwaiter(); + } + + public async System.Threading.Tasks.Task Run(U u) where U : MyTask, new() + { + try + { + int tests = 0; + + tests++; + var rez = await u; + if (rez == 0) + Driver.Count++; + + Driver.Result = Driver.Count - tests; + } + finally + { + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } + } + public class MyTaskAwaiter : {{(useCritical ? "ICriticalNotifyCompletion" : "INotifyCompletion")}} + { + public void OnCompleted(Action continuationAction) + { + } + + public void UnsafeOnCompleted(Action continuationAction) + { + } + + public T GetResult() + { + return default(T); + } + + public bool IsCompleted { get { return true; } } + } + //------------------------------------- + + class Driver + { + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + #pragma warning disable CS4014 // Task is unawaited + new MyTask().Run>(new MyTask()); + #pragma warning restore CS4014 // Task is unawaited + + CompletedSignal.WaitOne(); + + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } + } + """; + + CompileAndVerify(source, "0"); + + var comp = CreateRuntimeAsyncCompilation(source); + + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ReturnValueMissing("Run", "0x4e") + }); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("MyTask.Run", $$""" + { + // Code size 79 (0x4f) + .maxstack 2 + .locals init (int V_0, //tests + MyTaskAwaiter V_1) + .try + { + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.0 + IL_0006: ldarg.1 + IL_0007: box "U" + IL_000c: callvirt "MyTaskAwaiter MyTask.GetAwaiter()" + IL_0011: stloc.1 + IL_0012: ldloc.1 + IL_0013: callvirt "bool MyTaskAwaiter.IsCompleted.get" + IL_0018: brtrue.s IL_0020 + IL_001a: ldloc.1 + IL_001b: call "void System.Runtime.CompilerServices.AsyncHelpers.{{(useCritical ? "Unsafe" : "")}}AwaitAwaiter>(MyTaskAwaiter)" + IL_0020: ldloc.1 + IL_0021: callvirt "int MyTaskAwaiter.GetResult()" + IL_0026: brtrue.s IL_0034 + IL_0028: ldsfld "int Driver.Count" + IL_002d: ldc.i4.1 + IL_002e: add + IL_002f: stsfld "int Driver.Count" + IL_0034: ldsfld "int Driver.Count" + IL_0039: ldloc.0 + IL_003a: sub + IL_003b: stsfld "int Driver.Result" + IL_0040: leave.s IL_004e + } + finally + { + IL_0042: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0047: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_004c: pop + IL_004d: endfinally + } + IL_004e: ret + } + """); } [Fact] - public void Conformance_Awaiting_Methods_Method05() + public void Conformance_Awaiting_Methods_Method01() { var source = @" using System.Threading; using System.Threading.Tasks; using System; -class C -{ - public int Status; - public C(){} -} -interface IImplicit + +public interface IExplicit { - T Method(params decimal[] d) where T : Task; + Task Method(int x = 4); } -class Impl : IImplicit + +class C1 : IExplicit { - public T Method(params decimal[] d) where T : Task + Task IExplicit.Method(int x) { - //this will fail until Run and RunEx are merged - return (T) Task.Run(async() => + //This will fail until Run and RunEx are merged back together + return Task.Run(async () => { await Task.Delay(1); Driver.Count++; - return new C() { Status = 1 }; }); } } @@ -430,10 +1425,11 @@ public async void Run() try { int tests = 0; - Impl i = new Impl(); - tests++; - await i.Method>(3m, 4m); + + C1 c = new C1(); + IExplicit e = (IExplicit)c; + await e.Method(); Driver.Result = Driver.Count - tests; } @@ -466,20 +1462,151 @@ static void Main() } [Fact] - public void Conformance_Awaiting_Methods_Accessible010() + public void Conformance_Awaiting_Methods_Parameter003() { var source = @" using System; -using System.Collections.Generic; using System.Threading.Tasks; +using System.Collections.Generic; using System.Threading; -class TestCase:Test +class TestCase { public static int Count = 0; - public async static void Run() + public static T Goo(T t) { - try + return t; + } + + public async static Task Bar(T t) + { + await Task.Delay(1); + return t; + } + + public static async void Run() + { + try + { + int x1 = Goo(await Bar(4)); + Task t = Bar(5); + int x2 = Goo(await t); + if (x1 != 4) + Count++; + if (x2 != 5) + Count++; + } + finally + { + Driver.CompletedSignal.Set(); + } + } +} + +class Driver +{ + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + TestCase.Run(); + + CompletedSignal.WaitOne(); + + // 0 - success + Console.WriteLine(TestCase.Count); + } +}"; + CompileAndVerify(source, expectedOutput: "0"); + } + + [Fact] + public void Conformance_Awaiting_Methods_Method05() + { + var source = @" +using System.Threading; +using System.Threading.Tasks; +using System; + +class C +{ + public int Status; + public C(){} +} +interface IImplicit +{ + T Method(params decimal[] d) where T : Task; +} +class Impl : IImplicit +{ + public T Method(params decimal[] d) where T : Task + { + //this will fail until Run and RunEx are merged + return (T) Task.Run(async() => + { + await Task.Delay(1); + Driver.Count++; + return new C() { Status = 1 }; + }); + } +} + +class TestCase +{ + public async void Run() + { + try + { + int tests = 0; + Impl i = new Impl(); + + tests++; + await i.Method>(3m, 4m); + + Driver.Result = Driver.Count - tests; + } + finally + { + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } +} + +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); + t.Run(); + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + CompileAndVerify(source, "0"); + } + + [Fact] + public void Conformance_Awaiting_Methods_Accessible010() + { + var source = @" +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Threading; + +class TestCase:Test +{ + public static int Count = 0; + public async static void Run() + { + try { int x = await Test.GetValue(1); if (x != 1) @@ -1489,6 +2616,13 @@ static void Main() } }"; CompileAndVerify(source, "42"); + + var comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,16): error CS9328: Method 'Test.F1(dynamic)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // return await d; + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await d").WithArguments("Test.F1(dynamic)").WithLocation(9, 16) + ); } [Fact] @@ -1686,61 +2820,69 @@ static void Main() } [Fact] - public void Await43() + public void Await40_WithTask() { var source = @" +using System; using System.Threading; using System.Threading.Tasks; -using System; -struct MyClass +class C1 { - public static Task operator *(MyClass c, int x) + public async Task Method(int x) { - return Task.Run(async delegate - { - await Task.Delay(1); - TestCase.Count++; - }); + await Task.Delay(1); + return x; } +} - public static Task operator +(MyClass c, long x) +class C2 +{ + public int Status; + public C2(int x = 5) { - return Task.Run(async () => - { - await Task.Delay(1); - TestCase.Count++; - }); + this.Status = x; + } + + public C2(int x, int y) + { + this.Status = x + y; + } + + public int Bar(int x) + { + return x; } } class TestCase { - public static int Count = 0; - private int tests; - public async void Run() + public async Task Run() { - this.tests = 0; - dynamic dy = Task.Run(async () => { await Task.Delay(1); return new MyClass(); }); + int tests = 0; try { - this.tests++; - await (await dy * 5); + tests++; + dynamic c = new C1(); + C2 cc = new C2(x: await c.Method(1)); + if (cc.Status == 1) + Driver.Count++; - this.tests++; - dynamic d = new MyClass(); - dynamic dd = Task.Run(async () => { await Task.Delay(1); return 1L; }); - await (d + await dd); - } - catch (Exception ex) - { - Console.WriteLine(ex); - Console.WriteLine(ex.StackTrace); + tests++; + dynamic f = (Func>)(async () => { await Task.Delay(1); return 4; }); + cc = new C2(await c.Method(2), await f()); + if (cc.Status == 6) + Driver.Count++; + + tests++; + var x = new C2(2).Bar(await c.Method(1)); + if (cc.Status == 6 && x == 1) + Driver.Count++; } finally { - Driver.Result = TestCase.Count - this.tests; + Driver.Result = Driver.Count - tests; //When test complete, set the flag. Driver.CompletedSignal.Set(); } @@ -1750,11 +2892,12 @@ public async void Run() class Driver { public static int Result = -1; + public static int Count = 0; public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); static void Main() { var t = new TestCase(); - t.Run(); + t.Run().Wait(); CompletedSignal.WaitOne(); // 0 - success @@ -1764,19 +2907,35 @@ static void Main() } }"; CompileAndVerify(source, "0"); + + var comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (44,31): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // C2 cc = new C2(x: await c.Method(1)); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await c.Method(1)").WithArguments("TestCase.Run()").WithLocation(44, 31), + // (50,25): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // cc = new C2(await c.Method(2), await f()); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await c.Method(2)").WithArguments("TestCase.Run()").WithLocation(50, 25), + // (50,44): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // cc = new C2(await c.Method(2), await f()); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await f()").WithArguments("TestCase.Run()").WithLocation(50, 44), + // (55,35): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // var x = new C2(2).Bar(await c.Method(1)); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await c.Method(1)").WithArguments("TestCase.Run()").WithLocation(55, 35) + ); } [Fact] - public void Await44() + public void Await43() { var source = @" using System.Threading; using System.Threading.Tasks; using System; -class MyClass +struct MyClass { - public static implicit operator Task(MyClass c) + public static Task operator *(MyClass c, int x) { return Task.Run(async delegate { @@ -1784,7 +2943,17 @@ public static implicit operator Task(MyClass c) TestCase.Count++; }); } + + public static Task operator +(MyClass c, long x) + { + return Task.Run(async () => + { + await Task.Delay(1); + TestCase.Count++; + }); + } } + class TestCase { public static int Count = 0; @@ -1792,17 +2961,22 @@ class TestCase public async void Run() { this.tests = 0; - dynamic mc = new MyClass(); + dynamic dy = Task.Run(async () => { await Task.Delay(1); return new MyClass(); }); try { - tests++; - Task t1 = mc; - await t1; + this.tests++; + await (await dy * 5); - tests++; - dynamic t2 = (Task)mc; - await t2; + this.tests++; + dynamic d = new MyClass(); + dynamic dd = Task.Run(async () => { await Task.Delay(1); return 1L; }); + await (d + await dd); + } + catch (Exception ex) + { + Console.WriteLine(ex); + Console.WriteLine(ex.StackTrace); } finally { @@ -1833,20 +3007,256 @@ static void Main() } [Fact] - public void ThisShouldProbablyCompileToVerifiableCode() + public void Await43_WithTask() { var source = @" +using System.Threading; +using System.Threading.Tasks; using System; -class Driver +struct MyClass { - public static bool Run() + public static Task operator *(MyClass c, int x) { - dynamic dynamicThing = false; - return true && dynamicThing; - } - - static void Main() + return Task.Run(async delegate + { + await Task.Delay(1); + TestCase.Count++; + }); + } + + public static Task operator +(MyClass c, long x) + { + return Task.Run(async () => + { + await Task.Delay(1); + TestCase.Count++; + }); + } +} + +class TestCase +{ + public static int Count = 0; + private int tests; + public async Task Run() + { + this.tests = 0; + dynamic dy = Task.Run(async () => { await Task.Delay(1); return new MyClass(); }); + + try + { + this.tests++; + await (await dy * 5); + + this.tests++; + dynamic d = new MyClass(); + dynamic dd = Task.Run(async () => { await Task.Delay(1); return 1L; }); + await (d + await dd); + } + catch (Exception ex) + { + Console.WriteLine(ex); + Console.WriteLine(ex.StackTrace); + } + finally + { + Driver.Result = TestCase.Count - this.tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } +} + +class Driver +{ + public static int Result = -1; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); + t.Run().Wait(); + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + CompileAndVerify(source, "0"); + + var comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (39,13): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // await (await dy * 5); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (await dy * 5)").WithArguments("TestCase.Run()").WithLocation(39, 13), + // (39,20): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // await (await dy * 5); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await dy").WithArguments("TestCase.Run()").WithLocation(39, 20), + // (44,13): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // await (d + await dd); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (d + await dd)").WithArguments("TestCase.Run()").WithLocation(44, 13), + // (44,24): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // await (d + await dd); + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await dd").WithArguments("TestCase.Run()").WithLocation(44, 24) + ); + } + + [Fact] + public void Await44() + { + var source = @" +using System.Threading; +using System.Threading.Tasks; +using System; + +class MyClass +{ + public static implicit operator Task(MyClass c) + { + return Task.Run(async delegate + { + await Task.Delay(1); + TestCase.Count++; + }); + } +} +class TestCase +{ + public static int Count = 0; + private int tests; + public async void Run() + { + this.tests = 0; + dynamic mc = new MyClass(); + + try + { + tests++; + Task t1 = mc; + await t1; + + tests++; + dynamic t2 = (Task)mc; + await t2; + } + finally + { + Driver.Result = TestCase.Count - this.tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } +} + +class Driver +{ + public static int Result = -1; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); + t.Run(); + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + CompileAndVerify(source, "0"); + } + + [Fact] + public void Await44_WithTask() + { + var source = @" +using System.Threading; +using System.Threading.Tasks; +using System; + +class MyClass +{ + public static implicit operator Task(MyClass c) + { + return Task.Run(async delegate + { + await Task.Delay(1); + TestCase.Count++; + }); + } +} +class TestCase +{ + public static int Count = 0; + private int tests; + public async Task Run() + { + this.tests = 0; + dynamic mc = new MyClass(); + + try + { + tests++; + Task t1 = mc; + await t1; + + tests++; + dynamic t2 = (Task)mc; + await t2; + } + finally + { + Driver.Result = TestCase.Count - this.tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } + } +} + +class Driver +{ + public static int Result = -1; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + static void Main() + { + var t = new TestCase(); + t.Run().Wait(); + + CompletedSignal.WaitOne(); + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + CompileAndVerify(source, "0"); + + var comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (34,13): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // await t2; + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await t2").WithArguments("TestCase.Run()").WithLocation(34, 13) + ); + } + + [Fact] + public void ThisShouldProbablyCompileToVerifiableCode() + { + var source = @" +using System; + +class Driver +{ + public static bool Run() + { + dynamic dynamicThing = false; + return true && dynamicThing; + } + + static void Main() { Console.WriteLine(Run()); } @@ -1990,7 +3400,7 @@ public void MyTask_08() //Implementation of you own async pattern public class MyTask { - public async void Run() + public async System.Threading.Tasks.Task Run() { int tests = 0; @@ -2050,6 +3460,59 @@ static void Main() } }"; CompileAndVerify(source, "0"); + + var comp = CreateRuntimeAsyncCompilation(source); + + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ReturnValueMissing("Run", "0x4f") + }); + verifier.VerifyIL("MyTask.Run", """ + { + // Code size 80 (0x50) + .maxstack 2 + .locals init (int V_0, //tests + MyTaskAwaiter V_1) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldloc.0 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.0 + IL_0006: newobj "MyTask..ctor()" + IL_000b: call "MyTaskAwaiter Extension.GetAwaiter(MyTask)" + IL_0010: stloc.1 + IL_0011: ldloc.1 + IL_0012: callvirt "bool MyTaskAwaiter.IsCompleted.get" + IL_0017: brtrue.s IL_001f + IL_0019: ldloc.1 + IL_001a: call "void System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter(MyTaskAwaiter)" + IL_001f: ldloc.1 + IL_0020: callvirt "int MyTaskAwaiter.GetResult()" + IL_0025: ldc.i4.s 123 + IL_0027: bne.un.s IL_0035 + IL_0029: ldsfld "int Driver.Count" + IL_002e: ldc.i4.1 + IL_002f: add + IL_0030: stsfld "int Driver.Count" + IL_0035: leave.s IL_004f + } + finally + { + IL_0037: ldsfld "int Driver.Count" + IL_003c: ldloc.0 + IL_003d: sub + IL_003e: stsfld "int Driver.Result" + IL_0043: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0048: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_004d: pop + IL_004e: endfinally + } + IL_004f: ret + } + """); } [Fact] @@ -2068,7 +3531,7 @@ public MyTaskAwaiter GetAwaiter() return new MyTaskAwaiter(); } - public async void Run() + public async System.Threading.Tasks.Task Run() { int tests = 0; @@ -2126,6 +3589,59 @@ static void Main() } }"; CompileAndVerify(source, "0"); + + var comp = CreateRuntimeAsyncCompilation(source); + + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ReturnValueMissing("Run", "0x4f") + }); + verifier.VerifyIL("MyTask.Run", """ + { + // Code size 80 (0x50) + .maxstack 2 + .locals init (int V_0, //tests + MyTaskAwaiter V_1) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldloc.0 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.0 + IL_0006: newobj "MyTask..ctor()" + IL_000b: callvirt "MyTaskAwaiter MyTask.GetAwaiter()" + IL_0010: stloc.1 + IL_0011: ldloc.1 + IL_0012: callvirt "bool MyTaskBaseAwaiter.IsCompleted.get" + IL_0017: brtrue.s IL_001f + IL_0019: ldloc.1 + IL_001a: call "void System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter(MyTaskAwaiter)" + IL_001f: ldloc.1 + IL_0020: callvirt "int MyTaskBaseAwaiter.GetResult()" + IL_0025: ldc.i4.s 123 + IL_0027: bne.un.s IL_0035 + IL_0029: ldsfld "int Driver.Count" + IL_002e: ldc.i4.1 + IL_002f: add + IL_0030: stsfld "int Driver.Count" + IL_0035: leave.s IL_004f + } + finally + { + IL_0037: ldsfld "int Driver.Count" + IL_003c: ldloc.0 + IL_003d: sub + IL_003e: stsfld "int Driver.Result" + IL_0043: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0048: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_004d: pop + IL_004e: endfinally + } + IL_004f: ret + } + """); } [Fact] @@ -3297,25 +4813,96 @@ public static void Main() CompileAndVerify(source, expectedOutput: expected); } - [WorkItem(840843, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/840843")] + [WorkItem(640282, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/640282")] [Fact] - public void MissingAsyncVoidMethodBuilder() + public void CustomAsyncWithDynamic01_WithTask() { var source = @" -class C +using System; +using System.Threading; +using System.Threading.Tasks; + +class MyTask { - async void M() {} -} -"; + public dynamic GetAwaiter() + { + return new MyTaskAwaiter(); + } - var comp = CSharpTestBase.CreateEmptyCompilation(source, new[] { Net40.References.mscorlib }, TestOptions.ReleaseDll); // NOTE: 4.0, not 4.5, so it's missing the async helpers. + public async Task Run() + { + int tests = 0; - // CONSIDER: It would be nice if we didn't squiggle the whole method body, but this is a corner case. - comp.VerifyEmitDiagnostics( - // (4,16): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. - // async void M() {} - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 16), - // (4,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder' is not defined or imported + tests++; + dynamic myTask = new MyTask(); + var x = await myTask; + if (x == 123) Driver.Count++; + + Driver.Result = Driver.Count - tests; + //When test complete, set the flag. + Driver.CompletedSignal.Set(); + } +} +class MyTaskAwaiter +{ + public void OnCompleted(U continuationAction) + { + } + + public int GetResult() + { + return 123; + } + + public dynamic IsCompleted { get { return true; } } +} +class Driver +{ + public static int Result = -1; + public static int Count = 0; + public static AutoResetEvent CompletedSignal = new AutoResetEvent(false); + public static void Main() + { + new MyTask().Run().Wait(); + + CompletedSignal.WaitOne(); + + // 0 - success + // 1 - failed (test completed) + // -1 - failed (test incomplete - deadlock, etc) + Console.WriteLine(Driver.Result); + } +}"; + var expected = @"0"; + CompileAndVerify(source, expectedOutput: expected); + + var comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,17): error CS9328: Method 'MyTask.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // var x = await myTask; + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await myTask").WithArguments("MyTask.Run()").WithLocation(19, 17) + ); + } + + [WorkItem(840843, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/840843")] + [Fact] + public void MissingAsyncVoidMethodBuilder() + { + var source = @" +class C +{ + async void M() {} +} +"; + + var comp = CSharpTestBase.CreateEmptyCompilation(source, new[] { Net40.References.mscorlib }, TestOptions.ReleaseDll); // NOTE: 4.0, not 4.5, so it's missing the async helpers. + + // CONSIDER: It would be nice if we didn't squiggle the whole method body, but this is a corner case. + comp.VerifyEmitDiagnostics( + // (4,16): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // async void M() {} + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 16), + // (4,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder' is not defined or imported // async void M() {} Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "{}").WithArguments("System.Runtime.CompilerServices.AsyncVoidMethodBuilder").WithLocation(4, 20), // (4,20): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create' @@ -5788,7 +7375,37 @@ static async Task Main() } }"; var comp = CSharpTestBase.CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: "StructAwaitable"); + var expected = "StructAwaitable"; + CompileAndVerify(comp, expectedOutput: expected); + + comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ReturnValueMissing("Main", "0x2a") + }); + + verifier.VerifyIL("Program.Main()", """ + { + // Code size 43 (0x2b) + .maxstack 1 + .locals init (System.Runtime.CompilerServices.TaskAwaiter V_0, + StructAwaitable V_1) + IL_0000: ldloca.s V_1 + IL_0002: initobj "StructAwaitable" + IL_0008: ldloc.1 + IL_0009: box "StructAwaitable" + IL_000e: call "System.Runtime.CompilerServices.TaskAwaiter Extensions.GetAwaiter(IAwaitable)" + IL_0013: stloc.0 + IL_0014: ldloca.s V_0 + IL_0016: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_001b: brtrue.s IL_0023 + IL_001d: ldloc.0 + IL_001e: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.TaskAwaiter)" + IL_0023: ldloca.s V_0 + IL_0025: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_002a: ret + } + """); } [Fact] @@ -5821,7 +7438,38 @@ static async Task Main() } }"; var comp = CSharpTestBase.CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: "StructAwaitable"); + var expected = "StructAwaitable"; + CompileAndVerify(comp, expectedOutput: expected); + + comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = ReturnValueMissing("Main", "0x2f") + }); + + verifier.VerifyIL("Program.Main()", """ + { + // Code size 48 (0x30) + .maxstack 1 + .locals init (StructAwaitable V_0, + System.Runtime.CompilerServices.TaskAwaiter V_1) + IL_0000: ldloca.s V_0 + IL_0002: initobj "StructAwaitable" + IL_0008: ldloc.0 + IL_0009: newobj "StructAwaitable?..ctor(StructAwaitable)" + IL_000e: box "StructAwaitable?" + IL_0013: call "System.Runtime.CompilerServices.TaskAwaiter Extensions.GetAwaiter(object)" + IL_0018: stloc.1 + IL_0019: ldloca.s V_1 + IL_001b: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0020: brtrue.s IL_0028 + IL_0022: ldloc.1 + IL_0023: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.TaskAwaiter)" + IL_0028: ldloca.s V_1 + IL_002a: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_002f: ret + } + """); } [Fact, WorkItem(40251, "https://github.com/dotnet/roslyn/issues/40251")] @@ -6349,5 +7997,1621 @@ static void validate(ModuleSymbol m) m.GlobalNamespace.GetMember("Test1.d__0.x").GetAttributes().Select(a => a.ToString())); } } + + [Fact] + public void RuntimeAsync_CompilerFeatureFlag_EnabledWithRuntimeAsync() + { + var source = """ + using System.Threading.Tasks; + + await Task.CompletedTask; + """; + + var comp = CreateRuntimeAsyncCompilation(source); + + var verifier = CompileAndVerify(comp, verify: Verification.Fails with { ILVerifyMessage = ReturnValueMissing("
$", "0xa") }); + + verifier.VerifyIL("", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0005: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ret + } + """); + } + + [Theory] + [CombinatorialData] + public void RuntimeAsync_CompilerFeatureFlag_EnabledWithoutRuntimeAsync(bool withNonCoreLibSources) + { + var source = """ + using System.Threading.Tasks; + + await Task.CompletedTask; + """; + + var comp = CreateCompilation([source, withNonCoreLibSources ? RuntimeAsyncAwaitHelpers : ""], targetFramework: TargetFramework.Net90, parseOptions: WithRuntimeAsync(TestOptions.RegularPreview)); + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); + + verifier.VerifyIL("", """ + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (Program.<
$>d__0 V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncTaskMethodBuilder System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldc.i4.m1 + IL_000f: stfld "int Program.<
$>d__0.<>1__state" + IL_0014: ldloca.s V_0 + IL_0016: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder" + IL_001b: ldloca.s V_0 + IL_001d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start$>d__0>(ref Program.<
$>d__0)" + IL_0022: ldloca.s V_0 + IL_0024: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder" + IL_0029: call "System.Threading.Tasks.Task System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Task.get" + IL_002e: ret + } + """); + } + + [Theory] + [CombinatorialData] + public void RuntimeAsync_CompilerFeatureFlag_DisabledWithRuntimeAsync(bool explicitDisable) + { + var source = """ + using System.Threading.Tasks; + + await Task.CompletedTask; + """; + + var parseOptions = TestOptions.RegularPreview; + if (explicitDisable) + { + parseOptions = parseOptions.WithFeature("runtime-async", "off"); + } + + var comp = CreateRuntimeAsyncCompilation(source, parseOptions: parseOptions); + + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); + + verifier.VerifyIL("", """ + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (Program.<
$>d__0 V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncTaskMethodBuilder System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldc.i4.m1 + IL_000f: stfld "int Program.<
$>d__0.<>1__state" + IL_0014: ldloca.s V_0 + IL_0016: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder" + IL_001b: ldloca.s V_0 + IL_001d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start$>d__0>(ref Program.<
$>d__0)" + IL_0022: ldloca.s V_0 + IL_0024: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
$>d__0.<>t__builder" + IL_0029: call "System.Threading.Tasks.Task System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Task.get" + IL_002e: ret + } + """); + } + + [Theory] + [CombinatorialData] + public void RuntimeAsync_CompilerFeatureFlag_DisabledWithRuntimeAsync_EnabledOnMethod(bool explicitDisable) + { + var source = """ + using System.Threading.Tasks; + + class Program + { + [System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(true)] + static async Task Main() + { + await Task.CompletedTask; + } + } + """; + + var parseOptions = TestOptions.RegularPreview; + if (explicitDisable) + { + parseOptions = parseOptions.WithFeature("runtime-async", "off"); + } + + var comp = CreateRuntimeAsyncCompilation([source, RuntimeAsyncMethodGenerationAttributeDefinition], parseOptions: parseOptions); + + var verifier = CompileAndVerify(comp, verify: Verification.Fails with { ILVerifyMessage = ReturnValueMissing("Main", "0xa") }); + + verifier.VerifyIL("Program.Main()", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0005: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ret + } + """); + } + + [Theory] + [CombinatorialData] + public void RuntimeAsync_CompilerFeatureFlag_DisabledWithRuntimeAsync_EnabledOnMethod_NoPreferenceOnNestedLocalFunction(bool explicitDisable) + { + var source = """ + using System.Threading.Tasks; + + class Program + { + [System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(true)] + static async Task Main() + { + await Task.CompletedTask; + + async Task LocalFunc() => await Task.CompletedTask; + } + } + """; + + var parseOptions = TestOptions.RegularPreview; + if (explicitDisable) + { + parseOptions = parseOptions.WithFeature("runtime-async", "off"); + } + + var comp = CreateRuntimeAsyncCompilation([source, RuntimeAsyncMethodGenerationAttributeDefinition], parseOptions: parseOptions); + + var verifier = CompileAndVerify(comp, verify: Verification.Fails with { ILVerifyMessage = ReturnValueMissing("Main", "0xa") }); + + verifier.VerifyIL("Program.Main()", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0005: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ret + } + """); + + verifier.VerifyIL("Program.
g__LocalFunc|0_0()", """ + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (Program.<
g__LocalFunc|0_0>d V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncTaskMethodBuilder System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
g__LocalFunc|0_0>d.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldc.i4.m1 + IL_000f: stfld "int Program.<
g__LocalFunc|0_0>d.<>1__state" + IL_0014: ldloca.s V_0 + IL_0016: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
g__LocalFunc|0_0>d.<>t__builder" + IL_001b: ldloca.s V_0 + IL_001d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Startg__LocalFunc|0_0>d>(ref Program.<
g__LocalFunc|0_0>d)" + IL_0022: ldloca.s V_0 + IL_0024: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<
g__LocalFunc|0_0>d.<>t__builder" + IL_0029: call "System.Threading.Tasks.Task System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Task.get" + IL_002e: ret + } + """); + } + + [Theory] + [CombinatorialData] + public void RuntimeAsync_CompilerFeatureFlag_DisabledWithRuntimeAsync_EnabledOnMethod_NoPreferenceOnNestedLambda(bool explicitDisable) + { + var source = """ + using System.Threading.Tasks; + + class Program + { + [System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(true)] + static async Task Main() + { + await Task.CompletedTask; + + var a = async () => await Task.CompletedTask; + } + } + """; + + var parseOptions = TestOptions.RegularPreview; + if (explicitDisable) + { + parseOptions = parseOptions.WithFeature("runtime-async", "off"); + } + + var comp = CreateRuntimeAsyncCompilation([source, RuntimeAsyncMethodGenerationAttributeDefinition], parseOptions: parseOptions); + + var verifier = CompileAndVerify(comp, verify: Verification.Fails with { ILVerifyMessage = ReturnValueMissing("Main", "0x26") }); + + verifier.VerifyIL("Program.Main()", """ + { + // Code size 39 (0x27) + .maxstack 2 + IL_0000: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0005: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ldsfld "System.Func Program.<>c.<>9__0_0" + IL_000f: brtrue.s IL_0026 + IL_0011: ldsfld "Program.<>c Program.<>c.<>9" + IL_0016: ldftn "System.Threading.Tasks.Task Program.<>c.
b__0_0()" + IL_001c: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0021: stsfld "System.Func Program.<>c.<>9__0_0" + IL_0026: ret + } + """); + + verifier.VerifyIL("Program.<>c.
b__0_0()", """ + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (Program.<>c.<
b__0_0>d V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncTaskMethodBuilder System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<
b__0_0>d.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldc.i4.m1 + IL_000f: stfld "int Program.<>c.<
b__0_0>d.<>1__state" + IL_0014: ldloca.s V_0 + IL_0016: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<
b__0_0>d.<>t__builder" + IL_001b: ldloca.s V_0 + IL_001d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Startc.<
b__0_0>d>(ref Program.<>c.<
b__0_0>d)" + IL_0022: ldloca.s V_0 + IL_0024: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<
b__0_0>d.<>t__builder" + IL_0029: call "System.Threading.Tasks.Task System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Task.get" + IL_002e: ret + } + """); + } + + [Fact] + public void RuntimeAsync_CompilerFeatureFlag_EnabledWithRuntimeAsync_DisabledOnMethod() + { + var source = """ + using System.Threading.Tasks; + + class Program + { + [System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)] + static async Task Main() + { + await Task.CompletedTask; + } + } + """; + + var comp = CreateRuntimeAsyncCompilation([source, RuntimeAsyncMethodGenerationAttributeDefinition]); + + var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); + + verifier.VerifyIL("Program.Main()", """ + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (Program.
d__0 V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncTaskMethodBuilder System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.
d__0.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldc.i4.m1 + IL_000f: stfld "int Program.
d__0.<>1__state" + IL_0014: ldloca.s V_0 + IL_0016: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.
d__0.<>t__builder" + IL_001b: ldloca.s V_0 + IL_001d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Startd__0>(ref Program.
d__0)" + IL_0022: ldloca.s V_0 + IL_0024: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.
d__0.<>t__builder" + IL_0029: call "System.Threading.Tasks.Task System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Task.get" + IL_002e: ret + } + """); + } + + [Fact] + public void RuntimeAsync_CompilerFeatureFlag_EnabledWithRuntimeAsync_DisabledOnMethod_NoPreferenceOnNestedLocalFunction() + { + var source = """ + using System.Threading.Tasks; + + class Program + { + [System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)] + static async Task Main() + { + await Task.CompletedTask; + + async Task LocalFunc() => await Task.CompletedTask; + } + } + """; + + var comp = CreateRuntimeAsyncCompilation([source, RuntimeAsyncMethodGenerationAttributeDefinition]); + + var verifier = CompileAndVerify(comp, verify: Verification.Fails with { ILVerifyMessage = ReturnValueMissing("
g__LocalFunc|0_0", "0xa") }); + + verifier.VerifyIL("Program.Main()", """ + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (Program.
d__0 V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncTaskMethodBuilder System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.
d__0.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldc.i4.m1 + IL_000f: stfld "int Program.
d__0.<>1__state" + IL_0014: ldloca.s V_0 + IL_0016: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.
d__0.<>t__builder" + IL_001b: ldloca.s V_0 + IL_001d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Startd__0>(ref Program.
d__0)" + IL_0022: ldloca.s V_0 + IL_0024: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.
d__0.<>t__builder" + IL_0029: call "System.Threading.Tasks.Task System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Task.get" + IL_002e: ret + } + """); + + verifier.VerifyIL("Program.
g__LocalFunc|0_0()", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0005: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ret + } + """); + } + + [Fact] + public void RuntimeAsync_CompilerFeatureFlag_EnabledWithRuntimeAsync_DisabledOnMethod_NoPreferenceOnNestedLambda() + { + var source = """ + using System.Threading.Tasks; + + class Program + { + [System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)] + static async Task Main() + { + await Task.CompletedTask; + + var a = async () => await Task.CompletedTask; + } + } + """; + + var comp = CreateRuntimeAsyncCompilation([source, RuntimeAsyncMethodGenerationAttributeDefinition]); + + var verifier = CompileAndVerify(comp, verify: Verification.Fails with { ILVerifyMessage = ReturnValueMissing("
b__0_0", "0xa") }); + + verifier.VerifyIL("Program.Main()", """ + { + // Code size 47 (0x2f) + .maxstack 2 + .locals init (Program.
d__0 V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncTaskMethodBuilder System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.
d__0.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldc.i4.m1 + IL_000f: stfld "int Program.
d__0.<>1__state" + IL_0014: ldloca.s V_0 + IL_0016: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.
d__0.<>t__builder" + IL_001b: ldloca.s V_0 + IL_001d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Startd__0>(ref Program.
d__0)" + IL_0022: ldloca.s V_0 + IL_0024: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.
d__0.<>t__builder" + IL_0029: call "System.Threading.Tasks.Task System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Task.get" + IL_002e: ret + } + """); + + verifier.VerifyIL("Program.<>c.
b__0_0()", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0005: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ret + } + """); + } + + [Theory] + [InlineData("INotifyCompletion")] + [InlineData("ICriticalNotifyCompletion")] + public void CustomAwaitable_NonGeneric(string notifyType) + { + var source = $$""" + var c = new C(); + await c; + + class C + { + public class Awaiter : System.Runtime.CompilerServices.{{notifyType}} + { + private bool isCompleted = false; + public void OnCompleted(System.Action continuation) {} + public void UnsafeOnCompleted(System.Action continuation) {} + public bool IsCompleted + { + get + { + var isCompleted = this.isCompleted; + this.isCompleted = true; + return isCompleted; + } + } + public void GetResult() => System.Console.WriteLine("42"); + } + + public Awaiter GetAwaiter() => new Awaiter(); + } + """; + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = ReturnValueMissing("
$", "0x1f") }); + + var expectedAwait = notifyType == "INotifyCompletion" ? "AwaitAwaiter" : "UnsafeAwaitAwaiter"; + verifier.VerifyIL("", $$""" + { + // Code size 32 (0x20) + .maxstack 1 + .locals init (C.Awaiter V_0) + IL_0000: newobj "C..ctor()" + IL_0005: callvirt "C.Awaiter C.GetAwaiter()" + IL_000a: stloc.0 + IL_000b: ldloc.0 + IL_000c: callvirt "bool C.Awaiter.IsCompleted.get" + IL_0011: brtrue.s IL_0019 + IL_0013: ldloc.0 + IL_0014: call "void System.Runtime.CompilerServices.AsyncHelpers.{{expectedAwait}}(C.Awaiter)" + IL_0019: ldloc.0 + IL_001a: callvirt "void C.Awaiter.GetResult()" + IL_001f: ret + } + """); + } + + [Theory] + [InlineData("System.Runtime.CompilerServices.INotifyCompletion")] + [InlineData("System.Runtime.CompilerServices.ICriticalNotifyCompletion")] + [InlineData("System.Runtime.CompilerServices.ICriticalNotifyCompletion, System.Runtime.CompilerServices.INotifyCompletion")] + public void CustomAwaitable_WithNonVoidAwait(string notifyType) + { + var source = $$""" + var c = new C(); + System.Console.WriteLine(await c); + + class C + { + public class Awaiter : {{notifyType}} + { + private bool isCompleted = false; + public void OnCompleted(System.Action continuation) {} + public void UnsafeOnCompleted(System.Action continuation) {} + public bool IsCompleted + { + get + { + var isCompleted = this.isCompleted; + this.isCompleted = true; + return isCompleted; + } + } + public int GetResult() => 42; + } + + public Awaiter GetAwaiter() => new Awaiter(); + } + """; + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = ReturnValueMissing("
$", "0x24") }); + + var expectedAwait = notifyType.Contains("Critical") ? "UnsafeAwaitAwaiter" : "AwaitAwaiter"; + verifier.VerifyIL("", $$""" + { + // Code size 37 (0x25) + .maxstack 1 + .locals init (C.Awaiter V_0) + IL_0000: newobj "C..ctor()" + IL_0005: callvirt "C.Awaiter C.GetAwaiter()" + IL_000a: stloc.0 + IL_000b: ldloc.0 + IL_000c: callvirt "bool C.Awaiter.IsCompleted.get" + IL_0011: brtrue.s IL_0019 + IL_0013: ldloc.0 + IL_0014: call "void System.Runtime.CompilerServices.AsyncHelpers.{{expectedAwait}}(C.Awaiter)" + IL_0019: ldloc.0 + IL_001a: callvirt "int C.Awaiter.GetResult()" + IL_001f: call "void System.Console.WriteLine(int)" + IL_0024: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77897")] + public void AwaitYield() + { + var code = """ + using System.Threading.Tasks; + class C + { + static bool doYields = true; + + static async Task Main() + { + System.Console.WriteLine(await Fib(10)); + } + + static async Task Fib(int i) + { + if (i <= 1) + { + if (doYields) + { + await Task.Yield(); + } + + return 1; + } + + int i1 = await Fib(i - 1); + int i2 = await Fib(i - 2); + + return i1 + i2; + } + } + """; + + var comp = CreateRuntimeAsyncCompilation(code); + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("55", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = $$""" + {{ReturnValueMissing("Main", "0x11")}} + [Fib]: Unexpected type on the stack. { Offset = 0x30, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Fib]: Unexpected type on the stack. { Offset = 0x4e, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Fib(int)", """ + { + // Code size 79 (0x4f) + .maxstack 3 + .locals init (int V_0, //i2 + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2) + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: bgt.s IL_0031 + IL_0004: ldsfld "bool C.doYields" + IL_0009: brfalse.s IL_002f + IL_000b: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0010: stloc.2 + IL_0011: ldloca.s V_2 + IL_0013: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0018: stloc.1 + IL_0019: ldloca.s V_1 + IL_001b: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0020: brtrue.s IL_0028 + IL_0022: ldloc.1 + IL_0023: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0028: ldloca.s V_1 + IL_002a: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_002f: ldc.i4.1 + IL_0030: ret + IL_0031: ldarg.0 + IL_0032: ldc.i4.1 + IL_0033: sub + IL_0034: call "System.Threading.Tasks.Task C.Fib(int)" + IL_0039: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003e: ldarg.0 + IL_003f: ldc.i4.2 + IL_0040: sub + IL_0041: call "System.Threading.Tasks.Task C.Fib(int)" + IL_0046: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_004b: stloc.0 + IL_004c: ldloc.0 + IL_004d: add + IL_004e: ret + } + """); + } + + [Fact] + public void MultipleValidRuntimeAsyncAwaitMethods() + { + var code = """ + await System.Threading.Tasks.Task.CompletedTask; + """; + + var runtimeAsyncHelpers = """ + namespace System.Runtime.CompilerServices + { + public static class AsyncHelpers + { + public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INotifyCompletion + {} + public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion + {} + + public static void Await(object task) => throw null!; + public static void Await(System.Threading.Tasks.Task task) => throw null!; + public static void Await(System.Threading.Tasks.ValueTask task) => throw null!; + public static T Await(System.Threading.Tasks.Task task) => throw null!; + public static T Await(System.Threading.Tasks.ValueTask task) => throw null!; + } + } + """; + + var comp = CreateRuntimeAsyncCompilationWithCustomAwaitHelpers(code, runtimeAsyncAwaitHelpers: runtimeAsyncHelpers); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped); + // No error when multiple valid runtime async await methods are present, we just fall back to AwaitAwaiter + verifier.VerifyIL("", """ + { + // Code size 34 (0x22) + .maxstack 1 + .locals init (System.Runtime.CompilerServices.TaskAwaiter V_0) + IL_0000: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0005: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_000a: stloc.0 + IL_000b: ldloca.s V_0 + IL_000d: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0012: brtrue.s IL_001a + IL_0014: ldloc.0 + IL_0015: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.TaskAwaiter)" + IL_001a: ldloca.s V_0 + IL_001c: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0021: ret + } + """); + } + + private Compilation CreateRuntimeAsyncCompilationWithCustomAwaitHelpers(string source, string runtimeAsyncAwaitHelpers) + { + var corlib = """ + namespace System + { + public class Attribute {} + public enum AttributeTargets {} + public class AttributeUsageAttribute : Attribute + { + public AttributeUsageAttribute(AttributeTargets validOn) {} + public bool AllowMultiple { get; set; } + public bool Inherited { get; set; } + } + public struct Boolean {} + public abstract class Enum {} + public class Exception {} + public struct Int32 {} + public class Object {} + public class String {} + public class ValueType {} + public class Void {} + + namespace Threading.Tasks + { + public class Task + { + public Runtime.CompilerServices.TaskAwaiter GetAwaiter() => throw null!; + public static Task FromResult(T result) => throw null!; + public static Task CompletedTask => throw null!; + } + public class Task + { + public Runtime.CompilerServices.TaskAwaiter GetAwaiter() => throw null!; + } + public struct ValueTask + { + public Runtime.CompilerServices.ValueTaskAwaiter GetAwaiter() => throw null!; + } + public struct ValueTask + { + public Runtime.CompilerServices.ValueTaskAwaiter GetAwaiter() => throw null!; + } + } + + namespace Runtime.CompilerServices + { + public interface INotifyCompletion {} + public interface ICriticalNotifyCompletion : INotifyCompletion {} + public struct TaskAwaiter : ICriticalNotifyCompletion + { + public bool IsCompleted => false; + public void GetResult() {} + } + public struct TaskAwaiter : ICriticalNotifyCompletion + { + public bool IsCompleted => false; + public TResult GetResult() => default; + } + public struct ValueTaskAwaiter : ICriticalNotifyCompletion + { + public bool IsCompleted => false; + public void GetResult() {} + } + public struct ValueTaskAwaiter : ICriticalNotifyCompletion + { + public bool IsCompleted => false; + public TResult GetResult() => default; + } + } + } + """; + + var corlibComp = CreateEmptyCompilation([corlib, runtimeAsyncAwaitHelpers]); + return CreateEmptyCompilation(source, references: [corlibComp.EmitToImageReference()], parseOptions: WithRuntimeAsync(TestOptions.RegularPreview)); + } + + [Fact] + public void MissingAwaitTask() + { + var code = """ + await System.Threading.Tasks.Task.CompletedTask; + """; + + var runtimeAsyncHelpers = """ + namespace System.Runtime.CompilerServices + { + public static class AsyncHelpers + { + public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INotifyCompletion + {} + public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion + {} + + public static void Await(System.Threading.Tasks.ValueTask task) => throw null!; + public static T Await(System.Threading.Tasks.Task task) => throw null!; + public static T Await(System.Threading.Tasks.ValueTask task) => throw null!; + } + } + """; + + var comp = CreateRuntimeAsyncCompilationWithCustomAwaitHelpers(code, runtimeAsyncAwaitHelpers: runtimeAsyncHelpers); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped); + verifier.VerifyIL("", """ + { + // Code size 34 (0x22) + .maxstack 1 + .locals init (System.Runtime.CompilerServices.TaskAwaiter V_0) + IL_0000: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0005: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_000a: stloc.0 + IL_000b: ldloca.s V_0 + IL_000d: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0012: brtrue.s IL_001a + IL_0014: ldloc.0 + IL_0015: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.TaskAwaiter)" + IL_001a: ldloca.s V_0 + IL_001c: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0021: ret + } + """); + } + + [Fact] + public void MissingAwaitTaskT() + { + var code = """ + await System.Threading.Tasks.Task.FromResult(0); + """; + + var runtimeAsyncHelpers = """ + namespace System.Runtime.CompilerServices + { + public static class AsyncHelpers + { + public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INotifyCompletion + {} + public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion + {} + + public static void Await(System.Threading.Tasks.Task task) => throw null!; + public static void Await(System.Threading.Tasks.ValueTask task) => throw null!; + public static T Await(System.Threading.Tasks.ValueTask task) => throw null!; + } + } + """; + + var comp = CreateRuntimeAsyncCompilationWithCustomAwaitHelpers(code, runtimeAsyncAwaitHelpers: runtimeAsyncHelpers); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped); + verifier.VerifyIL("", """ + { + // Code size 36 (0x24) + .maxstack 1 + .locals init (System.Runtime.CompilerServices.TaskAwaiter V_0) + IL_0000: ldc.i4.0 + IL_0001: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" + IL_0006: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_000b: stloc.0 + IL_000c: ldloca.s V_0 + IL_000e: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0013: brtrue.s IL_001b + IL_0015: ldloc.0 + IL_0016: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter>(System.Runtime.CompilerServices.TaskAwaiter)" + IL_001b: ldloca.s V_0 + IL_001d: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0022: pop + IL_0023: ret + } + """); + } + + [Fact] + public void MissingAwaitValueTask() + { + var code = """ + await default(System.Threading.Tasks.ValueTask); + """; + + var runtimeAsyncHelpers = """ + namespace System.Runtime.CompilerServices + { + public static class AsyncHelpers + { + public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INotifyCompletion + {} + public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion + {} + + public static void Await(System.Threading.Tasks.Task task) => throw null!; + public static T Await(System.Threading.Tasks.Task task) => throw null!; + public static T Await(System.Threading.Tasks.ValueTask task) => throw null!; + } + } + """; + + var comp = CreateRuntimeAsyncCompilationWithCustomAwaitHelpers(code, runtimeAsyncAwaitHelpers: runtimeAsyncHelpers); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped); + verifier.VerifyIL("", """ + { + // Code size 38 (0x26) + .maxstack 2 + .locals init (System.Runtime.CompilerServices.ValueTaskAwaiter V_0, + System.Threading.Tasks.ValueTask V_1) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "System.Threading.Tasks.ValueTask" + IL_0009: call "System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()" + IL_000e: stloc.0 + IL_000f: ldloca.s V_0 + IL_0011: call "bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get" + IL_0016: brtrue.s IL_001e + IL_0018: ldloc.0 + IL_0019: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.ValueTaskAwaiter)" + IL_001e: ldloca.s V_0 + IL_0020: call "void System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()" + IL_0025: ret + } + """); + } + + [Fact] + public void MissingAwaitValueTaskT() + { + var code = """ + await default(System.Threading.Tasks.ValueTask); + """; + + var runtimeAsyncHelpers = """ + namespace System.Runtime.CompilerServices + { + public static class AsyncHelpers + { + public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INotifyCompletion + {} + public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion + {} + + public static void Await(System.Threading.Tasks.Task task) => throw null!; + public static void Await(System.Threading.Tasks.ValueTask task) => throw null!; + public static T Await(System.Threading.Tasks.Task task) => throw null!; + } + } + """; + + var comp = CreateRuntimeAsyncCompilationWithCustomAwaitHelpers(code, runtimeAsyncAwaitHelpers: runtimeAsyncHelpers); + var verifier = CompileAndVerify(comp, verify: Verification.Skipped); + verifier.VerifyIL("", """ + { + // Code size 39 (0x27) + .maxstack 2 + .locals init (System.Runtime.CompilerServices.ValueTaskAwaiter V_0, + System.Threading.Tasks.ValueTask V_1) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "System.Threading.Tasks.ValueTask" + IL_0009: call "System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()" + IL_000e: stloc.0 + IL_000f: ldloca.s V_0 + IL_0011: call "bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get" + IL_0016: brtrue.s IL_001e + IL_0018: ldloc.0 + IL_0019: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter>(System.Runtime.CompilerServices.ValueTaskAwaiter)" + IL_001e: ldloca.s V_0 + IL_0020: call "int System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()" + IL_0025: pop + IL_0026: ret + } + """); + } + + [Fact] + public void MissingAsyncHelpers() + { + var code = """ + await System.Threading.Tasks.Task.Yield(); + """; + + var comp = CreateRuntimeAsyncCompilation(code); + comp.MakeTypeMissing(InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers); + comp.VerifyDiagnostics( + // (1,7): error CS0518: Predefined type 'System.Runtime.CompilerServices.AsyncHelpers' is not defined or imported + // await System.Threading.Tasks.Task.Yield(); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "System.Threading.Tasks.Task.Yield()").WithArguments("System.Runtime.CompilerServices.AsyncHelpers").WithLocation(1, 7) + ); + + // Runtime async not turned on, so we shouldn't care about the missing member + comp = CreateRuntimeAsyncCompilation(code, parseOptions: TestOptions.RegularPreview); + comp.MakeTypeMissing(InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers); + CompileAndVerify(comp, verify: Verification.FailsPEVerify); + } + + [Fact] + public void MissingUnsafeAwaitAwaiter() + { + var code = """ + await System.Threading.Tasks.Task.Yield(); + """; + + var comp = CreateRuntimeAsyncCompilation(code); + comp.MakeMemberMissing(SpecialMember.System_Runtime_CompilerServices_AsyncHelpers__UnsafeAwaitAwaiter_TAwaiter); + comp.VerifyDiagnostics( + // (1,7): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter' + // await System.Threading.Tasks.Task.Yield(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "System.Threading.Tasks.Task.Yield()").WithArguments("System.Runtime.CompilerServices.AsyncHelpers", "UnsafeAwaitAwaiter").WithLocation(1, 7) + ); + + // Runtime async not turned on, so we shouldn't care about the missing member + comp = CreateRuntimeAsyncCompilation(code, parseOptions: TestOptions.RegularPreview); + comp.MakeMemberMissing(SpecialMember.System_Runtime_CompilerServices_AsyncHelpers__UnsafeAwaitAwaiter_TAwaiter); + CompileAndVerify(comp, verify: Verification.FailsPEVerify); + } + + [Fact] + public void MissingAwaitAwaiter() + { + var code = """ + using System.Runtime.CompilerServices; + + await new C(); + + class C + { + public CAwaiter GetAwaiter() => new CAwaiter(); + } + + class CAwaiter : INotifyCompletion + { + public void OnCompleted(System.Action continuation) {} + public bool IsCompleted => true; + public void GetResult() {} + } + """; + + var comp = CreateRuntimeAsyncCompilation(code); + comp.MakeMemberMissing(SpecialMember.System_Runtime_CompilerServices_AsyncHelpers__AwaitAwaiter_TAwaiter); + comp.VerifyDiagnostics( + // (3,7): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter' + // await new C(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "new C()").WithArguments("System.Runtime.CompilerServices.AsyncHelpers", "AwaitAwaiter").WithLocation(3, 7) + ); + + // Runtime async not turned on, so we shouldn't care about the missing member + comp = CreateRuntimeAsyncCompilation(code, parseOptions: TestOptions.RegularPreview); + comp.MakeMemberMissing(SpecialMember.System_Runtime_CompilerServices_AsyncHelpers__AwaitAwaiter_TAwaiter); + CompileAndVerify(comp, verify: Verification.FailsPEVerify); + } + + [Fact] + public void AwaitAwaiterConstraintsViolation() + { + var runtimeAsyncAwaitHelpers = """ + namespace System.Runtime.CompilerServices + { + public static class AsyncHelpers + { + public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : struct, INotifyCompletion + {} + public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion + {} + + public static void Await(System.Threading.Tasks.Task task) => throw null; + public static void Await(System.Threading.Tasks.ValueTask task) => throw null!; + public static T Await(System.Threading.Tasks.Task task) => throw null!; + public static T Await(System.Threading.Tasks.ValueTask task) => throw null!; + } + } + """; + + var code = """ + using System.Runtime.CompilerServices; + + await new C(); + + class C + { + public CAwaiter GetAwaiter() => throw null!; + } + + class CAwaiter : INotifyCompletion + { + public bool IsCompleted => true; + public void GetResult() {} + } + """; + + var comp = CreateRuntimeAsyncCompilationWithCustomAwaitHelpers(code, runtimeAsyncAwaitHelpers: runtimeAsyncAwaitHelpers); + comp.VerifyDiagnostics( + // (3,7): error CS0453: The type 'CAwaiter' must be a non-nullable value type in order to use it as parameter 'TAwaiter' in the generic type or method 'AsyncHelpers.AwaitAwaiter(TAwaiter)' + // await new C(); + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "new C()").WithArguments("System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter(TAwaiter)", "TAwaiter", "CAwaiter").WithLocation(3, 7) + ); + } + + [Fact] + public void UnsafeAwaitAwaiterConstraintsViolation() + { + var runtimeAsyncAwaitHelpers = """ + namespace System.Runtime.CompilerServices + { + public static class AsyncHelpers + { + public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INotifyCompletion + {} + public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : class, ICriticalNotifyCompletion + {} + + public static void Await(System.Threading.Tasks.Task task) => throw null!; + public static void Await(System.Threading.Tasks.ValueTask task) => throw null!; + public static T Await(System.Threading.Tasks.Task task) => throw null!; + public static T Await(System.Threading.Tasks.ValueTask task) => throw null!; + } + } + """; + + var code = """ + await default(Awaited); + + struct Awaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion + { + public bool IsCompleted => true; + public void GetResult() {} + } + + struct Awaited + { + public Awaiter GetAwaiter() => throw null!; + } + """; + + var comp = CreateRuntimeAsyncCompilationWithCustomAwaitHelpers(code, runtimeAsyncAwaitHelpers: runtimeAsyncAwaitHelpers); + comp.VerifyDiagnostics( + // (1,7): error CS0452: The type 'Awaiter' must be a reference type in order to use it as parameter 'TAwaiter' in the generic type or method 'AsyncHelpers.UnsafeAwaitAwaiter(TAwaiter)' + // await default(Awaited); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "default(Awaited)").WithArguments("System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(TAwaiter)", "TAwaiter", "Awaiter").WithLocation(1, 7) + ); + } + + [Fact] + public void TaskTAwaitConstraintsViolation() + { + var runtimeAsyncAwaitHelpers = """ + namespace System.Runtime.CompilerServices + { + public static class AsyncHelpers + { + public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INotifyCompletion + {} + public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : class, ICriticalNotifyCompletion + {} + + public static void Await(System.Threading.Tasks.Task task) => throw null!; + public static void Await(System.Threading.Tasks.ValueTask task) => throw null!; + public static T Await(System.Threading.Tasks.Task task) where T : class => throw null!; + public static T Await(System.Threading.Tasks.ValueTask task) => throw null!; + } + } + """; + + var code = """ + await System.Threading.Tasks.Task.FromResult(1); + """; + + var comp = CreateRuntimeAsyncCompilationWithCustomAwaitHelpers(code, runtimeAsyncAwaitHelpers: runtimeAsyncAwaitHelpers); + // Note: because of constraints failure, Await is skipped over, and then UnsafeAwaitAwaiter is attempted. + comp.VerifyDiagnostics( + // (1,7): error CS0452: The type 'TaskAwaiter' must be a reference type in order to use it as parameter 'TAwaiter' in the generic type or method 'AsyncHelpers.UnsafeAwaitAwaiter(TAwaiter)' + // await System.Threading.Tasks.Task.FromResult(1); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "System.Threading.Tasks.Task.FromResult(1)").WithArguments("System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(TAwaiter)", "TAwaiter", "System.Runtime.CompilerServices.TaskAwaiter").WithLocation(1, 7) + ); + } + + [Fact] + public void ValueTaskTAwaitConstraintsViolation() + { + var runtimeAsyncAwaitHelpers = """ + namespace System.Runtime.CompilerServices + { + public static class AsyncHelpers + { + public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INotifyCompletion + {} + public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : class, ICriticalNotifyCompletion + {} + + public static void Await(System.Threading.Tasks.Task task) => throw null!; + public static void Await(System.Threading.Tasks.ValueTask task) => throw null!; + public static T Await(System.Threading.Tasks.Task task) => throw null!; + public static T Await(System.Threading.Tasks.ValueTask task) where T : class => throw null!; + } + } + """; + + var code = """ + await default(System.Threading.Tasks.ValueTask); + """; + + var comp = CreateRuntimeAsyncCompilationWithCustomAwaitHelpers(code, runtimeAsyncAwaitHelpers: runtimeAsyncAwaitHelpers); + // Note: because of constraints failure, Await is skipped over, and then UnsafeAwaitAwaiter is attempted. + comp.VerifyDiagnostics( + // (1,7): error CS0452: The type 'ValueTaskAwaiter' must be a reference type in order to use it as parameter 'TAwaiter' in the generic type or method 'AsyncHelpers.UnsafeAwaitAwaiter(TAwaiter)' + // await default(System.Threading.Tasks.ValueTask); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "default(System.Threading.Tasks.ValueTask)").WithArguments("System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(TAwaiter)", "TAwaiter", "System.Runtime.CompilerServices.ValueTaskAwaiter").WithLocation(1, 7) + ); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78529")] + public void ExceptionHandlerReturn() + { + var code = """ + using System; + using System.Threading.Tasks; + + System.Console.WriteLine(await C.Handler()); + + class C + { + public static async Task Handler() + { + try + { + return await Throw(42); + } + catch (IntegerException ex) + { + return ex.Value; + } + } + + #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + public static async Task Throw(int value) => throw new IntegerException(value); + } + + public class IntegerException(int value) : Exception(value.ToString()) + { + public int Value => value; + } + """; + + var comp = CreateRuntimeAsyncCompilation(code); + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = $$""" + {{ReturnValueMissing("
$", "0xf")}} + [Handler]: Unexpected type on the stack. { Offset = 0x18, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Handler()", """ + { + // Code size 25 (0x19) + .maxstack 1 + .locals init (int V_0) + .try + { + IL_0000: ldc.i4.s 42 + IL_0002: call "System.Threading.Tasks.Task C.Throw(int)" + IL_0007: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000c: stloc.0 + IL_000d: leave.s IL_0017 + } + catch IntegerException + { + IL_000f: callvirt "int IntegerException.Value.get" + IL_0014: stloc.0 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: ret + } + """); + + comp = CreateRuntimeAsyncCompilation(code, options: TestOptions.DebugExe.WithDebugPlusMode(true).WithSpecificDiagnosticOptions("SYSLIB5007", ReportDiagnostic.Suppress)); + verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = $$""" + {{ReturnValueMissing("
$", "0x12")}} + [Handler]: Unexpected type on the stack. { Offset = 0x1f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Handler()", """ + { + // Code size 32 (0x20) + .maxstack 1 + .locals init (int V_0, + int V_1, + IntegerException V_2) //ex + IL_0000: nop + .try + { + IL_0001: nop + IL_0002: ldc.i4.s 42 + IL_0004: call "System.Threading.Tasks.Task C.Throw(int)" + IL_0009: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000e: stloc.0 + IL_000f: ldloc.0 + IL_0010: stloc.1 + IL_0011: leave.s IL_001e + } + catch IntegerException + { + IL_0013: stloc.2 + IL_0014: nop + IL_0015: ldloc.2 + IL_0016: callvirt "int IntegerException.Value.get" + IL_001b: stloc.1 + IL_001c: leave.s IL_001e + } + IL_001e: ldloc.1 + IL_001f: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78529")] + public void ExceptionHandlerReturn_NonAsyncMethod() + { + var code = """ + using System; + using System.Threading.Tasks; + + System.Console.WriteLine(await C.Handler()); + + class C + { + public static Task Handler() + { + try + { + return Throw(42); + } + catch (IntegerException ex) + { + return Task.FromResult(ex.Value); + } + } + + #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + public static async Task Throw(int value) => throw new IntegerException(value); + } + + public class IntegerException(int value) : Exception(value.ToString()) + { + public int Value => value; + } + """; + + var comp = CreateRuntimeAsyncCompilation(code); + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = $$""" + {{ReturnValueMissing("
$", "0xf")}} + """ + }); + verifier.VerifyIL("C.Handler()", """ + { + // Code size 25 (0x19) + .maxstack 1 + .locals init (System.Threading.Tasks.Task V_0) + .try + { + IL_0000: ldc.i4.s 42 + IL_0002: call "System.Threading.Tasks.Task C.Throw(int)" + IL_0007: stloc.0 + IL_0008: leave.s IL_0017 + } + catch IntegerException + { + IL_000a: callvirt "int IntegerException.Value.get" + IL_000f: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" + IL_0014: stloc.0 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: ret + } + """); + + comp = CreateRuntimeAsyncCompilation(code, options: TestOptions.DebugExe.WithDebugPlusMode(true).WithSpecificDiagnosticOptions("SYSLIB5007", ReportDiagnostic.Suppress)); + verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("42", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = $$""" + {{ReturnValueMissing("
$", "0x12")}} + """ + }); + verifier.VerifyIL("C.Handler()", """ + { + // Code size 30 (0x1e) + .maxstack 1 + .locals init (System.Threading.Tasks.Task V_0, + IntegerException V_1) //ex + IL_0000: nop + .try + { + IL_0001: nop + IL_0002: ldc.i4.s 42 + IL_0004: call "System.Threading.Tasks.Task C.Throw(int)" + IL_0009: stloc.0 + IL_000a: leave.s IL_001c + } + catch IntegerException + { + IL_000c: stloc.1 + IL_000d: nop + IL_000e: ldloc.1 + IL_000f: callvirt "int IntegerException.Value.get" + IL_0014: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" + IL_0019: stloc.0 + IL_001a: leave.s IL_001c + } + IL_001c: ldloc.0 + IL_001d: ret + } + """); + } + + [Fact] + public void TaskDerivedType() + { + var source = """ + using System; + using System.Threading.Tasks; + + await M(); + + DerivedTask M() => new DerivedTask(); + + class DerivedTask : Task + { + public DerivedTask() : base(() => { }) { } + } + """; + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [
$]: Return value missing on the stack. { Offset = 0xa } + """ + }); + verifier.VerifyIL("", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "DerivedTask Program.<
$>g__M|0_0()" + IL_0005: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: ret + } + """); + } + + [Fact] + public void TaskNonReferenceConversion() + { + var source = """ + using System; + using System.Threading.Tasks; + + await M(); + + DerivedTask M() => new DerivedTask(); + + class DerivedTask + { + public static implicit operator Task(DerivedTask d) => throw null!; + + private Task task; + + public DerivedTask() + { + task = Task.CompletedTask; + } + + public System.Runtime.CompilerServices.TaskAwaiter GetAwaiter() => task.GetAwaiter(); + } + """; + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [
$]: Return value missing on the stack. { Offset = 0x21 } + """ + }); + verifier.VerifyIL("", """ + { + // Code size 34 (0x22) + .maxstack 1 + .locals init (System.Runtime.CompilerServices.TaskAwaiter V_0) + IL_0000: call "DerivedTask Program.<
$>g__M|0_0()" + IL_0005: callvirt "System.Runtime.CompilerServices.TaskAwaiter DerivedTask.GetAwaiter()" + IL_000a: stloc.0 + IL_000b: ldloca.s V_0 + IL_000d: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0012: brtrue.s IL_001a + IL_0014: ldloc.0 + IL_0015: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.TaskAwaiter)" + IL_001a: ldloca.s V_0 + IL_001c: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0021: ret + } + """); + } + + [Fact] + public void TaskTDerivedType() + { + var source = """ + using System; + using System.Threading.Tasks; + + await M("1"); + + DerivedTask M(T t) => new DerivedTask(t); + + class DerivedTask : Task + { + public DerivedTask(T t) : base(() => t) { } + } + """; + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("1", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = $$""" + [
$]: Return value missing on the stack. { Offset = 0x10 } + """ + }); + verifier.VerifyIL("", """ + { + // Code size 17 (0x11) + .maxstack 1 + IL_0000: ldstr "1" + IL_0005: call "DerivedTask Program.<
$>g__M|0_0(string)" + IL_000a: call "string System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: pop + IL_0010: ret + } + """); + } + + [Fact] + public void TaskTNonReferenceConversion() + { + var source = """ + using System; + using System.Threading.Tasks; + + Console.WriteLine(await M("1")); + + DerivedTask M(T t) => new DerivedTask(t); + + class DerivedTask + { + public static implicit operator Task(DerivedTask d) => throw null!; + + private Task task; + + public DerivedTask(T t) + { + task = Task.FromResult(t); + } + + public System.Runtime.CompilerServices.TaskAwaiter GetAwaiter() => task.GetAwaiter(); + } + """; + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("1", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [
$]: Return value missing on the stack. { Offset = 0x2b } + """ + }); + verifier.VerifyIL("", """ + { + // Code size 44 (0x2c) + .maxstack 1 + .locals init (System.Runtime.CompilerServices.TaskAwaiter V_0) + IL_0000: ldstr "1" + IL_0005: call "DerivedTask Program.<
$>g__M|0_0(string)" + IL_000a: callvirt "System.Runtime.CompilerServices.TaskAwaiter DerivedTask.GetAwaiter()" + IL_000f: stloc.0 + IL_0010: ldloca.s V_0 + IL_0012: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0017: brtrue.s IL_001f + IL_0019: ldloc.0 + IL_001a: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter>(System.Runtime.CompilerServices.TaskAwaiter)" + IL_001f: ldloca.s V_0 + IL_0021: call "string System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0026: call "void System.Console.WriteLine(string)" + IL_002b: ret + } + """); + } + + [Fact] + public void ExperimentalDiagnosticsReportedOnAsyncHelpers() + { + var code = """ + using System.Threading.Tasks; + + await Task.CompletedTask; + await Task.CompletedTask.ConfigureAwait(false); + await Task.FromResult(1); + await Task.FromResult(1).ConfigureAwait(false); + await default(ValueTask); + await default(ValueTask).ConfigureAwait(false); + await new ValueTask(1); + await new ValueTask(1).ConfigureAwait(false); + await Task.Yield(); + """; + + var comp = CreateRuntimeAsyncCompilation(code, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics( + // (3,7): error SYSLIB5007: 'System.Runtime.CompilerServices.AsyncHelpers' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + // await Task.CompletedTask; + Diagnostic("SYSLIB5007", "Task.CompletedTask").WithArguments("System.Runtime.CompilerServices.AsyncHelpers").WithLocation(3, 7).WithWarningAsError(true), + // (4,7): error SYSLIB5007: 'System.Runtime.CompilerServices.AsyncHelpers' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + // await Task.CompletedTask.ConfigureAwait(false); + Diagnostic("SYSLIB5007", "Task.CompletedTask.ConfigureAwait(false)").WithArguments("System.Runtime.CompilerServices.AsyncHelpers").WithLocation(4, 7).WithWarningAsError(true), + // (5,7): error SYSLIB5007: 'System.Runtime.CompilerServices.AsyncHelpers' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + // await Task.FromResult(1).ConfigureAwait(false); + Diagnostic("SYSLIB5007", "Task.FromResult(1)").WithArguments("System.Runtime.CompilerServices.AsyncHelpers").WithLocation(5, 7).WithWarningAsError(true), + // (6,7): error SYSLIB5007: 'System.Runtime.CompilerServices.AsyncHelpers' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + // await Task.FromResult(1); + Diagnostic("SYSLIB5007", "Task.FromResult(1).ConfigureAwait(false)").WithArguments("System.Runtime.CompilerServices.AsyncHelpers").WithLocation(6, 7).WithWarningAsError(true), + // (7,7): error SYSLIB5007: 'System.Runtime.CompilerServices.AsyncHelpers' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + // await default(ValueTask); + Diagnostic("SYSLIB5007", "default(ValueTask)").WithArguments("System.Runtime.CompilerServices.AsyncHelpers").WithLocation(7, 7).WithWarningAsError(true), + // (8,7): error SYSLIB5007: 'System.Runtime.CompilerServices.AsyncHelpers' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + // await default(ValueTask).ConfigureAwait(false); + Diagnostic("SYSLIB5007", "default(ValueTask).ConfigureAwait(false)").WithArguments("System.Runtime.CompilerServices.AsyncHelpers").WithLocation(8, 7).WithWarningAsError(true), + // (9,7): error SYSLIB5007: 'System.Runtime.CompilerServices.AsyncHelpers' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + // await new ValueTask(1); + Diagnostic("SYSLIB5007", "new ValueTask(1)").WithArguments("System.Runtime.CompilerServices.AsyncHelpers").WithLocation(9, 7).WithWarningAsError(true), + // (10,7): error SYSLIB5007: 'System.Runtime.CompilerServices.AsyncHelpers' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + // await new ValueTask(1).ConfigureAwait(false); + Diagnostic("SYSLIB5007", "new ValueTask(1).ConfigureAwait(false)").WithArguments("System.Runtime.CompilerServices.AsyncHelpers").WithLocation(10, 7).WithWarningAsError(true), + // (11,7): error SYSLIB5007: 'System.Runtime.CompilerServices.AsyncHelpers' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + // await Task.Yield(); + Diagnostic("SYSLIB5007", "Task.Yield()").WithArguments("System.Runtime.CompilerServices.AsyncHelpers").WithLocation(11, 7).WithWarningAsError(true) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs index 52fe5242f2174..2f9a80ae2d516 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs @@ -173,13 +173,190 @@ public async ValueTask DisposeAsync() } } "; - var comp_checked = CreateCompilationWithTasksExtensions(new[] { source.Replace("REPLACE", "checked"), s_IAsyncEnumerable }, options: TestOptions.DebugExe); + var checkedSource = source.Replace("REPLACE", "checked"); + var comp_checked = CreateCompilationWithTasksExtensions(new[] { checkedSource, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp_checked.VerifyDiagnostics(); CompileAndVerify(comp_checked, expectedOutput: "overflow"); - var comp_unchecked = CreateCompilationWithTasksExtensions(new[] { source.Replace("REPLACE", "unchecked"), s_IAsyncEnumerable }, options: TestOptions.DebugExe); + var uncheckedSource = source.Replace("REPLACE", "unchecked"); + var comp_unchecked = CreateCompilationWithTasksExtensions(new[] { uncheckedSource, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp_unchecked.VerifyDiagnostics(); CompileAndVerify(comp_unchecked, expectedOutput: "0xFFFFFFFF"); + + var runtimeAsyncCompChecked = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(checkedSource); + var verifierChecked = CompileAndVerify(runtimeAsyncCompChecked, expectedOutput: CodeGenAsyncTests.ExpectedOutput("overflow", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x31, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifierChecked.VerifyIL("C.Main()", """ + { + // Code size 150 (0x96) + .maxstack 3 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_004e + IL_0018: ldloc.0 + IL_0019: callvirt "uint System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: conv.ovf.i4.un + IL_001f: stloc.3 + IL_0020: ldc.i4.2 + IL_0021: ldc.i4.1 + IL_0022: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0027: stloc.s V_4 + IL_0029: ldloca.s V_4 + IL_002b: ldstr "0x" + IL_0030: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0035: ldloca.s V_4 + IL_0037: ldloc.3 + IL_0038: ldstr "X8" + IL_003d: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, string)" + IL_0042: ldloca.s V_4 + IL_0044: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0049: call "void System.Console.Write(string)" + IL_004e: ldloc.0 + IL_004f: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0054: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0059: brtrue.s IL_0018 + IL_005b: leave.s IL_0060 + } + catch object + { + IL_005d: stloc.2 + IL_005e: leave.s IL_0060 + } + IL_0060: ldloc.0 + IL_0061: brfalse.s IL_006e + IL_0063: ldloc.0 + IL_0064: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0069: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_006e: ldloc.2 + IL_006f: brfalse.s IL_0086 + IL_0071: ldloc.2 + IL_0072: isinst "System.Exception" + IL_0077: dup + IL_0078: brtrue.s IL_007c + IL_007a: ldloc.2 + IL_007b: throw + IL_007c: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0081: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0086: leave.s IL_0095 + } + catch System.OverflowException + { + IL_0088: pop + IL_0089: ldstr "overflow" + IL_008e: call "void System.Console.Write(string)" + IL_0093: leave.s IL_0095 + } + IL_0095: ret + } + """); + + var runtimeAsyncCompUnchecked = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(uncheckedSource); + var verifierUnchecked = CompileAndVerify(runtimeAsyncCompUnchecked, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0xFFFFFFFF", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x94 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x31, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifierUnchecked.VerifyIL("C.Main()", """ + { + // Code size 149 (0x95) + .maxstack 3 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_004d + IL_0018: ldloc.0 + IL_0019: callvirt "uint System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldc.i4.2 + IL_0020: ldc.i4.1 + IL_0021: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0026: stloc.s V_4 + IL_0028: ldloca.s V_4 + IL_002a: ldstr "0x" + IL_002f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0034: ldloca.s V_4 + IL_0036: ldloc.3 + IL_0037: ldstr "X8" + IL_003c: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, string)" + IL_0041: ldloca.s V_4 + IL_0043: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0048: call "void System.Console.Write(string)" + IL_004d: ldloc.0 + IL_004e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0053: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0058: brtrue.s IL_0018 + IL_005a: leave.s IL_005f + } + catch object + { + IL_005c: stloc.2 + IL_005d: leave.s IL_005f + } + IL_005f: ldloc.0 + IL_0060: brfalse.s IL_006d + IL_0062: ldloc.0 + IL_0063: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0068: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_006d: ldloc.2 + IL_006e: brfalse.s IL_0085 + IL_0070: ldloc.2 + IL_0071: isinst "System.Exception" + IL_0076: dup + IL_0077: brtrue.s IL_007b + IL_0079: ldloc.2 + IL_007a: throw + IL_007b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0080: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0085: leave.s IL_0094 + } + catch System.OverflowException + { + IL_0087: pop + IL_0088: ldstr "overflow" + IL_008d: call "void System.Console.Write(string)" + IL_0092: leave.s IL_0094 + } + IL_0094: ret + } + """); } [Fact] @@ -816,6 +993,39 @@ public int Current var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync 1"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync 1", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x4f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 44 (0x2c) + .maxstack 2 + .locals init (C.Enumerator V_0, + System.Threading.CancellationToken V_1) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.Enumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: br.s IL_001d + IL_0016: ldloc.0 + IL_0017: callvirt "int C.Enumerator.Current.get" + IL_001c: pop + IL_001d: ldloc.0 + IL_001e: ldc.i4.1 + IL_001f: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync(int)" + IL_0024: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0029: brtrue.s IL_0016 + IL_002b: ret + } + """); } [Fact] @@ -851,6 +1061,39 @@ public int Current var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync 0"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync 0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2f } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x51, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 48 (0x30) + .maxstack 2 + .locals init (C.Enumerator V_0, + System.Threading.CancellationToken V_1) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.Enumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: br.s IL_001d + IL_0016: ldloc.0 + IL_0017: callvirt "int C.Enumerator.Current.get" + IL_001c: pop + IL_001d: ldloc.0 + IL_001e: call "int[] System.Array.Empty()" + IL_0023: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync(params int[])" + IL_0028: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002d: brtrue.s IL_0016 + IL_002f: ret + } + """); } [Fact] @@ -1038,8 +1281,88 @@ class Element var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); + string expectedOutput = "NextAsync(0) Current(1) Convert(1) Got(1) NextAsync(1) Current(2) Convert(2) Got(2) NextAsync(2) Current(3) Convert(3) Got(3) NextAsync(3) Dispose(4)"; CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(1) Convert(1) Got(1) NextAsync(1) Current(2) Convert(2) Got(2) NextAsync(2) Current(3) Convert(3) Got(3) NextAsync(3) Dispose(4)"); + expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x91 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x5f } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 146 (0x92) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + Element V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0059 + IL_0018: ldloc.0 + IL_0019: callvirt "int C.AsyncEnumerator.Current.get" + IL_001e: call "Element Element.op_Explicit(int)" + IL_0023: stloc.3 + IL_0024: ldc.i4.6 + IL_0025: ldc.i4.1 + IL_0026: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_002b: stloc.s V_4 + IL_002d: ldloca.s V_4 + IL_002f: ldstr "Got(" + IL_0034: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0039: ldloca.s V_4 + IL_003b: ldloc.3 + IL_003c: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(Element)" + IL_0041: ldloca.s V_4 + IL_0043: ldstr ") " + IL_0048: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_004d: ldloca.s V_4 + IL_004f: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0054: call "void System.Console.Write(string)" + IL_0059: ldloc.0 + IL_005a: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_005f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0064: brtrue.s IL_0018 + IL_0066: leave.s IL_006b + } + catch object + { + IL_0068: stloc.2 + IL_0069: leave.s IL_006b + } + IL_006b: ldloc.0 + IL_006c: brfalse.s IL_0079 + IL_006e: ldloc.0 + IL_006f: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0074: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0079: ldloc.2 + IL_007a: brfalse.s IL_0091 + IL_007c: ldloc.2 + IL_007d: isinst "System.Exception" + IL_0082: dup + IL_0083: brtrue.s IL_0087 + IL_0085: ldloc.2 + IL_0086: throw + IL_0087: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_008c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0091: ret + } + """); } [Fact] @@ -1082,6 +1405,99 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Got(1) Got(2) Captured(1)"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("Got(1) Got(2) Captured(1)", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xb8 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x21, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 185 (0xb9) + .maxstack 2 + .locals init (System.Action V_0, //f + C.AsyncEnumerator V_1, + System.Threading.CancellationToken V_2, + object V_3, + C.<>c__DisplayClass0_0 V_4, //CS$<>8__locals0 + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_5) + IL_0000: ldnull + IL_0001: stloc.0 + IL_0002: newobj "C..ctor()" + IL_0007: ldloca.s V_2 + IL_0009: initobj "System.Threading.CancellationToken" + IL_000f: ldloc.2 + IL_0010: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0015: stloc.1 + IL_0016: ldnull + IL_0017: stloc.3 + .try + { + IL_0018: br.s IL_007a + IL_001a: newobj "C.<>c__DisplayClass0_0..ctor()" + IL_001f: stloc.s V_4 + IL_0021: ldloc.s V_4 + IL_0023: ldloc.1 + IL_0024: callvirt "int C.AsyncEnumerator.Current.get" + IL_0029: stfld "int C.<>c__DisplayClass0_0.i" + IL_002e: ldc.i4.6 + IL_002f: ldc.i4.1 + IL_0030: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0035: stloc.s V_5 + IL_0037: ldloca.s V_5 + IL_0039: ldstr "Got(" + IL_003e: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0043: ldloca.s V_5 + IL_0045: ldloc.s V_4 + IL_0047: ldfld "int C.<>c__DisplayClass0_0.i" + IL_004c: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0051: ldloca.s V_5 + IL_0053: ldstr ") " + IL_0058: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_005d: ldloca.s V_5 + IL_005f: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0064: call "void System.Console.Write(string)" + IL_0069: ldloc.0 + IL_006a: brtrue.s IL_007a + IL_006c: ldloc.s V_4 + IL_006e: ldftn "void C.<>c__DisplayClass0_0.
b__0()" + IL_0074: newobj "System.Action..ctor(object, System.IntPtr)" + IL_0079: stloc.0 + IL_007a: ldloc.1 + IL_007b: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_0080: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0085: brtrue.s IL_001a + IL_0087: leave.s IL_008c + } + catch object + { + IL_0089: stloc.3 + IL_008a: leave.s IL_008c + } + IL_008c: ldloc.1 + IL_008d: brfalse.s IL_009a + IL_008f: ldloc.1 + IL_0090: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0095: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_009a: ldloc.3 + IL_009b: brfalse.s IL_00b2 + IL_009d: ldloc.3 + IL_009e: isinst "System.Exception" + IL_00a3: dup + IL_00a4: brtrue.s IL_00a8 + IL_00a6: ldloc.3 + IL_00a7: throw + IL_00a8: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00ad: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00b2: ldloc.0 + IL_00b3: callvirt "void System.Action.Invoke()" + IL_00b8: ret + } + """); } [Fact] @@ -1138,7 +1554,87 @@ public async ValueTask DisposeAsync() }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(1) Current(1) Got(1) NextAsync(2) Current(2) Got(2) NextAsync(3) Current(3) Got(3) NextAsync(4) Dispose(4)"); + var expectedOutput = "NextAsync(1) Current(1) Got(1) NextAsync(2) Current(2) Got(2) NextAsync(3) Current(3) Got(3) NextAsync(4) Dispose(4)"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x91 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x5f } + """ + }); + verifier.VerifyIL("Program.Main()", """ + { + // Code size 146 (0x92) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + IntContainer V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0059 + IL_0018: ldloc.0 + IL_0019: callvirt "IntContainer C.AsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldc.i4.6 + IL_0020: ldc.i4.1 + IL_0021: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0026: stloc.s V_4 + IL_0028: ldloca.s V_4 + IL_002a: ldstr "Got(" + IL_002f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0034: ldloca.s V_4 + IL_0036: ldloc.3 + IL_0037: callvirt "int IntContainer.Value.get" + IL_003c: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0041: ldloca.s V_4 + IL_0043: ldstr ") " + IL_0048: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_004d: ldloca.s V_4 + IL_004f: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0054: call "void System.Console.Write(string)" + IL_0059: ldloc.0 + IL_005a: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_005f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0064: brtrue.s IL_0018 + IL_0066: leave.s IL_006b + } + catch object + { + IL_0068: stloc.2 + IL_0069: leave.s IL_006b + } + IL_006b: ldloc.0 + IL_006c: brfalse.s IL_0079 + IL_006e: ldloc.0 + IL_006f: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0074: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0079: ldloc.2 + IL_007a: brfalse.s IL_0091 + IL_007c: ldloc.2 + IL_007d: isinst "System.Exception" + IL_0082: dup + IL_0083: brtrue.s IL_0087 + IL_0085: ldloc.2 + IL_0086: throw + IL_0087: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_008c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0091: ret + } + """); } [Fact] @@ -1179,6 +1675,77 @@ public ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "exception"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("exception", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x67 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2) + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0021 + IL_0018: ldloc.0 + IL_0019: callvirt "int C.AsyncEnumerator.Current.get" + IL_001e: pop + IL_001f: ldnull + IL_0020: throw + IL_0021: ldloc.0 + IL_0022: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_0027: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002c: brtrue.s IL_0018 + IL_002e: leave.s IL_0033 + } + catch object + { + IL_0030: stloc.2 + IL_0031: leave.s IL_0033 + } + IL_0033: ldloc.0 + IL_0034: brfalse.s IL_0041 + IL_0036: ldloc.0 + IL_0037: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_003c: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0041: ldloc.2 + IL_0042: brfalse.s IL_0059 + IL_0044: ldloc.2 + IL_0045: isinst "System.Exception" + IL_004a: dup + IL_004b: brtrue.s IL_004f + IL_004d: ldloc.2 + IL_004e: throw + IL_004f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0054: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0059: ldnull + IL_005a: throw + } + catch System.ArgumentException + { + IL_005b: callvirt "string System.Exception.Message.get" + IL_0060: call "void System.Console.Write(string)" + IL_0065: leave.s IL_0067 + } + IL_0067: ret + } + """); } [Fact] @@ -1222,6 +1789,78 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "dispose exception"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("dispose exception", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x67 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2) + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0021 + IL_0018: ldloc.0 + IL_0019: callvirt "int C.AsyncEnumerator.Current.get" + IL_001e: pop + IL_001f: ldnull + IL_0020: throw + IL_0021: ldloc.0 + IL_0022: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_0027: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002c: brtrue.s IL_0018 + IL_002e: leave.s IL_0033 + } + catch object + { + IL_0030: stloc.2 + IL_0031: leave.s IL_0033 + } + IL_0033: ldloc.0 + IL_0034: brfalse.s IL_0041 + IL_0036: ldloc.0 + IL_0037: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_003c: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0041: ldloc.2 + IL_0042: brfalse.s IL_0059 + IL_0044: ldloc.2 + IL_0045: isinst "System.Exception" + IL_004a: dup + IL_004b: brtrue.s IL_004f + IL_004d: ldloc.2 + IL_004e: throw + IL_004f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0054: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0059: ldnull + IL_005a: throw + } + catch System.ArgumentException + { + IL_005b: callvirt "string System.Exception.Message.get" + IL_0060: call "void System.Console.Write(string)" + IL_0065: leave.s IL_0067 + } + IL_0067: ret + } + """); } [Fact] @@ -1269,6 +1908,75 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "wait dispose exception"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("wait dispose exception", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5e } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 95 (0x5f) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + object V_1) + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0018 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.AsyncEnumerator.Current.get" + IL_0015: pop + IL_0016: ldnull + IL_0017: throw + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000f + IL_0025: leave.s IL_002a + } + catch object + { + IL_0027: stloc.1 + IL_0028: leave.s IL_002a + } + IL_002a: ldloc.0 + IL_002b: brfalse.s IL_0038 + IL_002d: ldloc.0 + IL_002e: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0033: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0038: ldloc.1 + IL_0039: brfalse.s IL_0050 + IL_003b: ldloc.1 + IL_003c: isinst "System.Exception" + IL_0041: dup + IL_0042: brtrue.s IL_0046 + IL_0044: ldloc.1 + IL_0045: throw + IL_0046: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004b: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0050: ldnull + IL_0051: throw + } + catch System.ArgumentException + { + IL_0052: callvirt "string System.Exception.Message.get" + IL_0057: call "void System.Console.Write(string)" + IL_005c: leave.s IL_005e + } + IL_005e: ret + } + """); } [Fact] @@ -1312,6 +2020,74 @@ public ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "wait exception"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("wait exception", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5e } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 95 (0x5f) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + object V_1) + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0018 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.AsyncEnumerator.Current.get" + IL_0015: pop + IL_0016: ldnull + IL_0017: throw + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000f + IL_0025: leave.s IL_002a + } + catch object + { + IL_0027: stloc.1 + IL_0028: leave.s IL_002a + } + IL_002a: ldloc.0 + IL_002b: brfalse.s IL_0038 + IL_002d: ldloc.0 + IL_002e: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0033: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0038: ldloc.1 + IL_0039: brfalse.s IL_0050 + IL_003b: ldloc.1 + IL_003c: isinst "System.Exception" + IL_0041: dup + IL_0042: brtrue.s IL_0046 + IL_0044: ldloc.1 + IL_0045: throw + IL_0046: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004b: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0050: ldnull + IL_0051: throw + } + catch System.ArgumentException + { + IL_0052: callvirt "string System.Exception.Message.get" + IL_0057: call "void System.Console.Write(string)" + IL_005c: leave.s IL_005e + } + IL_005e: ret + } + """); } [Fact] @@ -1328,10 +2104,13 @@ public static async System.Threading.Tasks.Task Main() } }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }); - comp.VerifyDiagnostics( - // (6,33): error CS8416: Cannot use a collection of dynamic type in an asynchronous foreach - // await foreach (var i in (dynamic)new C()) - Diagnostic(ErrorCode.ERR_BadDynamicAwaitForEach, "(dynamic)new C()").WithLocation(6, 33)); + // (6,33): error CS8416: Cannot use a collection of dynamic type in an asynchronous foreach + // await foreach (var i in (dynamic)new C()) + DiagnosticDescription expected = Diagnostic(ErrorCode.ERR_BadDynamicAwaitForEach, "(dynamic)new C()").WithLocation(6, 33); + comp.VerifyDiagnostics(expected); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expected); } [Fact] @@ -1594,6 +2373,34 @@ public async System.Threading.Tasks.Task MoveNextAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x21 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 34 (0x22) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_0014 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: pop + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_000d + IL_0021: ret + } + """); } [Fact] @@ -1762,9 +2569,10 @@ public Task MoveNextAsync() public ref int Current => ref _array[_index]; } } - """ + AsyncStreamsTypes; + """; - var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12); + CSharpTestSource sources = [source, AsyncStreamsTypes]; + var comp = CreateCompilationWithTasksExtensions(sources, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // (17,41): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater. // await foreach (ref readonly var i in new C()) @@ -1772,11 +2580,69 @@ public Task MoveNextAsync() var expectedOutput = "123"; - comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); + comp = CreateCompilationWithTasksExtensions(sources, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); - comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + comp = CreateCompilationWithTasksExtensions(sources, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5b } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2) + IL_0000: call "System.Collections.Generic.IAsyncEnumerable C.F()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0023 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: call "void System.Console.Write(int)" + IL_0023: ldloc.0 + IL_0024: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0029: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002e: brtrue.s IL_0018 + IL_0030: leave.s IL_0035 + } + catch object + { + IL_0032: stloc.2 + IL_0033: leave.s IL_0035 + } + IL_0035: ldloc.0 + IL_0036: brfalse.s IL_0043 + IL_0038: ldloc.0 + IL_0039: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_003e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0043: ldloc.2 + IL_0044: brfalse.s IL_005b + IL_0046: ldloc.2 + IL_0047: isinst "System.Exception" + IL_004c: dup + IL_004d: brtrue.s IL_0051 + IL_004f: ldloc.2 + IL_0050: throw + IL_0051: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0056: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005b: ret + } + """); } [Fact] @@ -2059,6 +2925,70 @@ public S(int i) var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "1 2 Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("1 2 Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x6e } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x21, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 111 (0x6f) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1, + int V_2) //s + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_002c + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: stloc.2 + IL_0016: ldloca.s V_2 + IL_0018: call "string int.ToString()" + IL_001d: ldstr " " + IL_0022: call "string string.Concat(string, string)" + IL_0027: call "void System.Console.Write(string)" + IL_002c: ldloc.0 + IL_002d: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0032: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0037: brtrue.s IL_000f + IL_0039: leave.s IL_003e + } + catch object + { + IL_003b: stloc.1 + IL_003c: leave.s IL_003e + } + IL_003e: ldloc.0 + IL_003f: brfalse.s IL_004c + IL_0041: ldloc.0 + IL_0042: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.DisposeAsync()" + IL_0047: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_004c: ldloc.1 + IL_004d: brfalse.s IL_0064 + IL_004f: ldloc.1 + IL_0050: isinst "System.Exception" + IL_0055: dup + IL_0056: brtrue.s IL_005a + IL_0058: ldloc.1 + IL_0059: throw + IL_005a: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_005f: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0064: ldstr "Done" + IL_0069: call "void System.Console.Write(string)" + IL_006e: ret + } + """); } [Theory] @@ -2232,6 +3162,68 @@ public async Task MoveNextAsync() """ + s_IAsyncEnumerable; var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "1 2 Done").VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("1 2 Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x7d } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x3b, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 126 (0x7e) + .maxstack 3 + .locals init (C.Enumerator V_0, + C.Enumerable V_1, + int V_2, //x + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_3, + System.Runtime.CompilerServices.YieldAwaitable V_4, + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_5) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "C.Enumerable" + IL_0009: call "C.Enumerator C.Enumerable.GetAsyncEnumerator()" + IL_000e: stloc.0 + IL_000f: br.s IL_0066 + IL_0011: ldloc.0 + IL_0012: callvirt "int C.Enumerator.Current.get" + IL_0017: stloc.2 + IL_0018: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_001d: stloc.s V_4 + IL_001f: ldloca.s V_4 + IL_0021: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0026: stloc.3 + IL_0027: ldloca.s V_3 + IL_0029: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_002e: brtrue.s IL_0036 + IL_0030: ldloc.3 + IL_0031: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0036: ldloca.s V_3 + IL_0038: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_003d: ldloca.s V_5 + IL_003f: ldc.i4.1 + IL_0040: ldc.i4.1 + IL_0041: call "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0046: ldloca.s V_5 + IL_0048: ldloc.2 + IL_0049: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_004e: ldloca.s V_5 + IL_0050: ldstr " " + IL_0055: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_005a: ldloca.s V_5 + IL_005c: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0061: call "void System.Console.Write(string)" + IL_0066: ldloc.0 + IL_0067: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_006c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0071: brtrue.s IL_0011 + IL_0073: ldstr "Done" + IL_0078: call "void System.Console.Write(string)" + IL_007d: ret + } + """); } [Fact] @@ -2276,9 +3268,85 @@ public async Task MoveNextAsync() } } } - """ + AsyncStreamsTypes; - var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + """; + var comp = CreateCompilationWithTasksExtensions([source, AsyncStreamsTypes], options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "2 4 -1 Done").VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("2 4 -1 Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x8a } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x3b, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 139 (0x8b) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + IL_0000: call "System.Collections.Generic.IAsyncEnumerable C.M()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0048 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldc.i4.1 + IL_0020: ldc.i4.1 + IL_0021: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0026: stloc.s V_4 + IL_0028: ldloca.s V_4 + IL_002a: ldloc.3 + IL_002b: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0030: ldloca.s V_4 + IL_0032: ldstr " " + IL_0037: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_003c: ldloca.s V_4 + IL_003e: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0043: call "void System.Console.Write(string)" + IL_0048: ldloc.0 + IL_0049: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_004e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0053: brtrue.s IL_0018 + IL_0055: leave.s IL_005a + } + catch object + { + IL_0057: stloc.2 + IL_0058: leave.s IL_005a + } + IL_005a: ldloc.0 + IL_005b: brfalse.s IL_0068 + IL_005d: ldloc.0 + IL_005e: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0063: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0068: ldloc.2 + IL_0069: brfalse.s IL_0080 + IL_006b: ldloc.2 + IL_006c: isinst "System.Exception" + IL_0071: dup + IL_0072: brtrue.s IL_0076 + IL_0074: ldloc.2 + IL_0075: throw + IL_0076: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_007b: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0080: ldstr "Done" + IL_0085: call "void System.Console.Write(string)" + IL_008a: ret + } + """); } [Fact] @@ -2384,6 +3452,72 @@ public S(int i) comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x74 } + [get_Current]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xb } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x3b, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 117 (0x75) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1, + S V_2) //s + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0032 + IL_000f: ldloc.0 + IL_0010: callvirt "S C.Enumerator.Current.get" + IL_0015: stloc.2 + IL_0016: ldloca.s V_2 + IL_0018: constrained. "S" + IL_001e: callvirt "string object.ToString()" + IL_0023: ldstr " " + IL_0028: call "string string.Concat(string, string)" + IL_002d: call "void System.Console.Write(string)" + IL_0032: ldloc.0 + IL_0033: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0038: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003d: brtrue.s IL_000f + IL_003f: leave.s IL_0044 + } + catch object + { + IL_0041: stloc.1 + IL_0042: leave.s IL_0044 + } + IL_0044: ldloc.0 + IL_0045: brfalse.s IL_0052 + IL_0047: ldloc.0 + IL_0048: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.DisposeAsync()" + IL_004d: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0052: ldloc.1 + IL_0053: brfalse.s IL_006a + IL_0055: ldloc.1 + IL_0056: isinst "System.Exception" + IL_005b: dup + IL_005c: brtrue.s IL_0060 + IL_005e: ldloc.1 + IL_005f: throw + IL_0060: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0065: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_006a: ldstr "Done" + IL_006f: call "void System.Console.Write(string)" + IL_0074: ret + } + """); } [Fact] @@ -2437,7 +3571,7 @@ public S(int i) } public override string ToString() => i.ToString(); } - """ + AsyncStreamsTypes; + """; var expectedDiagnostics = new[] { @@ -2446,15 +3580,85 @@ public S(int i) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "var").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(16, 24) }; - CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + CSharpTestSource sources = [source, AsyncStreamsTypes]; + CreateCompilationWithTasksExtensions(sources, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); var expectedOutput = "M:1 M:2 M:Done MainDone"; - var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular13, options: TestOptions.ReleaseExe); + var comp = CreateCompilationWithTasksExtensions(sources, parseOptions: TestOptions.Regular13, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); - comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + comp = CreateCompilationWithTasksExtensions(sources, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x76 } + [get_Current]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xb } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x3b, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 119 (0x77) + .maxstack 3 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + string V_3) //s + IL_0000: call "System.Collections.Generic.IAsyncEnumerable C.M()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0034 + IL_0018: ldloc.0 + IL_0019: callvirt "string System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldstr "M:" + IL_0024: ldloc.3 + IL_0025: ldstr " " + IL_002a: call "string string.Concat(string, string, string)" + IL_002f: call "void System.Console.Write(string)" + IL_0034: ldloc.0 + IL_0035: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_003a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003f: brtrue.s IL_0018 + IL_0041: leave.s IL_0046 + } + catch object + { + IL_0043: stloc.2 + IL_0044: leave.s IL_0046 + } + IL_0046: ldloc.0 + IL_0047: brfalse.s IL_0054 + IL_0049: ldloc.0 + IL_004a: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_004f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0054: ldloc.2 + IL_0055: brfalse.s IL_006c + IL_0057: ldloc.2 + IL_0058: isinst "System.Exception" + IL_005d: dup + IL_005e: brtrue.s IL_0062 + IL_0060: ldloc.2 + IL_0061: throw + IL_0062: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0067: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_006c: ldstr "MainDone" + IL_0071: call "void System.Console.Write(string)" + IL_0076: ret + } + """); } [Fact] @@ -2561,6 +3765,38 @@ public override string ToString() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "1 2 3 Done", verify: Verification.Fails); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("1 2 3 Done", isRuntimeAsync: true), verify: Verification.Fails); + verifier.VerifyIL("C.Main()", """ + { + // Code size 77 (0x4d) + .maxstack 2 + .locals init (C.Enumerator V_0, + S V_1) //s + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_0035 + IL_000d: ldloc.0 + IL_000e: callvirt "ref S C.Enumerator.Current.get" + IL_0013: ldobj "S" + IL_0018: stloc.1 + IL_0019: ldloca.s V_1 + IL_001b: constrained. "S" + IL_0021: callvirt "string object.ToString()" + IL_0026: ldstr " " + IL_002b: call "string string.Concat(string, string)" + IL_0030: call "void System.Console.Write(string)" + IL_0035: ldloc.0 + IL_0036: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_003b: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0040: brtrue.s IL_000d + IL_0042: ldstr "Done" + IL_0047: call "void System.Console.Write(string)" + IL_004c: ret + } + """); } [Fact] @@ -2616,6 +3852,59 @@ public S(int i) comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x64 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x4f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 101 (0x65) + .maxstack 3 + .locals init (C.Enumerator V_0, + S& V_1, //s + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_2) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_004d + IL_000d: ldloc.0 + IL_000e: callvirt "ref S C.Enumerator.Current.get" + IL_0013: stloc.1 + IL_0014: ldloca.s V_2 + IL_0016: ldc.i4.1 + IL_0017: ldc.i4.1 + IL_0018: call "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_001d: ldloca.s V_2 + IL_001f: ldloc.1 + IL_0020: ldobj "S" + IL_0025: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(S)" + IL_002a: ldloca.s V_2 + IL_002c: ldstr " " + IL_0031: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0036: ldloca.s V_2 + IL_0038: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_003d: call "void System.Console.Write(string)" + IL_0042: ldloc.1 + IL_0043: ldflda "int S.F" + IL_0048: dup + IL_0049: ldind.i4 + IL_004a: ldc.i4.1 + IL_004b: add + IL_004c: stind.i4 + IL_004d: ldloc.0 + IL_004e: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0053: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0058: brtrue.s IL_000d + IL_005a: ldstr "Done" + IL_005f: call "void System.Console.Write(string)" + IL_0064: ret + } + """); } [Fact] @@ -2666,20 +3955,88 @@ public S(int i) } public override string ToString() => F.ToString(); } - """ + AsyncStreamsTypes; + """; - CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + CSharpTestSource sources = [source, AsyncStreamsTypes]; + CreateCompilationWithTasksExtensions(sources, parseOptions: TestOptions.Regular12).VerifyDiagnostics( // (16,32): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater. // await foreach (ref var s in new C()) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "s").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(16, 32)); var expectedOutput = "M:2 M:4 M:Done MainDone"; - var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular13, options: TestOptions.ReleaseExe); + var comp = CreateCompilationWithTasksExtensions(sources, parseOptions: TestOptions.Regular13, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); - comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + comp = CreateCompilationWithTasksExtensions(sources, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x76 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x4f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 119 (0x77) + .maxstack 3 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + string V_3) //s + IL_0000: call "System.Collections.Generic.IAsyncEnumerable C.M()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0034 + IL_0018: ldloc.0 + IL_0019: callvirt "string System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldstr "M:" + IL_0024: ldloc.3 + IL_0025: ldstr " " + IL_002a: call "string string.Concat(string, string, string)" + IL_002f: call "void System.Console.Write(string)" + IL_0034: ldloc.0 + IL_0035: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_003a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003f: brtrue.s IL_0018 + IL_0041: leave.s IL_0046 + } + catch object + { + IL_0043: stloc.2 + IL_0044: leave.s IL_0046 + } + IL_0046: ldloc.0 + IL_0047: brfalse.s IL_0054 + IL_0049: ldloc.0 + IL_004a: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_004f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0054: ldloc.2 + IL_0055: brfalse.s IL_006c + IL_0057: ldloc.2 + IL_0058: isinst "System.Exception" + IL_005d: dup + IL_005e: brtrue.s IL_0062 + IL_0060: ldloc.2 + IL_0061: throw + IL_0062: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0067: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_006c: ldstr "MainDone" + IL_0071: call "void System.Console.Write(string)" + IL_0076: ret + } + """); } [Fact] @@ -2911,8 +4268,88 @@ public async ValueTask DisposeAsync() }"; var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(0) Got(1) NextAsync(1) Current(1) Got(2) NextAsync(2) Current(2) Got(3) NextAsync(3) Current(3) Got(4) NextAsync(4) DisposeAsync Done"); + var expectedOutput = "NextAsync(0) Current(0) Got(1) NextAsync(1) Current(1) Got(2) NextAsync(2) Current(2) Got(3) NextAsync(3) Current(3) Got(4) NextAsync(4) DisposeAsync Done"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + runtimeAsyncComp.VerifyEmitDiagnostics( + // (31,33): error CS9328: Method 'C.AsyncEnumerator.MoveNextAsync()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // public async Task MoveNextAsync() + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "MoveNextAsync").WithArguments("C.AsyncEnumerator.MoveNextAsync()").WithLocation(31, 33) + ); + // https://github.com/dotnet/roslyn/issues/79763 + // var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [Main]: Return value missing on the stack. { Offset = 0x8c } + // [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5d, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + // [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + // """ + // }); + // verifier.VerifyIL("C.Main()", """ + // { + // // Code size 141 (0x8d) + // .maxstack 2 + // .locals init (C.AsyncEnumerator V_0, + // object V_1, + // int V_2, //i + // System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3) + // IL_0000: newobj "C..ctor()" + // IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" + // IL_000a: stloc.0 + // IL_000b: ldnull + // IL_000c: stloc.1 + // .try + // { + // IL_000d: br.s IL_004b + // IL_000f: ldloca.s V_0 + // IL_0011: call "int C.AsyncEnumerator.Current.get" + // IL_0016: stloc.2 + // IL_0017: ldc.i4.6 + // IL_0018: ldc.i4.1 + // IL_0019: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + // IL_001e: stloc.3 + // IL_001f: ldloca.s V_3 + // IL_0021: ldstr "Got(" + // IL_0026: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + // IL_002b: ldloca.s V_3 + // IL_002d: ldloc.2 + // IL_002e: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + // IL_0033: ldloca.s V_3 + // IL_0035: ldstr ") " + // IL_003a: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + // IL_003f: ldloca.s V_3 + // IL_0041: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + // IL_0046: call "void System.Console.Write(string)" + // IL_004b: ldloca.s V_0 + // IL_004d: call "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + // IL_0052: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + // IL_0057: brtrue.s IL_000f + // IL_0059: leave.s IL_005e + // } + // catch object + // { + // IL_005b: stloc.1 + // IL_005c: leave.s IL_005e + // } + // IL_005e: ldloca.s V_0 + // IL_0060: call "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + // IL_0065: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + // IL_006a: ldloc.1 + // IL_006b: brfalse.s IL_0082 + // IL_006d: ldloc.1 + // IL_006e: isinst "System.Exception" + // IL_0073: dup + // IL_0074: brtrue.s IL_0078 + // IL_0076: ldloc.1 + // IL_0077: throw + // IL_0078: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + // IL_007d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + // IL_0082: ldstr "Done" + // IL_0087: call "void System.Console.Write(string)" + // IL_008c: ret + // } + // """); } [Fact] @@ -2962,7 +4399,8 @@ public async ValueTask DisposeAsync() }"; var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done"); + var expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -2976,6 +4414,86 @@ public async ValueTask DisposeAsync() var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x96 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x5f } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 151 (0x97) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0054 + IL_0018: ldloc.0 + IL_0019: callvirt "int C.AsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldc.i4.6 + IL_0020: ldc.i4.1 + IL_0021: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0026: stloc.s V_4 + IL_0028: ldloca.s V_4 + IL_002a: ldstr "Got(" + IL_002f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0034: ldloca.s V_4 + IL_0036: ldloc.3 + IL_0037: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_003c: ldloca.s V_4 + IL_003e: ldstr ") " + IL_0043: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0048: ldloca.s V_4 + IL_004a: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_004f: call "void System.Console.Write(string)" + IL_0054: ldloc.0 + IL_0055: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.MoveNextAsync()" + IL_005a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005f: brtrue.s IL_0018 + IL_0061: leave.s IL_0066 + } + catch object + { + IL_0063: stloc.2 + IL_0064: leave.s IL_0066 + } + IL_0066: ldloc.0 + IL_0067: brfalse.s IL_0074 + IL_0069: ldloc.0 + IL_006a: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_006f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0074: ldloc.2 + IL_0075: brfalse.s IL_008c + IL_0077: ldloc.2 + IL_0078: isinst "System.Exception" + IL_007d: dup + IL_007e: brtrue.s IL_0082 + IL_0080: ldloc.2 + IL_0081: throw + IL_0082: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0087: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_008c: ldstr "Done" + IL_0091: call "void System.Console.Write(string)" + IL_0096: ret + } + """); } [Fact] @@ -3036,6 +4554,91 @@ public void OnCompleted(System.Action continuation) { } Assert.Equal("C.Awaitable C.AsyncEnumerator.MoveNextAsync()", info.MoveNextMethod.ToTestDisplayString()); Assert.Equal("System.Int32", info.ElementType.ToTestDisplayString()); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("Item(1) Dispose Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa7 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 168 (0xa8) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + object V_1, + int V_2, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3, + C.Awaiter V_4) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_004c + IL_000f: ldloc.0 + IL_0010: callvirt "int C.AsyncEnumerator.Current.get" + IL_0015: stloc.2 + IL_0016: ldc.i4.7 + IL_0017: ldc.i4.1 + IL_0018: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_001d: stloc.3 + IL_001e: ldloca.s V_3 + IL_0020: ldstr "Item(" + IL_0025: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_002a: ldloca.s V_3 + IL_002c: ldloc.2 + IL_002d: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0032: ldloca.s V_3 + IL_0034: ldstr ") " + IL_0039: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_003e: ldloca.s V_3 + IL_0040: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0045: call "void System.Console.Write(string)" + IL_004a: br.s IL_0072 + IL_004c: ldloc.0 + IL_004d: callvirt "C.Awaitable C.AsyncEnumerator.MoveNextAsync()" + IL_0052: callvirt "C.Awaiter C.Awaitable.GetAwaiter()" + IL_0057: stloc.s V_4 + IL_0059: ldloc.s V_4 + IL_005b: callvirt "bool C.Awaiter.IsCompleted.get" + IL_0060: brtrue.s IL_0069 + IL_0062: ldloc.s V_4 + IL_0064: call "void System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter(C.Awaiter)" + IL_0069: ldloc.s V_4 + IL_006b: callvirt "bool C.Awaiter.GetResult()" + IL_0070: brtrue.s IL_000f + IL_0072: leave.s IL_0077 + } + catch object + { + IL_0074: stloc.1 + IL_0075: leave.s IL_0077 + } + IL_0077: ldloc.0 + IL_0078: brfalse.s IL_0085 + IL_007a: ldloc.0 + IL_007b: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0080: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0085: ldloc.1 + IL_0086: brfalse.s IL_009d + IL_0088: ldloc.1 + IL_0089: isinst "System.Exception" + IL_008e: dup + IL_008f: brtrue.s IL_0093 + IL_0091: ldloc.1 + IL_0092: throw + IL_0093: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0098: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_009d: ldstr "Done" + IL_00a2: call "void System.Console.Write(string)" + IL_00a7: ret + } + """); } [Fact] @@ -3296,11 +4899,86 @@ public async ValueTask DisposeAsync() ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + string expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"; CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"); + expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x82 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 131 (0x83) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1, + int V_2, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_004a + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: stloc.2 + IL_0016: ldc.i4.6 + IL_0017: ldc.i4.1 + IL_0018: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_001d: stloc.3 + IL_001e: ldloca.s V_3 + IL_0020: ldstr "Got(" + IL_0025: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_002a: ldloca.s V_3 + IL_002c: ldloc.2 + IL_002d: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0032: ldloca.s V_3 + IL_0034: ldstr ") " + IL_0039: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_003e: ldloca.s V_3 + IL_0040: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0045: call "void System.Console.Write(string)" + IL_004a: ldloc.0 + IL_004b: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0050: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0055: brtrue.s IL_000f + IL_0057: leave.s IL_005c + } + catch object + { + IL_0059: stloc.1 + IL_005a: leave.s IL_005c + } + IL_005c: ldloc.0 + IL_005d: brfalse.s IL_006a + IL_005f: ldloc.0 + IL_0060: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.DisposeAsync()" + IL_0065: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_006a: ldloc.1 + IL_006b: brfalse.s IL_0082 + IL_006d: ldloc.1 + IL_006e: isinst "System.Exception" + IL_0073: dup + IL_0074: brtrue.s IL_0078 + IL_0076: ldloc.1 + IL_0077: throw + IL_0078: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_007d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0082: ret + } + """); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void TestWithPattern_WithUnsealed_WithIAsyncDisposable() { string source = @" @@ -3550,7 +5228,82 @@ public async ValueTask DisposeAsync() ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"); + var expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x82 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 131 (0x83) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1, + int V_2, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_004a + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: stloc.2 + IL_0016: ldc.i4.6 + IL_0017: ldc.i4.1 + IL_0018: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_001d: stloc.3 + IL_001e: ldloca.s V_3 + IL_0020: ldstr "Got(" + IL_0025: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_002a: ldloca.s V_3 + IL_002c: ldloc.2 + IL_002d: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0032: ldloca.s V_3 + IL_0034: ldstr ") " + IL_0039: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_003e: ldloca.s V_3 + IL_0040: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0045: call "void System.Console.Write(string)" + IL_004a: ldloc.0 + IL_004b: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0050: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0055: brtrue.s IL_000f + IL_0057: leave.s IL_005c + } + catch object + { + IL_0059: stloc.1 + IL_005a: leave.s IL_005c + } + IL_005c: ldloc.0 + IL_005d: brfalse.s IL_006a + IL_005f: ldloc.0 + IL_0060: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.DisposeAsync()" + IL_0065: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_006a: ldloc.1 + IL_006b: brfalse.s IL_0082 + IL_006d: ldloc.1 + IL_006e: isinst "System.Exception" + IL_0073: dup + IL_0074: brtrue.s IL_0078 + IL_0076: ldloc.1 + IL_0077: throw + IL_0078: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_007d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0082: ret + } + """); } [Fact] @@ -3708,7 +5461,8 @@ async ValueTask IAsyncDisposable.DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"); + var expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.First(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -3730,6 +5484,84 @@ async ValueTask IAsyncDisposable.DisposeAsync() var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x8c } + [System.Collections.Generic.IAsyncEnumerator.MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [System.IAsyncDisposable.DisposeAsync]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 141 (0x8d) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0054 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldc.i4.6 + IL_0020: ldc.i4.1 + IL_0021: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0026: stloc.s V_4 + IL_0028: ldloca.s V_4 + IL_002a: ldstr "Got(" + IL_002f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0034: ldloca.s V_4 + IL_0036: ldloc.3 + IL_0037: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_003c: ldloca.s V_4 + IL_003e: ldstr ") " + IL_0043: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0048: ldloca.s V_4 + IL_004a: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_004f: call "void System.Console.Write(string)" + IL_0054: ldloc.0 + IL_0055: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_005a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005f: brtrue.s IL_0018 + IL_0061: leave.s IL_0066 + } + catch object + { + IL_0063: stloc.2 + IL_0064: leave.s IL_0066 + } + IL_0066: ldloc.0 + IL_0067: brfalse.s IL_0074 + IL_0069: ldloc.0 + IL_006a: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_006f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0074: ldloc.2 + IL_0075: brfalse.s IL_008c + IL_0077: ldloc.2 + IL_0078: isinst "System.Exception" + IL_007d: dup + IL_007e: brtrue.s IL_0082 + IL_0080: ldloc.2 + IL_0081: throw + IL_0082: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0087: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_008c: ret + } + """); } [Fact] @@ -3781,7 +5613,8 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"); + var expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); // The thing to notice here is that the call to GetAsyncEnumerator is a constrained call (we're not boxing to `IAsyncEnumerable`) verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" @@ -4000,6 +5833,88 @@ .locals init (int V_0, IL_01ee: nop IL_01ef: ret }"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var runtimeAsyncVerifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x98 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + runtimeAsyncVerifier.VerifyIL("C.Main()", """ + { + // Code size 153 (0x99) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + C V_1, + System.Threading.CancellationToken V_2, + object V_3, + int V_4, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_5) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "C" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: constrained. "C" + IL_0018: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_001d: stloc.0 + IL_001e: ldnull + IL_001f: stloc.3 + .try + { + IL_0020: br.s IL_0060 + IL_0022: ldloc.0 + IL_0023: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0028: stloc.s V_4 + IL_002a: ldc.i4.6 + IL_002b: ldc.i4.1 + IL_002c: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0031: stloc.s V_5 + IL_0033: ldloca.s V_5 + IL_0035: ldstr "Got(" + IL_003a: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_003f: ldloca.s V_5 + IL_0041: ldloc.s V_4 + IL_0043: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0048: ldloca.s V_5 + IL_004a: ldstr ") " + IL_004f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0054: ldloca.s V_5 + IL_0056: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_005b: call "void System.Console.Write(string)" + IL_0060: ldloc.0 + IL_0061: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0066: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_006b: brtrue.s IL_0022 + IL_006d: leave.s IL_0072 + } + catch object + { + IL_006f: stloc.3 + IL_0070: leave.s IL_0072 + } + IL_0072: ldloc.0 + IL_0073: brfalse.s IL_0080 + IL_0075: ldloc.0 + IL_0076: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_007b: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0080: ldloc.3 + IL_0081: brfalse.s IL_0098 + IL_0083: ldloc.3 + IL_0084: isinst "System.Exception" + IL_0089: dup + IL_008a: brtrue.s IL_008e + IL_008c: ldloc.3 + IL_008d: throw + IL_008e: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0093: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0098: ret + } + """); } [Fact] @@ -4052,6 +5967,85 @@ public ValueTask DisposeAsync() comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x96 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 151 (0x97) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0054 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldc.i4.6 + IL_0020: ldc.i4.1 + IL_0021: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0026: stloc.s V_4 + IL_0028: ldloca.s V_4 + IL_002a: ldstr "Got(" + IL_002f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0034: ldloca.s V_4 + IL_0036: ldloc.3 + IL_0037: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_003c: ldloca.s V_4 + IL_003e: ldstr ") " + IL_0043: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0048: ldloca.s V_4 + IL_004a: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_004f: call "void System.Console.Write(string)" + IL_0054: ldloc.0 + IL_0055: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_005a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005f: brtrue.s IL_0018 + IL_0061: leave.s IL_0066 + } + catch object + { + IL_0063: stloc.2 + IL_0064: leave.s IL_0066 + } + IL_0066: ldloc.0 + IL_0067: brfalse.s IL_0074 + IL_0069: ldloc.0 + IL_006a: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_006f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0074: ldloc.2 + IL_0075: brfalse.s IL_008c + IL_0077: ldloc.2 + IL_0078: isinst "System.Exception" + IL_007d: dup + IL_007e: brtrue.s IL_0082 + IL_0080: ldloc.2 + IL_0081: throw + IL_0082: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0087: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_008c: ldstr "Done" + IL_0091: call "void System.Console.Write(string)" + IL_0096: ret + } + """); } [Fact] @@ -4105,8 +6099,119 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); + string expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Continue(2) NextAsync(2) Current(3) Continue(3) NextAsync(3) Current(4) Break Dispose(4) Done"; CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Continue(2) NextAsync(2) Current(3) Continue(3) NextAsync(3) Current(4) Break Dispose(4) Done"); + expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xec } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5d, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 237 (0xed) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4, + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_5) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br IL_00a7 + IL_001b: ldloc.0 + IL_001c: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0021: stloc.3 + IL_0022: ldloc.3 + IL_0023: ldc.i4.2 + IL_0024: beq.s IL_002a + IL_0026: ldloc.3 + IL_0027: ldc.i4.3 + IL_0028: bne.un.s IL_0062 + IL_002a: ldc.i4.s 11 + IL_002c: ldc.i4.1 + IL_002d: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0032: stloc.s V_4 + IL_0034: ldloca.s V_4 + IL_0036: ldstr "Continue(" + IL_003b: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0040: ldloca.s V_4 + IL_0042: ldloc.3 + IL_0043: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0048: ldloca.s V_4 + IL_004a: ldstr ") " + IL_004f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0054: ldloca.s V_4 + IL_0056: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_005b: call "void System.Console.Write(string)" + IL_0060: br.s IL_00a7 + IL_0062: ldloc.3 + IL_0063: ldc.i4.4 + IL_0064: bne.un.s IL_0072 + IL_0066: ldstr "Break " + IL_006b: call "void System.Console.Write(string)" + IL_0070: br.s IL_00b7 + IL_0072: ldc.i4.6 + IL_0073: ldc.i4.1 + IL_0074: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0079: stloc.s V_5 + IL_007b: ldloca.s V_5 + IL_007d: ldstr "Got(" + IL_0082: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0087: ldloca.s V_5 + IL_0089: ldloc.3 + IL_008a: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_008f: ldloca.s V_5 + IL_0091: ldstr ") " + IL_0096: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_009b: ldloca.s V_5 + IL_009d: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_00a2: call "void System.Console.Write(string)" + IL_00a7: ldloc.0 + IL_00a8: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_00ad: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_00b2: brtrue IL_001b + IL_00b7: leave.s IL_00bc + } + catch object + { + IL_00b9: stloc.2 + IL_00ba: leave.s IL_00bc + } + IL_00bc: ldloc.0 + IL_00bd: brfalse.s IL_00ca + IL_00bf: ldloc.0 + IL_00c0: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_00c5: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_00ca: ldloc.2 + IL_00cb: brfalse.s IL_00e2 + IL_00cd: ldloc.2 + IL_00ce: isinst "System.Exception" + IL_00d3: dup + IL_00d4: brtrue.s IL_00d8 + IL_00d6: ldloc.2 + IL_00d7: throw + IL_00d8: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00dd: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00e2: ldstr "Done" + IL_00e7: call "void System.Console.Write(string)" + IL_00ec: ret + } + """); } [Fact] @@ -4161,8 +6266,129 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); + string expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Continue(2) NextAsync(2) Current(3) Continue(3) NextAsync(3) Current(4) Goto Dispose(4) Done"; CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Continue(2) NextAsync(2) Current(3) Continue(3) NextAsync(3) Current(4) Goto Dispose(4) Done"); + expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xfc } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5d, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 253 (0xfd) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3, + int V_4, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_5, + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_6) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + IL_0016: ldc.i4.0 + IL_0017: stloc.3 + .try + { + IL_0018: br IL_00af + IL_001d: ldloc.0 + IL_001e: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0023: stloc.s V_4 + IL_0025: ldloc.s V_4 + IL_0027: ldc.i4.2 + IL_0028: beq.s IL_002f + IL_002a: ldloc.s V_4 + IL_002c: ldc.i4.3 + IL_002d: bne.un.s IL_0068 + IL_002f: ldc.i4.s 11 + IL_0031: ldc.i4.1 + IL_0032: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0037: stloc.s V_5 + IL_0039: ldloca.s V_5 + IL_003b: ldstr "Continue(" + IL_0040: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0045: ldloca.s V_5 + IL_0047: ldloc.s V_4 + IL_0049: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_004e: ldloca.s V_5 + IL_0050: ldstr ") " + IL_0055: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_005a: ldloca.s V_5 + IL_005c: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0061: call "void System.Console.Write(string)" + IL_0066: br.s IL_00af + IL_0068: ldloc.s V_4 + IL_006a: ldc.i4.4 + IL_006b: bne.un.s IL_0079 + IL_006d: ldstr "Goto " + IL_0072: call "void System.Console.Write(string)" + IL_0077: br.s IL_00c1 + IL_0079: ldc.i4.6 + IL_007a: ldc.i4.1 + IL_007b: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0080: stloc.s V_6 + IL_0082: ldloca.s V_6 + IL_0084: ldstr "Got(" + IL_0089: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_008e: ldloca.s V_6 + IL_0090: ldloc.s V_4 + IL_0092: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0097: ldloca.s V_6 + IL_0099: ldstr ") " + IL_009e: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_00a3: ldloca.s V_6 + IL_00a5: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_00aa: call "void System.Console.Write(string)" + IL_00af: ldloc.0 + IL_00b0: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_00b5: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_00ba: brtrue IL_001d + IL_00bf: leave.s IL_00c8 + IL_00c1: ldc.i4.1 + IL_00c2: stloc.3 + IL_00c3: leave.s IL_00c8 + } + catch object + { + IL_00c5: stloc.2 + IL_00c6: leave.s IL_00c8 + } + IL_00c8: ldloc.0 + IL_00c9: brfalse.s IL_00d6 + IL_00cb: ldloc.0 + IL_00cc: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_00d1: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_00d6: ldloc.2 + IL_00d7: brfalse.s IL_00ee + IL_00d9: ldloc.2 + IL_00da: isinst "System.Exception" + IL_00df: dup + IL_00e0: brtrue.s IL_00e4 + IL_00e2: ldloc.2 + IL_00e3: throw + IL_00e4: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00e9: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00ee: ldloc.3 + IL_00ef: ldc.i4.1 + IL_00f0: pop + IL_00f1: pop + IL_00f2: ldstr "Done" + IL_00f7: call "void System.Console.Write(string)" + IL_00fc: ret + } + """); } [Fact] @@ -4216,8 +6442,97 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); + string expectedOutput = "NextAsync(0) Current(0) Got(1) NextAsync(1) Current(1) Got(2) NextAsync(2) Current(2) Got(3) NextAsync(3) Current(3) Got(4) NextAsync(4) Dispose(4) Done"; CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(0) Got(1) NextAsync(1) Current(1) Got(2) NextAsync(2) Current(2) Got(3) NextAsync(3) Current(3) Got(4) NextAsync(4) Dispose(4) Done"); + expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + runtimeAsyncComp.VerifyEmitDiagnostics( + // (32,38): error CS9328: Method 'C.AsyncEnumerator.MoveNextAsync()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // public async ValueTask MoveNextAsync() + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "MoveNextAsync").WithArguments("C.AsyncEnumerator.MoveNextAsync()").WithLocation(32, 38), + // (39,32): error CS9328: Method 'C.AsyncEnumerator.DisposeAsync()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // public async ValueTask DisposeAsync() + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "DisposeAsync").WithArguments("C.AsyncEnumerator.DisposeAsync()").WithLocation(39, 32) + ); + // var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + // { + // ILVerifyMessage = """ + // [Main]: Return value missing on the stack. { Offset = 0x96 } + // [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5d, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + // [DisposeAsync]: Return value missing on the stack. { Offset = 0x5f } + // """ + // }); + // verifier.VerifyIL("C.Main()", """ + // { + // // Code size 151 (0x97) + // .maxstack 2 + // .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + // System.Threading.CancellationToken V_1, + // object V_2, + // int V_3, //i + // System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + // IL_0000: newobj "C..ctor()" + // IL_0005: ldloca.s V_1 + // IL_0007: initobj "System.Threading.CancellationToken" + // IL_000d: ldloc.1 + // IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + // IL_0013: stloc.0 + // IL_0014: ldnull + // IL_0015: stloc.2 + // .try + // { + // IL_0016: br.s IL_0054 + // IL_0018: ldloc.0 + // IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + // IL_001e: stloc.3 + // IL_001f: ldc.i4.6 + // IL_0020: ldc.i4.1 + // IL_0021: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + // IL_0026: stloc.s V_4 + // IL_0028: ldloca.s V_4 + // IL_002a: ldstr "Got(" + // IL_002f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + // IL_0034: ldloca.s V_4 + // IL_0036: ldloc.3 + // IL_0037: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + // IL_003c: ldloca.s V_4 + // IL_003e: ldstr ") " + // IL_0043: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + // IL_0048: ldloca.s V_4 + // IL_004a: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + // IL_004f: call "void System.Console.Write(string)" + // IL_0054: ldloc.0 + // IL_0055: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + // IL_005a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + // IL_005f: brtrue.s IL_0018 + // IL_0061: leave.s IL_0066 + // } + // catch object + // { + // IL_0063: stloc.2 + // IL_0064: leave.s IL_0066 + // } + // IL_0066: ldloc.0 + // IL_0067: brfalse.s IL_0074 + // IL_0069: ldloc.0 + // IL_006a: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + // IL_006f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + // IL_0074: ldloc.2 + // IL_0075: brfalse.s IL_008c + // IL_0077: ldloc.2 + // IL_0078: isinst "System.Exception" + // IL_007d: dup + // IL_007e: brtrue.s IL_0082 + // IL_0080: ldloc.2 + // IL_0081: throw + // IL_0082: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + // IL_0087: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + // IL_008c: ldstr "Done" + // IL_0091: call "void System.Console.Write(string)" + // IL_0096: ret + // } + // """); } [Fact, WorkItem(27651, "https://github.com/dotnet/roslyn/issues/27651")] @@ -4307,6 +6622,78 @@ internal struct AsyncEnumerator : IAsyncEnumerator var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Success"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("Success", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x64 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 101 (0x65) + .maxstack 2 + .locals init (C V_0, //c + System.Collections.Generic.IAsyncEnumerator V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldnull + IL_0001: stloc.0 + .try + { + IL_0002: ldloc.0 + IL_0003: ldloca.s V_2 + IL_0005: initobj "System.Threading.CancellationToken" + IL_000b: ldloc.2 + IL_000c: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0011: stloc.1 + IL_0012: ldnull + IL_0013: stloc.3 + .try + { + IL_0014: br.s IL_001d + IL_0016: ldloc.1 + IL_0017: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001c: pop + IL_001d: ldloc.1 + IL_001e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0023: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0028: brtrue.s IL_0016 + IL_002a: leave.s IL_002f + } + catch object + { + IL_002c: stloc.3 + IL_002d: leave.s IL_002f + } + IL_002f: ldloc.1 + IL_0030: brfalse.s IL_003d + IL_0032: ldloc.1 + IL_0033: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0038: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003d: ldloc.3 + IL_003e: brfalse.s IL_0055 + IL_0040: ldloc.3 + IL_0041: isinst "System.Exception" + IL_0046: dup + IL_0047: brtrue.s IL_004b + IL_0049: ldloc.3 + IL_004a: throw + IL_004b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0050: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0055: leave.s IL_0064 + } + catch System.NullReferenceException + { + IL_0057: pop + IL_0058: ldstr "Success" + IL_005d: call "void System.Console.Write(string)" + IL_0062: leave.s IL_0064 + } + IL_0064: ret + } + """); } [Fact] @@ -4367,6 +6754,107 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Try NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var expectedOutput = "Try NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done"; + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xb2 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 179 (0xb3) + .maxstack 2 + .locals init (int V_0, + System.Collections.Generic.IAsyncEnumerator V_1, + System.Threading.CancellationToken V_2, + object V_3, + int V_4, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_5) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldstr "Try " + IL_0007: call "void System.Console.Write(string)" + IL_000c: ldnull + IL_000d: throw + } + catch System.NullReferenceException + { + IL_000e: pop + IL_000f: ldc.i4.1 + IL_0010: stloc.0 + IL_0011: leave.s IL_0013 + } + IL_0013: ldloc.0 + IL_0014: ldc.i4.1 + IL_0015: bne.un IL_00a8 + IL_001a: newobj "C..ctor()" + IL_001f: ldloca.s V_2 + IL_0021: initobj "System.Threading.CancellationToken" + IL_0027: ldloc.2 + IL_0028: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_002d: stloc.1 + IL_002e: ldnull + IL_002f: stloc.3 + .try + { + IL_0030: br.s IL_0070 + IL_0032: ldloc.1 + IL_0033: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0038: stloc.s V_4 + IL_003a: ldc.i4.6 + IL_003b: ldc.i4.1 + IL_003c: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0041: stloc.s V_5 + IL_0043: ldloca.s V_5 + IL_0045: ldstr "Got(" + IL_004a: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_004f: ldloca.s V_5 + IL_0051: ldloc.s V_4 + IL_0053: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0058: ldloca.s V_5 + IL_005a: ldstr ") " + IL_005f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0064: ldloca.s V_5 + IL_0066: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_006b: call "void System.Console.Write(string)" + IL_0070: ldloc.1 + IL_0071: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0076: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_007b: brtrue.s IL_0032 + IL_007d: leave.s IL_0082 + } + catch object + { + IL_007f: stloc.3 + IL_0080: leave.s IL_0082 + } + IL_0082: ldloc.1 + IL_0083: brfalse.s IL_0090 + IL_0085: ldloc.1 + IL_0086: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_008b: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0090: ldloc.3 + IL_0091: brfalse.s IL_00a8 + IL_0093: ldloc.3 + IL_0094: isinst "System.Exception" + IL_0099: dup + IL_009a: brtrue.s IL_009e + IL_009c: ldloc.3 + IL_009d: throw + IL_009e: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00a3: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00a8: ldstr "Done" + IL_00ad: call "void System.Console.Write(string)" + IL_00b2: ret + } + """); } /// Covered in greater details by @@ -4478,6 +6966,88 @@ class Element var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var expectedOutput = "NextAsync(0) Current(1) Convert(1) Got(1) NextAsync(1) Current(2) Convert(2) Got(2) NextAsync(2) Dispose(3) Done"; + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x9b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 156 (0x9c) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + Element V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0059 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: call "Element Element.op_Implicit(int)" + IL_0023: stloc.3 + IL_0024: ldc.i4.6 + IL_0025: ldc.i4.1 + IL_0026: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_002b: stloc.s V_4 + IL_002d: ldloca.s V_4 + IL_002f: ldstr "Got(" + IL_0034: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0039: ldloca.s V_4 + IL_003b: ldloc.3 + IL_003c: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(Element)" + IL_0041: ldloca.s V_4 + IL_0043: ldstr ") " + IL_0048: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_004d: ldloca.s V_4 + IL_004f: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0054: call "void System.Console.Write(string)" + IL_0059: ldloc.0 + IL_005a: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_005f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0064: brtrue.s IL_0018 + IL_0066: leave.s IL_006b + } + catch object + { + IL_0068: stloc.2 + IL_0069: leave.s IL_006b + } + IL_006b: ldloc.0 + IL_006c: brfalse.s IL_0079 + IL_006e: ldloc.0 + IL_006f: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0074: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0079: ldloc.2 + IL_007a: brfalse.s IL_0091 + IL_007c: ldloc.2 + IL_007d: isinst "System.Exception" + IL_0082: dup + IL_0083: brtrue.s IL_0087 + IL_0085: ldloc.2 + IL_0086: throw + IL_0087: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_008c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0091: ldstr "Done" + IL_0096: call "void System.Console.Write(string)" + IL_009b: ret + } + """); } [Fact] @@ -4529,7 +7099,8 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Dispose(3)"); + var expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Dispose(3)"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -4551,6 +7122,95 @@ public async ValueTask DisposeAsync() var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xae } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 175 (0xaf) + .maxstack 2 + .locals init (C? V_0, //c + C V_1, + System.Collections.Generic.IAsyncEnumerator V_2, + System.Threading.CancellationToken V_3, + object V_4, + int V_5, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_6) + IL_0000: ldloca.s V_0 + IL_0002: ldloca.s V_1 + IL_0004: initobj "C" + IL_000a: ldloc.1 + IL_000b: call "C?..ctor(C)" + IL_0010: ldloca.s V_0 + IL_0012: call "readonly C C?.Value.get" + IL_0017: stloc.1 + IL_0018: ldloca.s V_1 + IL_001a: ldloca.s V_3 + IL_001c: initobj "System.Threading.CancellationToken" + IL_0022: ldloc.3 + IL_0023: constrained. "C" + IL_0029: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_002e: stloc.2 + IL_002f: ldnull + IL_0030: stloc.s V_4 + .try + { + IL_0032: br.s IL_0072 + IL_0034: ldloc.2 + IL_0035: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_003a: stloc.s V_5 + IL_003c: ldc.i4.6 + IL_003d: ldc.i4.1 + IL_003e: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0043: stloc.s V_6 + IL_0045: ldloca.s V_6 + IL_0047: ldstr "Got(" + IL_004c: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0051: ldloca.s V_6 + IL_0053: ldloc.s V_5 + IL_0055: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_005a: ldloca.s V_6 + IL_005c: ldstr ") " + IL_0061: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0066: ldloca.s V_6 + IL_0068: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_006d: call "void System.Console.Write(string)" + IL_0072: ldloc.2 + IL_0073: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0078: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_007d: brtrue.s IL_0034 + IL_007f: leave.s IL_0085 + } + catch object + { + IL_0081: stloc.s V_4 + IL_0083: leave.s IL_0085 + } + IL_0085: ldloc.2 + IL_0086: brfalse.s IL_0093 + IL_0088: ldloc.2 + IL_0089: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_008e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0093: ldloc.s V_4 + IL_0095: brfalse.s IL_00ae + IL_0097: ldloc.s V_4 + IL_0099: isinst "System.Exception" + IL_009e: dup + IL_009f: brtrue.s IL_00a4 + IL_00a1: ldloc.s V_4 + IL_00a3: throw + IL_00a4: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00a9: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00ae: ret + } + """); } [Fact] @@ -4585,6 +7245,85 @@ IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(System.Threading. var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Success"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("Success", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x88 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 137 (0x89) + .maxstack 2 + .locals init (C? V_0, //c + System.Collections.Generic.IAsyncEnumerator V_1, + C V_2, + System.Threading.CancellationToken V_3, + object V_4) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C?" + .try + { + IL_0008: ldloca.s V_0 + IL_000a: call "readonly C C?.Value.get" + IL_000f: stloc.2 + IL_0010: ldloca.s V_2 + IL_0012: ldloca.s V_3 + IL_0014: initobj "System.Threading.CancellationToken" + IL_001a: ldloc.3 + IL_001b: constrained. "C" + IL_0021: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0026: stloc.1 + IL_0027: ldnull + IL_0028: stloc.s V_4 + .try + { + IL_002a: br.s IL_003d + IL_002c: ldloc.1 + IL_002d: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0032: pop + IL_0033: ldstr "UNREACHABLE" + IL_0038: call "void System.Console.Write(string)" + IL_003d: ldloc.1 + IL_003e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0043: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0048: brtrue.s IL_002c + IL_004a: leave.s IL_0050 + } + catch object + { + IL_004c: stloc.s V_4 + IL_004e: leave.s IL_0050 + } + IL_0050: ldloc.1 + IL_0051: brfalse.s IL_005e + IL_0053: ldloc.1 + IL_0054: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0059: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005e: ldloc.s V_4 + IL_0060: brfalse.s IL_0079 + IL_0062: ldloc.s V_4 + IL_0064: isinst "System.Exception" + IL_0069: dup + IL_006a: brtrue.s IL_006f + IL_006c: ldloc.s V_4 + IL_006e: throw + IL_006f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0074: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0079: leave.s IL_0088 + } + catch System.InvalidOperationException + { + IL_007b: pop + IL_007c: ldstr "Success" + IL_0081: call "void System.Console.Write(string)" + IL_0086: leave.s IL_0088 + } + IL_0088: ret + } + """); } [Fact] @@ -4639,7 +7378,8 @@ public static class Extensions var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Deconstruct(1) Got(1,-1) NextAsync(1) Current(2) Deconstruct(2) Got(2,-2) NextAsync(2) Dispose(3) Done"); + var expectedOutput = "NextAsync(0) Current(1) Deconstruct(1) Got(1,-1) NextAsync(1) Current(2) Deconstruct(2) Got(2,-2) NextAsync(2) Dispose(3) Done"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -4661,6 +7401,101 @@ public static class Extensions var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xba } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x5f } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 187 (0xbb) + .maxstack 3 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + string V_3, //i + int V_4, //j + string V_5, + int V_6, + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_7) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0078 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: ldloca.s V_5 + IL_0020: ldloca.s V_6 + IL_0022: call "void Extensions.Deconstruct(int, out string, out int)" + IL_0027: ldloc.s V_5 + IL_0029: stloc.3 + IL_002a: ldloc.s V_6 + IL_002c: stloc.s V_4 + IL_002e: ldc.i4.7 + IL_002f: ldc.i4.2 + IL_0030: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0035: stloc.s V_7 + IL_0037: ldloca.s V_7 + IL_0039: ldstr "Got(" + IL_003e: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0043: ldloca.s V_7 + IL_0045: ldloc.3 + IL_0046: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)" + IL_004b: ldloca.s V_7 + IL_004d: ldstr "," + IL_0052: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0057: ldloca.s V_7 + IL_0059: ldloc.s V_4 + IL_005b: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0060: ldloca.s V_7 + IL_0062: ldstr ") " + IL_0067: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_006c: ldloca.s V_7 + IL_006e: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0073: call "void System.Console.Write(string)" + IL_0078: ldloc.0 + IL_0079: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_007e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0083: brtrue.s IL_0018 + IL_0085: leave.s IL_008a + } + catch object + { + IL_0087: stloc.2 + IL_0088: leave.s IL_008a + } + IL_008a: ldloc.0 + IL_008b: brfalse.s IL_0098 + IL_008d: ldloc.0 + IL_008e: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0093: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0098: ldloc.2 + IL_0099: brfalse.s IL_00b0 + IL_009b: ldloc.2 + IL_009c: isinst "System.Exception" + IL_00a1: dup + IL_00a2: brtrue.s IL_00a6 + IL_00a4: ldloc.2 + IL_00a5: throw + IL_00a6: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00ab: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00b0: ldstr "Done" + IL_00b5: call "void System.Console.Write(string)" + IL_00ba: ret + } + """); } [Fact] @@ -4738,7 +7573,8 @@ public async ValueTask DisposeAsync() }"; var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1,-1) NextAsync(1) Current(2) Got(2,-2) NextAsync(2) Dispose(3) Done"); + var expectedOutput = "NextAsync(0) Current(1) Got(1,-1) NextAsync(1) Current(2) Got(2,-2) NextAsync(2) Dispose(3) Done"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -4755,6 +7591,97 @@ public async ValueTask DisposeAsync() Assert.Equal("(System.String, System.Int32)", info.ElementType.ToTestDisplayString()); Assert.Equal(ConversionKind.Identity, info.ElementConversion.Kind); Assert.Equal(ConversionKind.Identity, info.CurrentConversion.Kind); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xb8 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x5f } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 185 (0xb9) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator> V_0, + System.Threading.CancellationToken V_1, + object V_2, + string V_3, //i + int V_4, //j + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_5) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0076 + IL_0018: ldloc.0 + IL_0019: callvirt "System.ValueTuple System.Collections.Generic.IAsyncEnumerator>.Current.get" + IL_001e: dup + IL_001f: ldfld "string System.ValueTuple.Item1" + IL_0024: stloc.3 + IL_0025: ldfld "int System.ValueTuple.Item2" + IL_002a: stloc.s V_4 + IL_002c: ldc.i4.7 + IL_002d: ldc.i4.2 + IL_002e: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0033: stloc.s V_5 + IL_0035: ldloca.s V_5 + IL_0037: ldstr "Got(" + IL_003c: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0041: ldloca.s V_5 + IL_0043: ldloc.3 + IL_0044: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)" + IL_0049: ldloca.s V_5 + IL_004b: ldstr "," + IL_0050: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0055: ldloca.s V_5 + IL_0057: ldloc.s V_4 + IL_0059: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_005e: ldloca.s V_5 + IL_0060: ldstr ") " + IL_0065: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_006a: ldloca.s V_5 + IL_006c: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0071: call "void System.Console.Write(string)" + IL_0076: ldloc.0 + IL_0077: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator>.MoveNextAsync()" + IL_007c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0081: brtrue.s IL_0018 + IL_0083: leave.s IL_0088 + } + catch object + { + IL_0085: stloc.2 + IL_0086: leave.s IL_0088 + } + IL_0088: ldloc.0 + IL_0089: brfalse.s IL_0096 + IL_008b: ldloc.0 + IL_008c: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0091: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0096: ldloc.2 + IL_0097: brfalse.s IL_00ae + IL_0099: ldloc.2 + IL_009a: isinst "System.Exception" + IL_009f: dup + IL_00a0: brtrue.s IL_00a4 + IL_00a2: ldloc.2 + IL_00a3: throw + IL_00a4: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00a9: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00ae: ldstr "Done" + IL_00b3: call "void System.Console.Write(string)" + IL_00b8: ret + } + """); } [Fact] @@ -4818,7 +7745,101 @@ public static class Extensions var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1,-1) NextAsync(1) Current(2) Got(2,-2) NextAsync(2) Dispose(3) Done"); + string expectedOutput = "NextAsync(0) Current(1) Got(1,-1) NextAsync(1) Current(2) Got(2,-2) NextAsync(2) Dispose(3) Done"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xb7 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 184 (0xb8) + .maxstack 3 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, //e + System.Threading.CancellationToken V_1, + object V_2, + int V_3, //i + int V_4, //j + int V_5, + int V_6, + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_7) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0078 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: ldloca.s V_5 + IL_0020: ldloca.s V_6 + IL_0022: call "void Extensions.Deconstruct(int, out int, out int)" + IL_0027: ldloc.s V_5 + IL_0029: stloc.3 + IL_002a: ldloc.s V_6 + IL_002c: stloc.s V_4 + IL_002e: ldc.i4.7 + IL_002f: ldc.i4.2 + IL_0030: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0035: stloc.s V_7 + IL_0037: ldloca.s V_7 + IL_0039: ldstr "Got(" + IL_003e: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0043: ldloca.s V_7 + IL_0045: ldloc.3 + IL_0046: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_004b: ldloca.s V_7 + IL_004d: ldstr "," + IL_0052: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0057: ldloca.s V_7 + IL_0059: ldloc.s V_4 + IL_005b: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0060: ldloca.s V_7 + IL_0062: ldstr ") " + IL_0067: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_006c: ldloca.s V_7 + IL_006e: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0073: call "void System.Console.Write(string)" + IL_0078: ldloc.0 + IL_0079: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_007e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0083: brtrue.s IL_0018 + IL_0085: leave.s IL_008a + } + catch object + { + IL_0087: stloc.2 + IL_0088: leave.s IL_008a + } + IL_008a: ldloc.0 + IL_008b: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0090: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0095: ldloc.2 + IL_0096: brfalse.s IL_00ad + IL_0098: ldloc.2 + IL_0099: isinst "System.Exception" + IL_009e: dup + IL_009f: brtrue.s IL_00a3 + IL_00a1: ldloc.2 + IL_00a2: throw + IL_00a3: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00a8: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00ad: ldstr "Done" + IL_00b2: call "void System.Console.Write(string)" + IL_00b7: ret + } + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30257")] @@ -5042,7 +8063,8 @@ static async System.Threading.Tasks.Task Main() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3) Dispose(4)"); + var expectedOutput = "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3) Dispose(4)"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -5064,9 +8086,71 @@ static async System.Threading.Tasks.Task Main() var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x61 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x5f } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 98 (0x62) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2) + IL_0000: newobj "Collection..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0029 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: pop + IL_001f: ldstr "Got " + IL_0024: call "void System.Console.Write(string)" + IL_0029: ldloc.0 + IL_002a: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_002f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0034: brtrue.s IL_0018 + IL_0036: leave.s IL_003b + } + catch object + { + IL_0038: stloc.2 + IL_0039: leave.s IL_003b + } + IL_003b: ldloc.0 + IL_003c: brfalse.s IL_0049 + IL_003e: ldloc.0 + IL_003f: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0044: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0049: ldloc.2 + IL_004a: brfalse.s IL_0061 + IL_004c: ldloc.2 + IL_004d: isinst "System.Exception" + IL_0052: dup + IL_0053: brtrue.s IL_0057 + IL_0055: ldloc.2 + IL_0056: throw + IL_0057: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_005c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0061: ret + } + """); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void TestWithInterfaceImplementingPattern() { string source = @" @@ -5123,7 +8207,8 @@ static async System.Threading.Tasks.Task Main() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3)"); + string expectedOutput = "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3)"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -5287,9 +8372,43 @@ .locals init (int V_0, IL_0119: nop IL_011a: ret }", sequencePoints: "C+
d__0.MoveNext", source: source); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Main]: Return value missing on the stack. { Offset = 0x34 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 53 (0x35) + .maxstack 2 + .locals init (IMyAsyncEnumerator V_0, + System.Threading.CancellationToken V_1) + IL_0000: newobj "Collection..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "IMyAsyncEnumerator ICollection.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: br.s IL_0027 + IL_0016: ldloc.0 + IL_0017: callvirt "int IMyAsyncEnumerator.Current.get" + IL_001c: pop + IL_001d: ldstr "Got " + IL_0022: call "void System.Console.Write(string)" + IL_0027: ldloc.0 + IL_0028: callvirt "System.Threading.Tasks.Task IMyAsyncEnumerator.MoveNextAsync()" + IL_002d: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0032: brtrue.s IL_0016 + IL_0034: ret + } + """); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void TestWithInterfaceImplementingPattern_ChildImplementsDisposeAsync() { string source = @" @@ -5349,7 +8468,8 @@ static async System.Threading.Tasks.Task Main() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3)"); + var expectedOutput = "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3)"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -5357,6 +8477,40 @@ static async System.Threading.Tasks.Task Main() var info = model.GetForEachStatementInfo(foreachSyntax); Assert.Null(info.DisposeMethod); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier2 = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Main]: Return value missing on the stack. { Offset = 0x34 } + """ + }); + verifier2.VerifyIL("C.Main()", """ + { + // Code size 53 (0x35) + .maxstack 2 + .locals init (IMyAsyncEnumerator V_0, + System.Threading.CancellationToken V_1) + IL_0000: newobj "Collection..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "IMyAsyncEnumerator ICollection.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: br.s IL_0027 + IL_0016: ldloc.0 + IL_0017: callvirt "int IMyAsyncEnumerator.Current.get" + IL_001c: pop + IL_001d: ldstr "Got " + IL_0022: call "void System.Console.Write(string)" + IL_0027: ldloc.0 + IL_0028: callvirt "System.Threading.Tasks.Task IMyAsyncEnumerator.MoveNextAsync()" + IL_002d: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0032: brtrue.s IL_0016 + IL_0034: ret + } + """); } [Fact] @@ -5623,6 +8777,35 @@ public int Current var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x26 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 39 (0x27) + .maxstack 2 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "int[] System.Array.Empty()" + IL_000a: call "C.Enumerator C.GetAsyncEnumerator(params int[])" + IL_000f: stloc.0 + IL_0010: br.s IL_0019 + IL_0012: ldloc.0 + IL_0013: callvirt "int C.Enumerator.Current.get" + IL_0018: pop + IL_0019: ldloc.0 + IL_001a: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: brtrue.s IL_0012 + IL_0026: ret + } + """); } [Fact] @@ -5719,6 +8902,64 @@ public async Task DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x58 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_0036 + IL_002b: ldloc.0 + IL_002c: callvirt "System.Threading.Tasks.Task C.Enumerator.DisposeAsync()" + IL_0031: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0036: ldloc.1 + IL_0037: brfalse.s IL_004e + IL_0039: ldloc.1 + IL_003a: isinst "System.Exception" + IL_003f: dup + IL_0040: brtrue.s IL_0044 + IL_0042: ldloc.1 + IL_0043: throw + IL_0044: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0049: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004e: ldstr "Done" + IL_0053: call "void System.Console.Write(string)" + IL_0058: ret + } + """); } [Fact] @@ -5807,6 +9048,36 @@ public static class Extension var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 44 (0x2c) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_0014 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: pop + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_000d + IL_0021: ldstr "Done" + IL_0026: call "void System.Console.Write(string)" + IL_002b: ret + } + """); } [Fact] @@ -5855,6 +9126,36 @@ public static class Extension2 var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 44 (0x2c) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_0014 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: pop + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_000d + IL_0021: ldstr "Done" + IL_0026: call "void System.Console.Write(string)" + IL_002b: ret + } + """); } [Fact] @@ -5899,6 +9200,64 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x58 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_0036 + IL_002b: ldloc.0 + IL_002c: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.DisposeAsync()" + IL_0031: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0036: ldloc.1 + IL_0037: brfalse.s IL_004e + IL_0039: ldloc.1 + IL_003a: isinst "System.Exception" + IL_003f: dup + IL_0040: brtrue.s IL_0044 + IL_0042: ldloc.1 + IL_0043: throw + IL_0044: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0049: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004e: ldstr "Done" + IL_0053: call "void System.Console.Write(string)" + IL_0058: ret + } + """); } [Fact] @@ -6033,6 +9392,73 @@ public void OnCompleted(System.Action continuation) { } var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x6e } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 111 (0x6f) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1, + Awaiter V_2) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_004c + IL_002b: ldloc.0 + IL_002c: callvirt "Awaitable C.Enumerator.DisposeAsync()" + IL_0031: callvirt "Awaiter Awaitable.GetAwaiter()" + IL_0036: stloc.2 + IL_0037: ldloc.2 + IL_0038: callvirt "bool Awaiter.IsCompleted.get" + IL_003d: brtrue.s IL_0045 + IL_003f: ldloc.2 + IL_0040: call "void System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter(Awaiter)" + IL_0045: ldloc.2 + IL_0046: callvirt "bool Awaiter.GetResult()" + IL_004b: pop + IL_004c: ldloc.1 + IL_004d: brfalse.s IL_0064 + IL_004f: ldloc.1 + IL_0050: isinst "System.Exception" + IL_0055: dup + IL_0056: brtrue.s IL_005a + IL_0058: ldloc.1 + IL_0059: throw + IL_005a: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_005f: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0064: ldstr "Done" + IL_0069: call "void System.Console.Write(string)" + IL_006e: ret + } + """); } [Fact] @@ -6077,6 +9503,64 @@ public async Task DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x58 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_0036 + IL_002b: ldloc.0 + IL_002c: callvirt "System.Threading.Tasks.Task C.Enumerator.DisposeAsync()" + IL_0031: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0036: ldloc.1 + IL_0037: brfalse.s IL_004e + IL_0039: ldloc.1 + IL_003a: isinst "System.Exception" + IL_003f: dup + IL_0040: brtrue.s IL_0044 + IL_0042: ldloc.1 + IL_0043: throw + IL_0044: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0049: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004e: ldstr "Done" + IL_0053: call "void System.Console.Write(string)" + IL_0058: ret + } + """); } [Fact] @@ -6123,6 +9607,65 @@ public async Task DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x59 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 90 (0x5a) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_0037 + IL_002b: ldloc.0 + IL_002c: callvirt "System.Threading.Tasks.Task C.Enumerator.DisposeAsync()" + IL_0031: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0036: pop + IL_0037: ldloc.1 + IL_0038: brfalse.s IL_004f + IL_003a: ldloc.1 + IL_003b: isinst "System.Exception" + IL_0040: dup + IL_0041: brtrue.s IL_0045 + IL_0043: ldloc.1 + IL_0044: throw + IL_0045: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004a: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004f: ldstr "Done" + IL_0054: call "void System.Console.Write(string)" + IL_0059: ret + } + """); } [Fact] @@ -6168,6 +9711,66 @@ public async Task DisposeAsync(int i = 1) var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync 1 Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync 1 Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5a } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Unexpected type on the stack. { Offset = 0x5b, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 91 (0x5b) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_0038 + IL_002b: ldloc.0 + IL_002c: ldc.i4.1 + IL_002d: callvirt "System.Threading.Tasks.Task C.Enumerator.DisposeAsync(int)" + IL_0032: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0037: pop + IL_0038: ldloc.1 + IL_0039: brfalse.s IL_0050 + IL_003b: ldloc.1 + IL_003c: isinst "System.Exception" + IL_0041: dup + IL_0042: brtrue.s IL_0046 + IL_0044: ldloc.1 + IL_0045: throw + IL_0046: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004b: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0050: ldstr "Done" + IL_0055: call "void System.Console.Write(string)" + IL_005a: ret + } + """); } [Fact] @@ -6222,6 +9825,80 @@ static async Task Main() }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "StructAwaitable1StructAwaitable2"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source, options: TestOptions.ReleaseExe.WithSpecificDiagnosticOptions("SYSLIB5007", ReportDiagnostic.Suppress)); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("StructAwaitable1StructAwaitable2", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x86 } + """ + }); + verifier.VerifyIL("Program.Main()", """ + { + // Code size 135 (0x87) + .maxstack 2 + .locals init (Enumerable.Enumerator V_0, + object V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Runtime.CompilerServices.TaskAwaiter V_3) + IL_0000: newobj "Enumerable..ctor()" + IL_0005: call "Enumerable.Enumerator Enumerable.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "object Enumerable.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "StructAwaitable1 Enumerable.Enumerator.MoveNextAsync()" + IL_001c: box "StructAwaitable1" + IL_0021: call "System.Runtime.CompilerServices.TaskAwaiter Extensions.GetAwaiter(I1)" + IL_0026: stloc.2 + IL_0027: ldloca.s V_2 + IL_0029: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_002e: brtrue.s IL_0036 + IL_0030: ldloc.2 + IL_0031: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter>(System.Runtime.CompilerServices.TaskAwaiter)" + IL_0036: ldloca.s V_2 + IL_0038: call "bool System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_003d: brtrue.s IL_000f + IL_003f: leave.s IL_0044 + } + catch object + { + IL_0041: stloc.1 + IL_0042: leave.s IL_0044 + } + IL_0044: ldloc.0 + IL_0045: brfalse.s IL_006e + IL_0047: ldloc.0 + IL_0048: callvirt "StructAwaitable2 Enumerable.Enumerator.DisposeAsync()" + IL_004d: box "StructAwaitable2" + IL_0052: call "System.Runtime.CompilerServices.TaskAwaiter Extensions.GetAwaiter(I2)" + IL_0057: stloc.3 + IL_0058: ldloca.s V_3 + IL_005a: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_005f: brtrue.s IL_0067 + IL_0061: ldloc.3 + IL_0062: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.TaskAwaiter)" + IL_0067: ldloca.s V_3 + IL_0069: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_006e: ldloc.1 + IL_006f: brfalse.s IL_0086 + IL_0071: ldloc.1 + IL_0072: isinst "System.Exception" + IL_0077: dup + IL_0078: brtrue.s IL_007c + IL_007a: ldloc.1 + IL_007b: throw + IL_007c: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0081: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0086: ret + } + """); } [Fact] @@ -6322,6 +9999,72 @@ public static async Task Main() var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x72 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 115 (0x73) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + C? V_1, + C V_2, + System.Threading.CancellationToken V_3, + object V_4) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "C?" + IL_0009: call "readonly C C?.Value.get" + IL_000e: stloc.2 + IL_000f: ldloca.s V_2 + IL_0011: ldloca.s V_3 + IL_0013: initobj "System.Threading.CancellationToken" + IL_0019: ldloc.3 + IL_001a: constrained. "C" + IL_0020: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0025: stloc.0 + IL_0026: ldnull + IL_0027: stloc.s V_4 + .try + { + IL_0029: br.s IL_0036 + IL_002b: ldloc.0 + IL_002c: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0031: call "void System.Console.Write(int)" + IL_0036: ldloc.0 + IL_0037: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_003c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0041: brtrue.s IL_002b + IL_0043: leave.s IL_0049 + } + catch object + { + IL_0045: stloc.s V_4 + IL_0047: leave.s IL_0049 + } + IL_0049: ldloc.0 + IL_004a: brfalse.s IL_0057 + IL_004c: ldloc.0 + IL_004d: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0052: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0057: ldloc.s V_4 + IL_0059: brfalse.s IL_0072 + IL_005b: ldloc.s V_4 + IL_005d: isinst "System.Exception" + IL_0062: dup + IL_0063: brtrue.s IL_0068 + IL_0065: ldloc.s V_4 + IL_0067: throw + IL_0068: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_006d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0072: ret + } + """); } [Fact] @@ -6346,6 +10089,67 @@ public static async Task Main() var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 95 (0x5f) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + C? V_1, + C V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "C?" + IL_0009: call "readonly C C?.Value.get" + IL_000e: stloc.2 + IL_000f: ldloca.s V_2 + IL_0011: call "System.Collections.Generic.IAsyncEnumerator C.GetAsyncEnumerator()" + IL_0016: stloc.0 + IL_0017: ldnull + IL_0018: stloc.3 + .try + { + IL_0019: br.s IL_0026 + IL_001b: ldloc.0 + IL_001c: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0021: call "void System.Console.Write(int)" + IL_0026: ldloc.0 + IL_0027: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_002c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0031: brtrue.s IL_001b + IL_0033: leave.s IL_0038 + } + catch object + { + IL_0035: stloc.3 + IL_0036: leave.s IL_0038 + } + IL_0038: ldloc.0 + IL_0039: brfalse.s IL_0046 + IL_003b: ldloc.0 + IL_003c: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0041: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0046: ldloc.3 + IL_0047: brfalse.s IL_005e + IL_0049: ldloc.3 + IL_004a: isinst "System.Exception" + IL_004f: dup + IL_0050: brtrue.s IL_0054 + IL_0052: ldloc.3 + IL_0053: throw + IL_0054: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0059: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005e: ret + } + """); } [Fact] @@ -6446,6 +10250,33 @@ public static class Extensions Assert.Equal("System.Int32", info.ElementType.ToTestDisplayString()); Assert.Equal(ConversionKind.Identity, info.ElementConversion.Kind); Assert.Equal(ConversionKind.Identity, info.CurrentConversion.Kind); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -6476,6 +10307,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -6506,6 +10364,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x21 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 34 (0x22) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldnull + IL_0001: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_0006: stloc.0 + IL_0007: br.s IL_0014 + IL_0009: ldloc.0 + IL_000a: callvirt "int C.Enumerator.Current.get" + IL_000f: call "void System.Console.Write(int)" + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_0009 + IL_0021: ret + } + """); } [Fact] @@ -6536,6 +10421,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x27 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 40 (0x28) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0019 + IL_000d: ldloca.s V_0 + IL_000f: call "readonly int C.Enumerator.Current.get" + IL_0014: call "void System.Console.Write(int)" + IL_0019: ldloca.s V_0 + IL_001b: call "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0020: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: brtrue.s IL_000d + IL_0027: ret + } + """); } [Fact] @@ -6754,6 +10666,35 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xb } + [
g__Inner|0_0]: Return value missing on the stack. { Offset = 0x26 } + """ + }); + verifier.VerifyIL("C.
g__Inner|0_0(T)", """ + { + // Code size 39 (0x27) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldarg.0 + IL_0001: box "T" + IL_0006: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_000b: stloc.0 + IL_000c: br.s IL_0019 + IL_000e: ldloc.0 + IL_000f: callvirt "int C.Enumerator.Current.get" + IL_0014: call "void System.Console.Write(int)" + IL_0019: ldloc.0 + IL_001a: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: brtrue.s IL_000e + IL_0026: ret + } + """); } [Fact] @@ -6789,6 +10730,35 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xb } + [
g__Inner|0_0]: Return value missing on the stack. { Offset = 0x26 } + """ + }); + verifier.VerifyIL("C.
g__Inner|0_0(T)", """ + { + // Code size 39 (0x27) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldarg.0 + IL_0001: box "T" + IL_0006: call "C.Enumerator Extensions.GetAsyncEnumerator(System.IConvertible)" + IL_000b: stloc.0 + IL_000c: br.s IL_0019 + IL_000e: ldloc.0 + IL_000f: callvirt "int C.Enumerator.Current.get" + IL_0014: call "void System.Console.Write(int)" + IL_0019: ldloc.0 + IL_001a: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: brtrue.s IL_000e + IL_0026: ret + } + """); } [Fact] @@ -6820,6 +10790,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldstr " " + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -6852,6 +10849,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldstr " " + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(string)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -6951,6 +10975,37 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 47 (0x2f) + .maxstack 1 + .locals init (C.Enumerator V_0, + C V_1) + IL_0000: ldloca.s V_1 + IL_0002: initobj "C" + IL_0008: ldloc.1 + IL_0009: box "C" + IL_000e: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_0013: stloc.0 + IL_0014: br.s IL_0021 + IL_0016: ldloc.0 + IL_0017: callvirt "int C.Enumerator.Current.get" + IL_001c: call "void System.Console.Write(int)" + IL_0021: ldloc.0 + IL_0022: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0027: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002c: brtrue.s IL_0016 + IL_002e: ret + } + """); } [Fact] @@ -6982,6 +11037,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(I)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7012,6 +11094,41 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x3f } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 64 (0x40) + .maxstack 2 + .locals init (C.Enumerator V_0) + IL_0000: ldsfld "System.Func C.<>c.<>9__0_0" + IL_0005: dup + IL_0006: brtrue.s IL_001f + IL_0008: pop + IL_0009: ldsfld "C.<>c C.<>c.<>9" + IL_000e: ldftn "int C.<>c.
b__0_0()" + IL_0014: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0019: dup + IL_001a: stsfld "System.Func C.<>c.<>9__0_0" + IL_001f: call "C.Enumerator Extensions.GetAsyncEnumerator(System.Func)" + IL_0024: stloc.0 + IL_0025: br.s IL_0032 + IL_0027: ldloc.0 + IL_0028: callvirt "int C.Enumerator.Current.get" + IL_002d: call "void System.Console.Write(int)" + IL_0032: ldloc.0 + IL_0033: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0038: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003d: brtrue.s IL_0027 + IL_003f: ret + } + """); } [Fact] @@ -7043,6 +11160,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x21 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 34 (0x22) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldc.i4.0 + IL_0001: call "C.Enumerator Extensions.GetAsyncEnumerator(E)" + IL_0006: stloc.0 + IL_0007: br.s IL_0014 + IL_0009: ldloc.0 + IL_000a: callvirt "int C.Enumerator.Current.get" + IL_000f: call "void System.Console.Write(int)" + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_0009 + IL_0021: ret + } + """); } [Fact] @@ -7073,6 +11217,36 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x29 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 42 (0x2a) + .maxstack 1 + .locals init (C.Enumerator V_0, + int? V_1) + IL_0000: ldloca.s V_1 + IL_0002: initobj "int?" + IL_0008: ldloc.1 + IL_0009: call "C.Enumerator Extensions.GetAsyncEnumerator(int?)" + IL_000e: stloc.0 + IL_000f: br.s IL_001c + IL_0011: ldloc.0 + IL_0012: callvirt "int C.Enumerator.Current.get" + IL_0017: call "void System.Console.Write(int)" + IL_001c: ldloc.0 + IL_001d: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0022: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0027: brtrue.s IL_0011 + IL_0029: ret + } + """); } [Fact] @@ -7103,6 +11277,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x21 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 34 (0x22) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldnull + IL_0001: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_0006: stloc.0 + IL_0007: br.s IL_0014 + IL_0009: ldloc.0 + IL_000a: callvirt "int C.Enumerator.Current.get" + IL_000f: call "void System.Console.Write(int)" + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_0009 + IL_0021: ret + } + """); } [Fact] @@ -7133,6 +11334,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "object..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7169,6 +11397,64 @@ public static async IAsyncEnumerator GetAsyncEnumerator(this Range range) parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 95 (0x5f) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + object V_1) + IL_0000: ldc.i4.1 + IL_0001: call "System.Index System.Index.op_Implicit(int)" + IL_0006: ldc.i4.4 + IL_0007: call "System.Index System.Index.op_Implicit(int)" + IL_000c: newobj "System.Range..ctor(System.Index, System.Index)" + IL_0011: call "System.Collections.Generic.IAsyncEnumerator Extensions.GetAsyncEnumerator(System.Range)" + IL_0016: stloc.0 + IL_0017: ldnull + IL_0018: stloc.1 + .try + { + IL_0019: br.s IL_0026 + IL_001b: ldloc.0 + IL_001c: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0021: call "void System.Console.Write(int)" + IL_0026: ldloc.0 + IL_0027: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_002c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0031: brtrue.s IL_001b + IL_0033: leave.s IL_0038 + } + catch object + { + IL_0035: stloc.1 + IL_0036: leave.s IL_0038 + } + IL_0038: ldloc.0 + IL_0039: brfalse.s IL_0046 + IL_003b: ldloc.0 + IL_003c: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0041: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0046: ldloc.1 + IL_0047: brfalse.s IL_005e + IL_0049: ldloc.1 + IL_004a: isinst "System.Exception" + IL_004f: dup + IL_0050: brtrue.s IL_0054 + IL_0052: ldloc.1 + IL_0053: throw + IL_0054: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0059: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005e: ret + } + """); } [Fact] @@ -7201,6 +11487,63 @@ public static async IAsyncEnumerator GetAsyncEnumerator(this (T first, T s var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x55 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 86 (0x56) + .maxstack 3 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + object V_1) + IL_0000: ldc.i4.1 + IL_0001: ldc.i4.2 + IL_0002: ldc.i4.3 + IL_0003: newobj "System.ValueTuple..ctor(int, int, int)" + IL_0008: call "System.Collections.Generic.IAsyncEnumerator Extensions.GetAsyncEnumerator(System.ValueTuple)" + IL_000d: stloc.0 + IL_000e: ldnull + IL_000f: stloc.1 + .try + { + IL_0010: br.s IL_001d + IL_0012: ldloc.0 + IL_0013: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0018: call "void System.Console.Write(int)" + IL_001d: ldloc.0 + IL_001e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0023: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0028: brtrue.s IL_0012 + IL_002a: leave.s IL_002f + } + catch object + { + IL_002c: stloc.1 + IL_002d: leave.s IL_002f + } + IL_002f: ldloc.0 + IL_0030: brfalse.s IL_003d + IL_0032: ldloc.0 + IL_0033: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0038: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003d: ldloc.1 + IL_003e: brfalse.s IL_0055 + IL_0040: ldloc.1 + IL_0041: isinst "System.Exception" + IL_0046: dup + IL_0047: brtrue.s IL_004b + IL_0049: ldloc.1 + IL_004a: throw + IL_004b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0050: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0055: ret + } + """); } [Fact] @@ -7236,9 +11579,117 @@ public static class Extensions }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: @"1.1 + string expectedOutput = @"1.1 2.2 -3.3"); +3.3"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xd4 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 213 (0xd5) + .maxstack 9 + .locals init (System.Collections.Generic.IAsyncEnumerator> V_0, + System.ValueTuple> V_1, + object V_2, + int V_3, //a + decimal V_4, //b + decimal V_5) + IL_0000: ldloca.s V_1 + IL_0002: ldc.i4.3 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" + IL_000e: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0013: newobj "System.Collections.Generic.List..ctor()" + IL_0018: dup + IL_0019: ldc.i4.1 + IL_001a: ldc.i4.0 + IL_001b: ldc.i4.0 + IL_001c: ldc.i4.0 + IL_001d: ldc.i4.1 + IL_001e: newobj "decimal..ctor(int, int, int, bool, byte)" + IL_0023: callvirt "void System.Collections.Generic.List.Add(decimal)" + IL_0028: dup + IL_0029: ldc.i4.2 + IL_002a: ldc.i4.0 + IL_002b: ldc.i4.0 + IL_002c: ldc.i4.0 + IL_002d: ldc.i4.1 + IL_002e: newobj "decimal..ctor(int, int, int, bool, byte)" + IL_0033: callvirt "void System.Collections.Generic.List.Add(decimal)" + IL_0038: dup + IL_0039: ldc.i4.3 + IL_003a: ldc.i4.0 + IL_003b: ldc.i4.0 + IL_003c: ldc.i4.0 + IL_003d: ldc.i4.1 + IL_003e: newobj "decimal..ctor(int, int, int, bool, byte)" + IL_0043: callvirt "void System.Collections.Generic.List.Add(decimal)" + IL_0048: call "System.ValueTuple>..ctor(int[], System.Collections.Generic.List)" + IL_004d: ldloc.1 + IL_004e: ldfld "int[] System.ValueTuple>.Item1" + IL_0053: ldloc.1 + IL_0054: ldfld "System.Collections.Generic.List System.ValueTuple>.Item2" + IL_0059: newobj "System.ValueTuple, System.Collections.Generic.IEnumerable>..ctor(System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable)" + IL_005e: call "System.Collections.Generic.IAsyncEnumerator> Extensions.GetAsyncEnumerator(System.ValueTuple, System.Collections.Generic.IEnumerable>)" + IL_0063: stloc.0 + IL_0064: ldnull + IL_0065: stloc.2 + .try + { + IL_0066: br.s IL_009c + IL_0068: ldloc.0 + IL_0069: callvirt "System.ValueTuple System.Collections.Generic.IAsyncEnumerator>.Current.get" + IL_006e: dup + IL_006f: ldfld "int System.ValueTuple.Item1" + IL_0074: stloc.3 + IL_0075: ldfld "decimal System.ValueTuple.Item2" + IL_007a: stloc.s V_4 + IL_007c: ldloc.3 + IL_007d: call "decimal decimal.op_Implicit(int)" + IL_0082: ldloc.s V_4 + IL_0084: call "decimal decimal.op_Addition(decimal, decimal)" + IL_0089: stloc.s V_5 + IL_008b: ldloca.s V_5 + IL_008d: call "System.Globalization.CultureInfo System.Globalization.CultureInfo.InvariantCulture.get" + IL_0092: call "string decimal.ToString(System.IFormatProvider)" + IL_0097: call "void System.Console.WriteLine(string)" + IL_009c: ldloc.0 + IL_009d: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator>.MoveNextAsync()" + IL_00a2: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_00a7: brtrue.s IL_0068 + IL_00a9: leave.s IL_00ae + } + catch object + { + IL_00ab: stloc.2 + IL_00ac: leave.s IL_00ae + } + IL_00ae: ldloc.0 + IL_00af: brfalse.s IL_00bc + IL_00b1: ldloc.0 + IL_00b2: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_00b7: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_00bc: ldloc.2 + IL_00bd: brfalse.s IL_00d4 + IL_00bf: ldloc.2 + IL_00c0: isinst "System.Exception" + IL_00c5: dup + IL_00c6: brtrue.s IL_00ca + IL_00c8: ldloc.2 + IL_00c9: throw + IL_00ca: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00cf: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00d4: ret + } + """); } [Fact] @@ -7352,6 +11803,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator1 V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator1 C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator1.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator1.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7439,6 +11917,64 @@ public static class Extensions var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5b } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0023 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: call "void System.Console.Write(int)" + IL_0023: ldloc.0 + IL_0024: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0029: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002e: brtrue.s IL_0018 + IL_0030: leave.s IL_0035 + } + catch object + { + IL_0032: stloc.2 + IL_0033: leave.s IL_0035 + } + IL_0035: ldloc.0 + IL_0036: brfalse.s IL_0043 + IL_0038: ldloc.0 + IL_0039: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_003e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0043: ldloc.2 + IL_0044: brfalse.s IL_005b + IL_0046: ldloc.2 + IL_0047: isinst "System.Exception" + IL_004c: dup + IL_004d: brtrue.s IL_0051 + IL_004f: ldloc.2 + IL_0050: throw + IL_0051: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0056: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005b: ret + } + """); } [Fact] @@ -7469,12 +12005,14 @@ public static class Extensions { public static C.Enumerator2 GetAsyncEnumerator(this C self) => throw null; }"; + // (9,33): error CS8416: Cannot use a collection of dynamic type in an asynchronous foreach + // await foreach (var i in (dynamic)new C()) + DiagnosticDescription expected = Diagnostic(ErrorCode.ERR_BadDynamicAwaitForEach, "(dynamic)new C()").WithLocation(9, 33); CreateCompilationWithCSharp(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9) - .VerifyDiagnostics( - // (9,33): error CS8416: Cannot use a collection of dynamic type in an asynchronous foreach - // await foreach (var i in (dynamic)new C()) - Diagnostic(ErrorCode.ERR_BadDynamicAwaitForEach, "(dynamic)new C()").WithLocation(9, 33) - ); + .VerifyDiagnostics(expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expected); } [Fact] @@ -7629,6 +12167,33 @@ public static class Extensions2 var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions2.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7749,6 +12314,33 @@ public static class Extensions2 var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions2.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7825,6 +12417,33 @@ public static class Extensions2 var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions1.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7896,6 +12515,34 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "23"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("23", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x26 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 39 (0x27) + .maxstack 2 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: ldc.i4.1 + IL_0006: call "C.Enumerator Extensions.GetAsyncEnumerator(C, int)" + IL_000b: stloc.0 + IL_000c: br.s IL_0019 + IL_000e: ldloc.0 + IL_000f: callvirt "int C.Enumerator.Current.get" + IL_0014: call "void System.Console.Write(int)" + IL_0019: ldloc.0 + IL_001a: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: brtrue.s IL_000e + IL_0026: ret + } + """); } [Fact] @@ -7963,6 +12610,34 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2a } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 43 (0x2b) + .maxstack 2 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "int[] System.Array.Empty()" + IL_000a: call "C.Enumerator Extensions.GetAsyncEnumerator(C, params int[])" + IL_000f: stloc.0 + IL_0010: br.s IL_001d + IL_0012: ldloc.0 + IL_0013: callvirt "int C.Enumerator.Current.get" + IL_0018: call "void System.Console.Write(int)" + IL_001d: ldloc.0 + IL_001e: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0023: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: brtrue.s IL_0012 + IL_002a: ret + } + """); } [Fact] @@ -8099,6 +12774,36 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2b } + """ + }); + verifier.VerifyIL("C.Main()", $$""" + { + // Code size 44 (0x2c) + .maxstack 2 + .locals init (C.Enumerator V_0, + C V_1) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "C" + IL_0009: call "C.Enumerator Extensions.GetAsyncEnumerator({{modifier}} C)" + IL_000e: stloc.0 + IL_000f: br.s IL_001d + IL_0011: ldloca.s V_0 + IL_0013: call "readonly int C.Enumerator.Current.get" + IL_0018: call "void System.Console.Write(int)" + IL_001d: ldloca.s V_0 + IL_001f: call "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0024: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0029: brtrue.s IL_0011 + IL_002b: ret + } + """); } [Theory] @@ -8132,6 +12837,36 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2c } + """ + }); + verifier.VerifyIL("C.Main()", $$""" + { + // Code size 45 (0x2d) + .maxstack 1 + .locals init (C V_0, //c + C.Enumerator V_1) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C" + IL_0008: ldloca.s V_0 + IL_000a: call "C.Enumerator Extensions.GetAsyncEnumerator({{modifier}} C)" + IL_000f: stloc.1 + IL_0010: br.s IL_001e + IL_0012: ldloca.s V_1 + IL_0014: call "readonly int C.Enumerator.Current.get" + IL_0019: call "void System.Console.Write(int)" + IL_001e: ldloca.s V_1 + IL_0020: call "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0025: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002a: brtrue.s IL_0012 + IL_002c: ret + } + """); } [Fact] @@ -8195,6 +12930,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8225,6 +12987,33 @@ internal static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8374,6 +13163,33 @@ internal static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8407,6 +13223,33 @@ internal static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8440,6 +13283,58 @@ struct Enumerator : IAsyncDisposable var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: @"123Disposed"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123Disposed", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x52 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 83 (0x53) + .maxstack 2 + .locals init (Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001b + IL_000f: ldloca.s V_0 + IL_0011: call "readonly int Enumerator.Current.get" + IL_0016: call "void System.Console.Write(int)" + IL_001b: ldloca.s V_0 + IL_001d: call "System.Threading.Tasks.Task Enumerator.MoveNextAsync()" + IL_0022: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0027: brtrue.s IL_000f + IL_0029: leave.s IL_002e + } + catch object + { + IL_002b: stloc.1 + IL_002c: leave.s IL_002e + } + IL_002e: ldloca.s V_0 + IL_0030: call "System.Threading.Tasks.ValueTask Enumerator.DisposeAsync()" + IL_0035: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003a: ldloc.1 + IL_003b: brfalse.s IL_0052 + IL_003d: ldloc.1 + IL_003e: isinst "System.Exception" + IL_0043: dup + IL_0044: brtrue.s IL_0048 + IL_0046: ldloc.1 + IL_0047: throw + IL_0048: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0052: ret + } + """); } [Fact] @@ -8474,6 +13369,59 @@ struct Enumerator : IAsyncDisposable var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: @"123Disposed"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123Disposed", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x58 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 2 + .locals init (Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001b + IL_000f: ldloca.s V_0 + IL_0011: call "readonly int Enumerator.Current.get" + IL_0016: call "void System.Console.Write(int)" + IL_001b: ldloca.s V_0 + IL_001d: call "System.Threading.Tasks.Task Enumerator.MoveNextAsync()" + IL_0022: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0027: brtrue.s IL_000f + IL_0029: leave.s IL_002e + } + catch object + { + IL_002b: stloc.1 + IL_002c: leave.s IL_002e + } + IL_002e: ldloca.s V_0 + IL_0030: constrained. "Enumerator" + IL_0036: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_003b: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0040: ldloc.1 + IL_0041: brfalse.s IL_0058 + IL_0043: ldloc.1 + IL_0044: isinst "System.Exception" + IL_0049: dup + IL_004a: brtrue.s IL_004e + IL_004c: ldloc.1 + IL_004d: throw + IL_004e: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0053: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0058: ret + } + """); } [Fact] @@ -8507,6 +13455,58 @@ struct Enumerator var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: @"123Disposed"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123Disposed", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x52 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 83 (0x53) + .maxstack 2 + .locals init (Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001b + IL_000f: ldloca.s V_0 + IL_0011: call "readonly int Enumerator.Current.get" + IL_0016: call "void System.Console.Write(int)" + IL_001b: ldloca.s V_0 + IL_001d: call "System.Threading.Tasks.Task Enumerator.MoveNextAsync()" + IL_0022: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0027: brtrue.s IL_000f + IL_0029: leave.s IL_002e + } + catch object + { + IL_002b: stloc.1 + IL_002c: leave.s IL_002e + } + IL_002e: ldloca.s V_0 + IL_0030: call "System.Threading.Tasks.ValueTask Enumerator.DisposeAsync()" + IL_0035: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003a: ldloc.1 + IL_003b: brfalse.s IL_0052 + IL_003d: ldloc.1 + IL_003e: isinst "System.Exception" + IL_0043: dup + IL_0044: brtrue.s IL_0048 + IL_0046: ldloc.1 + IL_0047: throw + IL_0048: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0052: ret + } + """); } [Fact] @@ -8537,6 +13537,33 @@ public static class Extensions var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8586,6 +13613,60 @@ public static class Extensions Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "foreach").WithArguments("C.Enumerator.Current").WithLocation(8, 15) ); CompileAndVerify(comp, expectedOutput: "123Disposed"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123Disposed", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x52 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 83 (0x53) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001a + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: call "void System.Console.Write(int)" + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0020: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: brtrue.s IL_000f + IL_0027: leave.s IL_002c + } + catch object + { + IL_0029: stloc.1 + IL_002a: leave.s IL_002c + } + IL_002c: ldloc.0 + IL_002d: brfalse.s IL_003a + IL_002f: ldloc.0 + IL_0030: callvirt "System.Threading.Tasks.Task C.Enumerator.DisposeAsync()" + IL_0035: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003a: ldloc.1 + IL_003b: brfalse.s IL_0052 + IL_003d: ldloc.1 + IL_003e: isinst "System.Exception" + IL_0043: dup + IL_0044: brtrue.s IL_0048 + IL_0046: ldloc.1 + IL_0047: throw + IL_0048: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0052: ret + } + """); } [Fact] @@ -8620,6 +13701,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator N.Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8738,6 +13846,33 @@ public sealed class Enumerator var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("N1.N2.N3.C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (N1.N2.N3.C.Enumerator V_0) + IL_0000: newobj "N1.N2.N3.C..ctor()" + IL_0005: call "N1.N2.N3.C.Enumerator N1.N2.Extensions.GetAsyncEnumerator(N1.N2.N3.C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int N1.N2.N3.C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task N1.N2.N3.C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8790,6 +13925,33 @@ public static class Extensions // using N3; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N3;").WithLocation(5, 1)); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("N1.C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (N1.C.Enumerator V_0) + IL_0000: newobj "N1.C..ctor()" + IL_0005: call "N1.C.Enumerator N2.Extensions.GetAsyncEnumerator(N1.C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int N1.C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task N1.C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8932,6 +14094,33 @@ public sealed class Enumerator var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("Program.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Program.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8967,6 +14156,33 @@ public sealed class Enumerator var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("Program.Inner.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Program.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -9037,6 +14253,46 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x58 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "ref C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: ldobj "C.Enumerator" + IL_000f: stloc.0 + IL_0010: br.s IL_001e + IL_0012: ldloca.s V_0 + IL_0014: call "readonly int C.Enumerator.Current.get" + IL_0019: call "void System.Console.Write(int)" + IL_001e: ldloca.s V_0 + IL_0020: call "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0025: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002a: brtrue.s IL_0012 + IL_002c: newobj "C..ctor()" + IL_0031: call "ref C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_0036: ldobj "C.Enumerator" + IL_003b: stloc.0 + IL_003c: br.s IL_004a + IL_003e: ldloca.s V_0 + IL_0040: call "readonly int C.Enumerator.Current.get" + IL_0045: call "void System.Console.Write(int)" + IL_004a: ldloca.s V_0 + IL_004c: call "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0051: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0056: brtrue.s IL_003e + IL_0058: ret + } + """); } [Theory, WorkItem(59955, "https://github.com/dotnet/roslyn/issues/59955")] @@ -9092,6 +14348,67 @@ public async ValueTask DisposeAsync() { comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "RAN"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("RAN", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (AsyncEnumerator V_0, + AsyncEnumerable V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "AsyncEnumerable" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: call "AsyncEnumerator AsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0017: stloc.0 + IL_0018: ldnull + IL_0019: stloc.3 + .try + { + IL_001a: br.s IL_0024 + IL_001c: ldloca.s V_0 + IL_001e: call "int AsyncEnumerator.Current.get" + IL_0023: pop + IL_0024: ldloca.s V_0 + IL_0026: call "System.Threading.Tasks.ValueTask AsyncEnumerator.MoveNextAsync()" + IL_002b: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0030: brtrue.s IL_001c + IL_0032: leave.s IL_0037 + } + catch object + { + IL_0034: stloc.3 + IL_0035: leave.s IL_0037 + } + IL_0037: ldloca.s V_0 + IL_0039: call "System.Threading.Tasks.ValueTask AsyncEnumerator.DisposeAsync()" + IL_003e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0043: ldloc.3 + IL_0044: brfalse.s IL_005b + IL_0046: ldloc.3 + IL_0047: isinst "System.Exception" + IL_004c: dup + IL_004d: brtrue.s IL_0051 + IL_004f: ldloc.3 + IL_0050: throw + IL_0051: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0056: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005b: ret + } + """); } else { @@ -9168,6 +14485,67 @@ public async ValueTask DisposeAsync() { comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "RAN"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("RAN", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (AsyncEnumerator V_0, + AsyncEnumerable V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "AsyncEnumerable" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: call "AsyncEnumerator AsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0017: stloc.0 + IL_0018: ldnull + IL_0019: stloc.3 + .try + { + IL_001a: br.s IL_0024 + IL_001c: ldloca.s V_0 + IL_001e: call "int AsyncEnumerator.Current.get" + IL_0023: pop + IL_0024: ldloca.s V_0 + IL_0026: call "System.Threading.Tasks.ValueTask AsyncEnumerator.MoveNextAsync()" + IL_002b: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0030: brtrue.s IL_001c + IL_0032: leave.s IL_0037 + } + catch object + { + IL_0034: stloc.3 + IL_0035: leave.s IL_0037 + } + IL_0037: ldloca.s V_0 + IL_0039: call "System.Threading.Tasks.ValueTask AsyncEnumerator.DisposeAsync()" + IL_003e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0043: ldloc.3 + IL_0044: brfalse.s IL_005b + IL_0046: ldloc.3 + IL_0047: isinst "System.Exception" + IL_004c: dup + IL_004d: brtrue.s IL_0051 + IL_004f: ldloc.3 + IL_0050: throw + IL_0051: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0056: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005b: ret + } + """); } else { @@ -9246,6 +14624,70 @@ async ValueTask IAsyncDisposable.DisposeAsync() { comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "RAN"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("RAN", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x61 } + [System.Collections.Generic.IAsyncEnumerator.MoveNextAsync]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [System.IAsyncDisposable.DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 98 (0x62) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + AsyncEnumerable V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "AsyncEnumerable" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: constrained. "AsyncEnumerable" + IL_0018: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_001d: stloc.0 + IL_001e: ldnull + IL_001f: stloc.3 + .try + { + IL_0020: br.s IL_0029 + IL_0022: ldloc.0 + IL_0023: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0028: pop + IL_0029: ldloc.0 + IL_002a: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_002f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0034: brtrue.s IL_0022 + IL_0036: leave.s IL_003b + } + catch object + { + IL_0038: stloc.3 + IL_0039: leave.s IL_003b + } + IL_003b: ldloc.0 + IL_003c: brfalse.s IL_0049 + IL_003e: ldloc.0 + IL_003f: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0044: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0049: ldloc.3 + IL_004a: brfalse.s IL_0061 + IL_004c: ldloc.3 + IL_004d: isinst "System.Exception" + IL_0052: dup + IL_0053: brtrue.s IL_0057 + IL_0055: ldloc.3 + IL_0056: throw + IL_0057: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_005c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0061: ret + } + """); } else { @@ -9378,6 +14820,67 @@ public async ValueTask DisposeAsync() { comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "RAN"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("RAN", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (AsyncEnumerator V_0, + AsyncEnumerable V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "AsyncEnumerable" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: call "AsyncEnumerator AsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0017: stloc.0 + IL_0018: ldnull + IL_0019: stloc.3 + .try + { + IL_001a: br.s IL_0024 + IL_001c: ldloca.s V_0 + IL_001e: call "System.ValueTuple AsyncEnumerator.Current.get" + IL_0023: pop + IL_0024: ldloca.s V_0 + IL_0026: call "System.Threading.Tasks.ValueTask AsyncEnumerator.MoveNextAsync()" + IL_002b: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0030: brtrue.s IL_001c + IL_0032: leave.s IL_0037 + } + catch object + { + IL_0034: stloc.3 + IL_0035: leave.s IL_0037 + } + IL_0037: ldloca.s V_0 + IL_0039: call "System.Threading.Tasks.ValueTask AsyncEnumerator.DisposeAsync()" + IL_003e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0043: ldloc.3 + IL_0044: brfalse.s IL_005b + IL_0046: ldloc.3 + IL_0047: isinst "System.Exception" + IL_004c: dup + IL_004d: brtrue.s IL_0051 + IL_004f: ldloc.3 + IL_0050: throw + IL_0051: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0056: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005b: ret + } + """); } else { @@ -9479,9 +14982,77 @@ static async Task Test() } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var expectedOutput = ExecutionConditionUtil.IsMonoOrCoreClr ? "D" : null; CompileAndVerify(comp, - expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "D" : null, + expectedOutput: expectedOutput, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(src); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa } + [Test]: Return value missing on the stack. { Offset = 0x7f } + """ + }); + verifier.VerifyIL("C.Test()", """ + { + // Code size 128 (0x80) + .maxstack 2 + .locals init (TEnumerator V_0, + TEnumerable V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "TEnumerable" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: constrained. "TEnumerable" + IL_0018: callvirt "TEnumerator IGetEnumerator.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_001d: stloc.0 + IL_001e: ldnull + IL_001f: stloc.3 + .try + { + IL_0020: br.s IL_0034 + IL_0022: ldloca.s V_0 + IL_0024: constrained. "TEnumerator" + IL_002a: callvirt "int ICustomEnumerator.Current.get" + IL_002f: call "void System.Console.Write(int)" + IL_0034: ldloca.s V_0 + IL_0036: constrained. "TEnumerator" + IL_003c: callvirt "System.Threading.Tasks.ValueTask ICustomEnumerator.MoveNextAsync()" + IL_0041: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0046: brtrue.s IL_0022 + IL_0048: leave.s IL_004d + } + catch object + { + IL_004a: stloc.3 + IL_004b: leave.s IL_004d + } + IL_004d: ldloc.0 + IL_004e: box "TEnumerator" + IL_0053: brfalse.s IL_0067 + IL_0055: ldloca.s V_0 + IL_0057: constrained. "TEnumerator" + IL_005d: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0062: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0067: ldloc.3 + IL_0068: brfalse.s IL_007f + IL_006a: ldloc.3 + IL_006b: isinst "System.Exception" + IL_0070: dup + IL_0071: brtrue.s IL_0075 + IL_0073: ldloc.3 + IL_0074: throw + IL_0075: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_007a: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_007f: ret + } + """); } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs index 39bf18e7fc8e2..1319dbb8d1d89 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -212,7 +212,60 @@ public async ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "C body DisposeAsync1 DisposeAsync2 end"); + string expectedOutput = "C body DisposeAsync1 DisposeAsync2 end"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x29 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + [
b__1_0]: Return value missing on the stack. { Offset = 0x47 } + """ + }); + verifier.VerifyIL("C.<>c.
b__1_0()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (C V_0, //y + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "body " + IL_000d: call "void System.Console.Write(string)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "end" + IL_0042: call "void System.Console.Write(string)" + IL_0047: ret + } + """); } [Fact] @@ -400,7 +453,79 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "try using dispose_start dispose_end end"); + string expectedOutput = "try using dispose_start dispose_end end"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x62 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 99 (0x63) + .maxstack 2 + .locals init (int V_0, + C V_1, //x + object V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldstr "try " + IL_0007: call "void System.Console.Write(string)" + IL_000c: newobj "System.ArgumentNullException..ctor()" + IL_0011: throw + } + catch System.ArgumentNullException + { + IL_0012: pop + IL_0013: ldc.i4.1 + IL_0014: stloc.0 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: ldc.i4.1 + IL_0019: bne.un.s IL_0062 + IL_001b: newobj "C..ctor()" + IL_0020: stloc.1 + IL_0021: ldnull + IL_0022: stloc.2 + .try + { + IL_0023: ldstr "using " + IL_0028: call "void System.Console.Write(string)" + IL_002d: leave.s IL_0032 + } + catch object + { + IL_002f: stloc.2 + IL_0030: leave.s IL_0032 + } + IL_0032: ldloc.1 + IL_0033: brfalse.s IL_0040 + IL_0035: ldloc.1 + IL_0036: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_003b: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0040: ldloc.2 + IL_0041: brfalse.s IL_0058 + IL_0043: ldloc.2 + IL_0044: isinst "System.Exception" + IL_0049: dup + IL_004a: brtrue.s IL_004e + IL_004c: ldloc.2 + IL_004d: throw + IL_004e: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0053: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0058: ldstr "end" + IL_005d: call "void System.Console.Write(string)" + IL_0062: ret + } + """); } [Fact] @@ -513,9 +638,84 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() } } "; + string expectedOutput = "using dispose_start dispose_end return"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x67, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (object V_0, + C V_1, //x + object V_2) + IL_0000: ldnull + IL_0001: stloc.0 + .try + { + IL_0002: leave.s IL_0007 + } + catch object + { + IL_0004: stloc.0 + IL_0005: leave.s IL_0007 + } + IL_0007: newobj "C..ctor()" + IL_000c: stloc.1 + IL_000d: ldnull + IL_000e: stloc.2 + .try + { + IL_000f: ldstr "using " + IL_0014: call "void System.Console.Write(string)" + IL_0019: leave.s IL_001e + } + catch object + { + IL_001b: stloc.2 + IL_001c: leave.s IL_001e + } + IL_001e: ldloc.1 + IL_001f: brfalse.s IL_002c + IL_0021: ldloc.1 + IL_0022: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0027: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002c: ldloc.2 + IL_002d: brfalse.s IL_0044 + IL_002f: ldloc.2 + IL_0030: isinst "System.Exception" + IL_0035: dup + IL_0036: brtrue.s IL_003a + IL_0038: ldloc.2 + IL_0039: throw + IL_003a: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003f: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0044: ldloc.0 + IL_0045: brfalse.s IL_005c + IL_0047: ldloc.0 + IL_0048: isinst "System.Exception" + IL_004d: dup + IL_004e: brtrue.s IL_0052 + IL_0050: ldloc.0 + IL_0051: throw + IL_0052: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0057: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005c: ldstr "return" + IL_0061: call "void System.Console.Write(string)" + IL_0066: ldc.i4.1 + IL_0067: ret + } + """); } [Fact] @@ -551,7 +751,74 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using caught message"); + string expectedOutput = "using caught message"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x61 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x3c } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 98 (0x62) + .maxstack 2 + .locals init (C V_0, //x + object V_1, + System.Exception V_2) //e + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(string)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: leave.s IL_0057 + } + catch System.Exception + { + IL_003f: stloc.2 + IL_0040: ldstr "caught " + IL_0045: ldloc.2 + IL_0046: callvirt "string System.Exception.Message.get" + IL_004b: call "string string.Concat(string, string)" + IL_0050: call "void System.Console.Write(string)" + IL_0055: leave.s IL_0061 + } + IL_0057: ldstr "SKIPPED" + IL_005c: call "void System.Console.Write(string)" + IL_0061: ret + } + """); } [Fact] @@ -578,7 +845,65 @@ static async Task Main() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "before after"); + string expectedOutput = "before after"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x58, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 2 + .locals init (object V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2) + IL_0000: ldnull + IL_0001: stloc.0 + .try + { + IL_0002: leave.s IL_0007 + } + catch object + { + IL_0004: stloc.0 + IL_0005: leave.s IL_0007 + } + IL_0007: ldstr "before " + IL_000c: call "void System.Console.Write(string)" + IL_0011: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0016: stloc.2 + IL_0017: ldloca.s V_2 + IL_0019: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0026: brtrue.s IL_002e + IL_0028: ldloc.1 + IL_0029: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_002e: ldloca.s V_1 + IL_0030: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0035: ldstr "after" + IL_003a: call "void System.Console.Write(string)" + IL_003f: ldloc.0 + IL_0040: brfalse.s IL_0057 + IL_0042: ldloc.0 + IL_0043: isinst "System.Exception" + IL_0048: dup + IL_0049: brtrue.s IL_004d + IL_004b: ldloc.0 + IL_004c: throw + IL_004d: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0052: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0057: ldc.i4.1 + IL_0058: ret + } + """); } [Fact] @@ -908,7 +1233,70 @@ public void Dispose() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 75 (0x4b) + .maxstack 2 + .locals init (C V_0, //x + object V_1, + int V_2, + int V_3) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + IL_0008: ldc.i4.0 + IL_0009: stloc.2 + .try + { + IL_000a: ldstr "body " + IL_000f: call "void System.Console.Write(string)" + IL_0014: ldc.i4.1 + IL_0015: stloc.3 + IL_0016: ldc.i4.1 + IL_0017: stloc.2 + IL_0018: leave.s IL_001d + } + catch object + { + IL_001a: stloc.1 + IL_001b: leave.s IL_001d + } + IL_001d: ldloc.0 + IL_001e: brfalse.s IL_002b + IL_0020: ldloc.0 + IL_0021: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0026: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002b: ldloc.1 + IL_002c: brfalse.s IL_0043 + IL_002e: ldloc.1 + IL_002f: isinst "System.Exception" + IL_0034: dup + IL_0035: brtrue.s IL_0039 + IL_0037: ldloc.1 + IL_0038: throw + IL_0039: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0043: ldloc.2 + IL_0044: ldc.i4.1 + IL_0045: bne.un.s IL_0049 + IL_0047: ldloc.3 + IL_0048: ret + IL_0049: ldnull + IL_004a: throw + } + """); } [Fact] @@ -1065,7 +1453,17 @@ System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe, references: new[] { CSharpRef }); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body DisposeAsync end"); + var expectedOutput = "body DisposeAsync end"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // https://github.com/dotnet/roslyn/issues/79762: Test dynamic + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,30): error CS9328: Method 'C.Main()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // await using (dynamic x = new C()) + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "x = new C()").WithArguments("C.Main()").WithLocation(6, 30) + ); } [Fact] @@ -1092,7 +1490,16 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe, references: new[] { CSharpRef }); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body DisposeAsync end"); + string expectedOutput = "body DisposeAsync end"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // https://github.com/dotnet/roslyn/issues/79762: Test dynamic + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,30): error CS9328: Method 'C.Main()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. + // await using (dynamic x = new C()) + Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "x = new C()").WithArguments("C.Main()").WithLocation(6, 30) + ); } [Fact] @@ -1118,7 +1525,8 @@ System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 306 (0x132) @@ -1243,7 +1651,8 @@ .locals init (int V_0, IL_00fa: ldarg.0 IL_00fb: ldnull IL_00fc: stfld ""C C.
d__0.<>s__1"" - IL_0101: leave.s IL_011d + IL_0101: ldnull + IL_0102: throw } catch System.Exception { @@ -1267,6 +1676,64 @@ .locals init (int V_0, IL_0130: nop IL_0131: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x45 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (C V_0, + object V_1, + int V_2) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + IL_0008: ldc.i4.0 + IL_0009: stloc.2 + .try + { + IL_000a: ldstr "body " + IL_000f: call "void System.Console.Write(string)" + IL_0014: ldc.i4.1 + IL_0015: stloc.2 + IL_0016: leave.s IL_001b + } + catch object + { + IL_0018: stloc.1 + IL_0019: leave.s IL_001b + } + IL_001b: ldloc.0 + IL_001c: brfalse.s IL_0029 + IL_001e: ldloc.0 + IL_001f: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0024: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0029: ldloc.1 + IL_002a: brfalse.s IL_0041 + IL_002c: ldloc.1 + IL_002d: isinst "System.Exception" + IL_0032: dup + IL_0033: brtrue.s IL_0037 + IL_0035: ldloc.1 + IL_0036: throw + IL_0037: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0041: ldloc.2 + IL_0042: ldc.i4.1 + IL_0043: bne.un.s IL_0046 + IL_0045: ret + IL_0046: ldnull + IL_0047: throw + } + """); } [Fact] @@ -1292,7 +1759,8 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 306 (0x132) @@ -1417,7 +1885,8 @@ .locals init (int V_0, IL_00fa: ldarg.0 IL_00fb: ldnull IL_00fc: stfld ""C C.
d__0.<>s__1"" - IL_0101: leave.s IL_011d + IL_0101: ldnull + IL_0102: throw } catch System.Exception { @@ -1442,6 +1911,64 @@ .locals init (int V_0, IL_0131: ret } "); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x45 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (C V_0, + object V_1, + int V_2) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + IL_0008: ldc.i4.0 + IL_0009: stloc.2 + .try + { + IL_000a: ldstr "body " + IL_000f: call "void System.Console.Write(string)" + IL_0014: ldc.i4.1 + IL_0015: stloc.2 + IL_0016: leave.s IL_001b + } + catch object + { + IL_0018: stloc.1 + IL_0019: leave.s IL_001b + } + IL_001b: ldloc.0 + IL_001c: brfalse.s IL_0029 + IL_001e: ldloc.0 + IL_001f: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0024: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0029: ldloc.1 + IL_002a: brfalse.s IL_0041 + IL_002c: ldloc.1 + IL_002d: isinst "System.Exception" + IL_0032: dup + IL_0033: brtrue.s IL_0037 + IL_0035: ldloc.1 + IL_0036: throw + IL_0037: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0041: ldloc.2 + IL_0042: ldc.i4.1 + IL_0043: bne.un.s IL_0046 + IL_0045: ret + IL_0046: ldnull + IL_0047: throw + } + """); } [Fact] @@ -1467,7 +1994,9 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 306 (0x132) @@ -1592,7 +2121,8 @@ .locals init (int V_0, IL_00fa: ldarg.0 IL_00fb: ldnull IL_00fc: stfld ""C C.
d__0.<>s__1"" - IL_0101: leave.s IL_011d + IL_0101: ldnull + IL_0102: throw } catch System.Exception { @@ -1617,6 +2147,64 @@ .locals init (int V_0, IL_0131: ret } "); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x45 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (C V_0, + object V_1, + int V_2) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + IL_0008: ldc.i4.0 + IL_0009: stloc.2 + .try + { + IL_000a: ldstr "body " + IL_000f: call "void System.Console.Write(string)" + IL_0014: ldc.i4.1 + IL_0015: stloc.2 + IL_0016: leave.s IL_001b + } + catch object + { + IL_0018: stloc.1 + IL_0019: leave.s IL_001b + } + IL_001b: ldloc.0 + IL_001c: brfalse.s IL_0029 + IL_001e: ldloc.0 + IL_001f: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0024: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0029: ldloc.1 + IL_002a: brfalse.s IL_0041 + IL_002c: ldloc.1 + IL_002d: isinst "System.Exception" + IL_0032: dup + IL_0033: brtrue.s IL_0037 + IL_0035: ldloc.1 + IL_0036: throw + IL_0037: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0041: ldloc.2 + IL_0042: ldc.i4.1 + IL_0043: bne.un.s IL_0046 + IL_0045: ret + IL_0046: ldnull + IL_0047: throw + } + """); } [Fact] @@ -1637,7 +2225,26 @@ public static async System.Threading.Tasks.Task Main() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body"); + string expectedOutput = "body"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: ldstr "body" + IL_0005: call "void System.Console.Write(string)" + IL_000a: ret + } + """); } [Fact] @@ -1686,7 +2293,83 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe, references: new[] { CSharpRef }); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x86 } + """ + }); + + verifier.VerifyIL("C.Main()", """ + { + // Code size 137 (0x89) + .maxstack 3 + .locals init (object V_0, //d + System.IAsyncDisposable V_1, + object V_2, + int V_3) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0" + IL_000b: brtrue.s IL_0031 + IL_000d: ldc.i4.0 + IL_000e: ldtoken "System.IAsyncDisposable" + IL_0013: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_0018: ldtoken "C" + IL_001d: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_0022: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)" + IL_0027: call "System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)" + IL_002c: stsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0" + IL_0031: ldsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0" + IL_0036: ldfld "System.Func System.Runtime.CompilerServices.CallSite>.Target" + IL_003b: ldsfld "System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0" + IL_0040: ldloc.0 + IL_0041: callvirt "System.IAsyncDisposable System.Func.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)" + IL_0046: stloc.1 + IL_0047: ldnull + IL_0048: stloc.2 + IL_0049: ldc.i4.0 + IL_004a: stloc.3 + .try + { + IL_004b: ldstr "body " + IL_0050: call "void System.Console.Write(string)" + IL_0055: ldc.i4.1 + IL_0056: stloc.3 + IL_0057: leave.s IL_005c + } + catch object + { + IL_0059: stloc.2 + IL_005a: leave.s IL_005c + } + IL_005c: ldloc.1 + IL_005d: brfalse.s IL_006a + IL_005f: ldloc.1 + IL_0060: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0065: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_006a: ldloc.2 + IL_006b: brfalse.s IL_0082 + IL_006d: ldloc.2 + IL_006e: isinst "System.Exception" + IL_0073: dup + IL_0074: brtrue.s IL_0078 + IL_0076: ldloc.2 + IL_0077: throw + IL_0078: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_007d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0082: ldloc.3 + IL_0083: ldc.i4.1 + IL_0084: bne.un.s IL_0087 + IL_0086: ret + IL_0087: ldnull + IL_0088: throw + } + """); } [Fact] @@ -1712,7 +2395,8 @@ System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); verifier.VerifyIL("S.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 298 (0x12a) @@ -1832,7 +2516,8 @@ .locals init (int V_0, IL_00f2: ldarg.0 IL_00f3: ldnull IL_00f4: stfld ""object S.
d__0.<>s__2"" - IL_00f9: leave.s IL_0115 + IL_00f9: ldnull + IL_00fa: throw } catch System.Exception { @@ -1856,6 +2541,63 @@ .locals init (int V_0, IL_0128: nop IL_0129: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x4b } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 78 (0x4e) + .maxstack 2 + .locals init (S V_0, + object V_1, + int V_2) + IL_0000: ldloca.s V_0 + IL_0002: initobj "S" + IL_0008: ldnull + IL_0009: stloc.1 + IL_000a: ldc.i4.0 + IL_000b: stloc.2 + .try + { + IL_000c: ldstr "body " + IL_0011: call "void System.Console.Write(string)" + IL_0016: ldc.i4.1 + IL_0017: stloc.2 + IL_0018: leave.s IL_001d + } + catch object + { + IL_001a: stloc.1 + IL_001b: leave.s IL_001d + } + IL_001d: ldloca.s V_0 + IL_001f: constrained. "S" + IL_0025: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_002a: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002f: ldloc.1 + IL_0030: brfalse.s IL_0047 + IL_0032: ldloc.1 + IL_0033: isinst "System.Exception" + IL_0038: dup + IL_0039: brtrue.s IL_003d + IL_003b: ldloc.1 + IL_003c: throw + IL_003d: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0042: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0047: ldloc.2 + IL_0048: ldc.i4.1 + IL_0049: bne.un.s IL_004c + IL_004b: ret + IL_004c: ldnull + IL_004d: throw + } + """); } [Fact] @@ -1881,7 +2623,8 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); verifier.VerifyIL("S.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 292 (0x124) @@ -2000,7 +2743,8 @@ .locals init (int V_0, IL_00ec: ldarg.0 IL_00ed: ldnull IL_00ee: stfld ""object S.
d__0.<>s__2"" - IL_00f3: leave.s IL_010f + IL_00f3: ldnull + IL_00f4: throw } catch System.Exception { @@ -2024,6 +2768,62 @@ .locals init (int V_0, IL_0122: nop IL_0123: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x45 } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (S V_0, + object V_1, + int V_2) + IL_0000: ldloca.s V_0 + IL_0002: initobj "S" + IL_0008: ldnull + IL_0009: stloc.1 + IL_000a: ldc.i4.0 + IL_000b: stloc.2 + .try + { + IL_000c: ldstr "body " + IL_0011: call "void System.Console.Write(string)" + IL_0016: ldc.i4.1 + IL_0017: stloc.2 + IL_0018: leave.s IL_001d + } + catch object + { + IL_001a: stloc.1 + IL_001b: leave.s IL_001d + } + IL_001d: ldloca.s V_0 + IL_001f: call "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_0024: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0029: ldloc.1 + IL_002a: brfalse.s IL_0041 + IL_002c: ldloc.1 + IL_002d: isinst "System.Exception" + IL_0032: dup + IL_0033: brtrue.s IL_0037 + IL_0035: ldloc.1 + IL_0036: throw + IL_0037: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0041: ldloc.2 + IL_0042: ldc.i4.1 + IL_0043: bne.un.s IL_0046 + IL_0045: ret + IL_0046: ldnull + IL_0047: throw + } + """); } [Fact] @@ -2060,6 +2860,60 @@ ValueTask IAsyncDisposable.DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "True"); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("True", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x4f } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 80 (0x50) + .maxstack 2 + .locals init (S V_0, //s + S V_1, + object V_2) + IL_0000: ldloca.s V_0 + IL_0002: newobj "C..ctor()" + IL_0007: call "S..ctor(C)" + IL_000c: ldloc.0 + IL_000d: stloc.1 + IL_000e: ldnull + IL_000f: stloc.2 + .try + { + IL_0010: leave.s IL_0015 + } + catch object + { + IL_0012: stloc.2 + IL_0013: leave.s IL_0015 + } + IL_0015: ldloca.s V_1 + IL_0017: constrained. "S" + IL_001d: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0022: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0027: ldloc.2 + IL_0028: brfalse.s IL_003f + IL_002a: ldloc.2 + IL_002b: isinst "System.Exception" + IL_0030: dup + IL_0031: brtrue.s IL_0035 + IL_0033: ldloc.2 + IL_0034: throw + IL_0035: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003a: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003f: ldloc.0 + IL_0040: ldfld "C S._c" + IL_0045: ldfld "bool C._disposed" + IL_004a: call "void System.Console.WriteLine(bool)" + IL_004f: ret + } + """); } [Fact] @@ -2087,6 +2941,74 @@ public System.Threading.Tasks.ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("body DisposeAsync", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x63 } + """ + }); + verifier.VerifyIL("S.Main", """ + { + // Code size 102 (0x66) + .maxstack 2 + .locals init (S V_0, + S? V_1, + object V_2, + int V_3) + IL_0000: ldloca.s V_0 + IL_0002: initobj "S" + IL_0008: ldloc.0 + IL_0009: newobj "S?..ctor(S)" + IL_000e: stloc.1 + IL_000f: ldnull + IL_0010: stloc.2 + IL_0011: ldc.i4.0 + IL_0012: stloc.3 + .try + { + IL_0013: ldstr "body " + IL_0018: call "void System.Console.Write(string)" + IL_001d: ldc.i4.1 + IL_001e: stloc.3 + IL_001f: leave.s IL_0024 + } + catch object + { + IL_0021: stloc.2 + IL_0022: leave.s IL_0024 + } + IL_0024: ldloca.s V_1 + IL_0026: call "readonly bool S?.HasValue.get" + IL_002b: brfalse.s IL_0047 + IL_002d: ldloca.s V_1 + IL_002f: call "readonly S S?.GetValueOrDefault()" + IL_0034: stloc.0 + IL_0035: ldloca.s V_0 + IL_0037: constrained. "S" + IL_003d: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0042: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0047: ldloc.2 + IL_0048: brfalse.s IL_005f + IL_004a: ldloc.2 + IL_004b: isinst "System.Exception" + IL_0050: dup + IL_0051: brtrue.s IL_0055 + IL_0053: ldloc.2 + IL_0054: throw + IL_0055: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_005a: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005f: ldloc.3 + IL_0060: ldc.i4.1 + IL_0061: bne.un.s IL_0064 + IL_0063: ret + IL_0064: ldnull + IL_0065: throw + } + """); } [Fact] @@ -2114,6 +3036,73 @@ public System.Threading.Tasks.ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "body"); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("body", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5e } + """ + }); + verifier.VerifyIL("S.Main", """ + { + // Code size 97 (0x61) + .maxstack 2 + .locals init (S? V_0, + object V_1, + int V_2, + S V_3) + IL_0000: ldloca.s V_0 + IL_0002: initobj "S?" + IL_0008: ldloc.0 + IL_0009: stloc.0 + IL_000a: ldnull + IL_000b: stloc.1 + IL_000c: ldc.i4.0 + IL_000d: stloc.2 + .try + { + IL_000e: ldstr "body" + IL_0013: call "void System.Console.Write(string)" + IL_0018: ldc.i4.1 + IL_0019: stloc.2 + IL_001a: leave.s IL_001f + } + catch object + { + IL_001c: stloc.1 + IL_001d: leave.s IL_001f + } + IL_001f: ldloca.s V_0 + IL_0021: call "readonly bool S?.HasValue.get" + IL_0026: brfalse.s IL_0042 + IL_0028: ldloca.s V_0 + IL_002a: call "readonly S S?.GetValueOrDefault()" + IL_002f: stloc.3 + IL_0030: ldloca.s V_3 + IL_0032: constrained. "S" + IL_0038: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_003d: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0042: ldloc.1 + IL_0043: brfalse.s IL_005a + IL_0045: ldloc.1 + IL_0046: isinst "System.Exception" + IL_004b: dup + IL_004c: brtrue.s IL_0050 + IL_004e: ldloc.1 + IL_004f: throw + IL_0050: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0055: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005a: ldloc.2 + IL_005b: ldc.i4.1 + IL_005c: bne.un.s IL_005f + IL_005e: ret + IL_005f: ldnull + IL_0060: throw + } + """); } [Fact] @@ -2185,7 +3174,108 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "ctor1 ctor2 body dispose2_start dispose2_end dispose1_start dispose1_end"); + string expectedOutput = "ctor1 ctor2 body dispose2_start dispose2_end dispose1_start dispose1_end"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x8c } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x9a } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 143 (0x8f) + .maxstack 2 + .locals init (S V_0, //s1 + S V_1, //s2 + object V_2, + int V_3, + object V_4, + int V_5) + IL_0000: ldc.i4.1 + IL_0001: newobj "S..ctor(int)" + IL_0006: stloc.0 + IL_0007: ldnull + IL_0008: stloc.2 + IL_0009: ldc.i4.0 + IL_000a: stloc.3 + .try + { + IL_000b: ldc.i4.2 + IL_000c: newobj "S..ctor(int)" + IL_0011: stloc.1 + IL_0012: ldnull + IL_0013: stloc.s V_4 + IL_0015: ldc.i4.0 + IL_0016: stloc.s V_5 + .try + { + IL_0018: ldstr "body " + IL_001d: call "void System.Console.Write(string)" + IL_0022: ldc.i4.1 + IL_0023: stloc.s V_5 + IL_0025: leave.s IL_002b + } + catch object + { + IL_0027: stloc.s V_4 + IL_0029: leave.s IL_002b + } + IL_002b: ldloc.1 + IL_002c: brfalse.s IL_0039 + IL_002e: ldloc.1 + IL_002f: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_0034: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0039: ldloc.s V_4 + IL_003b: brfalse.s IL_0054 + IL_003d: ldloc.s V_4 + IL_003f: isinst "System.Exception" + IL_0044: dup + IL_0045: brtrue.s IL_004a + IL_0047: ldloc.s V_4 + IL_0049: throw + IL_004a: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004f: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0054: ldloc.s V_5 + IL_0056: ldc.i4.1 + IL_0057: beq.s IL_005b + IL_0059: leave.s IL_0062 + IL_005b: ldc.i4.1 + IL_005c: stloc.3 + IL_005d: leave.s IL_0062 + } + catch object + { + IL_005f: stloc.2 + IL_0060: leave.s IL_0062 + } + IL_0062: ldloc.0 + IL_0063: brfalse.s IL_0070 + IL_0065: ldloc.0 + IL_0066: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_006b: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0070: ldloc.2 + IL_0071: brfalse.s IL_0088 + IL_0073: ldloc.2 + IL_0074: isinst "System.Exception" + IL_0079: dup + IL_007a: brtrue.s IL_007e + IL_007c: ldloc.2 + IL_007d: throw + IL_007e: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0083: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0088: ldloc.3 + IL_0089: ldc.i4.1 + IL_008a: bne.un.s IL_008d + IL_008c: ret + IL_008d: ldnull + IL_008e: throw + } + """); } [Fact] @@ -2224,7 +3314,100 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "ctor1 ctor2 body dispose2 dispose1 caught"); + string expectedOutput = "ctor1 ctor2 body dispose2 dispose1 caught"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x85 } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 134 (0x86) + .maxstack 2 + .locals init (S V_0, //s1 + S V_1, //s2 + object V_2, + object V_3) + .try + { + IL_0000: ldc.i4.1 + IL_0001: newobj "S..ctor(int)" + IL_0006: stloc.0 + IL_0007: ldnull + IL_0008: stloc.2 + .try + { + IL_0009: ldc.i4.2 + IL_000a: newobj "S..ctor(int)" + IL_000f: stloc.1 + IL_0010: ldnull + IL_0011: stloc.3 + .try + { + IL_0012: ldstr "body " + IL_0017: call "void System.Console.Write(string)" + IL_001c: newobj "System.Exception..ctor()" + IL_0021: throw + } + catch object + { + IL_0022: stloc.3 + IL_0023: leave.s IL_0025 + } + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_0033 + IL_0028: ldloc.1 + IL_0029: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_002e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0033: ldloc.3 + IL_0034: brfalse.s IL_004b + IL_0036: ldloc.3 + IL_0037: isinst "System.Exception" + IL_003c: dup + IL_003d: brtrue.s IL_0041 + IL_003f: ldloc.3 + IL_0040: throw + IL_0041: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0046: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004b: leave.s IL_0050 + } + catch object + { + IL_004d: stloc.2 + IL_004e: leave.s IL_0050 + } + IL_0050: ldloc.0 + IL_0051: brfalse.s IL_005e + IL_0053: ldloc.0 + IL_0054: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_0059: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005e: ldloc.2 + IL_005f: brfalse.s IL_0076 + IL_0061: ldloc.2 + IL_0062: isinst "System.Exception" + IL_0067: dup + IL_0068: brtrue.s IL_006c + IL_006a: ldloc.2 + IL_006b: throw + IL_006c: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0071: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0076: leave.s IL_0085 + } + catch System.Exception + { + IL_0078: pop + IL_0079: ldstr "caught" + IL_007e: call "void System.Console.Write(string)" + IL_0083: leave.s IL_0085 + } + IL_0085: ret + } + """); } [Fact] @@ -2269,7 +3452,99 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "ctor1 ctor2 dispose1 caught"); + string expectedOutput = "ctor1 ctor2 dispose1 caught"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x81 } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 130 (0x82) + .maxstack 2 + .locals init (S V_0, //s1 + S V_1, //s2 + object V_2, + object V_3) + .try + { + IL_0000: ldc.i4.1 + IL_0001: newobj "S..ctor(int)" + IL_0006: stloc.0 + IL_0007: ldnull + IL_0008: stloc.2 + .try + { + IL_0009: ldc.i4.2 + IL_000a: newobj "S..ctor(int)" + IL_000f: stloc.1 + IL_0010: ldnull + IL_0011: stloc.3 + .try + { + IL_0012: ldstr "SKIPPED" + IL_0017: call "void System.Console.Write(string)" + IL_001c: leave.s IL_0021 + } + catch object + { + IL_001e: stloc.3 + IL_001f: leave.s IL_0021 + } + IL_0021: ldloc.1 + IL_0022: brfalse.s IL_002f + IL_0024: ldloc.1 + IL_0025: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_002a: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002f: ldloc.3 + IL_0030: brfalse.s IL_0047 + IL_0032: ldloc.3 + IL_0033: isinst "System.Exception" + IL_0038: dup + IL_0039: brtrue.s IL_003d + IL_003b: ldloc.3 + IL_003c: throw + IL_003d: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0042: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0047: leave.s IL_004c + } + catch object + { + IL_0049: stloc.2 + IL_004a: leave.s IL_004c + } + IL_004c: ldloc.0 + IL_004d: brfalse.s IL_005a + IL_004f: ldloc.0 + IL_0050: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_0055: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005a: ldloc.2 + IL_005b: brfalse.s IL_0072 + IL_005d: ldloc.2 + IL_005e: isinst "System.Exception" + IL_0063: dup + IL_0064: brtrue.s IL_0068 + IL_0066: ldloc.2 + IL_0067: throw + IL_0068: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_006d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0072: leave.s IL_0081 + } + catch System.Exception + { + IL_0074: pop + IL_0075: ldstr "caught" + IL_007a: call "void System.Console.Write(string)" + IL_007f: leave.s IL_0081 + } + IL_0081: ret + } + """); } [Fact] @@ -2397,9 +3672,60 @@ public System.Threading.Tasks.ValueTask DisposeAsync(params string[] s) => throw null; } "; + var expectedOutput = "dispose"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "dispose"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x35, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main", """ + { + // Code size 54 (0x36) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: leave.s IL_000d + } + catch object + { + IL_000a: stloc.1 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: brfalse.s IL_001c + IL_0010: ldloc.0 + IL_0011: ldc.i4.0 + IL_0012: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync(int)" + IL_0017: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_001c: ldloc.1 + IL_001d: brfalse.s IL_0034 + IL_001f: ldloc.1 + IL_0020: isinst "System.Exception" + IL_0025: dup + IL_0026: brtrue.s IL_002a + IL_0028: ldloc.1 + IL_0029: throw + IL_002a: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_002f: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0034: ldc.i4.1 + IL_0035: ret + } + """); } [Fact] @@ -2458,9 +3784,63 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() } } "; + var expectedOutput = "using dispose_start dispose_end return"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main", """ + { + // Code size 73 (0x49) + .maxstack 2 + .locals init (C V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(string)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "return" + IL_0042: call "void System.Console.Write(string)" + IL_0047: ldc.i4.1 + IL_0048: ret + } + """); } [Fact] @@ -2491,6 +3871,59 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("using dispose_start dispose_end return", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main", """ + { + // Code size 73 (0x49) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(string)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "return" + IL_0042: call "void System.Console.Write(string)" + IL_0047: ldc.i4.1 + IL_0048: ret + } + """); } [Fact] @@ -2523,7 +3956,60 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + string expectedOutput = "using dispose_start dispose_end return"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 73 (0x49) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(string)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "return" + IL_0042: call "void System.Console.Write(string)" + IL_0047: ldc.i4.1 + IL_0048: ret + } + """); } [Fact] @@ -2553,7 +4039,61 @@ public async System.Threading.Tasks.ValueTask DisposeAsync(int i = 0) "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + string expectedOutput = "using dispose_start dispose_end return"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x49, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 74 (0x4a) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(string)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0026 + IL_001a: ldloc.0 + IL_001b: ldc.i4.0 + IL_001c: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync(int)" + IL_0021: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0026: ldloc.1 + IL_0027: brfalse.s IL_003e + IL_0029: ldloc.1 + IL_002a: isinst "System.Exception" + IL_002f: dup + IL_0030: brtrue.s IL_0034 + IL_0032: ldloc.1 + IL_0033: throw + IL_0034: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0039: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003e: ldstr "return" + IL_0043: call "void System.Console.Write(string)" + IL_0048: ldc.i4.1 + IL_0049: ret + } + """); } [Fact] @@ -2583,7 +4123,61 @@ public async System.Threading.Tasks.ValueTask DisposeAsync(params int[] x) "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end(0) return"); + string expectedOutput = "using dispose_start dispose_end(0) return"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x4d, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x66 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 78 (0x4e) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(string)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_002a + IL_001a: ldloc.0 + IL_001b: call "int[] System.Array.Empty()" + IL_0020: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync(params int[])" + IL_0025: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002a: ldloc.1 + IL_002b: brfalse.s IL_0042 + IL_002d: ldloc.1 + IL_002e: isinst "System.Exception" + IL_0033: dup + IL_0034: brtrue.s IL_0038 + IL_0036: ldloc.1 + IL_0037: throw + IL_0038: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0042: ldstr "return" + IL_0047: call "void System.Console.Write(string)" + IL_004c: ldc.i4.1 + IL_004d: ret + } + """); } [Fact] @@ -2670,7 +4264,7 @@ private System.Threading.Tasks.ValueTask DisposeAsync() Diagnostic(ErrorCode.ERR_NoConvToIAsyncDisp, "var x = new C()").WithArguments("C").WithLocation(6, 22)); } - [ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] [WorkItem(32316, "https://github.com/dotnet/roslyn/issues/32316")] public void TestPatternBasedDisposal_InstanceMethod_UsingDeclaration() { @@ -2696,7 +4290,8 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + string expectedOutput = "using dispose_start dispose_end return"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); // Sequence point highlights `await using ...` verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" @@ -2866,6 +4461,58 @@ .locals init (int V_0, IL_012e: ret } ", sequencePoints: "C+
d__0.MoveNext", source: source); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 73 (0x49) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(string)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "return" + IL_0042: call "void System.Console.Write(string)" + IL_0047: ldc.i4.1 + IL_0048: ret + } + """); } [Fact] @@ -2903,9 +4550,67 @@ public class Awaiter : System.Runtime.CompilerServices.INotifyCompletion public void OnCompleted(System.Action continuation) { } } "; + var expectedOutput = "using dispose_start dispose_end return"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x5e, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ }); + verifier.VerifyIL("C.Main", """ + { + // Code size 95 (0x5f) + .maxstack 2 + .locals init (C V_0, //x + object V_1, + Awaiter V_2) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C" + IL_0008: ldnull + IL_0009: stloc.1 + .try + { + IL_000a: ldstr "using " + IL_000f: call "void System.Console.Write(string)" + IL_0014: leave.s IL_0019 + } + catch object + { + IL_0016: stloc.1 + IL_0017: leave.s IL_0019 + } + IL_0019: ldloca.s V_0 + IL_001b: call "Awaitable C.DisposeAsync()" + IL_0020: callvirt "Awaiter Awaitable.GetAwaiter()" + IL_0025: stloc.2 + IL_0026: ldloc.2 + IL_0027: callvirt "bool Awaiter.IsCompleted.get" + IL_002c: brtrue.s IL_0034 + IL_002e: ldloc.2 + IL_002f: call "void System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter(Awaiter)" + IL_0034: ldloc.2 + IL_0035: callvirt "bool Awaiter.GetResult()" + IL_003a: pop + IL_003b: ldloc.1 + IL_003c: brfalse.s IL_0053 + IL_003e: ldloc.1 + IL_003f: isinst "System.Exception" + IL_0044: dup + IL_0045: brtrue.s IL_0049 + IL_0047: ldloc.1 + IL_0048: throw + IL_0049: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0053: ldstr "return" + IL_0058: call "void System.Console.Write(string)" + IL_005d: ldc.i4.1 + IL_005e: ret + } + """); } [Fact] @@ -2932,9 +4637,61 @@ public async System.Threading.Tasks.Task DisposeAsync() } } "; + var expectedOutput = "using dispose_start dispose_end return"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main", """ + { + // Code size 73 (0x49) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C" + IL_0008: ldnull + IL_0009: stloc.1 + .try + { + IL_000a: ldstr "using " + IL_000f: call "void System.Console.Write(string)" + IL_0014: leave.s IL_0019 + } + catch object + { + IL_0016: stloc.1 + IL_0017: leave.s IL_0019 + } + IL_0019: ldloca.s V_0 + IL_001b: call "System.Threading.Tasks.Task C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "return" + IL_0042: call "void System.Console.Write(string)" + IL_0047: ldc.i4.1 + IL_0048: ret + } + """); } [Fact] @@ -2963,9 +4720,59 @@ public async System.Threading.Tasks.Task DisposeAsync() } "; // it's okay to await `Task` even if we don't care about the result + var expectedOutput = "using dispose_start dispose_end return"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x49, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Unexpected type on the stack. { Offset = 0x39, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ }); + verifier.VerifyIL("C.Main", """ + { + // Code size 74 (0x4a) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C" + IL_0008: ldnull + IL_0009: stloc.1 + .try + { + IL_000a: ldstr "using " + IL_000f: call "void System.Console.Write(string)" + IL_0014: leave.s IL_0019 + } + catch object + { + IL_0016: stloc.1 + IL_0017: leave.s IL_0019 + } + IL_0019: ldloca.s V_0 + IL_001b: call "System.Threading.Tasks.Task C.DisposeAsync()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: pop + IL_0026: ldloc.1 + IL_0027: brfalse.s IL_003e + IL_0029: ldloc.1 + IL_002a: isinst "System.Exception" + IL_002f: dup + IL_0030: brtrue.s IL_0034 + IL_0032: ldloc.1 + IL_0033: throw + IL_0034: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0039: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003e: ldstr "return" + IL_0043: call "void System.Console.Write(string)" + IL_0048: ldc.i4.1 + IL_0049: ret + } + """); } [Fact] @@ -3034,6 +4841,63 @@ static async Task Main() }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "StructAwaitable"); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("StructAwaitable", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x4f } + """ + }); + verifier.VerifyIL("Program.Main", """ + { + // Code size 80 (0x50) + .maxstack 2 + .locals init (Disposable V_0, + object V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2) + IL_0000: newobj "Disposable..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: leave.s IL_000d + } + catch object + { + IL_000a: stloc.1 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: brfalse.s IL_0037 + IL_0010: ldloc.0 + IL_0011: callvirt "StructAwaitable Disposable.DisposeAsync()" + IL_0016: box "StructAwaitable" + IL_001b: call "System.Runtime.CompilerServices.TaskAwaiter Extensions.GetAwaiter(object)" + IL_0020: stloc.2 + IL_0021: ldloca.s V_2 + IL_0023: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0028: brtrue.s IL_0030 + IL_002a: ldloc.2 + IL_002b: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.TaskAwaiter)" + IL_0030: ldloca.s V_2 + IL_0032: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0037: ldloc.1 + IL_0038: brfalse.s IL_004f + IL_003a: ldloc.1 + IL_003b: isinst "System.Exception" + IL_0040: dup + IL_0041: brtrue.s IL_0045 + IL_0043: ldloc.1 + IL_0044: throw + IL_0045: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004a: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004f: ret + } + """); } [Fact, WorkItem(45111, "https://github.com/dotnet/roslyn/issues/45111")] @@ -3057,6 +4921,54 @@ public Task DisposeAsync() Assert.Equal(TypeKind.Error, comp.GetWellKnownType(WellKnownType.System_IAsyncDisposable).TypeKind); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "DISPOSED"); + + // Runtime async verification (Note: This test doesn't require IAsyncDisposableDefinition since the interface is missing) + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.MakeTypeMissing(WellKnownType.System_IAsyncDisposable); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("DISPOSED", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [
$]: Return value missing on the stack. { Offset = 0x33 } + """ + }); + verifier.VerifyIL("", """ + { + // Code size 52 (0x34) + .maxstack 2 + .locals init (C V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: leave.s IL_000d + } + catch object + { + IL_000a: stloc.1 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: brfalse.s IL_001b + IL_0010: ldloc.0 + IL_0011: callvirt "System.Threading.Tasks.Task C.DisposeAsync()" + IL_0016: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001b: ldloc.1 + IL_001c: brfalse.s IL_0033 + IL_001e: ldloc.1 + IL_001f: isinst "System.Exception" + IL_0024: dup + IL_0025: brtrue.s IL_0029 + IL_0027: ldloc.1 + IL_0028: throw + IL_0029: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_002e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0033: ret + } + """); } [Fact, WorkItem(45111, "https://github.com/dotnet/roslyn/issues/45111")] @@ -3565,6 +5477,40 @@ public static ValueTask DisposeAsync(this IEnumerable objects) """; var comp = CreateCompilationWithTasksExtensions([source, IAsyncDisposableDefinition]); CompileAndVerify(comp, expectedOutput: "DISPOSED").VerifyDiagnostics(); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("DISPOSED", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [
$]: Return value missing on the stack. { Offset = 0x3b } + [System.IAsyncDisposable.DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("Class1.System.IAsyncDisposable.DisposeAsync", """ + { + // Code size 47 (0x2f) + .maxstack 1 + .locals init (System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_0, + System.Runtime.CompilerServices.YieldAwaitable V_1) + IL_0000: ldstr "DISPOSED" + IL_0005: call "void System.Console.Write(string)" + IL_000a: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_000f: stloc.1 + IL_0010: ldloca.s V_1 + IL_0012: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0017: stloc.0 + IL_0018: ldloca.s V_0 + IL_001a: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_001f: brtrue.s IL_0027 + IL_0021: ldloc.0 + IL_0022: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0027: ldloca.s V_0 + IL_0029: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_002e: ret + } + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73691")] diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index 374d2e0ff27ba..b5a75239170f5 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -3334,7 +3334,7 @@ static async Task F() diff1.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { - // Code size 678 (0x2a6) + // Code size 650 (0x28a) .maxstack 3 .locals init (int V_0, System.Runtime.CompilerServices.TaskAwaiter V_1, @@ -3382,7 +3382,7 @@ .locals init (int V_0, IL_0053: ldloca.s V_2 IL_0055: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)"" IL_005a: nop - IL_005b: leave IL_02a5 + IL_005b: leave IL_0289 IL_0060: ldarg.0 IL_0061: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1"" IL_0066: stloc.1 @@ -3468,7 +3468,7 @@ .locals init (int V_0, IL_0116: ldloca.s V_2 IL_0118: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.d__0)"" IL_011d: nop - IL_011e: leave IL_02a5 + IL_011e: leave IL_0289 IL_0123: ldarg.0 IL_0124: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.d__0.<>u__2"" IL_0129: stloc.s V_4 @@ -3553,7 +3553,7 @@ .locals init (int V_0, IL_01dc: ldloca.s V_2 IL_01de: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.d__0)"" IL_01e3: nop - IL_01e4: leave IL_02a5 + IL_01e4: leave IL_0289 IL_01e9: ldarg.0 IL_01ea: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.d__0.<>u__2"" IL_01ef: stloc.s V_8 @@ -3591,7 +3591,7 @@ .locals init (int V_0, IL_023d: ldc.i4.1 IL_023e: beq.s IL_0242 IL_0240: br.s IL_0244 - IL_0242: leave.s IL_0283 + IL_0242: leave.s IL_0275 IL_0244: ldarg.0 IL_0245: ldnull IL_0246: stfld ""object C.d__0.<>s__3"" @@ -3601,7 +3601,8 @@ .locals init (int V_0, IL_0252: ldarg.0 IL_0253: ldnull IL_0254: stfld ""System.IAsyncDisposable C.d__0.5__2"" - IL_0259: leave.s IL_0283 + IL_0259: ldnull + IL_025a: throw } catch System.Exception { @@ -3610,32 +3611,20 @@ .locals init (int V_0, IL_025e: ldc.i4.s -2 IL_0260: stfld ""int C.d__0.<>1__state"" IL_0265: ldarg.0 - IL_0266: ldnull - IL_0267: stfld ""System.IAsyncDisposable C.d__0.5__1"" - IL_026c: ldarg.0 - IL_026d: ldnull - IL_026e: stfld ""System.IAsyncDisposable C.d__0.5__2"" - IL_0273: ldarg.0 - IL_0274: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder"" - IL_0279: ldloc.s V_6 - IL_027b: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" - IL_0280: nop - IL_0281: leave.s IL_02a5 + IL_0266: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder"" + IL_026b: ldloc.s V_6 + IL_026d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_0272: nop + IL_0273: leave.s IL_0289 } - IL_0283: ldarg.0 - IL_0284: ldc.i4.s -2 - IL_0286: stfld ""int C.d__0.<>1__state"" - IL_028b: ldarg.0 - IL_028c: ldnull - IL_028d: stfld ""System.IAsyncDisposable C.d__0.5__1"" - IL_0292: ldarg.0 - IL_0293: ldnull - IL_0294: stfld ""System.IAsyncDisposable C.d__0.5__2"" - IL_0299: ldarg.0 - IL_029a: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder"" - IL_029f: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" - IL_02a4: nop - IL_02a5: ret + IL_0275: ldarg.0 + IL_0276: ldc.i4.s -2 + IL_0278: stfld ""int C.d__0.<>1__state"" + IL_027d: ldarg.0 + IL_027e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder"" + IL_0283: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_0288: nop + IL_0289: ret }"); } diff --git a/src/Compilers/CSharp/Test/Emit2/PDB/PDBAsyncTests.cs b/src/Compilers/CSharp/Test/Emit2/PDB/PDBAsyncTests.cs index e60ce9795253e..7e958adc673f7 100644 --- a/src/Compilers/CSharp/Test/Emit2/PDB/PDBAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/PDB/PDBAsyncTests.cs @@ -1878,7 +1878,7 @@ .locals init (int V_0, - + diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests.cs index 6ba97279d29d0..2f6eaebf9e365 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests.cs @@ -11792,7 +11792,8 @@ .locals init (int V_0, IL_01a0: ldarg.0 IL_01a1: ldnull IL_01a2: stfld ""System.Exception C.d__1.5__3"" - IL_01a7: leave.s IL_01c1 + IL_01a7: ldnull + IL_01a8: throw } catch System.Exception { @@ -12168,7 +12169,8 @@ .locals init (int V_0, IL_01d6: ldarg.0 IL_01d7: ldnull IL_01d8: stfld ""object C.d__1.<>s__1"" - IL_01dd: leave.s IL_01f9 + IL_01dd: ldnull + IL_01de: throw } catch System.Exception { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/CorLibrary/CorTypes.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/CorLibrary/CorTypes.cs index ec6de998246ec..12b502c04f786 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/CorLibrary/CorTypes.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/CorLibrary/CorTypes.cs @@ -55,18 +55,24 @@ public void PresentCorLib() MetadataOrSourceAssemblySymbol msCorLibRef = (MetadataOrSourceAssemblySymbol)assemblies[0]; - var knownMissingTypes = new HashSet() + var knownMissingSpecialTypes = new HashSet() { - (int)SpecialType.System_Runtime_CompilerServices_InlineArrayAttribute + SpecialType.System_Runtime_CompilerServices_InlineArrayAttribute, + }; + + var knownMissingInternalSpecialTypes = new HashSet() + { + InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers, }; for (int i = 1; i <= (int)SpecialType.Count; i++) { - var t = msCorLibRef.GetSpecialType((SpecialType)i); - Assert.Equal((SpecialType)i, t.SpecialType); + var specialType = (SpecialType)i; + var t = msCorLibRef.GetSpecialType(specialType); + Assert.Equal(specialType, t.SpecialType); Assert.Equal((ExtendedSpecialType)i, t.ExtendedSpecialType); Assert.Same(msCorLibRef, t.ContainingAssembly); - if (knownMissingTypes.Contains(i)) + if (knownMissingSpecialTypes.Contains(specialType)) { // not present on dotnet core 3.1 Assert.Equal(TypeKind.Error, t.TypeKind); @@ -79,11 +85,12 @@ public void PresentCorLib() for (int i = (int)InternalSpecialType.First; i < (int)InternalSpecialType.NextAvailable; i++) { - var t = msCorLibRef.GetSpecialType((InternalSpecialType)i); + var internalSpecialType = (InternalSpecialType)i; + var t = msCorLibRef.GetSpecialType(internalSpecialType); Assert.Equal(SpecialType.None, t.SpecialType); Assert.Equal((ExtendedSpecialType)i, t.ExtendedSpecialType); Assert.Same(msCorLibRef, t.ContainingAssembly); - if (knownMissingTypes.Contains(i)) + if (knownMissingInternalSpecialTypes.Contains(internalSpecialType)) { // not present on dotnet core 3.1 Assert.Equal(TypeKind.Error, t.TypeKind); @@ -128,8 +135,8 @@ public void PresentCorLib() } } - Assert.Equal((int)SpecialType.Count, count + knownMissingTypes.Count); - Assert.Equal(knownMissingTypes.Any(), msCorLibRef.KeepLookingForDeclaredSpecialTypes); + Assert.Equal((int)SpecialType.Count, count + knownMissingSpecialTypes.Count); + Assert.Equal(knownMissingSpecialTypes.Any(), msCorLibRef.KeepLookingForDeclaredSpecialTypes); } [Fact] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index f5da1dfbb4d88..8b77e18d6d5a4 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -567,7 +567,10 @@ public void AllSpecialTypeMembers() || special == SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__ByRefLikeGenerics || special == SpecialMember.System_Runtime_CompilerServices_PreserveBaseOverridesAttribute__ctor || special == SpecialMember.System_Runtime_CompilerServices_InlineArrayAttribute__ctor - || special == SpecialMember.System_ReadOnlySpan_T__ctor_Reference) + || special == SpecialMember.System_ReadOnlySpan_T__ctor_Reference + || special == SpecialMember.System_Runtime_CompilerServices_AsyncHelpers__AwaitAwaiter_TAwaiter + || special == SpecialMember.System_Runtime_CompilerServices_AsyncHelpers__UnsafeAwaitAwaiter_TAwaiter + ) { Assert.Null(symbol); // Not available } @@ -917,7 +920,7 @@ public void AllWellKnownTypesBeforeCSharp7() } // There were 200 well-known types prior to CSharp7 - Assert.Equal(200, (int)(WellKnownType.CSharp7Sentinel - WellKnownType.First)); + Assert.Equal(200, (int)(WellKnownType.CSharp7Sentinel - WellKnownType.First - 1 /* WellKnownTypes.ExtSentinel is before CSharp7Sentinel */)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index a24253bd6119e..449eaa80cdca2 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -2992,6 +2992,7 @@ public void TestIsBuildOnlyDiagnostic() case ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait: case ErrorCode.ERR_RefLocalAcrossAwait: case ErrorCode.ERR_DataSectionStringLiteralHashCollision: + case ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync: Assert.True(isBuildOnly, $"Check failed for ErrorCode.{errorCode}"); break; diff --git a/src/Compilers/Core/CodeAnalysisTest/Symbols/SpecialTypeTests.cs b/src/Compilers/Core/CodeAnalysisTest/Symbols/SpecialTypeTests.cs index 4689c2b19fc62..bd60fb5b790a0 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Symbols/SpecialTypeTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Symbols/SpecialTypeTests.cs @@ -17,8 +17,8 @@ public void ExtendedSpecialType_ToString() AssertEx.Equal("System_Runtime_CompilerServices_InlineArrayAttribute", ((ExtendedSpecialType)SpecialType.Count).ToString()); AssertEx.Equal("System_ReadOnlySpan_T", ((ExtendedSpecialType)InternalSpecialType.First).ToString()); AssertEx.Equal("System_ReadOnlySpan_T", ((ExtendedSpecialType)InternalSpecialType.System_ReadOnlySpan_T).ToString()); - AssertEx.Equal("System_Reflection_MethodInfo", ((ExtendedSpecialType)(InternalSpecialType.NextAvailable - 1)).ToString()); - AssertEx.Equal("52", ((ExtendedSpecialType)InternalSpecialType.NextAvailable).ToString()); + AssertEx.Equal("System_Runtime_CompilerServices_ICriticalNotifyCompletion", ((ExtendedSpecialType)(InternalSpecialType.NextAvailable - 1)).ToString()); + AssertEx.Equal("58", ((ExtendedSpecialType)InternalSpecialType.NextAvailable).ToString()); } } } diff --git a/src/Compilers/Core/Portable/ExtendedSpecialType.cs b/src/Compilers/Core/Portable/ExtendedSpecialType.cs index a5b5eb8212982..3cda91d247483 100644 --- a/src/Compilers/Core/Portable/ExtendedSpecialType.cs +++ b/src/Compilers/Core/Portable/ExtendedSpecialType.cs @@ -26,6 +26,7 @@ private ExtendedSpecialType(int value) public static explicit operator SpecialType(ExtendedSpecialType value) => value._value < (int)InternalSpecialType.First ? (SpecialType)value._value : SpecialType.None; public static implicit operator ExtendedSpecialType(InternalSpecialType value) => new ExtendedSpecialType((int)value); + public static explicit operator InternalSpecialType(ExtendedSpecialType value) => value._value is < (int)InternalSpecialType.First or >= (int)InternalSpecialType.NextAvailable ? InternalSpecialType.Unknown : (InternalSpecialType)value._value; public static explicit operator ExtendedSpecialType(int value) => new ExtendedSpecialType(value); public static explicit operator int(ExtendedSpecialType value) => value._value; @@ -56,6 +57,9 @@ public override int GetHashCode() return _value.GetHashCode(); } + /// + /// Returns a string representation of the ExtendedSpecialType. This exists only for debugging purposes. + /// public override string ToString() { if (_value > (int)SpecialType.None && _value <= (int)SpecialType.Count) @@ -63,7 +67,15 @@ public override string ToString() return ((SpecialType)_value).ToString(); } - if (_value >= (int)InternalSpecialType.First && _value < (int)InternalSpecialType.NextAvailable) + // When there are overlapping labels for a value in an enum, it is undefined which label is returned. + // ReadOnlySpan is the one we care about most from a debugging perspective. + Debug.Assert(InternalSpecialType.First == InternalSpecialType.System_ReadOnlySpan_T); + if (_value == (int)InternalSpecialType.System_ReadOnlySpan_T) + { + return nameof(InternalSpecialType.System_ReadOnlySpan_T); + } + + if (_value > (int)InternalSpecialType.First && _value < (int)InternalSpecialType.NextAvailable) { return ((InternalSpecialType)_value).ToString(); } diff --git a/src/Compilers/Core/Portable/InternalSpecialType.cs b/src/Compilers/Core/Portable/InternalSpecialType.cs index 23030db82f4ae..3483c6d11a9aa 100644 --- a/src/Compilers/Core/Portable/InternalSpecialType.cs +++ b/src/Compilers/Core/Portable/InternalSpecialType.cs @@ -63,6 +63,66 @@ internal enum InternalSpecialType : sbyte /// System_Reflection_MethodInfo, + /// + /// Indicates that the type is System.Runtime.CompilerServices.AsyncHelpers from the COR library. + /// + System_Runtime_CompilerServices_AsyncHelpers, + + /// + /// Indicates that the type is from the COR library. + /// + /// + /// Check for this special type cannot be used to find the "canonical" definition of + /// since it is fully legal for it to come from sources other than the COR library. + /// The should be used for that purpose instead. + /// This entry mostly exists so that compiler can tell this type apart when resolving other members of the COR library. + /// + System_Threading_Tasks_Task, + + /// + /// Indicates that the type is from the COR library. + /// + /// + /// Check for this special type cannot be used to find the "canonical" definition of + /// since it is fully legal for it to come from sources other than the COR library. + /// The should be used for that purpose instead. + /// This entry mostly exists so that compiler can tell this type apart when resolving other members of the COR library. + /// + System_Threading_Tasks_Task_T, + + /// + /// Indicates that the type is from the COR library. + /// + /// + /// Check for this special type cannot be used to find the "canonical" definition of + /// since it is fully legal for it to come from sources other than the COR library. + /// The should be used for that purpose instead. + /// This entry mostly exists so that compiler can tell this type apart when resolving other members of the COR library. + /// + System_Threading_Tasks_ValueTask, + + /// + /// Indicates that the type is from the COR library. + /// + /// + /// Check for this special type cannot be used to find the "canonical" definition of + /// since it is fully legal for it to come from sources other than the COR library. + /// The should be used for that purpose instead. + /// This entry mostly exists so that compiler can tell this type apart when resolving other members of the COR library. + /// + System_Threading_Tasks_ValueTask_T, + + /// + /// Indicates that the type is from the COR library. + /// + /// + /// Check for this special type cannot be used to find the "canonical" definition of + /// since it is fully legal for it to come from sources other than the COR library. + /// The should be used for that purpose instead. + /// This entry mostly exists so that compiler can tell this type apart when resolving other members of the COR library. + /// + System_Runtime_CompilerServices_ICriticalNotifyCompletion, + /// /// This item should be kept last and it doesn't represent any specific type. /// diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 49bfc11e3787f..3dd2e0369982c 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -18,6 +18,7 @@ Microsoft.CodeAnalysis.IEventSymbol.IsPartialDefinition.get -> bool Microsoft.CodeAnalysis.IEventSymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IEventSymbol? Microsoft.CodeAnalysis.IEventSymbol.PartialImplementationPart.get -> Microsoft.CodeAnalysis.IEventSymbol? Microsoft.CodeAnalysis.IncrementalGeneratorPostInitializationContext.AddEmbeddedAttributeDefinition() -> void +Microsoft.CodeAnalysis.RuntimeCapability.RuntimeAsyncMethods = 9 -> Microsoft.CodeAnalysis.RuntimeCapability override Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.ToString() -> string! Microsoft.CodeAnalysis.ITypeSymbol.IsExtension.get -> bool Microsoft.CodeAnalysis.TypeKind.Extension = 14 -> Microsoft.CodeAnalysis.TypeKind diff --git a/src/Compilers/Core/Portable/RuntimeCapability.cs b/src/Compilers/Core/Portable/RuntimeCapability.cs index 21bb31cc1b23e..af262f3e80c54 100644 --- a/src/Compilers/Core/Portable/RuntimeCapability.cs +++ b/src/Compilers/Core/Portable/RuntimeCapability.cs @@ -49,5 +49,10 @@ public enum RuntimeCapability /// Indicates that this version of runtime supports generic type parameters allowing substitution with a ref struct. /// ByRefLikeGenerics = 8, + + /// + /// Indicates that this version of the runtime supports generating async state machines. + /// + RuntimeAsyncMethods = 9, } } diff --git a/src/Compilers/Core/Portable/SpecialMember.cs b/src/Compilers/Core/Portable/SpecialMember.cs index d0448904afae2..7296e839a415e 100644 --- a/src/Compilers/Core/Portable/SpecialMember.cs +++ b/src/Compilers/Core/Portable/SpecialMember.cs @@ -194,6 +194,9 @@ internal enum SpecialMember System_Type__GetTypeFromHandle, + System_Runtime_CompilerServices_AsyncHelpers__AwaitAwaiter_TAwaiter, + System_Runtime_CompilerServices_AsyncHelpers__UnsafeAwaitAwaiter_TAwaiter, + Count } } diff --git a/src/Compilers/Core/Portable/SpecialMembers.cs b/src/Compilers/Core/Portable/SpecialMembers.cs index 227d60d38cf33..48f9e3e04908e 100644 --- a/src/Compilers/Core/Portable/SpecialMembers.cs +++ b/src/Compilers/Core/Portable/SpecialMembers.cs @@ -1313,6 +1313,22 @@ static SpecialMembers() 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_Type, // Return Type (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeTypeHandle, + + // System_Runtime_CompilerServices_AsyncHelpers__AwaitAwaiter_TAwaiter + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers, // DeclaringTypeId + 1, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + (byte)SignatureTypeCode.GenericMethodParameter, 0, + + // System_Runtime_CompilerServices_AsyncHelpers__UnsafeAwaitAwaiter_TAwaiter + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers, // DeclaringTypeId + 1, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + (byte)SignatureTypeCode.GenericMethodParameter, 0, }; string[] allNames = new string[(int)SpecialMember.Count] @@ -1474,6 +1490,8 @@ static SpecialMembers() "Empty", // System_Array__Empty "SetValue", // System_Array__SetValue "GetTypeFromHandle", // System_Type__GetTypeFromHandle + "AwaitAwaiter", // System_Runtime_CompilerServices_AsyncHelpers__AwaitAwaiter_TAwaiter + "UnsafeAwaitAwaiter", // System_Runtime_CompilerServices_AsyncHelpers__UnsafeAwaitAwaiter_TAwaiter }; s_descriptors = MemberDescriptor.InitializeFromStream(new System.IO.MemoryStream(initializationBytes, writable: false), allNames); diff --git a/src/Compilers/Core/Portable/SpecialTypes.cs b/src/Compilers/Core/Portable/SpecialTypes.cs index d4bca2aef4b50..d3d9b9926b403 100644 --- a/src/Compilers/Core/Portable/SpecialTypes.cs +++ b/src/Compilers/Core/Portable/SpecialTypes.cs @@ -75,6 +75,12 @@ internal static class SpecialTypes "System.Type", "System.Reflection.MethodBase", "System.Reflection.MethodInfo", + "System.Runtime.CompilerServices.AsyncHelpers", + "System.Threading.Tasks.Task", + "System.Threading.Tasks.Task`1", + "System.Threading.Tasks.ValueTask", + "System.Threading.Tasks.ValueTask`1", + "System.Runtime.CompilerServices.ICriticalNotifyCompletion", }; private static readonly Dictionary s_nameToTypeIdMap; diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index fdb6b79ccf444..e292d1f3f5f0e 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -492,5 +492,6 @@ static AttributeDescription() internal static readonly AttributeDescription OverloadResolutionPriorityAttribute = new AttributeDescription("System.Runtime.CompilerServices", "OverloadResolutionPriorityAttribute", s_signatures_HasThis_Void_Int32_Only); internal static readonly AttributeDescription CompilerLoweringPreserveAttribute = new AttributeDescription("System.Runtime.CompilerServices", "CompilerLoweringPreserveAttribute", s_signatures_HasThis_Void_Only); internal static readonly AttributeDescription ExtensionMarkerAttribute = new AttributeDescription("System.Runtime.CompilerServices", "ExtensionMarkerAttribute", s_signatures_HasThis_Void_String_Only); + internal static readonly AttributeDescription RuntimeAsyncMethodGenerationAttribute = new AttributeDescription("System.Runtime.CompilerServices", "RuntimeAsyncMethodGenerationAttribute", s_signatures_HasThis_Void_Boolean_Only); } } diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 3c0d4c14142ce..f7adb0a342851 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -1017,7 +1017,7 @@ static WellKnownMembers() 0, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, // Return Type - // System_Runtime_CompilerServices__GetSubArray_T + // System_Runtime_CompilerServices_RuntimeHelpers__GetSubArray_T (byte)(MemberFlags.Method | MemberFlags.Static), // Flags (byte)WellKnownType.System_Runtime_CompilerServices_RuntimeHelpers, // DeclaringTypeId 1, // Arity @@ -1026,7 +1026,7 @@ static WellKnownMembers() (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.GenericMethodParameter, 0, (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Range - WellKnownType.ExtSentinel), - // System_Runtime_CompilerServices__EnsureSufficientExecutionStack + // System_Runtime_CompilerServices_RuntimeHelpers__EnsureSufficientExecutionStack (byte)(MemberFlags.Method | MemberFlags.Static), // Flags (byte)WellKnownType.System_Runtime_CompilerServices_RuntimeHelpers, // DeclaringTypeId 0, // Arity @@ -2584,15 +2584,15 @@ static WellKnownMembers() // System_Windows_Forms_Application__RunForm (byte)(MemberFlags.Method | MemberFlags.Static), // Flags - (byte)WellKnownType.System_Windows_Forms_Application, // DeclaringTypeId + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Windows_Forms_Application - WellKnownType.ExtSentinel), // DeclaringTypeId 0, // Arity 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Windows_Forms_Form, + (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Windows_Forms_Form - WellKnownType.ExtSentinel), // System_Environment__CurrentManagedThreadId (byte)(MemberFlags.Property | MemberFlags.Static), // Flags - (byte)WellKnownType.System_Environment, // DeclaringTypeId + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Environment - WellKnownType.ExtSentinel), // DeclaringTypeId 0, // Arity 0, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, // Return Type @@ -2607,13 +2607,13 @@ static WellKnownMembers() // System_Runtime_GCLatencyMode__SustainedLowLatency (byte)(MemberFlags.Field | MemberFlags.Static), // Flags - (byte)WellKnownType.System_Runtime_GCLatencyMode, // DeclaringTypeId + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_GCLatencyMode - WellKnownType.ExtSentinel), // DeclaringTypeId 0, // Arity - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Runtime_GCLatencyMode, // Field Signature + (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_GCLatencyMode - WellKnownType.ExtSentinel), // Field Signature // System_ValueTuple_T1__Item1 (byte)MemberFlags.Field, // Flags - (byte)WellKnownType.System_ValueTuple_T1, // DeclaringTypeId + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_ValueTuple_T1 - WellKnownType.ExtSentinel), // DeclaringTypeId 0, // Arity (byte)SignatureTypeCode.GenericTypeParameter, 0, // Field Signature @@ -2829,7 +2829,7 @@ static WellKnownMembers() // System_ValueTuple_T1__ctor (byte)MemberFlags.Constructor, // Flags - (byte)WellKnownType.System_ValueTuple_T1, // DeclaringTypeId + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_ValueTuple_T1 - WellKnownType.ExtSentinel), // DeclaringTypeId 0, // Arity 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index f9ad8c6864ed4..71e672c16fd49 100644 --- a/src/Compilers/Core/Portable/WellKnownTypes.cs +++ b/src/Compilers/Core/Portable/WellKnownTypes.cs @@ -237,6 +237,8 @@ internal enum WellKnownType System_Runtime_CompilerServices_AsyncStateMachineAttribute, System_Runtime_CompilerServices_IteratorStateMachineAttribute, + ExtSentinel, // Not a real type, just a marker for types above 255 and strictly below 512 + System_Windows_Forms_Form, System_Windows_Forms_Application, @@ -249,9 +251,6 @@ internal enum WellKnownType System_ValueTuple, System_ValueTuple_T1, - - ExtSentinel, // Not a real type, just a marker for types above 255 and strictly below 512 - System_ValueTuple_T2, System_ValueTuple_T3, System_ValueTuple_T4, @@ -588,6 +587,8 @@ internal static class WellKnownTypes "System.Runtime.CompilerServices.AsyncStateMachineAttribute", "System.Runtime.CompilerServices.IteratorStateMachineAttribute", + "", // WellKnownType.ExtSentinel extension marker + "System.Windows.Forms.Form", "System.Windows.Forms.Application", @@ -596,10 +597,8 @@ internal static class WellKnownTypes "System.Runtime.GCLatencyMode", "System.ValueTuple", - "System.ValueTuple`1", - - "", // WellKnownType.ExtSentinel extension marker + "System.ValueTuple`1", "System.ValueTuple`2", "System.ValueTuple`3", "System.ValueTuple`4", @@ -760,8 +759,9 @@ private static void AssertEnumAndTableInSync() // Some compile time asserts { // We should not add new types to CSharp7 set - _ = new int[(int)WellKnownType.CSharp7Sentinel - 252]; - _ = new int[252 - (int)WellKnownType.CSharp7Sentinel]; + const int ExpectedCSharp7SentinelValue = 200 + (int)InternalSpecialType.NextAvailable + 1 /* Placeholder for ExtSentinel */; + _ = new int[(int)WellKnownType.CSharp7Sentinel - ExpectedCSharp7SentinelValue]; + _ = new int[ExpectedCSharp7SentinelValue - (int)WellKnownType.CSharp7Sentinel]; // The WellKnownType.ExtSentinel value must be 255 _ = new int[(int)WellKnownType.ExtSentinel - 255]; diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index ec89683726aa4..bbbb7328dd545 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -1244,6 +1244,31 @@ public static string GetCultureInvariantString(object value) """; #endregion A string containing expression-tree dumping utilities + internal const string RuntimeAsyncAwaitHelpers = """ + namespace System.Runtime.CompilerServices + { + public static class AsyncHelpers + { + public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INotifyCompletion + {} + public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion + {} + + public static void Await(System.Threading.Tasks.Task task) => task.GetAwaiter().GetResult(); + public static void Await(System.Threading.Tasks.ValueTask task) => task.GetAwaiter().GetResult(); + public static T Await(System.Threading.Tasks.Task task) => task.GetAwaiter().GetResult(); + public static T Await(System.Threading.Tasks.ValueTask task) => task.GetAwaiter().GetResult(); + } + } + """; + + internal const string RuntimeAsyncMethodGenerationAttributeDefinition = """ + namespace System.Runtime.CompilerServices; + + [AttributeUsage(AttributeTargets.Method)] + public class RuntimeAsyncMethodGenerationAttribute(bool runtimeAsync) : Attribute(); + """; + protected static T GetSyntax(SyntaxTree tree, string text) where T : notnull { @@ -1814,7 +1839,7 @@ private static CSharpCompilation CreateCompilationCore( return compilation; } - private static CSharpCompilationOptions CheckForTopLevelStatements(SyntaxTree[] syntaxTrees) + protected static CSharpCompilationOptions CheckForTopLevelStatements(SyntaxTree[] syntaxTrees) { bool hasTopLevelStatements = syntaxTrees.Any(s => s.GetRoot().ChildNodes().OfType().Any()); diff --git a/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb index a6f5411975db6..0c7d793b4308b 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb @@ -349,6 +349,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return Me.RuntimeSupportsInlineArrayTypes Case RuntimeCapability.ByRefLikeGenerics Return Me.RuntimeSupportsByRefLikeGenerics + Case RuntimeCapability.RuntimeAsyncMethods + Return Me.RuntimeSupportsAsyncMethods End Select Return False @@ -421,6 +423,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Private ReadOnly Property RuntimeSupportsAsyncMethods As Boolean + Get + ' Keep in sync with C#'s AssemblySymbol.RuntimeSupportsAsyncMethods + Dim asyncHelpers = GetSpecialType(InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers) + Return asyncHelpers IsNot Nothing AndAlso + asyncHelpers.IsClassType() AndAlso + asyncHelpers.IsMetadataAbstract AndAlso + asyncHelpers.IsMetadataSealed + End Get + End Property + Private Function RuntimeSupportsFeature(feature As SpecialMember) As Boolean Debug.Assert(SpecialMembers.GetDescriptor(feature).DeclaringSpecialType = SpecialType.System_Runtime_CompilerServices_RuntimeFeature) diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/CorLibrary/CorTypes.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/CorLibrary/CorTypes.vb index e6a81031b282d..329870724bda3 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/CorLibrary/CorTypes.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/CorLibrary/CorTypes.vb @@ -50,14 +50,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Symbols.CorLibrary Dim assemblies = MetadataTestHelpers.GetSymbolsForReferences({NetCoreApp.SystemRuntime}) Dim msCorLibRef As MetadataOrSourceAssemblySymbol = DirectCast(assemblies(0), MetadataOrSourceAssemblySymbol) - Dim knownMissingTypes As HashSet(Of Integer) = New HashSet(Of Integer) From {SpecialType.System_Runtime_CompilerServices_InlineArrayAttribute} + Dim knownMissingSpecialTypes As HashSet(Of SpecialType) = New HashSet(Of SpecialType) From {SpecialType.System_Runtime_CompilerServices_InlineArrayAttribute} + Dim knownMissingInternalSpecialTypes As HashSet(Of InternalSpecialType) = New HashSet(Of InternalSpecialType) From {InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers} For i As Integer = 1 To SpecialType.Count - Dim t = msCorLibRef.GetSpecialType(CType(i, SpecialType)) + Dim specialType = CType(i, SpecialType) + Dim t = msCorLibRef.GetSpecialType(specialType) Assert.Equal(CType(i, SpecialType), t.SpecialType) Assert.Equal(CType(i, ExtendedSpecialType), t.ExtendedSpecialType) Assert.Same(msCorLibRef, t.ContainingAssembly) - If knownMissingTypes.Contains(i) Then + If knownMissingSpecialTypes.Contains(specialType) Then ' not present on dotnet core 3.1 Assert.Equal(TypeKind.Error, t.TypeKind) Else @@ -66,11 +68,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Symbols.CorLibrary Next For i As Integer = InternalSpecialType.First To InternalSpecialType.NextAvailable - 1 - Dim t = msCorLibRef.GetSpecialType(CType(i, InternalSpecialType)) + Dim internalSpecialType = CType(i, InternalSpecialType) + Dim t = msCorLibRef.GetSpecialType(internalSpecialType) Assert.Equal(SpecialType.None, t.SpecialType) Assert.Equal(CType(i, ExtendedSpecialType), t.ExtendedSpecialType) Assert.Same(msCorLibRef, t.ContainingAssembly) - If knownMissingTypes.Contains(i) Then + If knownMissingInternalSpecialTypes.Contains(internalSpecialType) Then ' not present on dotnet core 3.1 Assert.Equal(TypeKind.Error, t.TypeKind) Else @@ -106,8 +109,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Symbols.CorLibrary Next End While - Assert.Equal(count + knownMissingTypes.Count, CType(SpecialType.Count, Integer)) - Assert.Equal(knownMissingTypes.Any(), msCorLibRef.KeepLookingForDeclaredSpecialTypes) + Assert.Equal(count + knownMissingSpecialTypes.Count, CType(SpecialType.Count, Integer)) + Assert.Equal(knownMissingSpecialTypes.Any(), msCorLibRef.KeepLookingForDeclaredSpecialTypes) End Sub diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index 96e9c8b46d284..3a313bcc7a440 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -497,7 +497,9 @@ End Namespace special = SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__ByRefLikeGenerics OrElse special = SpecialMember.System_Runtime_CompilerServices_PreserveBaseOverridesAttribute__ctor OrElse special = SpecialMember.System_Runtime_CompilerServices_InlineArrayAttribute__ctor OrElse - special = SpecialMember.System_ReadOnlySpan_T__ctor_Reference Then + special = SpecialMember.System_ReadOnlySpan_T__ctor_Reference OrElse + special = SpecialMember.System_Runtime_CompilerServices_AsyncHelpers__AwaitAwaiter_TAwaiter OrElse + special = SpecialMember.System_Runtime_CompilerServices_AsyncHelpers__UnsafeAwaitAwaiter_TAwaiter Then Assert.Null(symbol) ' Not available Else Assert.NotNull(symbol) diff --git a/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs b/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs index c19cb644ff215..b6e60db49b58f 100644 --- a/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs +++ b/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs @@ -61,7 +61,8 @@ namespace Microsoft.CodeAnalysis.CSharp.LanguageServer; "CS8419", // ErrorCode.ERR_PossibleAsyncIteratorWithoutYield "CS8420", // ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait "CS9217", // ErrorCode.ERR_RefLocalAcrossAwait - "CS9274" // ErrorCode.ERR_DataSectionStringLiteralHashCollision + "CS9274", // ErrorCode.ERR_DataSectionStringLiteralHashCollision + "CS9328" // ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync )] [Shared] internal sealed class CSharpLspBuildOnlyDiagnostics : ILspBuildOnlyDiagnostics diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt index 2092c158ffdf2..6b1f60e975c86 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt @@ -2760,6 +2760,7 @@ Microsoft.CodeAnalysis.RuntimeCapability.CovariantReturnsOfClasses Microsoft.CodeAnalysis.RuntimeCapability.DefaultImplementationsOfInterfaces Microsoft.CodeAnalysis.RuntimeCapability.InlineArrayTypes Microsoft.CodeAnalysis.RuntimeCapability.NumericIntPtr +Microsoft.CodeAnalysis.RuntimeCapability.RuntimeAsyncMethods Microsoft.CodeAnalysis.RuntimeCapability.UnmanagedSignatureCallingConvention Microsoft.CodeAnalysis.RuntimeCapability.VirtualStaticsInInterfaces Microsoft.CodeAnalysis.RuntimeCapability.value__