Пример #1
0
        public override BoundNode VisitSequence(BoundSequence node)
        {
            BoundSpillSequenceBuilder valueBuilder = null;
            var value = VisitExpression(ref valueBuilder, node.Value);

            BoundSpillSequenceBuilder builder = null;

            var sideEffects = VisitExpressionList(ref builder, node.SideEffects, forceSpill: valueBuilder != null, sideEffectsOnly: true);

            if (builder == null && valueBuilder == null)
            {
                return(node.Update(node.Locals, sideEffects, value, node.Type));
            }

            if (builder == null)
            {
                builder = new BoundSpillSequenceBuilder();
            }

            PromoteAndAddLocals(builder, node.Locals);
            builder.AddExpressions(sideEffects);
            builder.Include(valueBuilder);

            return(builder.Update(value));
        }
Пример #2
0
 public override BoundNode VisitSequence(BoundSequence node)
 {
     AddAll(node.Locals);
     base.VisitSequence(node);
     RemoveAll(node.Locals);
     return(null);
 }
Пример #3
0
        public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node)
        {
            Debug.Assert(node != null);

            // Rewrite the arguments.
            // NOTE: We may need additional argument rewriting such as generating a params array,
            //       re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc.
            // NOTE: This is done later by MakeArguments, for now we just lower each argument.
            var rewrittenArguments = VisitList(node.Arguments);

            // We have already lowered each argument, but we may need some additional rewriting for the arguments,
            // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc.
            ImmutableArray <LocalSymbol> temps;
            ImmutableArray <RefKind>     argumentRefKindsOpt = node.ArgumentRefKindsOpt;

            rewrittenArguments = MakeArguments(
                node.Syntax,
                rewrittenArguments,
                node.Constructor,
                node.Constructor,
                node.Expanded,
                node.ArgsToParamsOpt,
                ref argumentRefKindsOpt,
                out temps);

            BoundExpression rewrittenObjectCreation;

            rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, argumentRefKindsOpt, newInitializerExpression: null, changeTypeOpt: node.Type);

            // replace "new S()" with a default struct ctor with "default(S)"
            if (node.Constructor.IsDefaultValueTypeConstructor())
            {
                rewrittenObjectCreation = new BoundDefaultExpression(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type);
            }

            if (!temps.IsDefaultOrEmpty)
            {
                rewrittenObjectCreation = new BoundSequence(
                    node.Syntax,
                    temps,
                    ImmutableArray <BoundExpression> .Empty,
                    rewrittenObjectCreation,
                    node.Type);
            }

            if (node.Type.IsInterfaceType())
            {
                rewrittenObjectCreation = MakeConversionNode(rewrittenObjectCreation, node.Type, false, false);
            }

            if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors)
            {
                return(rewrittenObjectCreation);
            }

            return(MakeObjectCreationWithInitializer(node.Syntax, rewrittenObjectCreation, node.InitializerExpressionOpt, node.Type));
        }
        public override BoundNode VisitSequence(BoundSequence node)
        {
            // Spilled local temps do not appear here in a sequence expression, because any temps in a
            // sequence expression that need to be spilled would have been moved up to the
            // statement level by the AwaitLiftingRewriter.
            foreach (var local in node.Locals)
            {
                Debug.Assert(!NeedsProxy(local) || proxies.ContainsKey(local));
            }

            return(base.VisitSequence(node));
        }
