コード例 #1
0
        private BoundExpression MakeIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement)
        {
            BoundExpression result;

            if (node.OperatorKind.OperandTypes() == UnaryOperatorKind.UserDefined)
            {
                result = MakeUserDefinedIncrementOperator(node, rewrittenValueToIncrement);
            }
            else
            {
                result = MakeBuiltInIncrementOperator(node, rewrittenValueToIncrement);
            }

            // Generate the conversion back to the type of the original expression.

            // (X)(short)((int)(short)x + 1)
            if (!node.ResultConversion.IsIdentity)
            {
                result = MakeConversionNode(
                    syntax: node.Syntax,
                    rewrittenOperand: result,
                    conversion: node.ResultConversion,
                    rewrittenType: node.Type,
                    @checked: node.OperatorKind.IsChecked());
            }

            return(result);
        }
コード例 #2
0
        /// <summary>
        /// The rewrites are as follows: suppose the operand x is a variable of type X. The
        /// chosen increment/decrement operator is modelled as a static method on a type T,
        /// which takes a value of type T and returns the result of incrementing or decrementing
        /// that value.
        ///
        /// x++
        ///     X temp = x
        ///     x = (X)(T.Increment((T)temp))
        ///     return temp
        /// x--
        ///     X temp = x
        ///     x = (X)(T.Decrement((T)temp))
        ///     return temp
        /// ++x
        ///     X temp = (X)(T.Increment((T)x))
        ///     x = temp
        ///     return temp
        /// --x
        ///     X temp = (X)(T.Decrement((T)x))
        ///     x = temp
        ///     return temp
        ///
        /// Note:
        /// Dev11 implements dynamic prefix operators incorrectly.
        ///
        ///   result = ++x.P  is emitted as  result = SetMember{"P"}(t, UnaryOperation{Inc}(GetMember{"P"}(x)))
        ///
        /// The difference is that Dev11 relies on SetMember returning the same value as it was given as an argument.
        /// Failing to do so changes the semantics of ++/-- operator which is undesirable. We emit the same pattern for
        /// both dynamic and static operators.
        ///
        /// For example, we might have a class X with user-defined implicit conversions
        /// to and from short, but no user-defined increment or decrement operators. We
        /// would bind x++ as "X temp = x; x = (X)(short)((int)(short)temp + 1); return temp;"
        /// </summary>
        /// <param name="node">The unary operator expression representing the increment/decrement.</param>
        /// <returns>A bound sequence that uses a temp to achieve the correct side effects and return value.</returns>
        public override BoundNode VisitIncrementOperator(BoundIncrementOperator node)
        {
            bool isPrefix  = IsPrefix(node);
            bool isChecked = node.OperatorKind.IsChecked();

            ArrayBuilder <LocalSymbol> tempSymbols = ArrayBuilder <LocalSymbol> .GetInstance();

            ArrayBuilder <BoundExpression> tempInitializers = ArrayBuilder <BoundExpression> .GetInstance();

            SyntaxNode syntax = node.Syntax;

            // This will be filled in with the LHS that uses temporaries to prevent
            // double-evaluation of side effects.
            BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.Operand, tempInitializers, tempSymbols);
            TypeSymbol      operandType    = transformedLHS.Type; //type of the variable being incremented

            Debug.Assert(TypeSymbol.Equals(operandType, node.Type, TypeCompareKind.ConsiderEverything2));

            LocalSymbol tempSymbol = _factory.SynthesizedLocal(operandType);

            tempSymbols.Add(tempSymbol);
            // Not adding an entry to tempInitializers because the initial value depends on the case.

            BoundExpression boundTemp = new BoundLocal(
                syntax: syntax,
                localSymbol: tempSymbol,
                constantValueOpt: null,
                type: operandType);

            // prefix:  (X)(T.Increment((T)operand)))
            // postfix: (X)(T.Increment((T)temp)))
            var newValue = MakeIncrementOperator(node, rewrittenValueToIncrement: (isPrefix ? MakeRValue(transformedLHS) : boundTemp));

            // there are two strategies for completing the rewrite.
            // The reason is that indirect assignments read the target of the assignment before evaluating
            // of the assignment value and that may cause reads of operand and boundTemp to cross which
            // in turn would require one of them to be a real temp (not a stack local)
            //
            // To avoid this issue, in a case of ByRef operand, we perform a "nested sequence" rewrite.
            //
            // Ex:
            //    Seq{..., operand = Seq{temp = operand + 1, temp}, ...}
            //  instead of
            //    Seq{.... temp = operand + 1, operand = temp, ...}
            //
            // Such rewrite will nest reads of boundTemp relative to reads of operand so both
            // operand and boundTemp could be optimizable (subject to all other conditions of course).
            //
            // In a case of the non-byref operand we use a single-sequence strategy as it results in shorter
            // overall life time of temps and as such more appropriate. (problem of crossed reads does not affect that case)
            //
            if (IsIndirectOrInstanceField(transformedLHS))
            {
                return(RewriteWithRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue));
            }
            else
            {
                return(RewriteWithNotRefOperand(isPrefix, isChecked, tempSymbols, tempInitializers, syntax, transformedLHS, operandType, boundTemp, newValue));
            }
        }
