示例#1
0
        public override BoundNode VisitConditionalAccess(BoundConditionalAccess node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_NullPropagatingOpInExpressionTree, node);
            }

            return(base.VisitConditionalAccess(node));
        }
        // 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 VisitConditionalAccess(BoundConditionalAccess node)
 {
     return(RewriteConditionalAccess(node, used: true));
 }
示例#4
0
        private void EmitConditionalAccessExpression(BoundConditionalAccess expression, bool used)
        {
            var receiver = expression.Receiver;

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

            EmitExpression(receiver, used: true);

            var receiverConstant = receiver.ConstantValue;
            if (receiverConstant != null)
            {
                // const but not default
                EmitExpression(expression.AccessExpression, used);
                return;
            }

            builder.EmitOpCode(ILOpCode.Dup);

            if (!receiver.Type.IsVerifierReference())
            {
                EmitBox(receiver.Type, receiver.Syntax);
            }

            object whenNotNull = new object();
            object done = new object();

            builder.EmitBranch(ILOpCode.Brtrue, whenNotNull);

            builder.EmitOpCode(ILOpCode.Pop);
            EmitDefaultValue(expression.Type, used, expression.Syntax);
            builder.EmitBranch(ILOpCode.Br, done);

            if(!used)
            {
                // when unused, notNull branch just pops the reciever off the stack
                // but in whenNotNull branch we still have it
                builder.AdjustStack(+1);
            }
            builder.MarkLabel(whenNotNull);
            EmitExpression(expression.AccessExpression, used);
            builder.MarkLabel(done);
        }
示例#5
0
        public override BoundNode VisitConditionalAccess(BoundConditionalAccess node)
        {
            var origStack = _evalStack;
            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)
            _evalStack = origStack;
            BoundExpression access = (BoundExpression)this.Visit(node.AccessExpression);

            EnsureStackState(cookie);   // implicit label here

            return node.Update(receiver, access, node.Type);
        }
        private void EmitConditionalAccessExpression(BoundConditionalAccess 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, "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.AccessExpression, used);
                if (receiverTemp != null)
                {
                    FreeTemp(receiverTemp);
                }
                return;
            }

            // labels
            object whenNotNullLabel = new object();
            object doneLabel = new object();
            LocalDefinition temp = 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)
            var nullCheckOnCopy = LocalRewriter.NeedsTemp(receiver, localsMayBeAssigned: false) ||
                                   (receiverType.IsReferenceType && receiverType.TypeKind == TypeKind.TypeParameter) ;

            if (nullCheckOnCopy)
            {
                EmitReceiverRef(receiver, isAccessConstrained: !receiverType.IsReferenceType);
                if (!receiverType.IsReferenceType)
                {
                    // 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 realy 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);

                    temp = AllocateTemp(receiverType, receiver.Syntax);
                    builder.EmitLocalStore(temp);
                    builder.EmitLocalAddress(temp);
                    builder.EmitLocalLoad(temp);
                    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 }
                }
            }
            else
            {
                EmitExpression(receiver, true);
                if (!receiverType.IsReferenceType)
                {
                    EmitBox(receiverType, receiver.Syntax);
                }
                // here we have loaded just { O }
                // we have the most trivial case where we can just reload O when needed
            }

            builder.EmitBranch(ILOpCode.Brtrue, whenNotNullLabel);

            if (nullCheckOnCopy)
            {
                builder.EmitOpCode(ILOpCode.Pop);
            }

            EmitDefaultValue(expression.Type, used, expression.Syntax);
            builder.EmitBranch(ILOpCode.Br, doneLabel);

            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)
            {
                receiverTemp = EmitReceiverRef(receiver, isAccessConstrained: !receiverType.IsReferenceType);
                Debug.Assert(receiverTemp == null);
            }

            EmitExpression(expression.AccessExpression, used);
            builder.MarkLabel(doneLabel);

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

            if (receiverTemp != null)
            {
                FreeTemp(receiverTemp);
            }
        }
        // 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);
        }