private void CheckCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            BoundExpression left = node.Left;

            if (
                !node.Operator.Kind.IsDynamic() &&
                !node.LeftConversion.IsIdentity &&
                node.LeftConversion.Exists
                )
            {
                // Need to represent the implicit conversion as a node in order to be able to produce correct diagnostics.
                left = new BoundConversion(
                    left.Syntax,
                    left,
                    node.LeftConversion,
                    node.Operator.Kind.IsChecked(),
                    explicitCastInCode: false,
                    conversionGroupOpt: null,
                    constantValueOpt: null,
                    type: node.Operator.LeftType
                    );
            }

            CheckForBitwiseOrSignExtend(node, node.Operator.Kind, left, node.Right);
            CheckLiftedCompoundAssignment(node);

            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node);
            }
        }
Exemple #2
0
        private void CheckCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            BoundExpression left = node.Left;

            if (!node.Operator.Kind.IsDynamic() && node.LeftConversion is BoundConversion {
                Conversion : { IsIdentity : false, Exists : true } conversion
            })
        public override BoundNode VisitCompoundAssignmentOperator(
            BoundCompoundAssignmentOperator node
            )
        {
            CheckCompoundAssignmentOperator(node);

            return(base.VisitCompoundAssignmentOperator(node));
        }
        public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            CheckLiftedCompoundAssignment(node);
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node);
            }

            return(base.VisitCompoundAssignmentOperator(node));
        }
Exemple #5
0
        private void CheckCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            CheckForBitwiseOrSignExtend(node, node.Operator.Kind, node.Left, node.Right);
            CheckLiftedCompoundAssignment(node);

            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node);
            }
        }
Exemple #6
0
        private void CheckLiftedCompoundAssignment(BoundCompoundAssignmentOperator node)
        {
            Debug.Assert(node != null);
            if (!node.Operator.Kind.IsLifted())
            {
                return;
            }

            // CS0458: The result of the expression is always 'null' of type '{0}'
            if (node.Right.NullableNeverHasValue())
            {
                Error(ErrorCode.WRN_AlwaysNull, node, node.Type);
            }
        }
Exemple #7
0
        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);
                Debug.Assert(rewrittenAssignment.Type is { });
        public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            CheckLiftedCompoundAssignment(node);
            if (inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node);
            }

            return base.VisitCompoundAssignmentOperator(node);
        }
        private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node, bool used)
        {
            Debug.Assert(node.Right.Type == node.Operator.RightType);
            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();

            // if LHS is a member access and the operation is += or -=, we need to check at runtime if the LHS is an event.
            // We rewrite dyn.Member op= RHS to the following:
            //
            //   IsEvent("Member", dyn) ? InvokeMember("{add|remove}_Member", dyn, RHS) : SetMember(BinaryOperation("op=", GetMember("Member", dyn)), RHS)
            //
            bool isPossibleEventHandlerOperation = node.Left.Kind == BoundKind.DynamicMemberAccess &&
                                                   (binaryOperator == BinaryOperatorKind.Addition || binaryOperator == BinaryOperatorKind.Subtraction);

            // save RHS to a temp, we need to use it twice:
            if (isPossibleEventHandlerOperation && CanChangeValueBetweenReads(loweredRight))
            {
                BoundAssignmentOperator assignmentToTemp;
                var temp = _factory.StoreToTemp(loweredRight, out assignmentToTemp);
                loweredRight = temp;
                stores.Add(assignmentToTemp);
                temps.Add(temp.LocalSymbol);
            }

            // 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);

            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.)

            var lhsRead = MakeRValue(transformedLHS);

            BoundExpression opLHS = isDynamic ? lhsRead : MakeConversionNode(
                syntax: syntax,
                rewrittenOperand: lhsRead,
                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);

            BoundExpression rewrittenAssignment = MakeAssignmentOperator(syntax, transformedLHS, opFinal, node.Left.Type, used: used, isChecked: isChecked, isCompoundAssignment: true);

            // OK, at this point we have:
            //
            // * temps evaluating and storing portions of the LHS that must be evaluated only once.
            // * the "transformed" left hand side, rebuilt to use temps where necessary
            // * the assignment "xlhs = (FINAL)((LEFT)xlhs op (RIGHT)rhs)"
            //
            // Notice that we have recursively rewritten the bound nodes that are things stored in
            // the temps, and by calling the "Make" methods we have rewritten the conversions and
            // assignments too, if necessary.

            if (isPossibleEventHandlerOperation)
            {
                // IsEvent("Foo", dyn) ? InvokeMember("{add|remove}_Foo", dyn, RHS) : rewrittenAssignment
                var memberAccess = (BoundDynamicMemberAccess)transformedLHS;

                var isEventCondition = _dynamicFactory.MakeDynamicIsEventTest(memberAccess.Name, memberAccess.Receiver);

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

                rewrittenAssignment = _factory.Conditional(isEventCondition.ToExpression(), invokeEventAccessor.ToExpression(), rewrittenAssignment, rewrittenAssignment.Type);
            }

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

            temps.Free();
            stores.Free();
            return(result);
        }
