internal void Parse(BoundLoweredConditionalAccess boundLoweredConditionalAccess)
 {
     base.Parse(boundLoweredConditionalAccess);
     this.Type        = boundLoweredConditionalAccess.Receiver.Type;
     this.Receiver    = Deserialize(boundLoweredConditionalAccess.Receiver) as Expression;
     this.WhenNotNull = Deserialize(boundLoweredConditionalAccess.WhenNotNull) as Expression;
 }
 public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node)
 {
     BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver);
     BoundExpression whenNotNull = (BoundExpression)this.Visit(node.WhenNotNull);
     BoundExpression whenNullOpt = (BoundExpression)this.Visit(node.WhenNullOpt);
     TypeSymbol type = this.VisitType(node.Type);
     return node.Update(receiver, VisitMethodSymbol(node.HasValueMethodOpt), whenNotNull, whenNullOpt, node.Id, type);
 }
Exemple #3
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.SpecialType == SpecialType.System_Void)
            {
                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)));
            }
        }
Exemple #4
0
        private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAccess expression, bool used)
        {
            var receiver = expression.Receiver;

            if (receiver.IsDefaultValue())
            {
                EmitDefaultValue(expression.Type, used, expression.Syntax);
                return;
            }

            var receiverType = receiver.Type;
            LocalDefinition receiverTemp = null;
            Debug.Assert(!receiverType.IsValueType ||
                (receiverType.IsNullableType() && expression.HasValueMethodOpt != null), "conditional receiver cannot be a struct");

            var receiverConstant = receiver.ConstantValue;
            if (receiverConstant != null)
            {
                // const but not default
                receiverTemp = EmitReceiverRef(receiver, isAccessConstrained: !receiverType.IsReferenceType);
                EmitExpression(expression.WhenNotNull, used);
                if (receiverTemp != null)
                {
                    FreeTemp(receiverTemp);
                }
                return;
            }

            // labels
            object whenNotNullLabel = new object();
            object doneLabel = new object();
            LocalDefinition cloneTemp = null;

            // we need a copy if we deal with nonlocal value (to capture the value)
            // or if we have a ref-constrained T (to do box just once) 
            // or if we deal with stack local (reads are destructive)
            var nullCheckOnCopy = LocalRewriter.CanChangeValueBetweenReads(receiver, localsMayBeAssignedOrCaptured: false) ||
                                   (receiverType.IsReferenceType && receiverType.TypeKind == TypeKind.TypeParameter) ||
                                   (receiver.Kind == BoundKind.Local && IsStackLocal(((BoundLocal)receiver).LocalSymbol));

            var unconstrainedReceiver = !receiverType.IsReferenceType && !receiverType.IsValueType;


            // ===== RECEIVER
            if (nullCheckOnCopy)
            {
                receiverTemp = EmitReceiverRef(receiver, isAccessConstrained: unconstrainedReceiver);
                if (unconstrainedReceiver)
                {
                    // unconstrained case needs to handle case where T is actually a struct.
                    // such values are never nulls
                    // we will emit a check for such case, but the check is really a JIT-time 
                    // constant since JIT will know if T is a struct or not.

                    // if ((object)default(T) != null) 
                    // {
                    //     goto whenNotNull
                    // }
                    // else
                    // {
                    //     temp = receiverRef
                    //     receiverRef = ref temp
                    // }
                    EmitDefaultValue(receiverType, true, receiver.Syntax);
                    EmitBox(receiverType, receiver.Syntax);
                    _builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel);
                    EmitLoadIndirect(receiverType, receiver.Syntax);

                    cloneTemp = AllocateTemp(receiverType, receiver.Syntax);
                    _builder.EmitLocalStore(cloneTemp);
                    _builder.EmitLocalAddress(cloneTemp);
                    _builder.EmitLocalLoad(cloneTemp);
                    EmitBox(receiver.Type, receiver.Syntax);

                    // here we have loaded a ref to a temp and its boxed value { &T, O }
                }
                else
                {
                    _builder.EmitOpCode(ILOpCode.Dup);
                    // here we have loaded two copies of a reference   { O, O }  or  {&nub, &nub}
                }
            }
            else
            {
                receiverTemp = EmitReceiverRef(receiver, isAccessConstrained: false);
                // here we have loaded just { O } or  {&nub}
                // we have the most trivial case where we can just reload receiver when needed again
            }

            // ===== CONDITION

            var hasValueOpt = expression.HasValueMethodOpt;
            if (hasValueOpt != null)
            {
                Debug.Assert(receiver.Type.IsNullableType());
                _builder.EmitOpCode(ILOpCode.Call, stackAdjustment: 0);
                EmitSymbolToken(hasValueOpt, expression.Syntax, null);
            }

            _builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel);

            // no longer need the temp if we are not holding a copy
            if (receiverTemp != null && !nullCheckOnCopy)
            {
                FreeTemp(receiverTemp);
                receiverTemp = null;
            }

            // ===== WHEN NULL
            if (nullCheckOnCopy)
            {
                _builder.EmitOpCode(ILOpCode.Pop);
            }

            var whenNull = expression.WhenNullOpt;
            if (whenNull == null)
            {
                EmitDefaultValue(expression.Type, used, expression.Syntax);
            }
            else
            {
                EmitExpression(whenNull, used);
            }

            _builder.EmitBranch(ILOpCode.Br, doneLabel);


            // ===== WHEN NOT NULL 
            if (nullCheckOnCopy)
            {
                // notNull branch pops copy of receiver off the stack when nullCheckOnCopy
                // however on the isNull branch we still have the stack as it was and need 
                // to adjust stack depth correspondingly.
                _builder.AdjustStack(+1);
            }

            if (used)
            {
                // notNull branch pushes default on the stack when used
                // however on the isNull branch we still have the stack as it was and need 
                // to adjust stack depth correspondingly.
                _builder.AdjustStack(-1);
            }

            _builder.MarkLabel(whenNotNullLabel);

            if (!nullCheckOnCopy)
            {
                Debug.Assert(receiverTemp == null);
                receiverTemp = EmitReceiverRef(receiver, isAccessConstrained: unconstrainedReceiver);
                Debug.Assert(receiverTemp == null);
            }

            EmitExpression(expression.WhenNotNull, used);

            // ===== DONE
            _builder.MarkLabel(doneLabel);

            if (cloneTemp != null)
            {
                FreeTemp(cloneTemp);
            }

            if (receiverTemp != null)
            {
                FreeTemp(receiverTemp);
            }
        }
