Permalink
Browse files

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

Merge dev16.0-preview2 to master
  • Loading branch information...
dotnet-automerge-bot committed Jan 10, 2019
2 parents 4ca8c46 + e70424c commit 8d00d4fb2b9d362740222120106ea1ba1edec0df
Showing with 762 additions and 203 deletions.
  1. +5 −3 docs/features/async-streams.md
  2. +9 −22 src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
  3. +18 −11 src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs
  4. +2 −2 src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs
  5. +9 −0 src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
  6. +3 −0 src/Compilers/CSharp/Portable/CSharpResources.resx
  7. +1 −0 src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
  8. +3 −1 src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
  9. +1 −1 src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncIteratorMethodToStateMachineRewriter.cs
  10. +48 −30 src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs
  11. +30 −23 src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs
  12. +7 −4 src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineRewriter.cs
  13. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
  14. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
  15. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
  16. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
  17. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
  18. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
  19. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
  20. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
  21. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
  22. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
  23. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
  24. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
  25. +5 −0 src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
  26. +92 −0 src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs
  27. +464 −105 src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs
  28. +5 −1 src/Dependencies/PooledObjects/ArrayBuilder.cs
@@ -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();
}

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
@@ -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>
Oops, something went wrong.

0 comments on commit 8d00d4f

Please sign in to comment.