Exemple #10
0
 public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
 {
     return(VisitCompoundAssignmentOperator(node, true));
 }
Exemple #11
0
        private static void GetSymbolsAndResultKind(BoundCompoundAssignmentOperator compoundAssignment, out bool isDynamic, ref LookupResultKind resultKind, ref ImmutableArray<Symbol> symbols)
        {
            BinaryOperatorKind operandType = compoundAssignment.Operator.Kind.OperandTypes();
            BinaryOperatorKind op = compoundAssignment.Operator.Kind.Operator();
            isDynamic = compoundAssignment.Operator.Kind.IsDynamic();

            if (operandType == 0 || operandType == BinaryOperatorKind.UserDefined || compoundAssignment.ResultKind != LookupResultKind.Viable)
            {
                if (!isDynamic)
                {
                    GetSymbolsAndResultKind(compoundAssignment, compoundAssignment.Operator.Method, compoundAssignment.OriginalUserDefinedOperatorsOpt, out symbols, out resultKind);
                }
            }
            else
            {
                Debug.Assert((object)compoundAssignment.Operator.Method == null && compoundAssignment.OriginalUserDefinedOperatorsOpt.IsDefaultOrEmpty);

                symbols = ImmutableArray.Create(GetIntrinsicOperatorSymbol(op, isDynamic,
                                                                             compoundAssignment.Operator.LeftType,
                                                                             compoundAssignment.Operator.RightType,
                                                                             compoundAssignment.Operator.ReturnType,
                                                                             compoundAssignment.Operator.Kind.IsChecked()));
                resultKind = compoundAssignment.ResultKind;
            }
        }
 public override BoundNode?VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
 {
     _mightAssignSomething = true;
     return(null);
 }
        public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            CheckCompoundAssignmentOperator(node);

            return base.VisitCompoundAssignmentOperator(node);
        }
