未验证 提交 8d00d4fb 编写于 作者: D dotnet-automerge-bot 提交者: GitHub

Merge pull request #32321 from dotnet/merges/dev16.0-preview2-to-master

Merge dev16.0-preview2 to master
......@@ -67,14 +67,16 @@ An asynchronous `using` is lowered just like a regular `using`, except that `Dis
### Detailed design for `await foreach` statement
An `await foreach` is lowered just like a regular `foreach`, except that:
- `GetEnumerator()` is replaced with `await GetAsyncEnumerator(default)`
- `GetEnumerator()` is replaced with `await GetAsyncEnumerator()`
- `MoveNext()` is replaced with `await MoveNextAsync()`
- `Dispose()` is replaced with `await DisposeAsync()`
Note that pattern-based lookup for `GetAsyncEnumerator` and `MoveNextAsync` do not place particular requirements on those methods,
as long as they could be invoked without arguments.
Asynchronous foreach loops are disallowed on collections of type dynamic,
as there is no asynchronous equivalent of the non-generic `IEnumerable` interface.
The `CancellationToken` is always passed as `default` by the `await foreach` statement.
But wrapper types can pass non-default values (see `.WithCancellation(CancellationToken)` extension method),
thereby allowing consumers of async-streams to control cancellation.
A producer of async-streams can make use of the cancellation token by writing an
......@@ -308,7 +310,7 @@ But if the suspension was a `yield return` (-N), you could also call DisposeAsyn
When in dispose mode, MoveNext continues to suspend (N) and resume (-1) until the end of the method is reached (-2).
The result of invoking `DisposeAsync` from states -1 or N is unspecified. This compiler throws `NotSupportException` for those cases.
The result of invoking `DisposeAsync` from states -1 or N is unspecified. This compiler generates `throw new NotSupportException()` for those cases.
```
DisposeAsync await
......
......@@ -900,19 +900,7 @@ private bool SatisfiesGetEnumeratorPattern(ref ForEachEnumeratorInfo.Builder bui
{
var lookupResult = LookupResult.GetInstance();
string methodName = isAsync ? GetAsyncEnumeratorMethodName : GetEnumeratorMethodName;
ImmutableArray<BoundExpression> arguments;
if (isAsync)
{
var cancellationTokenType = Compilation.GetWellKnownType(WellKnownType.System_Threading_CancellationToken);
arguments = ImmutableArray.Create<BoundExpression>(new BoundAwaitableValuePlaceholder(_syntax, cancellationTokenType));
}
else
{
arguments = ImmutableArray<BoundExpression>.Empty;
}
MethodSymbol getEnumeratorMethod = FindForEachPatternMethod(collectionExprType, methodName, lookupResult, warningsOnly: true, diagnostics: diagnostics, isAsync: isAsync, arguments);
MethodSymbol getEnumeratorMethod = FindForEachPatternMethod(collectionExprType, methodName, lookupResult, warningsOnly: true, diagnostics: diagnostics, isAsync: isAsync);
lookupResult.Free();
builder.GetEnumeratorMethod = getEnumeratorMethod;
......@@ -929,10 +917,9 @@ private bool SatisfiesGetEnumeratorPattern(ref ForEachEnumeratorInfo.Builder bui
/// <param name="warningsOnly">True if failures should result in warnings; false if they should result in errors.</param>
/// <param name="diagnostics">Populated with binding diagnostics.</param>
/// <returns>The desired method or null.</returns>
private MethodSymbol FindForEachPatternMethod(TypeSymbol patternType, string methodName, LookupResult lookupResult, bool warningsOnly, DiagnosticBag diagnostics, bool isAsync, ImmutableArray<BoundExpression> arguments)
private MethodSymbol FindForEachPatternMethod(TypeSymbol patternType, string methodName, LookupResult lookupResult, bool warningsOnly, DiagnosticBag diagnostics, bool isAsync)
{
Debug.Assert(lookupResult.IsClear);
Debug.Assert(!arguments.IsDefault);
// Not using LookupOptions.MustBeInvocableMember because we don't want the corresponding lookup error.
// We filter out non-methods below.
......@@ -977,13 +964,16 @@ private MethodSymbol FindForEachPatternMethod(TypeSymbol patternType, string met
// some custom logic in ExpressionBinder.BindGrpToParams. The biggest difference
// we've found (so far) is that it only considers methods with expected number of parameters
// (i.e. doesn't work with "params" or optional parameters).
if (method.ParameterCount == arguments.Length)
// Note: for pattern-based lookup for `await foreach` we accept `GetAsyncEnumerator` and
// `MoveNextAsync` methods with optional/params parameters.
if (method.ParameterCount == 0 || isAsync)
{
candidateMethods.Add((MethodSymbol)member);
}
}
MethodSymbol patternMethod = PerformForEachPatternOverloadResolution(patternType, candidateMethods, warningsOnly, diagnostics, isAsync, arguments);
MethodSymbol patternMethod = PerformForEachPatternOverloadResolution(patternType, candidateMethods, warningsOnly, diagnostics, isAsync);
candidateMethods.Free();
......@@ -994,12 +984,9 @@ private MethodSymbol FindForEachPatternMethod(TypeSymbol patternType, string met
/// The overload resolution portion of FindForEachPatternMethod.
/// If no arguments are passed in, then an empty argument list will be used.
/// </summary>
private MethodSymbol PerformForEachPatternOverloadResolution(TypeSymbol patternType, ArrayBuilder<MethodSymbol> candidateMethods, bool warningsOnly, DiagnosticBag diagnostics, bool isAsync, ImmutableArray<BoundExpression> arguments)
private MethodSymbol PerformForEachPatternOverloadResolution(TypeSymbol patternType, ArrayBuilder<MethodSymbol> candidateMethods, bool warningsOnly, DiagnosticBag diagnostics, bool isAsync)
{
Debug.Assert(!arguments.IsDefault);
var analyzedArguments = AnalyzedArguments.GetInstance();
analyzedArguments.Arguments.AddRange(arguments);
var typeArguments = ArrayBuilder<TypeSymbolWithAnnotations>.GetInstance();
var overloadResolutionResult = OverloadResolutionResult<MethodSymbol>.GetInstance();
......@@ -1152,7 +1139,7 @@ private bool SatisfiesForEachPattern(ref ForEachEnumeratorInfo.Builder builder,
MethodSymbol moveNextMethodCandidate = FindForEachPatternMethod(enumeratorType,
isAsync ? MoveNextAsyncMethodName : MoveNextMethodName,
lookupResult, warningsOnly: false, diagnostics: diagnostics, isAsync: isAsync, arguments: ImmutableArray<BoundExpression>.Empty);
lookupResult, warningsOnly: false, diagnostics: diagnostics, isAsync: isAsync);
if ((object)moveNextMethodCandidate == null ||
moveNextMethodCandidate.IsStatic || moveNextMethodCandidate.DeclaredAccessibility != Accessibility.Public ||
......
......@@ -137,28 +137,32 @@ internal override TypeSymbol GetIteratorElementType(YieldStatementSyntax node, D
// and deduce an iterator element type from the return type. If we didn't do this, the
// TypeInfo.ConvertedType of the yield statement would always be an error type. However, we will
// not mutate any state (i.e. we won't store the result).
return GetIteratorElementTypeFromReturnType(refKind, returnType, node, diagnostics) ?? CreateErrorType();
return GetIteratorElementTypeFromReturnType(refKind, returnType, node, diagnostics).elementType ?? CreateErrorType();
}
if (_iteratorInfo == IteratorInfo.Empty)
{
TypeSymbol elementType = null;
DiagnosticBag elementTypeDiagnostics = DiagnosticBag.GetInstance();
elementType = GetIteratorElementTypeFromReturnType(refKind, returnType, node, elementTypeDiagnostics);
(TypeSymbol elementType, bool asyncInterface) = GetIteratorElementTypeFromReturnType(refKind, returnType, node, elementTypeDiagnostics);
Location errorLocation = _methodSymbol.Locations[0];
if ((object)elementType == null)
{
if (refKind != RefKind.None)
{
Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturnRef, _methodSymbol.Locations[0], _methodSymbol);
Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturnRef, errorLocation, _methodSymbol);
}
else if (!returnType.IsErrorType())
{
Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturn, _methodSymbol.Locations[0], _methodSymbol, returnType);
Error(elementTypeDiagnostics, ErrorCode.ERR_BadIteratorReturn, errorLocation, _methodSymbol, returnType);
}
elementType = CreateErrorType();
}
else if (asyncInterface && !_methodSymbol.IsAsync)
{
Error(elementTypeDiagnostics, ErrorCode.ERR_IteratorMustBeAsync, errorLocation, _methodSymbol, returnType);
}
var info = new IteratorInfo(elementType, elementTypeDiagnostics.ToReadOnlyAndFree());
......@@ -175,12 +179,15 @@ internal override TypeSymbol GetIteratorElementType(YieldStatementSyntax node, D
return _iteratorInfo.ElementType;
}
private TypeSymbol GetIteratorElementTypeFromReturnType(RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
private (TypeSymbol elementType, bool asyncInterface) GetIteratorElementTypeFromReturnType(RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
{
return GetIteratorElementTypeFromReturnType(Compilation, refKind, returnType, errorLocationNode, diagnostics).TypeSymbol;
(TypeSymbolWithAnnotations elementType, bool asyncInterface) = GetIteratorElementTypeFromReturnType(Compilation, refKind, returnType, errorLocationNode, diagnostics);
return (elementType.TypeSymbol, asyncInterface);
}
internal static TypeSymbolWithAnnotations GetIteratorElementTypeFromReturnType(CSharpCompilation compilation, RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
// If an element type is found, we also return whether the interface is meant to be used with async.
internal static (TypeSymbolWithAnnotations elementType, bool asyncInterface) GetIteratorElementTypeFromReturnType(CSharpCompilation compilation,
RefKind refKind, TypeSymbol returnType, CSharpSyntaxNode errorLocationNode, DiagnosticBag diagnostics)
{
if (refKind == RefKind.None && returnType.Kind == SymbolKind.NamedType)
{
......@@ -194,17 +201,17 @@ internal static TypeSymbolWithAnnotations GetIteratorElementTypeFromReturnType(C
{
ReportUseSiteDiagnostics(objectType, diagnostics, errorLocationNode);
}
return TypeSymbolWithAnnotations.Create(objectType);
return (TypeSymbolWithAnnotations.Create(objectType), false);
case SpecialType.System_Collections_Generic_IEnumerable_T:
case SpecialType.System_Collections_Generic_IEnumerator_T:
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0];
return (((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0], false);
}
if (TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T), TypeCompareKind.ConsiderEverything2) ||
TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T), TypeCompareKind.ConsiderEverything2))
{
return ((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0];
return (((NamedTypeSymbol)returnType).TypeArgumentsNoUseSiteDiagnostics[0], true);
}
}
......
......@@ -1503,7 +1503,7 @@ private bool ExactOrBoundsNullableInference(ExactOrBoundsKind kind, TypeSymbolWi
return true;
}
if (s_isNullableOnly(source) && s_isNullableOnly(target))
if (isNullableOnly(source) && isNullableOnly(target))
{
ExactOrBoundsInference(kind, source.AsNotNullableReferenceType(), target.AsNotNullableReferenceType(), ref useSiteDiagnostics);
return true;
......@@ -1512,7 +1512,7 @@ private bool ExactOrBoundsNullableInference(ExactOrBoundsKind kind, TypeSymbolWi
return false;
// True if the type is nullable but not an unconstrained type parameter.
bool s_isNullableOnly(TypeSymbolWithAnnotations type)
bool isNullableOnly(TypeSymbolWithAnnotations type)
=> type.NullableAnnotation.IsAnyNullable() && !type.TypeSymbol.IsTypeParameterDisallowingAnnotation();
}
......
......@@ -6306,6 +6306,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Method &apos;{0}&apos; with an iterator block must be &apos;async&apos; to return &apos;{1}&apos;.
/// </summary>
internal static string ERR_IteratorMustBeAsync {
get {
return ResourceManager.GetString("ERR_IteratorMustBeAsync", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No such label &apos;{0}&apos; within the scope of the goto statement.
/// </summary>
......
......@@ -2794,6 +2794,9 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep
<data name="ERR_BadYieldInFinally" xml:space="preserve">
<value>Cannot yield in the body of a finally clause</value>
</data>
<data name="ERR_IteratorMustBeAsync" xml:space="preserve">
<value>Method '{0}' with an iterator block must be 'async' to return '{1}'</value>
</data>
<data name="ERR_BadYieldInTryOfCatch" xml:space="preserve">
<value>Cannot yield a value in the body of a try block with a catch clause</value>
</data>
......
......@@ -1583,6 +1583,7 @@ internal enum ErrorCode
ERR_FeatureNotAvailableInVersion8 = 8400,
ERR_AltInterpolatedVerbatimStringsNotAvailable = 8401,
WRN_DefaultLiteralConvertedToNullIsNotIntended = 8402,
ERR_IteratorMustBeAsync = 8403,
ERR_NoConvToIAsyncDisp = 8410,
ERR_AwaitForEachMissingMember = 8411,
......
......@@ -5143,7 +5143,9 @@ public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement no
return null;
}
var method = (MethodSymbol)_symbol;
TypeSymbolWithAnnotations elementType = InMethodBinder.GetIteratorElementTypeFromReturnType(compilation, RefKind.None, method.ReturnType.TypeSymbol, errorLocationNode: null, diagnostics: null);
TypeSymbolWithAnnotations elementType = InMethodBinder.GetIteratorElementTypeFromReturnType(compilation, RefKind.None,
method.ReturnType.TypeSymbol, errorLocationNode: null, diagnostics: null).elementType;
VisitOptionalImplicitConversion(expr, elementType, useLegacyWarnings: false, AssignmentKind.Return);
return null;
}
......
......@@ -135,7 +135,7 @@ protected override BoundBinaryOperator ShouldEnterFinallyBlock()
/// <summary>
/// Lower the body, adding an entry state (-3) at the start,
/// so that we can differentiate a async-iterator that was never moved forward with MoveNextAsync()
/// so that we can differentiate an async-iterator that was never moved forward with MoveNextAsync()
/// from one that is running (-1).
/// Then we can guard against some bad usages of DisposeAsync.
/// </summary>
......
......@@ -6,6 +6,7 @@
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
namespace Microsoft.CodeAnalysis.CSharp
{
......@@ -85,7 +86,7 @@ private bool CanRewriteForEachAsFor(SyntaxNode forEachSyntax, TypeSymbol nodeExp
/// Lower a foreach loop that will enumerate a collection using an enumerator.
///
/// <![CDATA[
/// E e = ((C)(x)).GetEnumerator() OR ((C)(x)).GetAsyncEnumerator(default)
/// E e = ((C)(x)).GetEnumerator() OR ((C)(x)).GetAsyncEnumerator()
/// try {
/// while (e.MoveNext()) OR while (await e.MoveNextAsync())
/// {
......@@ -101,6 +102,7 @@ private bool CanRewriteForEachAsFor(SyntaxNode forEachSyntax, TypeSymbol nodeExp
private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement node)
{
var forEachSyntax = (CommonForEachStatementSyntax)node.Syntax;
bool isAsync = node.AwaitOpt != null;
ForEachEnumeratorInfo enumeratorInfo = node.EnumeratorInfoOpt;
Debug.Assert(enumeratorInfo != null);
......@@ -119,11 +121,12 @@ private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement n
// Reference to e.
BoundLocal boundEnumeratorVar = MakeBoundLocal(forEachSyntax, enumeratorVar, enumeratorType);
// ((C)(x)).GetEnumerator(); OR (x).GetEnumerator(); OR async variants
var arguments = (node.AwaitOpt != null)
? ImmutableArray.Create<BoundExpression>(new BoundDefaultExpression(forEachSyntax, getEnumeratorMethod.Parameters[0].Type.TypeSymbol))
: ImmutableArray<BoundExpression>.Empty;
BoundExpression enumeratorVarInitValue = SynthesizeCall(forEachSyntax, rewrittenExpression, getEnumeratorMethod, enumeratorInfo.CollectionConversion, enumeratorInfo.CollectionType, arguments);
// ((C)(x)).GetEnumerator(); OR (x).GetEnumerator(); OR async variants (which fill-in arguments for optional parameters)
BoundExpression enumeratorVarInitValue = SynthesizeCall(
forEachSyntax,
ConvertReceiverForInvocation(forEachSyntax, rewrittenExpression, getEnumeratorMethod, enumeratorInfo.CollectionConversion, enumeratorInfo.CollectionType),
getEnumeratorMethod,
allowExtensionAndOptionalParameters: isAsync);
// E e = ((C)(x)).GetEnumerator();
BoundStatement enumeratorVarDecl = MakeLocalDeclaration(forEachSyntax, enumeratorVar, enumeratorVarInitValue);
......@@ -160,12 +163,12 @@ private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement n
// }
var rewrittenBodyBlock = CreateBlockDeclaringIterationVariables(iterationVariables, iterationVarDecl, rewrittenBody, forEachSyntax);
BoundExpression rewrittenCondition = BoundCall.Synthesized(
BoundExpression rewrittenCondition = SynthesizeCall(
syntax: forEachSyntax,
receiverOpt: boundEnumeratorVar,
method: enumeratorInfo.MoveNextMethod);
if (node.AwaitOpt != null)
receiver: boundEnumeratorVar,
method: enumeratorInfo.MoveNextMethod,
allowExtensionAndOptionalParameters: isAsync);
if (isAsync)
{
rewrittenCondition = RewriteAwaitExpression(forEachSyntax, rewrittenCondition, node.AwaitOpt, node.AwaitOpt.GetResult.ReturnType.TypeSymbol, used: true);
}
......@@ -248,7 +251,12 @@ private bool TryGetDisposeMethod(CommonForEachStatementSyntax forEachSyntax, For
Conversion.ImplicitReference;
// ((IDisposable)e).Dispose() or e.Dispose() or await ((IAsyncDisposable)e).DisposeAsync() or await e.DisposeAsync()
BoundExpression disposeCall = SynthesizeCall(forEachSyntax, boundEnumeratorVar, disposeMethod, receiverConversion, idisposableTypeSymbol, arguments: ImmutableArray<BoundExpression>.Empty);
// Note: pattern-based async disposal is not allowed (cannot use ref structs in async methods), so the arguments are known to be empty even for async case
BoundExpression disposeCall = BoundCall.Synthesized(
forEachSyntax,
ConvertReceiverForInvocation(forEachSyntax, boundEnumeratorVar, disposeMethod, receiverConversion, idisposableTypeSymbol),
disposeMethod,
ImmutableArray<BoundExpression>.Empty);
BoundStatement disposeCallStatement;
if (disposeAwaitableInfoOpt != null)
{
......@@ -391,10 +399,10 @@ private BoundStatement WrapWithAwait(CommonForEachStatementSyntax forEachSyntax,
}
/// <summary>
/// Synthesize a call to a given method, possibly applying a conversion to the receiver.
///
/// If the receiver is of struct type and the method is an interface method, then skip the conversion
/// and just call the interface method directly - the code generator will detect this and generate a
/// Optionally apply a conversion to the receiver.
///
/// If the receiver is of struct type and the method is an interface method, then skip the conversion.
/// When we call the interface method directly - the code generator will detect it and generate a
/// constrained virtual call.
/// </summary>
/// <param name="syntax">A syntax node to attach to the synthesized bound node.</param>
......@@ -402,9 +410,9 @@ private BoundStatement WrapWithAwait(CommonForEachStatementSyntax forEachSyntax,
/// <param name="method">Method to invoke.</param>
/// <param name="receiverConversion">Conversion to be applied to the receiver if not calling an interface method on a struct.</param>
/// <param name="convertedReceiverType">Type of the receiver after applying the conversion.</param>
/// <returns>A BoundExpression representing the call.</returns>
private BoundExpression SynthesizeCall(CSharpSyntaxNode syntax, BoundExpression receiver, MethodSymbol method, Conversion receiverConversion, TypeSymbol convertedReceiverType, ImmutableArray<BoundExpression> arguments)
private BoundExpression ConvertReceiverForInvocation(CSharpSyntaxNode syntax, BoundExpression receiver, MethodSymbol method, Conversion receiverConversion, TypeSymbol convertedReceiverType)
{
Debug.Assert(!method.IsExtensionMethod);
if (!receiver.Type.IsReferenceType && method.ContainingType.IsInterface)
{
Debug.Assert(receiverConversion.IsImplicit && !receiverConversion.IsUserDefined);
......@@ -427,25 +435,35 @@ private BoundExpression SynthesizeCall(CSharpSyntaxNode syntax, BoundExpression
// We're invoking the interface method directly on the struct (which may have a private
// explicit implementation). The code generator knows how to handle it though.
// receiver.InterfaceMethod() OR receiver.InterfaceMethod(default)
return BoundCall.Synthesized(syntax, receiver, method, arguments);
// receiver.InterfaceMethod()
}
else
{
// ((Interface)receiver).InterfaceMethod() OR ((Interface)receiver).InterfaceMethod(default)
// ((Interface)receiver).InterfaceMethod()
Debug.Assert(!receiverConversion.IsNumeric);
return BoundCall.Synthesized(
receiver = MakeConversionNode(
syntax: syntax,
receiverOpt: MakeConversionNode(
syntax: syntax,
rewrittenOperand: receiver,
conversion: receiverConversion,
@checked: false,
rewrittenType: convertedReceiverType),
method: method,
arguments);
rewrittenOperand: receiver,
conversion: receiverConversion,
@checked: false,
rewrittenType: convertedReceiverType);
}
return receiver;
}
private BoundExpression SynthesizeCall(CSharpSyntaxNode syntax, BoundExpression receiver, MethodSymbol method, bool allowExtensionAndOptionalParameters)
{
Debug.Assert(!method.IsExtensionMethod);
if (allowExtensionAndOptionalParameters)
{
// Generate a call with zero explicit arguments, but with implicit arguments for optional and params parameters.
return MakeCallWithNoExplicitArgument(syntax, receiver, method);
}
// Generate a call with literally zero arguments
return BoundCall.Synthesized(syntax, receiver, method, arguments: ImmutableArray<BoundExpression>.Empty);
}
/// <summary>
......
......@@ -422,29 +422,7 @@ private BoundExpression GenerateDisposeCall(SyntaxNode syntax, BoundExpression d
}
else
{
var receiver = methodOpt.IsExtensionMethod
? null
: disposedExpression;
var args = methodOpt.IsExtensionMethod
? ImmutableArray.Create(disposedExpression)
: ImmutableArray<BoundExpression>.Empty;
var refs = methodOpt.IsExtensionMethod && !methodOpt.ParameterRefKinds.IsDefaultOrEmpty
? ImmutableArray.Create(methodOpt.ParameterRefKinds[0])
: default;
disposeCall = MakeCall(syntax: syntax,
rewrittenReceiver: receiver,
method: methodOpt,
rewrittenArguments: args,
argumentRefKindsOpt: refs,
expanded: methodOpt.HasParamsParameter(),
invokedAsExtensionMethod: methodOpt.IsExtensionMethod,
argsToParamsOpt: default,
resultKind: LookupResultKind.Viable,
type: methodOpt.ReturnType.TypeSymbol,
nodeOpt: null);
disposeCall = MakeCallWithNoExplicitArgument(syntax, disposedExpression, methodOpt);
if (!(awaitOpt is null))
{
......@@ -458,5 +436,34 @@ private BoundExpression GenerateDisposeCall(SyntaxNode syntax, BoundExpression d
return disposeCall;
}
/// <summary>
/// Synthesize a call `expression.Method()`, but with some extra smarts to handle extension methods, and to fill-in optional and params parameters.
/// </summary>
private BoundExpression MakeCallWithNoExplicitArgument(SyntaxNode syntax, BoundExpression expression, MethodSymbol method)
{
var receiver = method.IsExtensionMethod ? null : expression;
var args = method.IsExtensionMethod
? ImmutableArray.Create(expression)
: ImmutableArray<BoundExpression>.Empty;
var refKinds = method.IsExtensionMethod && !method.ParameterRefKinds.IsDefaultOrEmpty
? ImmutableArray.Create(method.ParameterRefKinds[0])
: default;
BoundExpression disposeCall = MakeCall(syntax: syntax,
rewrittenReceiver: receiver,
method: method,
rewrittenArguments: args,
argumentRefKindsOpt: refKinds,
expanded: method.HasParamsParameter(),
invokedAsExtensionMethod: method.IsExtensionMethod,
argsToParamsOpt: default,
resultKind: LookupResultKind.Viable,
type: method.ReturnType.TypeSymbol);
return disposeCall;
}
}
}
......@@ -409,10 +409,13 @@ protected SynthesizedImplementationMethod GenerateIteratorGetEnumerator(MethodSy
thenBuilder.Add(extraReset);
}
thenBuilder.Add(
method.IsStatic || method.ThisParameter.Type.IsReferenceType ? // if this is a reference type, no need to copy it since it is not assignable
F.Goto(thisInitialized) : // goto thisInitialized
(BoundStatement)F.StatementList());
if (method.IsStatic || method.ThisParameter.Type.IsReferenceType)
{
// if this is a reference type, no need to copy it since it is not assignable
thenBuilder.Add(
// goto thisInitialized;
F.Goto(thisInitialized));
}
makeIterator = F.If(
// if (this.state == -2 && this.initialThreadId == Thread.CurrentThread.ManagedThreadId)
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -177,6 +177,11 @@
<target state="new">An expression of type '{0}' can never match the provided pattern.</target>
<note />
</trans-unit>
<trans-unit id="ERR_IteratorMustBeAsync">
<source>Method '{0}' with an iterator block must be 'async' to return '{1}'</source>
<target state="new">Method '{0}' with an iterator block must be 'async' to return '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ERR_MissingPattern">
<source>Pattern missing</source>
<target state="new">Pattern missing</target>
......
......@@ -1168,6 +1168,7 @@ static async System.Collections.Generic.IAsyncEnumerator<int> M(int value)
[ConditionalFact(typeof(WindowsDesktopOnly))]
[WorkItem(31057, "https://github.com/dotnet/roslyn/issues/31057")]
[WorkItem(31113, "https://github.com/dotnet/roslyn/issues/31113")]
[WorkItem(31608, "https://github.com/dotnet/roslyn/issues/31608")]
public void AsyncIteratorReturningEnumerator_WithoutAsync()
{
string source = @"
......@@ -1181,6 +1182,9 @@ static System.Collections.Generic.IAsyncEnumerator<int> M(int value)
}";
var comp = CreateCompilationWithAsyncIterator(source);
comp.VerifyDiagnostics(
// (4,61): error CS8403: Method 'C.M(int)' with an iterator block must be 'async' to return 'IAsyncEnumerator<int>'
// static System.Collections.Generic.IAsyncEnumerator<int> M(int value)
Diagnostic(ErrorCode.ERR_IteratorMustBeAsync, "M").WithArguments("C.M(int)", "System.Collections.Generic.IAsyncEnumerator<int>").WithLocation(4, 61),
// (7,9): error CS4032: The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task<IAsyncEnumerator<int>>'.
// await System.Threading.Tasks.Task.CompletedTask;
Diagnostic(ErrorCode.ERR_BadAwaitWithoutAsyncMethod, "await System.Threading.Tasks.Task.CompletedTask").WithArguments("System.Collections.Generic.IAsyncEnumerator<int>").WithLocation(7, 9)
......@@ -1343,6 +1347,94 @@ public static async System.Collections.Generic.IAsyncEnumerable<int> M()
);
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
[WorkItem(31608, "https://github.com/dotnet/roslyn/issues/31608")]
public void AsyncIterator_WithoutAwait()
{
string source = @"
public class C
{
public static async System.Collections.Generic.IAsyncEnumerable<int> M()
{
yield return 1;
}
}";
var comp = CreateCompilationWithAsyncIterator(new[] { Run(iterations: 2), source }, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (4,74): 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.
// public static async System.Collections.Generic.IAsyncEnumerable<int> M()
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74)
);
CompileAndVerify(comp, expectedOutput: "1 END DISPOSAL DONE");
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
[WorkItem(31608, "https://github.com/dotnet/roslyn/issues/31608")]
public void AsyncIterator_WithoutAwait_WithoutAsync()
{
string source = @"
class C
{
static System.Collections.Generic.IAsyncEnumerable<int> M()
{
yield return 1;
}
}";
var comp = CreateCompilationWithAsyncIterator(source);
comp.VerifyDiagnostics(
// (4,61): error CS8403: Method 'C.M()' with an iterator block must be 'async' to return 'IAsyncEnumerable<int>'
// static System.Collections.Generic.IAsyncEnumerable<int> M()
Diagnostic(ErrorCode.ERR_IteratorMustBeAsync, "M").WithArguments("C.M()", "System.Collections.Generic.IAsyncEnumerable<int>").WithLocation(4, 61)
);
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
[WorkItem(31608, "https://github.com/dotnet/roslyn/issues/31608")]
public void AsyncIterator_WithoutAwait_WithoutAsync_LocalFunction()
{
string source = @"
class C
{
void M()
{
_ = local();
static System.Collections.Generic.IAsyncEnumerator<int> local()
{
yield break;
}
}
}";
var comp = CreateCompilationWithAsyncIterator(source);
comp.VerifyDiagnostics(
// (7,65): error CS8403: Method 'local()' with an iterator block must be 'async' to return 'IAsyncEnumerator<int>'
// static System.Collections.Generic.IAsyncEnumerator<int> local()
Diagnostic(ErrorCode.ERR_IteratorMustBeAsync, "local").WithArguments("local()", "System.Collections.Generic.IAsyncEnumerator<int>").WithLocation(7, 65)
);
}
[ConditionalFact(typeof(WindowsDesktopOnly))]
[WorkItem(31608, "https://github.com/dotnet/roslyn/issues/31608")]
public void Iterator_WithAsync()
{
string source = @"
class C
{
static async System.Collections.Generic.IEnumerable<int> M()
{
yield return 1;
}
}";
var comp = CreateCompilationWithAsyncIterator(source);
comp.VerifyDiagnostics(
// (4,62): error CS1983: The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T>
// static async System.Collections.Generic.IEnumerable<int> M()
Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(4, 62),
// (4,62): 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 System.Collections.Generic.IEnumerable<int> M()
Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 62)
);
}
[Fact]
[WorkItem(31552, "https://github.com/dotnet/roslyn/issues/31552")]
public void AsyncIteratorReturningEnumerator_WithoutBody()
......
......@@ -273,7 +273,11 @@ public ImmutableArray<U> ToDowncastedImmutable<U>()
public ImmutableArray<T> ToImmutableAndFree()
{
ImmutableArray<T> result;
if (_builder.Capacity == Count)
if (Count == 0)
{
result = ImmutableArray<T>.Empty;
}
else if (_builder.Capacity == Count)
{
result = _builder.MoveToImmutable();
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册