Exemple #5
0
        public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node)
        {
            var origStack = StackDepth();
            BoundExpression receiver = VisitCallReceiver(node.Receiver);

            var cookie = GetStackStateCookie();     // implicit branch here

            // right is evaluated with original stack 
            // (this is not entirely true, codegen will keep receiver on the stack, but that is irrelevant here)
            SetStackDepth(origStack);
            BoundExpression whenNotNull = (BoundExpression)this.Visit(node.WhenNotNull);

            EnsureStackState(cookie);   // implicit label here

            var whenNull = node.WhenNullOpt;
            if (whenNull != null)
            {
                SetStackDepth(origStack);  // whenNull is evaluated with original stack
                whenNull = (BoundExpression)this.Visit(whenNull);
                EnsureStackState(cookie);   // implicit label here
            }
            else
            {
                // compensate for the whenNull that we are not visiting.
                _counter += 1;
            }

            return node.Update(receiver, node.HasValueMethodOpt, whenNotNull, whenNull, node.Id, node.Type);
        }
        // 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() && receiverType.IsReferenceType)
            {
                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;

            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 (!TypeSymbol.Equals(accessExpressionType, nodeType, TypeCompareKind.ConsiderEverything2) && nodeType.IsNullableType())
            {
                Debug.Assert(TypeSymbol.Equals(accessExpressionType, nodeType.GetNullableUnderlyingType(), TypeCompareKind.ConsiderEverything2));
                loweredAccessExpression = _factory.New((NamedTypeSymbol)nodeType, loweredAccessExpression);
            }
            else
            {
                Debug.Assert(TypeSymbol.Equals(accessExpressionType, nodeType, TypeCompareKind.ConsiderEverything2) ||
                             (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() ?
                    UnsafeGetNullableMethod(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.MakeSequence(
                    _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,
                                                    isRef: false);

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

            default:
                throw ExceptionUtilities.UnexpectedValue(loweringKind);
            }

            return(result);
        }
 public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node)
 {
     throw ExceptionUtilities.Unreachable;
 }
 public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node)
 {
     BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver);
     BoundExpression whenNotNull = (BoundExpression)this.Visit(node.WhenNotNull);
     BoundExpression whenNullOpt = (BoundExpression)this.Visit(node.WhenNullOpt);
     TypeSymbol type = this.VisitType(node.Type);
     return node.Update(receiver, VisitMethodSymbol(node.HasValueMethodOpt), whenNotNull, whenNullOpt, node.Id, type);
 }
        // IL gen can generate more compact code for certain conditional accesses 
        // by utilizing stack dup/pop instructions 
        internal BoundExpression RewriteConditionalAccess(BoundConditionalAccess node, bool used, BoundExpression rewrittenWhenNull = null)
        {
            Debug.Assert(!_inExpressionLambda);

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

            // Check trivial case
            if (loweredReceiver.IsDefaultValue())
            {
                return rewrittenWhenNull ?? _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 hapens 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 previousConditionalAccesTarget = _currentConditionalAccessTarget;
            var currentConditionalAccessID = ++this._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 = used ?
                        this.VisitExpression(node.AccessExpression) :
                        this.VisitUnusedExpression(node.AccessExpression);

            _currentConditionalAccessTarget = previousConditionalAccesTarget;

            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, 
                        rewrittenWhenNull,
                        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,
                            rewrittenWhenNull ?? _factory.Default(nodeType),
                            null,
                            nodeType);

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

                default:
                    throw ExceptionUtilities.Unreachable;
            }

            return result;
        }
        /// <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.TypeSymbol;
            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
            VariableDeclarationSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf <VariableDeclarationSyntax>();

            Debug.Assert(declarator != null);

            // pinned ref int pinnedTemp
            pinnedTemp = factory.SynthesizedLocal(
                getPinnableMethod.ReturnType.TypeSymbol,
                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)
        {
            var loweredReceiver = this.VisitExpression(node.Receiver);
            var receiverType    = loweredReceiver.Type;

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

            ConditionalAccessLoweringKind loweringKind;

            // trivial cases are directly supported in IL gen
            loweringKind = ConditionalAccessLoweringKind.LoweredConditionalAccess;

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

            LocalSymbol temp = 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 (!TypeSymbol.Equals(accessExpressionType, nodeType, TypeCompareKind.ConsiderEverything2) && nodeType.IsNullableType())
            {
                Debug.Assert(TypeSymbol.Equals(accessExpressionType, nodeType.GetNullableUnderlyingType(), TypeCompareKind.ConsiderEverything2));
                loweredAccessExpression = _factory.New((NamedTypeSymbol)nodeType, loweredAccessExpression);
            }
            else
            {
                Debug.Assert(TypeSymbol.Equals(accessExpressionType, nodeType, TypeCompareKind.ConsiderEverything2) ||
                             (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() ?
                    UnsafeGetNullableMethod(node.Syntax, loweredReceiver.Type, SpecialMember.core_Option_T_get_has_value) :
                    null,
                    loweredAccessExpression,
                    null,
                    currentConditionalAccessID,
                    type);

                break;

            case ConditionalAccessLoweringKind.TernaryCaptureReceiverByVal:
                // capture the receiver into a temp
                loweredReceiver = _factory.MakeSequence(
                    _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,
                                                    isRef: false);

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

            default:
                throw ExceptionUtilities.UnexpectedValue(loweringKind);
            }

            return(result);
        }