Example #1
0
        /// <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));
        }
        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();
                }
            }
        }
        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));
        }