コード例 #3
0
        public override BoundNode VisitIncrementOperator(BoundIncrementOperator node)
        {
            if (_inExpressionLambda)
            {
                Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node);
            }

            return(base.VisitIncrementOperator(node));
        }
コード例 #4
0
        private static BinaryOperatorKind GetCorrespondingBinaryOperator(BoundIncrementOperator node)
        {
            // We need to create expressions that have the semantics of incrementing or decrementing:
            // sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal and
            // any enum.  However, the binary addition operators we have at our disposal are just
            // int, uint, long, ulong, float, double and decimal.

            UnaryOperatorKind  unaryOperatorKind = node.OperatorKind;
            BinaryOperatorKind result;

            switch (unaryOperatorKind.OperandTypes())
            {
            case UnaryOperatorKind.Int32:
            case UnaryOperatorKind.Int8:
            case UnaryOperatorKind.Int16:
                result = BinaryOperatorKind.Int32;
                break;

            case UnaryOperatorKind.UInt8:
            case UnaryOperatorKind.UShort:
            case UnaryOperatorKind.Rune:
            case UnaryOperatorKind.UInt32:
                result = BinaryOperatorKind.UInt32;
                break;

            case UnaryOperatorKind.Int:
                result = BinaryOperatorKind.Int;
                break;

            case UnaryOperatorKind.UInt:
                result = BinaryOperatorKind.UInt;
                break;

            case UnaryOperatorKind.Int64:
                result = BinaryOperatorKind.Int64;
                break;

            case UnaryOperatorKind.UInt64:
                result = BinaryOperatorKind.UInt64;
                break;

            case UnaryOperatorKind.Float32:
                result = BinaryOperatorKind.Float32;
                break;

            case UnaryOperatorKind.Float64:
                result = BinaryOperatorKind.Float64;
                break;

            case UnaryOperatorKind.Enum:
            {
                TypeSymbol underlyingType = node.Type;
                if (underlyingType.IsNullableType())
                {
                    underlyingType = underlyingType.GetNullableUnderlyingType();
                }
                Debug.Assert(underlyingType.IsEnumType());
                underlyingType = underlyingType.GetEnumUnderlyingType();

                // Operator overload resolution will not have chosen the enumerated type
                // unless the operand actually is of the enumerated type (or nullable enum type.)

                switch (underlyingType.SpecialType)
                {
                case SpecialType.System_Int8:
                case SpecialType.System_Int16:
                case SpecialType.System_Int32:
                    result = BinaryOperatorKind.Int32;
                    break;

                case SpecialType.System_UInt8:
                case SpecialType.System_UInt16:
                case SpecialType.System_UInt32:
                    result = BinaryOperatorKind.UInt32;
                    break;

                case SpecialType.System_Int64:
                    result = BinaryOperatorKind.Int64;
                    break;

                case SpecialType.System_UInt64:
                    result = BinaryOperatorKind.UInt64;
                    break;

                default:
                    throw ExceptionUtilities.UnexpectedValue(underlyingType.SpecialType);
                }
            }
            break;

            case UnaryOperatorKind.Pointer:
                result = BinaryOperatorKind.PointerAndInt32;
                break;

            case UnaryOperatorKind.UserDefined:
            case UnaryOperatorKind.Bool:
            default:
                throw ExceptionUtilities.UnexpectedValue(unaryOperatorKind.OperandTypes());
            }

            switch (result)
            {
            case BinaryOperatorKind.UInt32:
            case BinaryOperatorKind.Int32:
            case BinaryOperatorKind.UInt64:
            case BinaryOperatorKind.Int64:
            case BinaryOperatorKind.Int:
            case BinaryOperatorKind.PointerAndInt32:
                result |= (BinaryOperatorKind)unaryOperatorKind.OverflowChecks();
                break;
            }

            if (unaryOperatorKind.IsLifted())
            {
                result |= BinaryOperatorKind.Lifted;
            }

            return(result);
        }
