internal UsingCSharpStatement(ReadOnlyCollection <ParameterExpression> variables, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose) { Variables = variables; Body = body; AwaitInfo = awaitInfo; PatternDispose = patternDispose; }
/// <summary> /// Creates a <see cref="UsingCSharpStatement"/> that represents an await using statement. /// </summary> /// <param name="awaitInfo">The information required to await the DisposeAsync operation.</param> /// <param name="variables">The variables introduced by the statement.</param> /// <param name="declarations">The resources managed by the statement.</param> /// <param name="body">The body of the statement.</param> /// <param name="patternDispose">The (optional) lambda expression representing how to call the dispose method.</param> /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns> public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, IEnumerable <LocalDeclaration> declarations, Expression body, LambdaExpression patternDispose) { RequiresNotNull(declarations, nameof(declarations)); AssertUsingAwaitInfo(ref awaitInfo, patternDispose); return(Using(awaitInfo, variables, declarations, body, patternDispose)); }
/// <summary> /// Creates a <see cref="UsingCSharpStatement"/> that represents an await using statement. /// </summary> /// <param name="awaitInfo">The information required to await the DisposeAsync operation.</param> /// <param name="variables">The variables introduced by the statement.</param> /// <param name="resource">The resource managed by the statement.</param> /// <param name="body">The body of the statement.</param> /// <param name="patternDispose">The (optional) lambda expression representing how to call the dispose method.</param> /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns> public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, Expression resource, Expression body, LambdaExpression patternDispose) { RequiresNotNull(resource, nameof(resource)); AssertUsingAwaitInfo(ref awaitInfo, patternDispose); return(Using(awaitInfo, variables, resource, body, patternDispose)); }
public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, ParameterExpression variable, Expression resource, Expression body) { RequiresNotNull(variable, nameof(variable)); var variables = new[] { variable }; var declaration = LocalDeclaration(variable, resource); return(AwaitUsing(awaitInfo, variables, new[] { declaration }, body, patternDispose: null)); }
/// <summary> /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will return this expression. /// </summary> /// <param name="operand">The <see cref="UnaryCSharpExpression.Operand" /> property of the result.</param> /// <param name="info">The <see cref="AwaitCSharpExpression.Info"/> property of the result.</param> /// <returns>This expression if no children changed, or an expression with the updated children.</returns> public AwaitCSharpExpression Update(Expression operand, AwaitInfo info) { if (operand == Operand && info == Info) { return(this); } return(Rewrite(operand, info)); }
protected internal virtual AwaitInfo VisitAwaitInfo(AwaitInfo node) { if (node == null) { return(null); } return(node.Accept(this)); }
/// <summary> /// Creates an <see cref="AwaitCSharpExpression"/> that represents awaiting an asynchronous operation. /// </summary> /// <param name="operand">An <see cref="Expression" /> that specifies the asynchronous operation to await.</param> /// <param name="info">An <see cref="AwaitInfo"/> that specifies binding information for the await operation.</param> /// <returns>An instance of the <see cref="AwaitCSharpExpression"/>.</returns> public static AwaitCSharpExpression Await(Expression operand, AwaitInfo info) { // NB: This is the overload the C# compiler binds to. RequiresNotNull(operand, nameof(operand)); RequiresNotNull(info, nameof(info)); RequiresCanRead(operand, nameof(operand)); info.RequiresCanBind(operand); return(new AwaitCSharpExpression(operand, info)); }
/// <summary> /// Creates a new <see cref="EnumeratorInfo"/> object providing binding information for foreach operations. /// </summary> /// <param name="isAsync">A Boolean indicating whether the enumeration is asynchronous.</param> /// <param name="collectionType">The type of the collection being enumerated.</param> /// <param name="getEnumerator">The <see cref="LambdaExpression"/> representing the operation used to obtain an enumerator from the collection.</param> /// <param name="moveNext">The <see cref="LambdaExpression"/> representing the operation used to invoke MoveNext or MoveNextAsync on the enumerator instance.</param> /// <param name="currentPropertyGetMethod">The get method of the Current property on the enumerator.</param> /// <param name="currentConversion">The <see cref="LambdaExpression"/> representing the conversion of the object returned from the Current property to the element type.</param> /// <param name="elementType">The type of the elements obtained by the iteration.</param> /// <param name="needsDisposal">A Boolean indicating whether a call to Dispose or DisposeAsync is required.</param> /// <param name="disposeAwaitInfo">The information required to await the DisposeAsync operation for await foreach statements.</param> /// <param name="patternDispose">The (optional) <see cref="LambdaExpression"/> representing how to call the dispose method.</param> /// <returns>A <see cref="EnumeratorInfo"/> object providing binding information for foreach operations.</returns> public static EnumeratorInfo EnumeratorInfo( bool isAsync, Type collectionType, LambdaExpression getEnumerator, LambdaExpression moveNext, MethodInfo currentPropertyGetMethod, LambdaExpression currentConversion, Type elementType, bool needsDisposal, AwaitInfo disposeAwaitInfo, LambdaExpression patternDispose) => EnumeratorInfo( isAsync, collectionType, getEnumerator, moveNext, GetProperty(currentPropertyGetMethod), currentConversion, elementType, needsDisposal, disposeAwaitInfo, patternDispose );
internal EnumeratorInfo( bool isAsync, Type collectionType, LambdaExpression getEnumerator, LambdaExpression moveNext, PropertyInfo current, LambdaExpression currentConversion, Type elementType, bool needsDisposal, AwaitInfo disposeAwaitInfo, LambdaExpression patternDispose) { IsAsync = isAsync; CollectionType = collectionType; GetEnumerator = getEnumerator; MoveNext = moveNext; Current = current; CurrentConversion = currentConversion; ElementType = elementType; NeedsDisposal = needsDisposal; DisposeAwaitInfo = disposeAwaitInfo; PatternDispose = patternDispose; }
internal static WithResources Make(ReadOnlyCollection <ParameterExpression> variables, ReadOnlyCollection <LocalDeclaration> resources, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose) { RequiresNotNullItems(resources, nameof(resources)); RequiresNotEmpty(resources, nameof(resources)); var resourceType = default(Type); foreach (var declaration in resources) { var declType = declaration.Variable.Type; ValidateType(declType); if (resourceType == null) { resourceType = declType; } else if (resourceType != declType) { // NB: `using (ResourceType r1 = e1, r2 = e2, ...)`. throw Error.UsingVariableDeclarationsShouldBeConsistentlyTyped(); } // // REVIEW: This is cumbersome and makes "declaration" a misnomer. It'd likely be better to only use variables for // additional locals, thus excluding the declared resource variables. The real issue is that we piggyback // on Roslyn to establish these scopes for locals (e.g. introduced through `out` variables or in patterns // using `var` or declarations), and here we're getting a union of variables. Our using node is the first // node to have a first-class notion of "declarations" to reflect the C# grammar. // if (!variables.Contains(declaration.Variable)) { throw Error.UsingVariableNotInScope(declaration.Variable); } } CheckUsingResourceType(resourceType, awaitInfo, patternDispose); RequiresCanRead(body, nameof(body)); return(new WithResources(variables, resources, body, awaitInfo, patternDispose)); }
internal WithResources(ReadOnlyCollection <ParameterExpression> variables, ReadOnlyCollection <LocalDeclaration> resources, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose) : base(variables, body, awaitInfo, patternDispose) { Declarations = resources; }
internal static WithResource Make(ReadOnlyCollection <ParameterExpression> variables, Expression resource, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose) { RequiresCanRead(resource, nameof(resource)); CheckUsingResourceType(resource.Type, awaitInfo, patternDispose); RequiresCanRead(body, nameof(body)); return(new WithResource(variables, resource, body, awaitInfo, patternDispose)); }
internal WithResource(ReadOnlyCollection <ParameterExpression> variables, Expression resource, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose) : base(variables, body, awaitInfo, patternDispose) { Resource = resource; }
/// <summary> /// Creates a <see cref="UsingCSharpStatement"/> that represents an await using statement. /// </summary> /// <param name="awaitInfo">The information required to await the DisposeAsync operation.</param> /// <param name="variables">The variables introduced by the statement.</param> /// <param name="resource">The resource managed by the statement.</param> /// <param name="body">The body of the statement.</param> /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns> public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, Expression resource, Expression body) => AwaitUsing(awaitInfo, variables, resource, body, patternDispose: null);
internal AwaitCSharpExpression(Expression operand, AwaitInfo info) : base(operand) { Info = info; }
internal static ForEachCSharpStatement Make(EnumeratorInfo enumeratorInfo, AwaitInfo awaitInfo, ReadOnlyCollection <ParameterExpression> variables, Expression collection, Expression body, LabelTarget breakLabel, LabelTarget continueLabel, LambdaExpression conversion, LambdaExpression deconstruction) { if (variables.Count == 0) { throw Error.ForEachNeedsOneOrMoreVariables(); } RequiresNotNullItems(variables, nameof(variables)); if (!AreReferenceAssignable(enumeratorInfo.CollectionType, collection.Type)) { throw Error.ForEachCollectionTypeNotCompatibleWithCollectionExpression(enumeratorInfo.CollectionType, collection.Type); } RequiresCanRead(body, nameof(body)); ValidateLoop(body, breakLabel, continueLabel); var firstVariable = variables[0]; var firstVariableType = firstVariable.Type; if (variables.Count == 1) { if (deconstruction != null) { throw Error.ForEachDeconstructionNotSupportedWithOneVariable(); } ValidateConversion(firstVariableType, enumeratorInfo.ElementType, ref conversion); } else { if (deconstruction == null) { throw Error.ForEachDeconstructionRequiredForMultipleVariables(); } ValidateDeconstruction(enumeratorInfo.ElementType, ref conversion, deconstruction, variables); } if (awaitInfo == null) { if (collection.Type == typeof(string) && variables.Count == 1 && firstVariableType == typeof(char) && conversion == null && deconstruction == null) { return(new StringForEachStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel)); } else if (collection.Type.IsArray) { if (collection.Type.IsVector()) { if (conversion == null && deconstruction == null) { return(new SimpleArrayForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel)); } else { return(new ArrayForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel, conversion, deconstruction)); } } else { return(new MultiDimensionalArrayForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel, conversion, deconstruction)); } } } else { awaitInfo.RequiresCanBind(enumeratorInfo.MoveNext.Body); } return(new BoundForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel, conversion, deconstruction, awaitInfo)); }
/// <summary> /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will return this expression. /// </summary> /// <param name="enumeratorInfo">The <see cref="EnumeratorInfo" /> property of the result.</param> /// <param name="breakLabel">The <see cref="LoopCSharpStatement.BreakLabel" /> property of the result.</param> /// <param name="continueLabel">The <see cref="LoopCSharpStatement.ContinueLabel" /> property of the result.</param> /// <param name="variables">The <see cref="Variables" /> property of the result.</param> /// <param name="collection">The <see cref="Collection" /> property of the result.</param> /// <param name="conversion">The <see cref="Conversion"/> property of the result.</param> /// <param name="body">The <see cref="LoopCSharpStatement.Body" /> property of the result.</param> /// <param name="deconstruction">The <see cref="Deconstruction"/> property of the result.</param> /// <param name="awaitInfo">The <see cref="AwaitInfo"/> property of the result.</param> /// <returns>This expression if no children changed, or an expression with the updated children.</returns> public ForEachCSharpStatement Update(EnumeratorInfo enumeratorInfo, LabelTarget breakLabel, LabelTarget continueLabel, IEnumerable <ParameterExpression> variables, Expression collection, LambdaExpression conversion, Expression body, LambdaExpression deconstruction, AwaitInfo awaitInfo) { if (enumeratorInfo == EnumeratorInfo && breakLabel == BreakLabel && continueLabel == ContinueLabel && SameElements(ref variables, Variables) && collection == Collection && conversion == Conversion && body == Body && deconstruction == Deconstruction && awaitInfo == AwaitInfo) { return(this); } return(CSharpExpression.ForEach(awaitInfo, variables, collection, body, breakLabel, continueLabel, conversion, deconstruction)); }
/// <summary> /// Creates a <see cref="UsingCSharpStatement"/> that represents an await using statement. /// </summary> /// <param name="awaitInfo">The information required to await the DisposeAsync operation.</param> /// <param name="resource">The resource managed by the statement.</param> /// <param name="body">The body of the statement.</param> /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns> public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, Expression resource, Expression body) => AwaitUsing(awaitInfo, variables: null, resource, body, patternDispose: null);
/// <summary> /// Creates a <see cref="UsingCSharpStatement"/> that represents an await using statement. /// </summary> /// <param name="awaitInfo">The information required to await the DisposeAsync operation.</param> /// <param name="variables">The variables introduced by the statement.</param> /// <param name="declarations">The resources managed by the statement.</param> /// <param name="body">The body of the statement.</param> /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns> public static UsingCSharpStatement AwaitUsing(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, IEnumerable <LocalDeclaration> declarations, Expression body) => AwaitUsing(awaitInfo, variables, declarations, body, patternDispose: null);
/// <summary> /// Creates a new expression that is like this one, but using the supplied children. /// </summary> /// <param name="operand">The <see cref="UnaryCSharpExpression.Operand" /> property of the result.</param> /// <param name="info">The <see cref="AwaitCSharpExpression.Info"/> property of the result.</param> /// <returns>An expression with the updated children.</returns> protected internal virtual AwaitCSharpExpression Rewrite(Expression operand, AwaitInfo info) => CSharpExpression.Await(operand, info);
/// <summary> /// Creates a new <see cref="EnumeratorInfo"/> object providing binding information for foreach operations. /// </summary> /// <param name="isAsync">A Boolean indicating whether the enumeration is asynchronous.</param> /// <param name="collectionType">The type of the collection being enumerated.</param> /// <param name="getEnumerator">The <see cref="LambdaExpression"/> representing the operation used to obtain an enumerator from the collection.</param> /// <param name="moveNext">The <see cref="LambdaExpression"/> representing the operation used to invoke MoveNext or MoveNextAsync on the enumerator instance.</param> /// <param name="current">The Current property on the enumerator.</param> /// <param name="currentConversion">The <see cref="LambdaExpression"/> representing the conversion of the object returned from the Current property to the element type.</param> /// <param name="elementType">The type of the elements obtained by the iteration.</param> /// <param name="needsDisposal">A Boolean indicating whether a call to Dispose or DisposeAsync is required.</param> /// <param name="disposeAwaitInfo">The information required to await the DisposeAsync operation for await foreach statements.</param> /// <param name="patternDispose">The (optional) <see cref="LambdaExpression"/> representing how to call the dispose method.</param> /// <returns>A <see cref="EnumeratorInfo"/> object providing binding information for foreach operations.</returns> public static EnumeratorInfo EnumeratorInfo( bool isAsync, Type collectionType, LambdaExpression getEnumerator, LambdaExpression moveNext, PropertyInfo current, LambdaExpression currentConversion, Type elementType, bool needsDisposal, AwaitInfo disposeAwaitInfo, LambdaExpression patternDispose) { RequiresNotNull(collectionType, nameof(collectionType)); ValidateType(collectionType); RequiresCanRead(getEnumerator, nameof(getEnumerator)); if (getEnumerator.Parameters.Count != 1) { throw new Exception(); // TODO } if (!AreReferenceAssignable(getEnumerator.Parameters[0].Type, collectionType)) { throw new Exception(); // TODO } var enumeratorType = getEnumerator.ReturnType; RequiresCanRead(moveNext, nameof(moveNext)); if (moveNext.Parameters.Count != 1) { throw new Exception(); // TODO } if (!AreReferenceAssignable(moveNext.Parameters[0].Type, enumeratorType)) { throw new Exception(); // TODO } if (!isAsync && moveNext.ReturnType != typeof(bool)) { throw new Exception(); // TOOD } // // REVIEW: We don't have info about the await operation on MoveNextAsync here, so can't validate the return type. // // Note we can't call CSharpExpression.Await to attempt to infer an await operation, because GetAwaiter // could come in through an extension method. We'll have the ForEach factory do the final check. // RequiresNotNull(current, nameof(current)); var currentGetMethod = current.GetGetMethod(nonPublic: false); if (currentGetMethod == null) { throw new Exception(); // TOOD } if (currentGetMethod.IsStatic) { throw new Exception(); } if (current.GetIndexParameters().Length != 0) { throw new Exception(); } var currentType = current.PropertyType; if (currentType == typeof(void)) { throw new Exception(); // TOOD } if (currentConversion != null) { RequiresCanRead(currentConversion, nameof(currentConversion)); if (currentConversion.Parameters.Count != 1) { throw new Exception(); // TODO } if (!AreReferenceAssignable(currentConversion.Parameters[0].Type, currentType)) { throw new Exception(); // TODO } if (!AreReferenceAssignable(elementType, currentConversion.ReturnType)) { throw new Exception(); // TODO } } else { if (!AreReferenceAssignable(elementType, currentType)) { throw new Exception(); // TODO } } if (patternDispose != null) { if (patternDispose.Parameters.Count != 1) { throw Error.UsingPatternDisposeShouldHaveOneParameter(); } } if (isAsync) { AssertUsingAwaitInfo(ref disposeAwaitInfo, patternDispose); } CheckUsingResourceType(enumeratorType, disposeAwaitInfo, patternDispose, allowConvertToDisposable: true); return(new EnumeratorInfo(isAsync, collectionType, getEnumerator, moveNext, current, currentConversion, elementType, needsDisposal, disposeAwaitInfo, patternDispose)); }
// NB: The Roslyn compiler binds to the overloads below. /// <summary> /// Creates a <see cref="UsingCSharpStatement"/> that represents a using or an await using statement. /// </summary> /// <param name="awaitInfo">The information required to await the DisposeAsync operation, or null if not asynchronous.</param> /// <param name="variables">The variables introduced by the statement.</param> /// <param name="resource">The resource managed by the statement.</param> /// <param name="body">The body of the statement.</param> /// <param name="patternDispose">The (optional) lambda expression representing how to call the dispose method.</param> /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns> public static UsingCSharpStatement Using(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, Expression resource, Expression body, LambdaExpression patternDispose) { RequiresNotNull(resource, nameof(resource)); return(UsingCSharpStatement.Make(variables, resource, resources: null, body, awaitInfo, patternDispose)); }
private static void AssertUsingAwaitInfo(ref AwaitInfo awaitInfo, LambdaExpression patternDispose) { awaitInfo ??= AwaitInfo(patternDispose?.ReturnType ?? typeof(ValueTask)); }
/// <summary> /// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will return this expression. /// </summary> /// <param name="variables">The <see cref="Variables" /> property of the result.</param> /// <param name="resource">The <see cref="Resource" /> property of the result.</param> /// <param name="declarations">The <see cref="Declarations" /> property of the result.</param> /// <param name="body">The <see cref="Body" /> property of the result.</param> /// <param name="awaitInfo">The <see cref="AwaitInfo"/> property of the result.</param> /// <param name="patternDispose">The <see cref="PatternDispose"/> property of the result.</param> /// <returns>This expression if no children changed, or an expression with the updated children.</returns> public UsingCSharpStatement Update(IEnumerable <ParameterExpression> variables, Expression resource, IEnumerable <LocalDeclaration> declarations, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose) { if (SameElements(ref variables, Variables) && resource == Resource && SameElements(ref declarations, Declarations) && body == Body && awaitInfo == AwaitInfo && patternDispose == PatternDispose) { return(this); } return(Make(variables, resource, declarations, body, awaitInfo, patternDispose)); }
/// <summary> /// Creates a new object that is like this one, but using the supplied children. If all of the children are the same, it will return this object. /// </summary> /// <param name="getEnumerator">The <see cref="GetEnumerator"/> property of the result.</param> /// <param name="moveNext">The <see cref="MoveNext"/> property of the result.</param> /// <param name="currentConversion">The <see cref="CurrentConversion"/> property of the result.</param> /// <param name="disposeAwaitInfo">The <see cref="DisposeAwaitInfo"/> property of the result.</param> /// <param name="patternDispose">The <see cref="PatternDispose"/> property of the result.</param> /// <returns>This object if no children changed, or an object with the updated children.</returns> public EnumeratorInfo Update(LambdaExpression getEnumerator, LambdaExpression moveNext, LambdaExpression currentConversion, AwaitInfo disposeAwaitInfo, LambdaExpression patternDispose) { if (getEnumerator == GetEnumerator && moveNext == MoveNext && currentConversion == CurrentConversion && disposeAwaitInfo == DisposeAwaitInfo && patternDispose == PatternDispose) { return(this); } return(CSharpExpression.EnumeratorInfo(IsAsync, CollectionType, getEnumerator, moveNext, Current, currentConversion, ElementType, NeedsDisposal, DisposeAwaitInfo, patternDispose)); }
/// <summary> /// Creates a <see cref="UsingCSharpStatement"/> that represents a using or an await using statement. /// </summary> /// <param name="awaitInfo">The information required to await the DisposeAsync operation, or null if not asynchronous.</param> /// <param name="variables">The variables introduced by the statement.</param> /// <param name="declarations">The resources managed by the statement.</param> /// <param name="body">The body of the statement.</param> /// <param name="patternDispose">The (optional) lambda expression representing how to call the dispose method.</param> /// <returns>The created <see cref="UsingCSharpStatement"/>.</returns> public static UsingCSharpStatement Using(AwaitInfo awaitInfo, IEnumerable <ParameterExpression> variables, IEnumerable <LocalDeclaration> declarations, Expression body, LambdaExpression patternDispose) { RequiresNotNull(declarations, nameof(declarations)); return(UsingCSharpStatement.Make(variables, resource: null, declarations, body, awaitInfo, patternDispose)); }
internal static UsingCSharpStatement Make(IEnumerable <ParameterExpression> variables, Expression resource, IEnumerable <LocalDeclaration> resources, Expression body, AwaitInfo awaitInfo, LambdaExpression patternDispose) { if (patternDispose != null) { if (patternDispose.Parameters.Count != 1) { throw Error.UsingPatternDisposeShouldHaveOneParameter(); } } var variablesList = CheckUniqueVariables(variables, nameof(variables)); if (resource != null) { if (resources != null) { throw Error.InvalidUsingStatement(); } return(WithResource.Make(variablesList, resource, body, awaitInfo, patternDispose)); } if (resources != null) { if (resource != null) { throw Error.InvalidUsingStatement(); } var resourcesList = resources.ToReadOnly(); return(WithResources.Make(variablesList, resourcesList, body, awaitInfo, patternDispose)); } throw Error.InvalidUsingStatement(); }
internal static void CheckUsingResourceType(Type resourceType, AwaitInfo awaitInfo, LambdaExpression patternDispose, bool allowConvertToDisposable = false) { ValidateType(resourceType); var resourceTypeNonNull = resourceType.GetNonNullableType(); Type disposeReturnType; if (patternDispose != null) { var patternDisposeInputType = patternDispose.Parameters[0].Type; if (!AreReferenceAssignable(patternDisposeInputType, resourceTypeNonNull)) { throw Error.UsingPatternDisposeInputNotCompatibleWithResource(patternDisposeInputType, resourceTypeNonNull); } disposeReturnType = patternDispose.ReturnType; } else { Type disposableInterface; if (awaitInfo != null) { disposableInterface = typeof(IAsyncDisposable); disposeReturnType = typeof(ValueTask); } else { disposableInterface = typeof(IDisposable); disposeReturnType = typeof(void); } if (allowConvertToDisposable) { // NB: In the case of foreach, we allow for a conversion to a disposable interface to be emitted. // While this conversion or type check can sometimes be elided at runtime, we call the factory // here for its side-effect of doing the neccessary checks. _ = Expression.Convert(Expression.Variable(resourceType), disposableInterface); } else { // NB: We don't handle implicit conversions here; the C# compiler can emit a Convert node, // just like it does for those type of conversions in various other places. if (!disposableInterface.IsAssignableFrom(resourceTypeNonNull)) { throw LinqError.ExpressionTypeDoesNotMatchAssignment(resourceTypeNonNull, disposableInterface); } } } if (awaitInfo != null) { awaitInfo.RequiresCanBind(Expression.Parameter(disposeReturnType)); } else { if (disposeReturnType != typeof(void)) { throw Error.UsingDisposeShouldReturnVoid(); } } }
private XNode Visit(AwaitInfo node) { VisitAwaitInfo(node); return(_nodes.Pop()); }