コード例 #1
0
        private BoundExpression RewritePointerElementAccess(BoundPointerElementAccess node, BoundExpression rewrittenExpression, BoundExpression rewrittenIndex)
        {
            // Optimization: p[0] == *p
            if (rewrittenIndex.IsDefaultValue())
            {
                return(new BoundPointerIndirectionOperator(
                           node.Syntax,
                           rewrittenExpression,
                           node.Type));
            }

            BinaryOperatorKind additionKind = BinaryOperatorKind.Addition;

            switch (rewrittenIndex.Type.SpecialType)
            {
            case SpecialType.System_Int32:
                additionKind |= BinaryOperatorKind.PointerAndInt32Addition;
                break;

            case SpecialType.System_UInt32:
                additionKind |= BinaryOperatorKind.PointerAndUInt32Addition;
                break;

            case SpecialType.System_Int64:
                additionKind |= BinaryOperatorKind.PointerAndInt64Addition;
                break;

            case SpecialType.System_UInt64:
                additionKind |= BinaryOperatorKind.PointerAndUInt64Addition;
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(rewrittenIndex.Type.SpecialType);
            }

            if (node.Checked)
            {
                additionKind |= BinaryOperatorKind.Checked;
            }

            return(new BoundPointerIndirectionOperator(
                       node.Syntax,
                       MakeBinaryOperator(
                           node.Syntax,
                           additionKind,
                           rewrittenExpression,
                           rewrittenIndex,
                           rewrittenExpression.Type,
                           method: null,
                           isPointerElementAccess: true), //see RewriterPointerNumericOperator
                       node.Type));
        }
コード例 #2
0
        /// <summary>
        /// Variables local to current frame do not need temps when re-read multiple times
        /// as long as there is no code that may write to locals in between accesses and they
        /// are not captured.
        ///
        /// Example:
        ///        l += goo(ref l);
        ///
        /// even though l is a local, we must access it via a temp since "goo(ref l)" may change it
        /// on between accesses.
        ///
        /// Note: In <c>this.x++</c>, <c>this</c> cannot change between reads. But in <c>(this, ...) == (..., this.Mutate())</c> it can.
        /// </summary>
        internal static bool CanChangeValueBetweenReads(
            BoundExpression expression,
            bool localsMayBeAssignedOrCaptured        = true,
            bool structThisCanChangeValueBetweenReads = false)
        {
            if (expression.IsDefaultValue())
            {
                return(false);
            }

            if (expression.ConstantValue != null)
            {
                var type = expression.Type;
                return(!ConstantValueIsTrivial(type));
            }

            switch (expression.Kind)
            {
            case BoundKind.ThisReference:
                return(structThisCanChangeValueBetweenReads && ((BoundThisReference)expression).Type.IsStructType());

            case BoundKind.BaseReference:
                return(false);

            case BoundKind.Literal:
                var type = expression.Type;
                return(!ConstantValueIsTrivial(type));

            case BoundKind.Parameter:
                return(localsMayBeAssignedOrCaptured || ((BoundParameter)expression).ParameterSymbol.RefKind != RefKind.None);

            case BoundKind.Local:
                return(localsMayBeAssignedOrCaptured || ((BoundLocal)expression).LocalSymbol.RefKind != RefKind.None);

            default:
                return(true);
            }
        }
