Skip to content

Commit

Permalink
Fix IOperation for asynchronous using and foreach statements (#37963)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv authored Aug 23, 2019
1 parent 4b8c6a7 commit 036d931
Show file tree
Hide file tree
Showing 25 changed files with 315 additions and 124 deletions.
9 changes: 6 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/ForEachEnumeratorInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ internal sealed class ForEachEnumeratorInfo
// Computed during initial binding so that we can expose it in the semantic model.
public readonly bool NeedsDisposal;

public readonly bool IsAsync;

// When async and needs disposal, this stores the information to await the DisposeAsync() invocation
public AwaitableInfo DisposeAwaitableInfo;

Expand All @@ -41,15 +43,13 @@ internal sealed class ForEachEnumeratorInfo

public readonly BinderFlags Location;

internal bool IsAsync
=> DisposeAwaitableInfo != null;

private ForEachEnumeratorInfo(
TypeSymbol collectionType,
TypeWithAnnotations elementType,
MethodSymbol getEnumeratorMethod,
MethodSymbol currentPropertyGetter,
MethodSymbol moveNextMethod,
bool isAsync,
bool needsDisposal,
AwaitableInfo disposeAwaitableInfo,
MethodSymbol disposeMethod,
Expand All @@ -69,6 +69,7 @@ private ForEachEnumeratorInfo(
this.GetEnumeratorMethod = getEnumeratorMethod;
this.CurrentPropertyGetter = currentPropertyGetter;
this.MoveNextMethod = moveNextMethod;
this.IsAsync = isAsync;
this.NeedsDisposal = needsDisposal;
this.DisposeAwaitableInfo = disposeAwaitableInfo;
this.DisposeMethod = disposeMethod;
Expand All @@ -89,6 +90,7 @@ internal struct Builder
public MethodSymbol CurrentPropertyGetter;
public MethodSymbol MoveNextMethod;

public bool IsAsync;
public bool NeedsDisposal;
public AwaitableInfo DisposeAwaitableInfo;
public MethodSymbol DisposeMethod;
Expand All @@ -112,6 +114,7 @@ public ForEachEnumeratorInfo Build(BinderFlags location)
GetEnumeratorMethod,
CurrentPropertyGetter,
MoveNextMethod,
IsAsync,
NeedsDisposal,
DisposeAwaitableInfo,
DisposeMethod,
Expand Down
8 changes: 5 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ internal override BoundStatement BindForEachDeconstruction(DiagnosticBag diagnos
// Use the right binder to avoid seeing iteration variable
BoundExpression collectionExpr = originalBinder.GetBinder(_syntax.Expression).BindRValueWithoutTargetType(_syntax.Expression, diagnostics);

ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder();
var builder = new ForEachEnumeratorInfo.Builder();
TypeWithAnnotations inferredType;
bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType);

Expand Down Expand Up @@ -198,7 +198,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
// Use the right binder to avoid seeing iteration variable
BoundExpression collectionExpr = originalBinder.GetBinder(_syntax.Expression).BindRValueWithoutTargetType(_syntax.Expression, diagnostics);

ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder();
var builder = new ForEachEnumeratorInfo.Builder();
TypeWithAnnotations inferredType;
bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType);

Expand Down Expand Up @@ -533,7 +533,7 @@ internal TypeWithAnnotations InferCollectionElementType(DiagnosticBag diagnostic
// Use the right binder to avoid seeing iteration variable
BoundExpression collectionExpr = this.GetBinder(collectionSyntax).BindValue(collectionSyntax, diagnostics, BindValueKind.RValue);

ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder();
var builder = new ForEachEnumeratorInfo.Builder();
GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out TypeWithAnnotations inferredType);
return inferredType;
}
Expand Down Expand Up @@ -626,6 +626,8 @@ private void UnwrapCollectionExpressionIfNullable(ref BoundExpression collection
private bool GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder builder, BoundExpression collectionExpr, DiagnosticBag diagnostics)
{
bool isAsync = IsAsync;
builder.IsAsync = isAsync;

EnumeratorResult found = GetEnumeratorInfo(ref builder, collectionExpr, isAsync, diagnostics);
switch (found)
{
Expand Down
17 changes: 13 additions & 4 deletions src/Compilers/CSharp/Portable/Compilation/ForEachStatementInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ namespace Microsoft.CodeAnalysis.CSharp
/// </summary>
public struct ForEachStatementInfo : IEquatable<ForEachStatementInfo>
{
/// <summary>
/// Whether this is an asynchronous foreach.
/// </summary>
public bool IsAsynchronous { get; }

/// <summary>
/// Gets the &quot;GetEnumerator&quot; method.
/// </summary>
Expand Down Expand Up @@ -55,14 +60,16 @@ public struct ForEachStatementInfo : IEquatable<ForEachStatementInfo>
/// <summary>
/// Initializes a new instance of the <see cref="ForEachStatementInfo" /> structure.
/// </summary>
internal ForEachStatementInfo(IMethodSymbol getEnumeratorMethod,
internal ForEachStatementInfo(bool isAsync,
IMethodSymbol getEnumeratorMethod,
IMethodSymbol moveNextMethod,
IPropertySymbol currentProperty,
IMethodSymbol disposeMethod,
ITypeSymbol elementType,
Conversion elementConversion,
Conversion currentConversion)
{
this.IsAsynchronous = isAsync;
this.GetEnumeratorMethod = getEnumeratorMethod;
this.MoveNextMethod = moveNextMethod;
this.CurrentProperty = currentProperty;
Expand All @@ -79,7 +86,8 @@ public override bool Equals(object obj)

public bool Equals(ForEachStatementInfo other)
{
return object.Equals(this.GetEnumeratorMethod, other.GetEnumeratorMethod)
return this.IsAsynchronous == other.IsAsynchronous
&& object.Equals(this.GetEnumeratorMethod, other.GetEnumeratorMethod)
&& object.Equals(this.MoveNextMethod, other.MoveNextMethod)
&& object.Equals(this.CurrentProperty, other.CurrentProperty)
&& object.Equals(this.DisposeMethod, other.DisposeMethod)
Expand All @@ -90,13 +98,14 @@ public bool Equals(ForEachStatementInfo other)

public override int GetHashCode()
{
return Hash.Combine(GetEnumeratorMethod,
return Hash.Combine(IsAsynchronous,
Hash.Combine(GetEnumeratorMethod,
Hash.Combine(MoveNextMethod,
Hash.Combine(CurrentProperty,
Hash.Combine(DisposeMethod,
Hash.Combine(ElementType,
Hash.Combine(ElementConversion.GetHashCode(),
CurrentConversion.GetHashCode()))))));
CurrentConversion.GetHashCode())))))));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,7 @@ public override ForEachStatementInfo GetForEachStatementInfo(CommonForEachStatem
}

return new ForEachStatementInfo(
enumeratorInfoOpt.IsAsync,
enumeratorInfoOpt.GetEnumeratorMethod,
enumeratorInfoOpt.MoveNextMethod,
currentProperty: (PropertySymbol)enumeratorInfoOpt.CurrentPropertyGetter?.AssociatedSymbol,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1533,7 +1533,8 @@ internal ForEachLoopOperationInfo GetForEachLoopOperatorInfo(BoundForEachStateme
enumeratorInfoOpt.GetEnumeratorMethod,
(PropertySymbol)enumeratorInfoOpt.CurrentPropertyGetter.AssociatedSymbol,
enumeratorInfoOpt.MoveNextMethod,
enumeratorInfoOpt.NeedsDisposal,
isAsynchronous: enumeratorInfoOpt.IsAsync,
needsDispose: enumeratorInfoOpt.NeedsDisposal,
knownToImplementIDisposable: enumeratorInfoOpt.NeedsDisposal && (object)enumeratorInfoOpt.GetEnumeratorMethod != null ?
compilation.Conversions.
ClassifyImplicitConversionFromType(enumeratorInfoOpt.GetEnumeratorMethod.ReturnType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ internal sealed class CSharpLazyForEachLoopOperation : LazyForEachLoopOperation
private readonly BoundForEachStatement _forEachStatement;

internal CSharpLazyForEachLoopOperation(CSharpOperationFactory operationFactory, BoundForEachStatement forEachStatement, ImmutableArray<ILocalSymbol> locals, ILabelSymbol continueLabel, ILabelSymbol exitLabel, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional<object> constantValue, bool isImplicit) :
base(LoopKind.ForEach, locals, continueLabel, exitLabel, semanticModel, syntax, type, constantValue, isImplicit)
base(forEachStatement.AwaitOpt != null, LoopKind.ForEach, locals, continueLabel, exitLabel, semanticModel, syntax, type, constantValue, isImplicit)
{
_operationFactory = operationFactory;
_forEachStatement = forEachStatement;
Expand Down Expand Up @@ -1368,7 +1368,7 @@ internal sealed class CSharpLazyUsingOperation : LazyUsingOperation
private readonly BoundUsingStatement _usingStatement;

internal CSharpLazyUsingOperation(CSharpOperationFactory operationFactory, BoundUsingStatement usingStatement, ImmutableArray<ILocalSymbol> locals, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional<object> constantValue, bool isImplicit) :
base(locals, semanticModel, syntax, type, constantValue, isImplicit)
base(locals, usingStatement.AwaitOpt != null, semanticModel, syntax, type, constantValue, isImplicit)
{
_operationFactory = operationFactory;
_usingStatement = usingStatement;
Expand Down
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*REMOVED*override Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousMethodExpressionSyntax.Body.get -> Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode
Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.CSharpCompilationOptions(Microsoft.CodeAnalysis.OutputKind outputKind, bool reportSuppressedDiagnostics = false, string moduleName = null, string mainTypeName = null, string scriptClassName = null, System.Collections.Generic.IEnumerable<string> usings = null, Microsoft.CodeAnalysis.OptimizationLevel optimizationLevel = Microsoft.CodeAnalysis.OptimizationLevel.Debug, bool checkOverflow = false, bool allowUnsafe = false, string cryptoKeyContainer = null, string cryptoKeyFile = null, System.Collections.Immutable.ImmutableArray<byte> cryptoPublicKey = default(System.Collections.Immutable.ImmutableArray<byte>), bool? delaySign = null, Microsoft.CodeAnalysis.Platform platform = Microsoft.CodeAnalysis.Platform.AnyCpu, Microsoft.CodeAnalysis.ReportDiagnostic generalDiagnosticOption = Microsoft.CodeAnalysis.ReportDiagnostic.Default, int warningLevel = 4, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, Microsoft.CodeAnalysis.ReportDiagnostic>> specificDiagnosticOptions = null, bool concurrentBuild = true, bool deterministic = false, Microsoft.CodeAnalysis.XmlReferenceResolver xmlReferenceResolver = null, Microsoft.CodeAnalysis.SourceReferenceResolver sourceReferenceResolver = null, Microsoft.CodeAnalysis.MetadataReferenceResolver metadataReferenceResolver = null, Microsoft.CodeAnalysis.AssemblyIdentityComparer assemblyIdentityComparer = null, Microsoft.CodeAnalysis.StrongNameProvider strongNameProvider = null, bool publicSign = false, Microsoft.CodeAnalysis.MetadataImportOptions metadataImportOptions = Microsoft.CodeAnalysis.MetadataImportOptions.Public, Microsoft.CodeAnalysis.NullableContextOptions nullableContextOptions = Microsoft.CodeAnalysis.NullableContextOptions.Disable) -> void
Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.WithNullableContextOptions(Microsoft.CodeAnalysis.NullableContextOptions options) -> Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions
Microsoft.CodeAnalysis.CSharp.ForEachStatementInfo.IsAsynchronous.get -> bool
Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax.AddBlockStatements(params Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax.Body.get -> Microsoft.CodeAnalysis.CSharp.CSharpSyntaxNode
Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax.WithBlock(Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax block) -> Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ internal override ISymbol CommonGetWellKnownTypeMember(WellKnownMember member)
return GetWellKnownTypeMember(member);
}

internal override ITypeSymbol CommonGetWellKnownType(WellKnownType wellknownType)
{
return GetWellKnownType(wellknownType);
}

internal static Symbol GetRuntimeMember(NamedTypeSymbol declaringType, ref MemberDescriptor descriptor, SignatureComparer<MethodSymbol, FieldSymbol, PropertySymbol, TypeSymbol, ParameterSymbol> comparer, AssemblySymbol accessWithinOpt)
{
Symbol result = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,7 @@ public int Current
var foreachSyntax = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
var info = model.GetForEachStatementInfo(foreachSyntax);

Assert.True(info.IsAsynchronous);
Assert.Equal("C.Enumerator C.GetAsyncEnumerator()", info.GetEnumeratorMethod.ToTestDisplayString());
Assert.Equal("System.Threading.Tasks.Task<System.Boolean> C.Enumerator.MoveNextAsync()", info.MoveNextMethod.ToTestDisplayString());
Assert.Equal("System.Int32 C.Enumerator.Current { get; }", info.CurrentProperty.ToTestDisplayString());
Expand Down
Loading

0 comments on commit 036d931

Please sign in to comment.