Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix IOperation for asynchronous using and foreach statements #37963

Merged
merged 7 commits into from
Aug 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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