コード例 #3
0
        private BoundExpression MakeNullCoalescingOperator(
            SyntaxNode syntax,
            BoundExpression rewrittenLeft,
            BoundExpression rewrittenRight,
            Conversion leftConversion,
            BoundNullCoalescingOperatorResultKind resultKind,
            TypeSymbol rewrittenResultType)
        {
            Debug.Assert(rewrittenLeft != null);
            Debug.Assert(rewrittenRight != null);
            Debug.Assert(leftConversion.IsValid);
            Debug.Assert((object)rewrittenResultType != null);
            Debug.Assert(rewrittenRight.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes));

            var isUnconstrainedTypeParameter = (object)rewrittenLeft.Type != null && !rewrittenLeft.Type.IsReferenceType && !rewrittenLeft.Type.IsValueType;

            // first we can make a small optimization:
            // If left is a constant then we already know whether it is null or not. If it is null then we
            // can simply generate "right". If it is not null then we can simply generate
            // MakeConversion(left). This does not hold when the left is an unconstrained type parameter: at runtime,
            // it can be either left or right depending on the runtime type of T
            if (!isUnconstrainedTypeParameter)
            {
                if (rewrittenLeft.IsDefaultValue())
                {
                    return(rewrittenRight);
                }

                if (rewrittenLeft.ConstantValue != null)
                {
                    Debug.Assert(!rewrittenLeft.ConstantValue.IsNull);

                    return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType));
                }
            }

            // string concatenation is never null.
            // interpolated string lowering may introduce redundant null coalescing, which we have to remove.
            if (IsStringConcat(rewrittenLeft))
            {
                return(GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType));
            }

            // if left conversion is intrinsic implicit (always succeeds) and results in a reference type
            // we can apply conversion before doing the null check that allows for a more efficient IL emit.
            if (rewrittenLeft.Type.IsReferenceType &&
                leftConversion.IsImplicit &&
                !leftConversion.IsUserDefined)
            {
                if (!leftConversion.IsIdentity)
                {
                    rewrittenLeft = MakeConversionNode(rewrittenLeft.Syntax, rewrittenLeft, leftConversion, rewrittenResultType, @checked: false);
                }
                return(new BoundNullCoalescingOperator(syntax, rewrittenLeft, rewrittenRight, Conversion.Identity, resultKind, rewrittenResultType));
            }

            if (leftConversion.IsIdentity || leftConversion.Kind == ConversionKind.ExplicitNullable)
            {
                var conditionalAccess = rewrittenLeft as BoundLoweredConditionalAccess;
                if (conditionalAccess != null &&
                    (conditionalAccess.WhenNullOpt == null || NullableNeverHasValue(conditionalAccess.WhenNullOpt)))
                {
                    var notNullAccess = NullableAlwaysHasValue(conditionalAccess.WhenNotNull);
                    if (notNullAccess != null)
                    {
                        var whenNullOpt = rewrittenRight;

                        if (whenNullOpt.Type.IsNullableType())
                        {
                            notNullAccess = conditionalAccess.WhenNotNull;
                        }

                        if (whenNullOpt.IsDefaultValue())
                        {
                            whenNullOpt = null;
                        }

                        return(conditionalAccess.Update(
                                   conditionalAccess.Receiver,
                                   conditionalAccess.HasValueMethodOpt,
                                   whenNotNull: notNullAccess,
                                   whenNullOpt: whenNullOpt,
                                   id: conditionalAccess.Id,
                                   type: rewrittenResultType
                                   ));
                    }
                }
            }

            // Optimize left ?? right to left.GetValueOrDefault() when left is T? and right is the default value of T
            if (rewrittenLeft.Type.IsNullableType() &&
                RemoveIdentityConversions(rewrittenRight).IsDefaultValue() &&
                rewrittenRight.Type.Equals(rewrittenLeft.Type.GetNullableUnderlyingType(), TypeCompareKind.AllIgnoreOptions) &&
                TryGetNullableMethod(rewrittenLeft.Syntax, rewrittenLeft.Type, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault))
            {
                return(BoundCall.Synthesized(rewrittenLeft.Syntax, rewrittenLeft, getValueOrDefault));
            }

            // We lower left ?? right to
            //
            // var temp = left;
            // (temp != null) ? MakeConversion(temp) : right
            //

            BoundAssignmentOperator tempAssignment;
            BoundLocal boundTemp = _factory.StoreToTemp(rewrittenLeft, out tempAssignment);

            // temp != null
            BoundExpression nullCheck = MakeNullCheck(syntax, boundTemp, BinaryOperatorKind.NotEqual);

            // MakeConversion(temp, rewrittenResultType)
            BoundExpression convertedLeft = GetConvertedLeftForNullCoalescingOperator(boundTemp, leftConversion, rewrittenResultType);

            Debug.Assert(convertedLeft.HasErrors || convertedLeft.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes));

            // (temp != null) ? MakeConversion(temp, LeftConversion) : RightOperand
            BoundExpression conditionalExpression = RewriteConditionalOperator(
                syntax: syntax,
                rewrittenCondition: nullCheck,
                rewrittenConsequence: convertedLeft,
                rewrittenAlternative: rewrittenRight,
                constantValueOpt: null,
                rewrittenType: rewrittenResultType,
                isRef: false);

            Debug.Assert(conditionalExpression.ConstantValue == null); // we shouldn't have hit this else case otherwise
            Debug.Assert(conditionalExpression.Type.Equals(rewrittenResultType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes));

            return(new BoundSequence(
                       syntax: syntax,
                       locals: ImmutableArray.Create(boundTemp.LocalSymbol),
                       sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment),
                       value: conditionalExpression,
                       type: rewrittenResultType));
        }
コード例 #4
0
        // a simple check for common non-side-effecting expressions
        internal static bool ReadIsSideeffecting(
            BoundExpression expression)
        {
            if (expression.ConstantValue != null)
            {
                return(false);
            }

            if (expression.IsDefaultValue())
            {
                return(false);
            }

            switch (expression.Kind)
            {
            case BoundKind.ThisReference:
            case BoundKind.BaseReference:
            case BoundKind.Literal:
            case BoundKind.Parameter:
            case BoundKind.Local:
            case BoundKind.Lambda:
                return(false);

            case BoundKind.Conversion:
                var conv = (BoundConversion)expression;
                return(conv.ConversionHasSideEffects() ||
                       ReadIsSideeffecting(conv.Operand));

            case BoundKind.PassByCopy:
                return(ReadIsSideeffecting(((BoundPassByCopy)expression).Expression));

            case BoundKind.ObjectCreationExpression:
                // common production of lowered conversions to nullable
                // new S?(arg)
                if (expression.Type.IsNullableType())
                {
                    var objCreation = (BoundObjectCreationExpression)expression;
                    return(objCreation.Arguments.Length == 1 && ReadIsSideeffecting(objCreation.Arguments[0]));
                }

                return(true);

            case BoundKind.Call:
                var call   = (BoundCall)expression;
                var method = call.Method;

                // common production of lowered lifted operators
                // GetValueOrDefault is known to be not sideeffecting.
                if (method.ContainingType?.IsNullableType() == true)
                {
                    if (IsSpecialMember(method, SpecialMember.System_Nullable_T_GetValueOrDefault) ||
                        IsSpecialMember(method, SpecialMember.System_Nullable_T_get_HasValue))
                    {
                        return(ReadIsSideeffecting(call.ReceiverOpt));
                    }
                }

                return(true);

            default:
                return(true);
            }
        }
コード例 #5
0
 private static bool IsNullOrEmptyStringConstant(BoundExpression operand)
 {
     return((operand.ConstantValue != null && string.IsNullOrEmpty(operand.ConstantValue.StringValue)) ||
            operand.IsDefaultValue());
 }