Exemplo n.º 1
0
        public override BoundNode VisitNullCoalescingAssignmentOperator(BoundNullCoalescingAssignmentOperator node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeCantContainNullCoalescingAssignment, node);
            }

            return(base.VisitNullCoalescingAssignmentOperator(node));
        }
Exemplo n.º 2
0
        public override BoundNode VisitNullCoalescingAssignmentOperator(BoundNullCoalescingAssignmentOperator node)
        {
            SyntaxNode syntax = node.Syntax;
            var        temps  = ArrayBuilder <LocalSymbol> .GetInstance();

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

            // Rewrite LHS with temporaries to prevent double-evaluation of side effects, as we'll need to use it multiple times.
            BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.LeftOperand, stores, temps, node.LeftOperand.HasDynamicType());
            var             lhsRead        = MakeRValue(transformedLHS);
            BoundExpression loweredRight   = VisitExpression(node.RightOperand);

            // Now that LHS is transformed with temporaries, we rewrite this node into a coalesce expression:
            // lhsRead ?? (transformedLHS = loweredRight)

            // transformedLHS = loweredRight
            // isCompoundAssignment is only used for dynamic scenarios, and we want those scenarios to treat this like a standard assignment.
            // See CodeGenNullCoalescingAssignmentTests.CoalescingAssignment_DynamicRuntimeCastFailure, which will fail if
            // isCompoundAssignment is set to true. It will fail to throw a runtime binder cast exception.
            BoundExpression assignment = MakeAssignmentOperator(syntax, transformedLHS, loweredRight, node.LeftOperand.Type, used: true, isChecked: false, isCompoundAssignment: false);

            // lhsRead ?? (transformedLHS = loweredRight)
            BoundExpression conditionalExpression = MakeNullCoalescingOperator(syntax, lhsRead, assignment, Conversion.Identity, BoundNullCoalescingOperatorResultKind.LeftType, node.LeftOperand.Type);

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

            temps.Free();
            stores.Free();

            return(result);
        }
