Beispiel #1
0
 static bool ValidateCompoundAssign(BinaryNumericInstruction binary, Conv conv, IType targetType)
 {
     if (!NumericCompoundAssign.IsBinaryCompatibleWithType(binary, targetType))
     {
         return(false);
     }
     if (conv != null && !(conv.TargetType == targetType.ToPrimitiveType() && conv.CheckForOverflow == binary.CheckForOverflow))
     {
         return(false);                // conv does not match binary operation
     }
     return(true);
 }
Beispiel #2
0
        /// <summary>
        /// Transform compound assignments where the return value is not being used,
        /// or where there's an inlined assignment within the setter call.
        ///
        /// Patterns handled:
        /// 1.
        ///   callvirt set_Property(ldloc S_1, binary.op(callvirt get_Property(ldloc S_1), value))
        ///   ==> compound.op.new(callvirt get_Property(ldloc S_1), value)
        /// 2.
        ///   callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value)))
        ///   ==> stloc v(compound.op.new(callvirt get_Property(ldloc S_1), value))
        /// 3.
        ///   stobj(target, binary.op(ldobj(target), ...))
        ///     where target is pure
        ///   => compound.op(target, ...)
        /// </summary>
        /// <remarks>
        /// Called by ExpressionTransforms, or after the inline-assignment transform for setters.
        /// </remarks>
        internal static bool HandleCompoundAssign(ILInstruction compoundStore, StatementTransformContext context)
        {
            if (!context.Settings.MakeAssignmentExpressions || !context.Settings.IntroduceIncrementAndDecrement)
            {
                return(false);
            }
            if (compoundStore is CallInstruction && compoundStore.SlotInfo != Block.InstructionSlot)
            {
                // replacing 'call set_Property' with a compound assignment instruction
                // changes the return value of the expression, so this is only valid on block-level.
                return(false);
            }
            if (!IsCompoundStore(compoundStore, out var targetType, out var setterValue, context.TypeSystem))
            {
                return(false);
            }
            // targetType = The type of the property/field/etc. being stored to.
            // setterValue = The value being stored.
            var storeInSetter = setterValue as StLoc;

            if (storeInSetter != null)
            {
                // We'll move the stloc to top-level:
                // callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value)))
                // ==> stloc v(compound.op.new(callvirt get_Property(ldloc S_1), value))
                setterValue = storeInSetter.Value;
                if (storeInSetter.Variable.Type.IsSmallIntegerType())
                {
                    // 'stloc v' implicitly truncates the value.
                    // Ensure that type of 'v' matches the type of the property:
                    if (storeInSetter.Variable.Type.GetSize() != targetType.GetSize())
                    {
                        return(false);
                    }
                    if (storeInSetter.Variable.Type.GetSign() != targetType.GetSign())
                    {
                        return(false);
                    }
                }
            }
            ILInstruction newInst;

            if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary)
            {
                if (compoundStore is StLoc)
                {
                    // transform local variables only for user-defined operators
                    return(false);
                }
                if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable))
                {
                    return(false);
                }
                if (!ValidateCompoundAssign(binary, smallIntConv, targetType))
                {
                    return(false);
                }
                context.Step($"Compound assignment (binary.numeric)", compoundStore);
                finalizeMatch?.Invoke(context);
                newInst = new NumericCompoundAssign(
                    binary, target, targetKind, binary.Right,
                    targetType, CompoundEvalMode.EvaluatesToNewValue);
            }
            else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator)
            {
                if (operatorCall.Arguments.Count == 0)
                {
                    return(false);
                }
                if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable))
                {
                    return(false);
                }
                ILInstruction rhs;
                if (operatorCall.Arguments.Count == 2)
                {
                    if (CSharp.ExpressionBuilder.GetAssignmentOperatorTypeFromMetadataName(operatorCall.Method.Name) == null)
                    {
                        return(false);
                    }
                    rhs = operatorCall.Arguments[1];
                }
                else if (operatorCall.Arguments.Count == 1)
                {
                    if (!(operatorCall.Method.Name == "op_Increment" || operatorCall.Method.Name == "op_Decrement"))
                    {
                        return(false);
                    }
                    // use a dummy node so that we don't need a dedicated instruction for user-defined unary operator calls
                    rhs = new LdcI4(1);
                }
                else
                {
                    return(false);
                }
                if (operatorCall.IsLifted)
                {
                    return(false);                    // TODO: add tests and think about whether nullables need special considerations
                }
                context.Step($"Compound assignment (user-defined binary)", compoundStore);
                finalizeMatch?.Invoke(context);
                newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue,
                                                        target, targetKind, rhs);
            }