Пример #5
0
        private BoundNode RewriteWithRefOperand(
            bool isPrefix,
            bool isChecked,
            ArrayBuilder <LocalSymbol> tempSymbols,
            ArrayBuilder <BoundExpression> tempInitializers,
            SyntaxNode syntax,
            BoundExpression operand,
            TypeSymbol operandType,
            BoundExpression boundTemp,
            BoundExpression newValue)
        {
            var tempValue      = isPrefix ? newValue : MakeRValue(operand);
            var tempAssignment = MakeAssignmentOperator(syntax, boundTemp, tempValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false);

            var operandValue = isPrefix ? boundTemp : newValue;
            var tempAssignedAndOperandValue = new BoundSequence(
                syntax,
                ImmutableArray <LocalSymbol> .Empty,
                ImmutableArray.Create <BoundExpression>(tempAssignment),
                operandValue,
                tempValue.Type);

            // prefix:  operand = Seq{temp = (T)(operand + 1);  temp;}
            // postfix: operand = Seq{temp = operand;        ;  (T)(temp + 1);}
            BoundExpression operandAssignment = MakeAssignmentOperator(syntax, operand, tempAssignedAndOperandValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false);

            // prefix:  Seq{operand initializers; operand = Seq{temp = (T)(operand + 1);  temp;}          result: temp}
            // postfix: Seq{operand initializers; operand = Seq{temp = operand;        ;  (T)(temp + 1);} result: temp}
            tempInitializers.Add(operandAssignment);
            return(new BoundSequence(
                       syntax: syntax,
                       locals: tempSymbols.ToImmutableAndFree(),
                       sideEffects: tempInitializers.ToImmutableAndFree(),
                       value: boundTemp,
                       type: operandType));
        }