コード例 #5
0
        // There are ++ and -- operators defined on sbyte, byte, short, ushort, int,
        // uint, long, ulong, char, float, double, decimal and any enum type.
        // Given a built-in increment operator, get the associated type.  Note
        // that this need not be the result type or the operand type of the node!
        // We could have a user-defined conversion from the type of the operand
        // to short, and a user-defined conversion from short to the result
        // type.
        private TypeSymbol GetUnaryOperatorType(BoundIncrementOperator node)
        {
            UnaryOperatorKind kind = node.OperatorKind.OperandTypes();

            // If overload resolution chose an enum operator then the operand
            // type and the return type really are an enum; we are not in a user-
            // defined conversion scenario.
            if (kind == UnaryOperatorKind.Enum)
            {
                return(node.Type);
            }

            SpecialType specialType;

            switch (kind)
            {
            case UnaryOperatorKind.Int32:
                specialType = SpecialType.System_Int32;
                break;

            case UnaryOperatorKind.Int8:
                specialType = SpecialType.System_Int8;
                break;

            case UnaryOperatorKind.Int16:
                specialType = SpecialType.System_Int16;
                break;

            case UnaryOperatorKind.UInt8:
                specialType = SpecialType.System_UInt8;
                break;

            case UnaryOperatorKind.UShort:
                specialType = SpecialType.System_UInt16;
                break;

            case UnaryOperatorKind.Rune:
                specialType = SpecialType.System_Rune;
                break;

            case UnaryOperatorKind.UInt32:
                specialType = SpecialType.System_UInt32;
                break;

            case UnaryOperatorKind.Int64:
                specialType = SpecialType.System_Int64;
                break;

            case UnaryOperatorKind.UInt64:
                specialType = SpecialType.System_UInt64;
                break;

            case UnaryOperatorKind.Float32:
                specialType = SpecialType.System_Float32;
                break;

            case UnaryOperatorKind.Float64:
                specialType = SpecialType.System_Float64;
                break;

            case UnaryOperatorKind.Int:
                specialType = SpecialType.System_Int;
                break;

            case UnaryOperatorKind.UInt:
                specialType = SpecialType.System_UInt;
                break;

            case UnaryOperatorKind.Pointer:
                return(node.Type);

            case UnaryOperatorKind.UserDefined:
            case UnaryOperatorKind.Bool:
            default:
                throw ExceptionUtilities.UnexpectedValue(kind);
            }

            NamedTypeSymbol type = _compilation.GetSpecialType(specialType);

            if (node.OperatorKind.IsLifted())
            {
                type = _compilation.GetSpecialType(SpecialType.core_Option_T).Construct(type);
            }

            return(type);
        }
