Exemplo n.º 1
0
        public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node)
        {
            var receiverRefKind = ReceiverSpillRefKind(node.Receiver);

            BoundSpillSequenceBuilder receiverBuilder = null;
            var receiver = VisitExpression(ref receiverBuilder, node.Receiver);

            BoundSpillSequenceBuilder whenNotNullBuilder = null;
            var whenNotNull = VisitExpression(ref whenNotNullBuilder, node.WhenNotNull);

            BoundSpillSequenceBuilder whenNullBuilder = null;
            var whenNullOpt = VisitExpression(ref whenNullBuilder, node.WhenNullOpt);

            if (whenNotNullBuilder == null && whenNullBuilder == null)
            {
                return(UpdateExpression(receiverBuilder, node.Update(receiver, node.HasValueMethodOpt, whenNotNull, whenNullOpt, node.Id, node.Type)));
            }

            if (receiverBuilder == null)
            {
                receiverBuilder = new BoundSpillSequenceBuilder();
            }
            if (whenNotNullBuilder == null)
            {
                whenNotNullBuilder = new BoundSpillSequenceBuilder();
            }
            if (whenNullBuilder == null)
            {
                whenNullBuilder = new BoundSpillSequenceBuilder();
            }


            BoundExpression condition;

            if (receiver.Type.IsReferenceType || receiver.Type.IsValueType || receiverRefKind == RefKind.None)
            {
                // spill to a clone
                receiver = Spill(receiverBuilder, receiver, RefKind.None);
                var hasValueOpt = node.HasValueMethodOpt;

                if (hasValueOpt == null)
                {
                    condition = _F.ObjectNotEqual(
                        _F.Convert(_F.SpecialType(SpecialType.System_Object), receiver),
                        _F.Null(_F.SpecialType(SpecialType.System_Object)));
                }
                else
                {
                    condition = _F.Call(receiver, hasValueOpt);
                }
            }
            else
            {
                Debug.Assert(node.HasValueMethodOpt == null);
                receiver = Spill(receiverBuilder, receiver, RefKind.Ref);

                var clone = _F.SynthesizedLocal(receiver.Type, _F.Syntax, refKind: RefKind.None, kind: SynthesizedLocalKind.Spill);
                receiverBuilder.AddLocal(clone);

                //  (object)default(T) != null
                var isNotClass = _F.ObjectNotEqual(
                    _F.Convert(_F.SpecialType(SpecialType.System_Object), _F.Default(receiver.Type)),
                    _F.Null(_F.SpecialType(SpecialType.System_Object)));

                // isNotCalss || {clone = receiver; (object)clone != null}
                condition = _F.LogicalOr(
                    isNotClass,
                    _F.MakeSequence(
                        _F.AssignmentExpression(_F.Local(clone), receiver),
                        _F.ObjectNotEqual(
                            _F.Convert(_F.SpecialType(SpecialType.System_Object), _F.Local(clone)),
                            _F.Null(_F.SpecialType(SpecialType.System_Object))))
                    );

                receiver = _F.ComplexConditionalReceiver(receiver, _F.Local(clone));
            }

            if (node.Type.IsVoidType())
            {
                var whenNotNullStatement = UpdateStatement(whenNotNullBuilder, _F.ExpressionStatement(whenNotNull));
                whenNotNullStatement = ConditionalReceiverReplacer.Replace(whenNotNullStatement, receiver, node.Id, RecursionDepth);

                Debug.Assert(whenNullOpt == null || !LocalRewriter.ReadIsSideeffecting(whenNullOpt));

                receiverBuilder.AddStatement(_F.If(condition, whenNotNullStatement));

                return(receiverBuilder.Update(_F.Default(node.Type)));
            }
            else
            {
                var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax);
                var whenNotNullStatement = UpdateStatement(whenNotNullBuilder, _F.Assignment(_F.Local(tmp), whenNotNull));
                whenNotNullStatement = ConditionalReceiverReplacer.Replace(whenNotNullStatement, receiver, node.Id, RecursionDepth);

                whenNullOpt = whenNullOpt ?? _F.Default(node.Type);

                receiverBuilder.AddLocal(tmp);
                receiverBuilder.AddStatement(
                    _F.If(condition,
                          whenNotNullStatement,
                          UpdateStatement(whenNullBuilder, _F.Assignment(_F.Local(tmp), whenNullOpt))));

                return(receiverBuilder.Update(_F.Local(tmp)));
            }
        }
 public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node)
 {
     throw ExceptionUtilities.Unreachable;
 }
        /// <summary>
        /// <![CDATA[
        /// fixed(int* ptr = &v){ ... }    == becomes ===>
        ///
        /// pinned ref int pinnedTemp = ref v;    // pinning managed ref
        /// int* ptr = (int*)&pinnedTemp;         // unsafe cast to unmanaged ptr
        ///   . . .
        /// ]]>
        /// </summary>
        private BoundStatement InitializeFixedStatementGetPinnable(
            BoundLocalDeclaration localDecl,
            LocalSymbol localSymbol,
            BoundFixedLocalCollectionInitializer fixedInitializer,
            SyntheticBoundNodeFactory factory,
            out LocalSymbol pinnedTemp)
        {
            TypeSymbol      localType       = localSymbol.Type;
            BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression);

            var initializerType   = initializerExpr.Type;
            var initializerSyntax = initializerExpr.Syntax;
            var getPinnableMethod = fixedInitializer.GetPinnableOpt;

            // intervening parens may have been skipped by the binder; find the declarator
            VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf <VariableDeclaratorSyntax>();

            Debug.Assert(declarator != null);

            // pinned ref int pinnedTemp
            pinnedTemp = factory.SynthesizedLocal(
                getPinnableMethod.ReturnType,
                syntax: declarator,
                isPinned: true,
                //NOTE: different from the array and string cases
                //      RefReadOnly to allow referring to readonly variables. (technically we only "read" through the temp anyways)
                refKind: RefKind.RefReadOnly,
                kind: SynthesizedLocalKind.FixedReference);

            BoundExpression callReceiver;
            int             currentConditionalAccessID = 0;

            bool needNullCheck = !initializerType.IsValueType;

            if (needNullCheck)
            {
                currentConditionalAccessID = ++_currentConditionalAccessID;
                callReceiver = new BoundConditionalReceiver(
                    initializerSyntax,
                    currentConditionalAccessID,
                    initializerType);
            }
            else
            {
                callReceiver = initializerExpr;
            }

            // .GetPinnable()
            var getPinnableCall = getPinnableMethod.IsStatic ?
                                  factory.Call(null, getPinnableMethod, callReceiver) :
                                  factory.Call(callReceiver, getPinnableMethod);

            // temp =ref .GetPinnable()
            var tempAssignment = factory.AssignmentExpression(
                factory.Local(pinnedTemp),
                getPinnableCall,
                isRef: true);

            // &pinnedTemp
            var addr = new BoundAddressOfOperator(
                factory.Syntax,
                factory.Local(pinnedTemp),
                type: fixedInitializer.ElementPointerType);

            // (int*)&pinnedTemp
            var pointerValue = factory.Convert(
                localType,
                addr,
                fixedInitializer.ElementPointerTypeConversion);

            // {pinnedTemp =ref .GetPinnable(), (int*)&pinnedTemp}
            BoundExpression pinAndGetPtr = factory.Sequence(
                locals: ImmutableArray <LocalSymbol> .Empty,
                sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment),
                result: pointerValue);

            if (needNullCheck)
            {
                // initializer?.{temp =ref .GetPinnable(), (int*)&pinnedTemp} ?? default;
                pinAndGetPtr = new BoundLoweredConditionalAccess(
                    initializerSyntax,
                    initializerExpr,
                    hasValueMethodOpt: null,
                    whenNotNull: pinAndGetPtr,
                    whenNullOpt: null, // just return default(T*)
                    currentConditionalAccessID,
                    localType);
            }

            // ptr = initializer?.{temp =ref .GetPinnable(), (int*)&pinnedTemp} ?? default;
            BoundStatement localInit = InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, factory.Assignment(factory.Local(localSymbol), pinAndGetPtr));

            return(localInit);
        }
        // IL gen can generate more compact code for certain conditional accesses
        // by utilizing stack dup/pop instructions
        internal BoundExpression RewriteConditionalAccess(BoundConditionalAccess node, bool used)
        {
            Debug.Assert(!_inExpressionLambda);

            var loweredReceiver = this.VisitExpression(node.Receiver);
            var receiverType    = loweredReceiver.Type;

            // Check trivial case
            if (loweredReceiver.IsDefaultValue())
            {
                return(_factory.Default(node.Type));
            }

            ConditionalAccessLoweringKind loweringKind;
            // dynamic receivers are not directly supported in codegen and need to be lowered to a ternary
            var lowerToTernary = node.AccessExpression.Type.IsDynamic();

            if (!lowerToTernary)
            {
                // trivial cases are directly supported in IL gen
                loweringKind = ConditionalAccessLoweringKind.LoweredConditionalAccess;
            }
            else if (CanChangeValueBetweenReads(loweredReceiver))
            {
                // NOTE: dynamic operations historically do not propagate mutations
                // to the receiver if that happens to be a value type
                // so we can capture receiver by value in dynamic case regardless of
                // the type of receiver
                // Nullable receivers are immutable so should be captured by value as well.
                loweringKind = ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal;
            }
            else
            {
                loweringKind = ConditionalAccessLoweringKind.Ternary;
            }


            var previousConditionalAccessTarget = _currentConditionalAccessTarget;
            var currentConditionalAccessID      = ++_currentConditionalAccessID;

            LocalSymbol     temp = null;
            BoundExpression unconditionalAccess = null;

            switch (loweringKind)
            {
            case ConditionalAccessLoweringKind.LoweredConditionalAccess:
                _currentConditionalAccessTarget = new BoundConditionalReceiver(
                    loweredReceiver.Syntax,
                    currentConditionalAccessID,
                    receiverType);

                break;

            case ConditionalAccessLoweringKind.Ternary:
                _currentConditionalAccessTarget = loweredReceiver;
                break;

            case ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal:
                temp = _factory.SynthesizedLocal(receiverType);
                _currentConditionalAccessTarget = _factory.Local(temp);
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(loweringKind);
            }

            BoundExpression loweredAccessExpression;

            if (used)
            {
                loweredAccessExpression = this.VisitExpression(node.AccessExpression);
            }
            else
            {
                loweredAccessExpression = this.VisitUnusedExpression(node.AccessExpression);
                if (loweredAccessExpression == null)
                {
                    return(null);
                }
            }

            Debug.Assert(loweredAccessExpression != null);
            _currentConditionalAccessTarget = previousConditionalAccessTarget;

            TypeSymbol type = this.VisitType(node.Type);

            TypeSymbol nodeType             = node.Type;
            TypeSymbol accessExpressionType = loweredAccessExpression.Type;

            if (accessExpressionType.SpecialType == SpecialType.System_Void)
            {
                type = nodeType = accessExpressionType;
            }

            if (accessExpressionType != nodeType && nodeType.IsNullableType())
            {
                Debug.Assert(accessExpressionType == nodeType.GetNullableUnderlyingType());
                loweredAccessExpression = _factory.New((NamedTypeSymbol)nodeType, loweredAccessExpression);

                if (unconditionalAccess != null)
                {
                    unconditionalAccess = _factory.New((NamedTypeSymbol)nodeType, unconditionalAccess);
                }
            }
            else
            {
                Debug.Assert(accessExpressionType == nodeType ||
                             (nodeType.SpecialType == SpecialType.System_Void && !used));
            }

            BoundExpression result;
            var             objectType = _compilation.GetSpecialType(SpecialType.System_Object);

            switch (loweringKind)
            {
            case ConditionalAccessLoweringKind.LoweredConditionalAccess:
                result = new BoundLoweredConditionalAccess(
                    node.Syntax,
                    loweredReceiver,
                    receiverType.IsNullableType() ?
                    GetNullableMethod(node.Syntax, loweredReceiver.Type, SpecialMember.System_Nullable_T_get_HasValue) :
                    null,
                    loweredAccessExpression,
                    null,
                    currentConditionalAccessID,
                    type);

                break;

            case ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal:
                // capture the receiver into a temp
                loweredReceiver = _factory.Sequence(
                    _factory.AssignmentExpression(_factory.Local(temp), loweredReceiver),
                    _factory.Local(temp));

                goto case ConditionalAccessLoweringKind.Ternary;

            case ConditionalAccessLoweringKind.Ternary:
            {
                // (object)r != null ? access : default(T)
                var condition = _factory.ObjectNotEqual(
                    _factory.Convert(objectType, loweredReceiver),
                    _factory.Null(objectType));

                var consequence = loweredAccessExpression;

                result = RewriteConditionalOperator(node.Syntax,
                                                    condition,
                                                    consequence,
                                                    _factory.Default(nodeType),
                                                    null,
                                                    nodeType);

                if (temp != null)
                {
                    result = _factory.Sequence(temp, result);
                }
            }
            break;

            default:
                throw ExceptionUtilities.UnexpectedValue(loweringKind);
            }

            return(result);
        }