Пример #6
0
        private BoundExpression OptimizeLiftedUnaryOperator(
            UnaryOperatorKind operatorKind,
            SyntaxNode syntax,
            MethodSymbol method,
            BoundExpression loweredOperand,
            TypeSymbol type)
        {
            if (NullableNeverHasValue(loweredOperand))
            {
                return(new BoundDefaultExpression(syntax, null, type));
            }

            // Second, another simple optimization. If we know that the operand is never null
            // then we can obtain the non-null value and skip generating the temporary. That is,
            // "~(new int?(M()))" is the same as "new int?(~M())".

            BoundExpression neverNull = NullableAlwaysHasValue(loweredOperand);

            if (neverNull != null)
            {
                return(GetLiftedUnaryOperatorConsequence(operatorKind, syntax, method, type, neverNull));
            }

            var conditionalLeft = loweredOperand as BoundLoweredConditionalAccess;

            // NOTE: we could in theory handle side-effecting loweredRight here too
            //       by including it as a part of whenNull, but there is a concern
            //       that it can lead to code duplication
            var optimize = conditionalLeft != null &&
                           (conditionalLeft.WhenNullOpt == null || conditionalLeft.WhenNullOpt.IsDefaultValue());

            if (optimize)
            {
                var result = LowerLiftedUnaryOperator(operatorKind, syntax, method, conditionalLeft.WhenNotNull, type);

                return(conditionalLeft.Update(
                           conditionalLeft.Receiver,
                           conditionalLeft.HasValueMethodOpt,
                           whenNotNull: result,
                           whenNullOpt: null,
                           id: conditionalLeft.Id,
                           type: result.Type
                           ));
            }

            // This optimization is analogous to DistributeLiftedConversionIntoLiftedOperand.

            // Suppose we have a lifted unary conversion whose operand is itself a lifted operation.
            // That is, we have something like:
            //
            // int? r = - (M() + N());
            //
            // where M() and N() return nullable ints. We would simply codegen this as first
            // creating the nullable int result of M() + N(), then checking it for nullity,
            // and then doing the unary minus. That is:
            //
            // int? m = M();
            // int? n = N();
            // int? t = m.HasValue && n.HasValue ? new int?(m.Value + n.Value) : new int?();
            // int? r = t.HasValue ? new int?(-t.Value) : new int?();
            //
            // However, we also observe that we can distribute the unary minus into both branches of
            // the conditional:
            //
            // int? m = M();
            // int? n = N();
            // int? r = m.HasValue && n.HasValue ? - (new int?(m.Value + n.Value))) : - new int?();
            //
            // And we already optimize those! So we could reduce this to:
            //
            // int? m = M();
            // int? n = N();
            // int? r = m.HasValue && n.HasValue ? new int?(- (m.Value + n.Value)) : new int?());
            //
            // which avoids entirely the creation of the unnecessary nullable int and the unnecessary
            // extra null check.

            if (loweredOperand.Kind == BoundKind.Sequence)
            {
                BoundSequence seq = (BoundSequence)loweredOperand;
                if (seq.Value.Kind == BoundKind.ConditionalOperator)
                {
                    BoundConditionalOperator conditional = (BoundConditionalOperator)seq.Value;
                    Debug.Assert(TypeSymbol.Equals(seq.Type, conditional.Type, TypeCompareKind.ConsiderEverything2));
                    Debug.Assert(TypeSymbol.Equals(conditional.Type, conditional.Consequence.Type, TypeCompareKind.ConsiderEverything2));
                    Debug.Assert(TypeSymbol.Equals(conditional.Type, conditional.Alternative.Type, TypeCompareKind.ConsiderEverything2));

                    if (NullableAlwaysHasValue(conditional.Consequence) != null && NullableNeverHasValue(conditional.Alternative))
                    {
                        return(new BoundSequence(
                                   syntax,
                                   seq.Locals,
                                   seq.SideEffects,
                                   RewriteConditionalOperator(
                                       syntax,
                                       conditional.Condition,
                                       MakeUnaryOperator(operatorKind, syntax, method, conditional.Consequence, type),
                                       MakeUnaryOperator(operatorKind, syntax, method, conditional.Alternative, type),
                                       ConstantValue.NotAvailable,
                                       type,
                                       isRef: false),
                                   type));
                    }
                }
            }

            return(null);
        }
        private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node, bool used)
        {
            Debug.Assert(TypeSymbol.Equals(node.Right.Type, node.Operator.RightType, TypeCompareKind.ConsiderEverything2));
            BoundExpression loweredRight = VisitExpression(node.Right);

            var temps = ArrayBuilder <LocalSymbol> .GetInstance();

            var stores = ArrayBuilder <BoundExpression> .GetInstance();

            var  kind           = node.Operator.Kind;
            bool isChecked      = kind.IsChecked();
            bool isDynamic      = kind.IsDynamic();
            var  binaryOperator = kind.Operator();

            // This will be filled in with the LHS that uses temporaries to prevent
            // double-evaluation of side effects.
            BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.Left, stores, temps, isDynamic);
            var             lhsRead        = MakeRValue(transformedLHS);
            BoundExpression rewrittenAssignment;

            if (node.Left.Kind == BoundKind.DynamicMemberAccess &&
                (binaryOperator == BinaryOperatorKind.Addition || binaryOperator == BinaryOperatorKind.Subtraction))
            {
                // If this could be an event assignment at runtime, we need to rewrite to the following form:
                // Original:
                //   receiver.EV += handler
                // Rewritten:
                //   dynamic memberAccessReceiver = receiver;
                //   bool isEvent = Runtime.IsEvent(memberAccessReceiver, "EV");
                //   dynamic storeNonEvent = !isEvent ? memberAccessReceiver.EV : null;
                //   var loweredRight = handler; // Only necessary if handler can change values, or is something like a lambda
                //   isEvent ? add_Event(memberAccessReceiver, "EV", loweredRight) : transformedLHS = storeNonEvent + loweredRight;
                //
                // This is to ensure that if handler is something like a lambda, we evaluate fully evaluate the left
                // side before storing the lambda to a temp for use in both possible branches.
                // The first store to memberAccessReceiver has already been taken care of above by TransformCompoundAssignmentLHS

                var eventTemps = ArrayBuilder <LocalSymbol> .GetInstance();

                var sequence = ArrayBuilder <BoundExpression> .GetInstance();

                //   dynamic memberAccessReceiver = receiver;
                var memberAccess = (BoundDynamicMemberAccess)transformedLHS;

                //   bool isEvent = Runtime.IsEvent(memberAccessReceiver, "EV");
                var isEvent = _factory.StoreToTemp(_dynamicFactory.MakeDynamicIsEventTest(memberAccess.Name, memberAccess.Receiver).ToExpression(), out BoundAssignmentOperator isEventAssignment);
                eventTemps.Add(isEvent.LocalSymbol);
                sequence.Add(isEventAssignment);

                // dynamic storeNonEvent = !isEvent ? memberAccessReceiver.EV : null;
                lhsRead = _factory.StoreToTemp(lhsRead, out BoundAssignmentOperator receiverAssignment);
                eventTemps.Add(((BoundLocal)lhsRead).LocalSymbol);
                var storeNonEvent = _factory.StoreToTemp(_factory.Conditional(_factory.Not(isEvent), receiverAssignment, _factory.Null(receiverAssignment.Type), receiverAssignment.Type), out BoundAssignmentOperator nonEventStore);
                eventTemps.Add(storeNonEvent.LocalSymbol);
                sequence.Add(nonEventStore);

                // var loweredRight = handler;
                if (CanChangeValueBetweenReads(loweredRight))
                {
                    loweredRight = _factory.StoreToTemp(loweredRight, out BoundAssignmentOperator possibleHandlerAssignment);
                    eventTemps.Add(((BoundLocal)loweredRight).LocalSymbol);
                    sequence.Add(possibleHandlerAssignment);
                }

                // add_Event(t1, "add_EV");
                var invokeEventAccessor = _dynamicFactory.MakeDynamicEventAccessorInvocation(
                    (binaryOperator == BinaryOperatorKind.Addition ? "add_" : "remove_") + memberAccess.Name,
                    memberAccess.Receiver,
                    loweredRight);

                // transformedLHS = storeNonEvent + loweredRight
                rewrittenAssignment = rewriteAssignment(lhsRead);

                // Final conditional
                var condition = _factory.Conditional(isEvent, invokeEventAccessor.ToExpression(), rewrittenAssignment, rewrittenAssignment.Type);

                rewrittenAssignment = new BoundSequence(node.Syntax, eventTemps.ToImmutableAndFree(), sequence.ToImmutableAndFree(), condition, condition.Type);
            }
            else
            {
                rewrittenAssignment = rewriteAssignment(lhsRead);
            }

            BoundExpression result = (temps.Count == 0 && stores.Count == 0) ?
                                     rewrittenAssignment :
                                     new BoundSequence(
                node.Syntax,
                temps.ToImmutable(),
                stores.ToImmutable(),
                rewrittenAssignment,
                rewrittenAssignment.Type);

            temps.Free();
            stores.Free();
            return(result);

            BoundExpression rewriteAssignment(BoundExpression leftRead)
            {
                SyntaxNode syntax = node.Syntax;

                // OK, we now have the temporary declarations, the temporary stores, and the transformed left hand side.
                // We need to generate
                //
                // xlhs = (FINAL)((LEFT)xlhs op rhs)
                //
                // And then wrap it up with the generated temporaries.
                //
                // (The right hand side has already been converted to the type expected by the operator.)

                BoundExpression opLHS = isDynamic ? leftRead : MakeConversionNode(
                    syntax: syntax,
                    rewrittenOperand: leftRead,
                    conversion: node.LeftConversion,
                    rewrittenType: node.Operator.LeftType,
                    @checked: isChecked);

                BoundExpression operand = MakeBinaryOperator(syntax, node.Operator.Kind, opLHS, loweredRight, node.Operator.ReturnType, node.Operator.Method, isCompoundAssignment: true);

                BoundExpression opFinal = MakeConversionNode(
                    syntax: syntax,
                    rewrittenOperand: operand,
                    conversion: node.FinalConversion,
                    rewrittenType: node.Left.Type,
                    explicitCastInCode: isDynamic,
                    @checked: isChecked);

                return(MakeAssignmentOperator(syntax, transformedLHS, opFinal, node.Left.Type, used: used, isChecked: isChecked, isCompoundAssignment: true));
            }
        }