コード例 #6
0
        private BoundExpression MakeBuiltInIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement)
        {
            BoundExpression result;
            // If we have a built-in increment or decrement then things get a bit trickier. Suppose for example we have
            // a user-defined conversion from X to short and from short to X, but no user-defined increment operator on
            // X.  The increment portion of "++x" is then: (X)(short)((int)(short)x + 1). That is, first x must be
            // converted to short via an implicit user- defined conversion, then to int via an implicit numeric
            // conversion, then the addition is performed in integers. The resulting integer is converted back to short,
            // and then the short is converted to X.

            // This is the input and output type of the unary increment operator we're going to call.
            // That is, "short" in the example above.
            TypeSymbol unaryOperandType = GetUnaryOperatorType(node);

            // This is the kind of binary operator that we're going to realize the unary operator
            // as. That is, "int + int --> int" in the example above.
            BinaryOperatorKind binaryOperatorKind = GetCorrespondingBinaryOperator(node);

            binaryOperatorKind |= IsIncrement(node) ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction;

            // The "1" in the example above.
            ConstantValue constantOne = GetConstantOneForBinOp(binaryOperatorKind);

            Debug.Assert(constantOne != null);
            Debug.Assert(constantOne.SpecialType != SpecialType.None);
            Debug.Assert(binaryOperatorKind.OperandTypes() != 0);

            // The input/output type of the binary operand. "int" in the example.
            TypeSymbol binaryOperandType = _compilation.GetSpecialType(constantOne.SpecialType);

            // 1
            BoundExpression boundOne = MakeLiteral(
                syntax: node.Syntax,
                constantValue: constantOne,
                type: binaryOperandType);

            if (binaryOperatorKind.IsLifted())
            {
                binaryOperandType = _compilation.GetSpecialType(SpecialType.core_Option_T).Construct(binaryOperandType);
                MethodSymbol ctor = UnsafeGetNullableMethod(node.Syntax, binaryOperandType, SpecialMember.System_Nullable_T__ctor);
                boundOne = new BoundObjectCreationExpression(node.Syntax, ctor, null, boundOne);
            }

            // Now we construct the other operand to the binary addition. We start with just plain "x".
            BoundExpression binaryOperand = rewrittenValueToIncrement;

            bool @checked = node.OperatorKind.IsChecked();

            // If we need to make a conversion from the original operand type to the operand type of the
            // underlying increment operation, do it now.
            if (!node.OperandConversion.IsIdentity)
            {
                // (short)x
                binaryOperand = MakeConversionNode(
                    syntax: node.Syntax,
                    rewrittenOperand: binaryOperand,
                    conversion: node.OperandConversion,
                    rewrittenType: unaryOperandType,
                    @checked: @checked);
            }

            // Early-out for pointer increment - we don't need to convert the operands to a common type.
            if (node.OperatorKind.OperandTypes() == UnaryOperatorKind.Pointer)
            {
                Debug.Assert(binaryOperatorKind.OperandTypes() == BinaryOperatorKind.PointerAndInt32);
                Debug.Assert(binaryOperand.Type.IsPointerType());
                Debug.Assert(boundOne.Type.SpecialType == SpecialType.System_Int32);
                return(MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperand.Type, method: null));
            }

            // If we need to make a conversion from the unary operator type to the binary operator type,
            // do it now.

            // (int)(short)x
            binaryOperand = MakeConversionNode(binaryOperand, binaryOperandType, @checked);

            // Perform the addition.

            // (int)(short)x + 1
            BoundExpression binOp;

            binOp = MakeBinaryOperator(node.Syntax, binaryOperatorKind, binaryOperand, boundOne, binaryOperandType, method: null);

            // Generate the conversion back to the type of the unary operator.

            // (short)((int)(short)x + 1)
            result = MakeConversionNode(binOp, unaryOperandType, @checked);
            return(result);
        }