Exemplo n.º 3
0
 public override BoundNode VisitNullCoalescingAssignmentOperator(BoundNullCoalescingAssignmentOperator node)
 {
     Debug.Assert(node.Type is { });
Exemplo n.º 4
0
        public override BoundNode VisitNullCoalescingAssignmentOperator(BoundNullCoalescingAssignmentOperator node)
        {
            SyntaxNode syntax = node.Syntax;
            var        temps  = ArrayBuilder <LocalSymbol> .GetInstance();

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

            // Rewrite LHS with temporaries to prevent double-evaluation of side effects, as we'll need to use it multiple times.
            BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.LeftOperand, stores, temps, node.LeftOperand.HasDynamicType());
            var             lhsRead        = MakeRValue(transformedLHS);
            BoundExpression loweredRight   = VisitExpression(node.RightOperand);

            return(node.IsNullableValueTypeAssignment ?
                   rewriteNullCoalescingAssignmentForValueType() :
                   rewriteNullCoalscingAssignmentStandard());

            BoundExpression rewriteNullCoalscingAssignmentStandard()
            {
                // Now that LHS is transformed with temporaries, we rewrite this node into a coalesce expression:
                // lhsRead ?? (transformedLHS = loweredRight)

                // transformedLHS = loweredRight
                // isCompoundAssignment is only used for dynamic scenarios, and we want those scenarios to treat this like a standard assignment.
                // See CodeGenNullCoalescingAssignmentTests.CoalescingAssignment_DynamicRuntimeCastFailure, which will fail if
                // isCompoundAssignment is set to true. It will fail to throw a runtime binder cast exception.
                BoundExpression assignment = MakeAssignmentOperator(syntax, transformedLHS, loweredRight, node.LeftOperand.Type, used: true, isChecked: false, isCompoundAssignment: false);

                // lhsRead ?? (transformedLHS = loweredRight)
                BoundExpression conditionalExpression = MakeNullCoalescingOperator(syntax, lhsRead, assignment, Conversion.Identity, BoundNullCoalescingOperatorResultKind.LeftType, node.LeftOperand.Type);

                return((temps.Count == 0 && stores.Count == 0) ?
                       conditionalExpression :
                       new BoundSequence(
                           syntax,
                           temps.ToImmutableAndFree(),
                           stores.ToImmutableAndFree(),
                           conditionalExpression,
                           conditionalExpression.Type));
            }

            // Rewrites the null coalescing operator in the case where the result type is the underlying
            // non-nullable value type of the left side
            BoundExpression rewriteNullCoalescingAssignmentForValueType()
            {
                Debug.Assert(node.LeftOperand.Type.IsNullableType());
                Debug.Assert(node.Type.Equals(node.RightOperand.Type));

                // We lower the expression to this form:
                //
                // var tmp = lhsRead.GetValueOrDefault()
                // lhsRead.HasValue ? tmp : { /* sequence */ tmp = loweredRight; transformedLhs = tmp; tmp }

                var leftOperand = node.LeftOperand;

                if (!TryGetNullableMethod(leftOperand.Syntax,
                                          leftOperand.Type,
                                          SpecialMember.System_Nullable_T_GetValueOrDefault,
                                          out var getValueOrDefault))
                {
                    return(BadExpression(node));
                }

                if (!TryGetNullableMethod(leftOperand.Syntax,
                                          leftOperand.Type,
                                          SpecialMember.System_Nullable_T_get_HasValue,
                                          out var hasValue))
                {
                    return(BadExpression(node));
                }

                // If null coalescing assignment is supported in expression trees, the below code
                // will need to be updated to support property accesses as well as calls. Currently,
                // MakeRValue will never return a BoundPropertyAccess except in expression trees.
                Debug.Assert(!_inExpressionLambda && lhsRead.Kind != BoundKind.PropertyAccess);

                // If lhsRead is a call, such as to a property accessor, save the result off to a temp. This doesn't affect
                // the standard ??= case because it only uses lhsRead once.
                if (lhsRead.Kind == BoundKind.Call)
                {
                    var lhsTemp = _factory.StoreToTemp(lhsRead, out var store, kind: SynthesizedLocalKind.Spill);
                    stores.Add(store);
                    temps.Add(lhsTemp.LocalSymbol);
                    lhsRead = lhsTemp;
                }

                // tmp = lhsRead.GetValueOrDefault();
                var tmp = _factory.StoreToTemp(BoundCall.Synthesized(leftOperand.Syntax, lhsRead, getValueOrDefault),
                                               out var getValueOrDefaultStore,
                                               kind: SynthesizedLocalKind.Spill);

                stores.Add(getValueOrDefaultStore);
                temps.Add(tmp.LocalSymbol);

                // tmp = loweredRight;
                var tmpAssignment = MakeAssignmentOperator(node.Syntax, tmp, loweredRight, node.Type, used: true, isChecked: false, isCompoundAssignment: false);

                // transformedLhs = tmp;
                var transformedLhsAssignment =
                    MakeAssignmentOperator(
                        node.Syntax,
                        transformedLHS,
                        MakeConversionNode(tmp, transformedLHS.Type, @checked: false),
                        node.LeftOperand.Type,
                        used: true,
                        isChecked: false,
                        isCompoundAssignment: false);

                // lhsRead.HasValue
                var lhsReadHasValue = BoundCall.Synthesized(leftOperand.Syntax, lhsRead, hasValue);

                // { tmp = b; transformedLhs = tmp; tmp }
                var alternative = _factory.Sequence(ImmutableArray <LocalSymbol> .Empty, ImmutableArray.Create(tmpAssignment, transformedLhsAssignment), tmp);

                // lhsRead.HasValue ? tmp : { /* sequence */ tmp = loweredRight; transformedLhs = tmp; tmp }
                var ternary = _factory.Conditional(lhsReadHasValue, tmp, alternative, tmp.Type);

                return(_factory.Sequence(temps.ToImmutableAndFree(), stores.ToImmutableAndFree(), ternary));
            }
        }