Exemple #14
0
        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));
            }
        }
 public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
 {
     return VisitCompoundAssignmentOperator(node, true);
 }
        private void CheckCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            CheckForBitwiseOrSignExtend(node, node.Operator.Kind, node.Left, node.Right);
            CheckLiftedCompoundAssignment(node);

            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node);
            }
        }
        private void CheckCompoundAssignmentOperator(BoundCompoundAssignmentOperator node)
        {
            BoundExpression left = node.Left;

            if (!node.Operator.Kind.IsDynamic() && !node.LeftConversion.IsIdentity && node.LeftConversion.Exists)
            {
                // Need to represent the implicit conversion as a node in order to be able to produce correct diagnostics.
                left = new BoundConversion(left.Syntax, left, node.LeftConversion, node.Operator.Kind.IsChecked(),
                                           explicitCastInCode: false, constantValueOpt: null, type: node.Operator.LeftType);
            }

            CheckForBitwiseOrSignExtend(node, node.Operator.Kind, left, node.Right);
            CheckLiftedCompoundAssignment(node);

            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node);
            }
        }
        private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node, bool used)
        {
            Debug.Assert(node.Right.Type == node.Operator.RightType);
            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();

            // if LHS is a member access and the operation is += or -=, we need to check at runtime if the LHS is an event.
            // We rewrite dyn.Member op= RHS to the following:
            //
            //   IsEvent("Member", dyn) ? InvokeMember("{add|remove}_Member", dyn, RHS) : SetMember(BinaryOperation("op=", GetMember("Member", dyn)), RHS)
            //
            bool isPossibleEventHandlerOperation = node.Left.Kind == BoundKind.DynamicMemberAccess &&
                (binaryOperator == BinaryOperatorKind.Addition || binaryOperator == BinaryOperatorKind.Subtraction);

            // save RHS to a temp, we need to use it twice:
            if (isPossibleEventHandlerOperation && NeedsTemp(loweredRight, localsMayBeAssigned: false))
            {
                BoundAssignmentOperator assignmentToTemp;
                var temp = this.factory.StoreToTemp(loweredRight, out assignmentToTemp);
                loweredRight = temp;
                stores.Add(assignmentToTemp);
                temps.Add(temp.LocalSymbol);
            }

            // 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);

            CSharpSyntaxNode 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.)

            var lhsRead = MakeRValue(transformedLHS);

            BoundExpression opLHS = isDynamic ? lhsRead : MakeConversion(
                syntax: syntax,
                rewrittenOperand: lhsRead,
                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 = MakeConversion(
                syntax: syntax,
                rewrittenOperand: operand,
                conversion: node.FinalConversion,
                rewrittenType: node.Left.Type,
                explicitCastInCode: isDynamic,
                @checked: isChecked);

            BoundExpression rewrittenAssignment = MakeAssignmentOperator(syntax, transformedLHS, opFinal, node.Left.Type, used: used, isChecked: isChecked, isCompoundAssignment: true);

            // OK, at this point we have:
            //
            // * temps evaluating and storing portions of the LHS that must be evaluated only once.
            // * the "transformed" left hand side, rebuilt to use temps where necessary
            // * the assignment "xlhs = (FINAL)((LEFT)xlhs op (RIGHT)rhs)"
            // 
            // Notice that we have recursively rewritten the bound nodes that are things stored in
            // the temps, and by calling the "Make" methods we have rewritten the conversions and
            // assignments too, if necessary.

            if (isPossibleEventHandlerOperation)
            {
                // IsEvent("Foo", dyn) ? InvokeMember("{add|remove}_Foo", dyn, RHS) : rewrittenAssignment
                var memberAccess = (BoundDynamicMemberAccess)transformedLHS;

                var isEventCondition = dynamicFactory.MakeDynamicIsEventTest(memberAccess.Name, memberAccess.Receiver);

                var invokeEventAccessor = dynamicFactory.MakeDynamicEventAccessorInvocation(
                    (binaryOperator == BinaryOperatorKind.Addition ? "add_" : "remove_") + memberAccess.Name,
                    memberAccess.Receiver,
                    loweredRight);

                rewrittenAssignment = factory.Conditional(isEventCondition.ToExpression(), invokeEventAccessor.ToExpression(), rewrittenAssignment, rewrittenAssignment.Type);
            }

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

            temps.Free();
            stores.Free();
            return result;
        }
        private void CheckLiftedCompoundAssignment(BoundCompoundAssignmentOperator node)
        {
            Debug.Assert(node != null);
            if (!node.Operator.Kind.IsLifted())
            {
                return;
            }

            // CS0458: The result of the expression is always 'null' of type '{0}'
            if (node.Right.NullableNeverHasValue())
            {
                Error(ErrorCode.WRN_AlwaysNull, node, node.Type);
            }
        }