コード例 #7
0
        private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator node, BoundExpression rewrittenValueToIncrement)
        {
            Debug.Assert((object)node.MethodOpt != null);
            Debug.Assert(node.MethodOpt.ParameterCount == 1);

            bool isLifted = node.OperatorKind.IsLifted();
            bool @checked = node.OperatorKind.IsChecked();

            BoundExpression rewrittenArgument = rewrittenValueToIncrement;
            SyntaxNode      syntax            = node.Syntax;

            TypeSymbol type = node.MethodOpt.ParameterTypes[0].TypeSymbol;

            if (isLifted)
            {
                type = _compilation.GetSpecialType(SpecialType.core_Option_T).Construct(type);
                Debug.Assert(TypeSymbol.Equals(node.MethodOpt.ParameterTypes[0].TypeSymbol, node.MethodOpt.ReturnType.TypeSymbol, TypeCompareKind.ConsiderEverything2));
            }

            if (!node.OperandConversion.IsIdentity)
            {
                rewrittenArgument = MakeConversionNode(
                    syntax: syntax,
                    rewrittenOperand: rewrittenValueToIncrement,
                    conversion: node.OperandConversion,
                    rewrittenType: type,
                    @checked: @checked);
            }

            if (!isLifted)
            {
                return(BoundCall.Synthesized(syntax, null, node.MethodOpt, rewrittenArgument));
            }

            // S? temp = operand;
            // S? r = temp.HasValue ?
            //        new S?(op_Increment(temp.GetValueOrDefault())) :
            //        default(S?);

            // Unlike the other unary operators, we do not attempt to optimize nullable user-defined
            // increment or decrement. The operand is a variable (or property), and so we do not know if
            // it is always null/never null.

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

            MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, type, SpecialMember.System_Nullable_T_GetValueOrDefault);
            MethodSymbol ctor = UnsafeGetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor);

            // temp.HasValue
            BoundExpression condition = MakeNullableHasValue(node.Syntax, boundTemp);

            // temp.GetValueOrDefault()
            BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault);

            // op_Increment(temp.GetValueOrDefault())
            BoundExpression userDefinedCall = BoundCall.Synthesized(syntax, null, node.MethodOpt, call_GetValueOrDefault);

            // new S?(op_Increment(temp.GetValueOrDefault()))
            BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, null, userDefinedCall);

            // default(S?)
            BoundExpression alternative = new BoundDefaultExpression(syntax, null, type);

            // temp.HasValue ?
            //          new S?(op_Increment(temp.GetValueOrDefault())) :
            //          default(S?);
            BoundExpression conditionalExpression = RewriteConditionalOperator(
                syntax: syntax,
                rewrittenCondition: condition,
                rewrittenConsequence: consequence,
                rewrittenAlternative: alternative,
                constantValueOpt: null,
                rewrittenType: type,
                isRef: false);

            // temp = operand;
            // temp.HasValue ?
            //          new S?(op_Increment(temp.GetValueOrDefault())) :
            //          default(S?);
            return(new BoundSequence(
                       syntax: syntax,
                       locals: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol),
                       sideEffects: ImmutableArray.Create <BoundExpression>(tempAssignment),
                       value: conditionalExpression,
                       type: type));
        }
コード例 #8
0
        private static bool IsPrefix(BoundIncrementOperator node)
        {
            var op = node.OperatorKind.Operator();

            return(op == UnaryOperatorKind.PrefixIncrement || op == UnaryOperatorKind.PrefixDecrement);
        }
コード例 #9
0
 public override BoundNode VisitIncrementOperator(BoundIncrementOperator node)
 {
     _mightAssignSomething = true;
     return(null);
 }