Ejemplo n.º 1
0
        private TypeSymbol LiftedType(BinaryOperatorKind kind)
        {
            Debug.Assert(kind.IsLifted());

            var nullable = _compilation.GetSpecialType(SpecialType.core_Option_T);

            switch (kind.OperandTypes())
            {
            case BinaryOperatorKind.Int32: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Int32)));

            case BinaryOperatorKind.UInt32: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_UInt32)));

            case BinaryOperatorKind.Int64: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Int64)));

            case BinaryOperatorKind.UInt64: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_UInt64)));

            case BinaryOperatorKind.Float32: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Float32)));

            case BinaryOperatorKind.Float64: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Float64)));

            case BinaryOperatorKind.Int: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Int)));

            case BinaryOperatorKind.UInt: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_UInt)));

            case BinaryOperatorKind.Bool: return(nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Boolean)));
            }
            Debug.Assert(false, "Bad operator kind in lifted type");
            return(null);
        }
Ejemplo n.º 2
0
        private TypeSymbol LiftedType(BinaryOperatorKind kind)
        {
            Debug.Assert(kind.IsLifted());

            var nullable = Compilation.GetSpecialType(SpecialType.System_Nullable_T);

            switch (kind.OperandTypes())
            {
            case BinaryOperatorKind.Int: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Int32)));

            case BinaryOperatorKind.UInt: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_UInt32)));

            case BinaryOperatorKind.Long: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Int64)));

            case BinaryOperatorKind.ULong: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_UInt64)));

            case BinaryOperatorKind.Float: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Single)));

            case BinaryOperatorKind.Double: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Double)));

            case BinaryOperatorKind.Decimal: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Decimal)));

            case BinaryOperatorKind.Bool: return(nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Boolean)));
            }
            Debug.Assert(false, "Bad operator kind in lifted type");
            return(null);
        }
Ejemplo n.º 3
0
        private BoundExpression VisitBinaryOperator(BinaryOperatorKind opKind, MethodSymbol methodOpt, TypeSymbol type, BoundExpression left, BoundExpression right)
        {
            bool   isChecked, isLifted, requiresLifted;
            string opName = GetBinaryOperatorName(opKind, out isChecked, out isLifted, out requiresLifted);

            // Fix up the null value for a nullable comparison vs null
            if ((object)left.Type == null && left.IsLiteralNull())
            {
                left = _bound.Default(right.Type);
            }
            if ((object)right.Type == null && right.IsLiteralNull())
            {
                right = _bound.Default(left.Type);
            }


            // Enums are handled as per their promoted underlying type
            switch (opKind.OperandTypes())
            {
            case BinaryOperatorKind.EnumAndUnderlying:
            case BinaryOperatorKind.UnderlyingAndEnum:
            case BinaryOperatorKind.Enum:
            {
                var enumOperand  = (opKind.OperandTypes() == BinaryOperatorKind.UnderlyingAndEnum) ? right : left;
                var promotedType = PromotedType(enumOperand.Type.StrippedType().GetEnumUnderlyingType());
                if (opKind.IsLifted())
                {
                    promotedType = _nullableType.Construct(promotedType);
                }

                var loweredLeft  = VisitAndPromoteEnumOperand(left, promotedType, isChecked);
                var loweredRight = VisitAndPromoteEnumOperand(right, promotedType, isChecked);

                var result = MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight);
                return(Demote(result, type, isChecked));
            }

            default:
            {
                var loweredLeft  = Visit(left);
                var loweredRight = Visit(right);
                return(MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight));
            }
            }
        }
Ejemplo n.º 4
0
        private static string GetBinaryOperatorName(BinaryOperatorKind opKind, out bool isChecked, out bool isLifted, out bool requiresLifted)
        {
            isChecked      = opKind.IsChecked();
            isLifted       = opKind.IsLifted();
            requiresLifted = opKind.IsComparison();

            switch (opKind.Operator())
            {
            case BinaryOperatorKind.Addition: return(isChecked ? "AddChecked" : "Add");

            case BinaryOperatorKind.Multiplication: return(isChecked ? "MultiplyChecked" : "Multiply");

            case BinaryOperatorKind.Subtraction: return(isChecked ? "SubtractChecked" : "Subtract");

            case BinaryOperatorKind.Division: return("Divide");

            case BinaryOperatorKind.Remainder: return("Modulo");

            case BinaryOperatorKind.And: return(opKind.IsLogical() ? "AndAlso" : "And");

            case BinaryOperatorKind.Xor: return("ExclusiveOr");

            case BinaryOperatorKind.Or: return(opKind.IsLogical() ? "OrElse" : "Or");

            case BinaryOperatorKind.LeftShift: return("LeftShift");

            case BinaryOperatorKind.RightShift: return("RightShift");

            case BinaryOperatorKind.Equal: return("Equal");

            case BinaryOperatorKind.NotEqual: return("NotEqual");

            case BinaryOperatorKind.LessThan: return("LessThan");

            case BinaryOperatorKind.LessThanOrEqual: return("LessThanOrEqual");

            case BinaryOperatorKind.GreaterThan: return("GreaterThan");

            case BinaryOperatorKind.GreaterThanOrEqual: return("GreaterThanOrEqual");

            default:
                throw ExceptionUtilities.UnexpectedValue(opKind.Operator());
            }
        }
Ejemplo n.º 5
0
        private TypeSymbol RightType(BinaryOperatorKind kind)
        {
            if (kind.IsLifted())
            {
                return(LiftedType(kind));
            }
            else
            {
                switch (kind.OperandTypes())
                {
                case BinaryOperatorKind.Int32: return(_compilation.GetSpecialType(SpecialType.System_Int32));

                case BinaryOperatorKind.UInt32: return(_compilation.GetSpecialType(SpecialType.System_UInt32));

                case BinaryOperatorKind.Int64: return(_compilation.GetSpecialType(SpecialType.System_Int64));

                case BinaryOperatorKind.UInt64: return(_compilation.GetSpecialType(SpecialType.System_UInt64));

                case BinaryOperatorKind.Float32: return(_compilation.GetSpecialType(SpecialType.System_Float32));

                case BinaryOperatorKind.Float64: return(_compilation.GetSpecialType(SpecialType.System_Float64));

                case BinaryOperatorKind.Int: return(_compilation.GetSpecialType(SpecialType.System_Int));

                case BinaryOperatorKind.UInt: return(_compilation.GetSpecialType(SpecialType.System_UInt));

                case BinaryOperatorKind.Bool: return(_compilation.GetSpecialType(SpecialType.System_Boolean));

                case BinaryOperatorKind.ObjectAndString:
                case BinaryOperatorKind.String:
                    return(_compilation.GetSpecialType(SpecialType.System_String));

                case BinaryOperatorKind.StringAndObject:
                case BinaryOperatorKind.Object:
                    return(_compilation.GetSpecialType(SpecialType.System_Object));
                }
            }
            Debug.Assert(false, "Bad operator kind in right type");
            return(null);
        }
Ejemplo n.º 6
0
        internal BinaryOperatorSignature GetSignature(BinaryOperatorKind kind)
        {
            var left = LeftType(kind);

            switch (kind.Operator())
            {
            case BinaryOperatorKind.Multiplication:
            case BinaryOperatorKind.Division:
            case BinaryOperatorKind.Subtraction:
            case BinaryOperatorKind.Remainder:
            case BinaryOperatorKind.And:
            case BinaryOperatorKind.Or:
            case BinaryOperatorKind.Xor:
                return(new BinaryOperatorSignature(kind, left, left, left));

            case BinaryOperatorKind.Addition:
                return(new BinaryOperatorSignature(kind, LeftType(kind), RightType(kind), ReturnType(kind)));

            case BinaryOperatorKind.LeftShift:

            case BinaryOperatorKind.RightShift:
                TypeSymbol returnType = Compilation.GetSpecialType(SpecialType.System_Int32);

                if (kind.IsLifted())
                {
                    returnType = Compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(returnType);
                }

                return(new BinaryOperatorSignature(kind, left, returnType, left));

            case BinaryOperatorKind.Equal:
            case BinaryOperatorKind.NotEqual:
            case BinaryOperatorKind.GreaterThan:
            case BinaryOperatorKind.LessThan:
            case BinaryOperatorKind.GreaterThanOrEqual:
            case BinaryOperatorKind.LessThanOrEqual:
                return(new BinaryOperatorSignature(kind, left, left, Compilation.GetSpecialType(SpecialType.System_Boolean)));
            }
            return(new BinaryOperatorSignature(kind, LeftType(kind), RightType(kind), ReturnType(kind)));
        }
Ejemplo n.º 7
0
        private TypeSymbol LeftType(BinaryOperatorKind kind)
        {
            if (kind.IsLifted())
            {
                return(LiftedType(kind));
            }
            else
            {
                switch (kind.OperandTypes())
                {
                case BinaryOperatorKind.Int: return(Compilation.GetSpecialType(SpecialType.System_Int32));

                case BinaryOperatorKind.UInt: return(Compilation.GetSpecialType(SpecialType.System_UInt32));

                case BinaryOperatorKind.Long: return(Compilation.GetSpecialType(SpecialType.System_Int64));

                case BinaryOperatorKind.ULong: return(Compilation.GetSpecialType(SpecialType.System_UInt64));

                case BinaryOperatorKind.Float: return(Compilation.GetSpecialType(SpecialType.System_Single));

                case BinaryOperatorKind.Double: return(Compilation.GetSpecialType(SpecialType.System_Double));

                case BinaryOperatorKind.Decimal: return(Compilation.GetSpecialType(SpecialType.System_Decimal));

                case BinaryOperatorKind.Bool: return(Compilation.GetSpecialType(SpecialType.System_Boolean));

                case BinaryOperatorKind.ObjectAndString:
                case BinaryOperatorKind.Object:
                    return(Compilation.GetSpecialType(SpecialType.System_Object));

                case BinaryOperatorKind.String:
                case BinaryOperatorKind.StringAndObject:
                    return(Compilation.GetSpecialType(SpecialType.System_String));
                }
            }
            Debug.Assert(false, "Bad operator kind in left type");
            return(null);
        }
        private BoundExpression LowerUserDefinedBinaryOperator(
            CSharpSyntaxNode syntax,
            BinaryOperatorKind operatorKind,
            BoundExpression loweredLeft,
            BoundExpression loweredRight,
            TypeSymbol type,
            MethodSymbol method)
        {
            Debug.Assert(!operatorKind.IsLogical());

            if (operatorKind.IsLifted())
            {
                return RewriteLiftedBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method);
            }

            // Otherwise, nothing special here.
            Debug.Assert((object)method != null);
            Debug.Assert(method.ReturnType == type);
            return BoundCall.Synthesized(syntax, null, method, loweredLeft, loweredRight);
        }
        private BoundExpression MakeBinaryOperator(
            BoundBinaryOperator oldNode,
            CSharpSyntaxNode syntax,
            BinaryOperatorKind operatorKind,
            BoundExpression loweredLeft,
            BoundExpression loweredRight,
            TypeSymbol type,
            MethodSymbol method,
            bool isPointerElementAccess = false,
            bool isCompoundAssignment = false,
            BoundUnaryOperator applyParentUnaryOperator = null)
        {
            Debug.Assert(oldNode == null || (oldNode.Syntax == syntax));

            if (_inExpressionLambda)
            {
                switch (operatorKind.Operator() | operatorKind.OperandTypes())
                {
                    case BinaryOperatorKind.ObjectAndStringConcatenation:
                    case BinaryOperatorKind.StringAndObjectConcatenation:
                    case BinaryOperatorKind.StringConcatenation:
                        return RewriteStringConcatenation(syntax, operatorKind, loweredLeft, loweredRight, type);
                    case BinaryOperatorKind.DelegateCombination:
                        return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Combine);
                    case BinaryOperatorKind.DelegateRemoval:
                        return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Remove);
                    case BinaryOperatorKind.DelegateEqual:
                        return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Equality);
                    case BinaryOperatorKind.DelegateNotEqual:
                        return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Inequality);
                }
            }
            else
            // try to lower the expression.
            {
                if (operatorKind.IsDynamic())
                {
                    Debug.Assert(!isPointerElementAccess);

                    if (operatorKind.IsLogical())
                    {
                        return MakeDynamicLogicalBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, method, type, isCompoundAssignment, applyParentUnaryOperator);
                    }
                    else
                    {
                        Debug.Assert((object)method == null);
                        return _dynamicFactory.MakeDynamicBinaryOperator(operatorKind, loweredLeft, loweredRight, isCompoundAssignment, type).ToExpression();
                    }
                }

                if (operatorKind.IsLifted())
                {
                    return RewriteLiftedBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method);
                }

                if (operatorKind.IsUserDefined())
                {
                    return LowerUserDefinedBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method);
                }

                switch (operatorKind.OperatorWithLogical() | operatorKind.OperandTypes())
                {
                    case BinaryOperatorKind.NullableNullEqual:
                    case BinaryOperatorKind.NullableNullNotEqual:
                        return RewriteNullableNullEquality(syntax, operatorKind, loweredLeft, loweredRight, type);

                    case BinaryOperatorKind.ObjectAndStringConcatenation:
                    case BinaryOperatorKind.StringAndObjectConcatenation:
                    case BinaryOperatorKind.StringConcatenation:
                        return RewriteStringConcatenation(syntax, operatorKind, loweredLeft, loweredRight, type);

                    case BinaryOperatorKind.StringEqual:
                        return RewriteStringEquality(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_String__op_Equality);

                    case BinaryOperatorKind.StringNotEqual:
                        return RewriteStringEquality(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_String__op_Inequality);

                    case BinaryOperatorKind.DelegateCombination:
                        return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Combine);

                    case BinaryOperatorKind.DelegateRemoval:
                        return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__Remove);

                    case BinaryOperatorKind.DelegateEqual:
                        return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Equality);

                    case BinaryOperatorKind.DelegateNotEqual:
                        return RewriteDelegateOperation(syntax, operatorKind, loweredLeft, loweredRight, type, SpecialMember.System_Delegate__op_Inequality);

                    case BinaryOperatorKind.LogicalBoolAnd:
                        if (loweredRight.ConstantValue == ConstantValue.True) return loweredLeft;
                        if (loweredLeft.ConstantValue == ConstantValue.True) return loweredRight;
                        if (loweredLeft.ConstantValue == ConstantValue.False) return loweredLeft;

                        if (loweredRight.Kind == BoundKind.Local || loweredRight.Kind == BoundKind.Parameter)
                        {
                            operatorKind &= ~BinaryOperatorKind.Logical;
                        }

                        goto default;

                    case BinaryOperatorKind.LogicalBoolOr:
                        if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft;
                        if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight;
                        if (loweredLeft.ConstantValue == ConstantValue.True) return loweredLeft;

                        if (loweredRight.Kind == BoundKind.Local || loweredRight.Kind == BoundKind.Parameter)
                        {
                            operatorKind &= ~BinaryOperatorKind.Logical;
                        }

                        goto default;

                    case BinaryOperatorKind.BoolAnd:
                        if (loweredRight.ConstantValue == ConstantValue.True) return loweredLeft;
                        if (loweredLeft.ConstantValue == ConstantValue.True) return loweredRight;
                        goto default;

                    case BinaryOperatorKind.BoolOr:
                        if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft;
                        if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight;
                        goto default;

                    case BinaryOperatorKind.BoolEqual:
                        if (loweredLeft.ConstantValue == ConstantValue.True) return loweredRight;
                        if (loweredRight.ConstantValue == ConstantValue.True) return loweredLeft;

                        if (loweredLeft.ConstantValue == ConstantValue.False)
                            return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredRight, loweredRight.Type);

                        if (loweredRight.ConstantValue == ConstantValue.False)
                            return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredLeft, loweredLeft.Type);

                        goto default;

                    case BinaryOperatorKind.BoolNotEqual:
                        if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight;
                        if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft;

                        if (loweredLeft.ConstantValue == ConstantValue.True)
                            return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredRight, loweredRight.Type);

                        if (loweredRight.ConstantValue == ConstantValue.True)
                            return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredLeft, loweredLeft.Type);

                        goto default;

                    case BinaryOperatorKind.BoolXor:
                        if (loweredLeft.ConstantValue == ConstantValue.False) return loweredRight;
                        if (loweredRight.ConstantValue == ConstantValue.False) return loweredLeft;

                        if (loweredLeft.ConstantValue == ConstantValue.True)
                            return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredRight, loweredRight.Type);

                        if (loweredRight.ConstantValue == ConstantValue.True)
                            return MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, loweredLeft, loweredLeft.Type);

                        goto default;

                    case BinaryOperatorKind.IntLeftShift:
                    case BinaryOperatorKind.UIntLeftShift:
                    case BinaryOperatorKind.IntRightShift:
                    case BinaryOperatorKind.UIntRightShift:
                        return RewriteBuiltInShiftOperation(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, 0x1F);

                    case BinaryOperatorKind.LongLeftShift:
                    case BinaryOperatorKind.ULongLeftShift:
                    case BinaryOperatorKind.LongRightShift:
                    case BinaryOperatorKind.ULongRightShift:
                        return RewriteBuiltInShiftOperation(oldNode, syntax, operatorKind, loweredLeft, loweredRight, type, 0x3F);

                    case BinaryOperatorKind.DecimalAddition:
                    case BinaryOperatorKind.DecimalSubtraction:
                    case BinaryOperatorKind.DecimalMultiplication:
                    case BinaryOperatorKind.DecimalDivision:
                    case BinaryOperatorKind.DecimalRemainder:
                    case BinaryOperatorKind.DecimalEqual:
                    case BinaryOperatorKind.DecimalNotEqual:
                    case BinaryOperatorKind.DecimalLessThan:
                    case BinaryOperatorKind.DecimalLessThanOrEqual:
                    case BinaryOperatorKind.DecimalGreaterThan:
                    case BinaryOperatorKind.DecimalGreaterThanOrEqual:
                        return RewriteDecimalBinaryOperation(syntax, loweredLeft, loweredRight, operatorKind);

                    case BinaryOperatorKind.PointerAndIntAddition:
                    case BinaryOperatorKind.PointerAndUIntAddition:
                    case BinaryOperatorKind.PointerAndLongAddition:
                    case BinaryOperatorKind.PointerAndULongAddition:
                    case BinaryOperatorKind.PointerAndIntSubtraction:
                    case BinaryOperatorKind.PointerAndUIntSubtraction:
                    case BinaryOperatorKind.PointerAndLongSubtraction:
                    case BinaryOperatorKind.PointerAndULongSubtraction:
                        if (loweredRight.IsDefaultValue())
                        {
                            return loweredLeft;
                        }
                        return RewritePointerNumericOperator(syntax, operatorKind, loweredLeft, loweredRight, type, isPointerElementAccess, isLeftPointer: true);

                    case BinaryOperatorKind.IntAndPointerAddition:
                    case BinaryOperatorKind.UIntAndPointerAddition:
                    case BinaryOperatorKind.LongAndPointerAddition:
                    case BinaryOperatorKind.ULongAndPointerAddition:
                        if (loweredLeft.IsDefaultValue())
                        {
                            return loweredRight;
                        }
                        return RewritePointerNumericOperator(syntax, operatorKind, loweredLeft, loweredRight, type, isPointerElementAccess, isLeftPointer: false);

                    case BinaryOperatorKind.PointerSubtraction:
                        return RewritePointerSubtraction(operatorKind, loweredLeft, loweredRight, type);

                    case BinaryOperatorKind.IntAddition:
                    case BinaryOperatorKind.UIntAddition:
                    case BinaryOperatorKind.LongAddition:
                    case BinaryOperatorKind.ULongAddition:
                        if (loweredLeft.IsDefaultValue())
                        {
                            return loweredRight;
                        }
                        if (loweredRight.IsDefaultValue())
                        {
                            return loweredLeft;
                        }
                        goto default;

                    case BinaryOperatorKind.IntSubtraction:
                    case BinaryOperatorKind.LongSubtraction:
                    case BinaryOperatorKind.UIntSubtraction:
                    case BinaryOperatorKind.ULongSubtraction:
                        if (loweredRight.IsDefaultValue())
                        {
                            return loweredLeft;
                        }
                        goto default;

                    case BinaryOperatorKind.IntMultiplication:
                    case BinaryOperatorKind.LongMultiplication:
                    case BinaryOperatorKind.UIntMultiplication:
                    case BinaryOperatorKind.ULongMultiplication:
                        if (loweredLeft.IsDefaultValue())
                        {
                            return loweredLeft;
                        }
                        if (loweredRight.IsDefaultValue())
                        {
                            return loweredRight;
                        }
                        if (loweredLeft.ConstantValue?.UInt64Value == 1)
                        {
                            return loweredRight;
                        }
                        if (loweredRight.ConstantValue?.UInt64Value == 1)
                        {
                            return loweredLeft;
                        }
                        goto default;

                    case BinaryOperatorKind.IntGreaterThan:
                    case BinaryOperatorKind.IntLessThanOrEqual:
                        if (loweredLeft.Kind == BoundKind.ArrayLength && loweredRight.IsDefaultValue())
                        {
                            //array length is never negative
                            var newOp = operatorKind == BinaryOperatorKind.IntGreaterThan ?
                                                        BinaryOperatorKind.NotEqual :
                                                        BinaryOperatorKind.Equal;

                            operatorKind &= ~BinaryOperatorKind.OpMask;
                            operatorKind |= newOp;
                            loweredLeft = UnconvertArrayLength((BoundArrayLength)loweredLeft);
                        }
                        goto default;

                    case BinaryOperatorKind.IntLessThan:
                    case BinaryOperatorKind.IntGreaterThanOrEqual:
                        if (loweredRight.Kind == BoundKind.ArrayLength && loweredLeft.IsDefaultValue())
                        {
                            //array length is never negative
                            var newOp = operatorKind == BinaryOperatorKind.IntLessThan ?
                                                        BinaryOperatorKind.NotEqual :
                                                        BinaryOperatorKind.Equal;

                            operatorKind &= ~BinaryOperatorKind.OpMask;
                            operatorKind |= newOp;
                            loweredRight = UnconvertArrayLength((BoundArrayLength)loweredRight);
                        }
                        goto default;

                    case BinaryOperatorKind.IntEqual:
                    case BinaryOperatorKind.IntNotEqual:
                        if (loweredLeft.Kind == BoundKind.ArrayLength && loweredRight.IsDefaultValue())
                        {
                            loweredLeft = UnconvertArrayLength((BoundArrayLength)loweredLeft);
                        }
                        else if (loweredRight.Kind == BoundKind.ArrayLength && loweredLeft.IsDefaultValue())
                        {
                            loweredRight = UnconvertArrayLength((BoundArrayLength)loweredRight);
                        }

                        goto default;

                    default:
                        break;
                }
            }

            return (oldNode != null) ?
                oldNode.Update(operatorKind, loweredLeft, loweredRight, oldNode.ConstantValueOpt, oldNode.MethodOpt, oldNode.ResultKind, type) :
                new BoundBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, null, null, LookupResultKind.Viable, type);
        }
Ejemplo n.º 10
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.System_Nullable_T).Construct(binaryOperandType);
                MethodSymbol ctor = UnsafeGetNullableMethod(node.Syntax, binaryOperandType, SpecialMember.System_Nullable_T__ctor);
                boundOne = new BoundObjectCreationExpression(node.Syntax, ctor, 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.PointerAndInt);
                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;

            if (unaryOperandType.SpecialType == SpecialType.System_Decimal)
            {
                binOp = MakeDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand);
            }
            else if (unaryOperandType.IsNullableType() && unaryOperandType.GetNullableUnderlyingType().SpecialType == SpecialType.System_Decimal)
            {
                binOp = MakeLiftedDecimalIncDecOperator(node.Syntax, binaryOperatorKind, binaryOperand);
            }
            else
            {
                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);
        }
        private BoundExpression VisitBinaryOperator(BinaryOperatorKind opKind, MethodSymbol methodOpt, TypeSymbol type, BoundExpression left, BoundExpression right)
        {
            bool isChecked, isLifted, requiresLifted;
            string opName = GetBinaryOperatorName(opKind, out isChecked, out isLifted, out requiresLifted);

            // Fix up the null value for a nullable comparison vs null
            if ((object)left.Type == null && left.IsLiteralNull())
            {
                left = _bound.Default(right.Type);
            }
            if ((object)right.Type == null && right.IsLiteralNull())
            {
                right = _bound.Default(left.Type);
            }

            var loweredLeft = Visit(left);
            var loweredRight = Visit(right);

            // Enums are handled as per their promoted underlying type
            switch (opKind.OperandTypes())
            {
                case BinaryOperatorKind.Enum:
                case BinaryOperatorKind.EnumAndUnderlying:
                case BinaryOperatorKind.UnderlyingAndEnum:
                    {
                        var enumOperand = (opKind.OperandTypes() == BinaryOperatorKind.UnderlyingAndEnum) ? right : left;
                        var promotedType = PromotedType(enumOperand.Type.StrippedType().GetEnumUnderlyingType());
                        if (opKind.IsLifted())
                        {
                            promotedType = _nullableType.Construct(promotedType);
                        }

                        loweredLeft = PromoteEnumOperand(left, loweredLeft, promotedType, isChecked);
                        loweredRight = PromoteEnumOperand(right, loweredRight, promotedType, isChecked);

                        var result = MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight);
                        return Demote(result, type, isChecked);
                    }
                default:
                    return MakeBinary(methodOpt, type, isLifted, requiresLifted, opName, loweredLeft, loweredRight);
            }
        }
        private string GetBinaryOperatorName(BinaryOperatorKind opKind, out bool isChecked, out bool isLifted, out bool requiresLifted)
        {
            isChecked = opKind.IsChecked();
            isLifted = opKind.IsLifted();
            requiresLifted = opKind.IsComparison();

            switch (opKind.Operator())
            {
                case BinaryOperatorKind.Addition: return isChecked ? "AddChecked" : "Add";
                case BinaryOperatorKind.Multiplication: return isChecked ? "MultiplyChecked" : "Multiply";
                case BinaryOperatorKind.Subtraction: return isChecked ? "SubtractChecked" : "Subtract";
                case BinaryOperatorKind.Division: return "Divide";
                case BinaryOperatorKind.Remainder: return "Modulo";
                case BinaryOperatorKind.And: return opKind.IsLogical() ? "AndAlso" : "And";
                case BinaryOperatorKind.Xor: return "ExclusiveOr";
                case BinaryOperatorKind.Or: return opKind.IsLogical() ? "OrElse" : "Or";
                case BinaryOperatorKind.LeftShift: return "LeftShift";
                case BinaryOperatorKind.RightShift: return "RightShift";
                case BinaryOperatorKind.Equal: return "Equal";
                case BinaryOperatorKind.NotEqual: return "NotEqual";
                case BinaryOperatorKind.LessThan: return "LessThan";
                case BinaryOperatorKind.LessThanOrEqual: return "LessThanOrEqual";
                case BinaryOperatorKind.GreaterThan: return "GreaterThan";
                case BinaryOperatorKind.GreaterThanOrEqual: return "GreaterThanOrEqual";
                default:
                    throw ExceptionUtilities.UnexpectedValue(opKind.Operator());
            }
        }
Ejemplo n.º 13
0
        /// <summary>
        /// If one of the (unconverted) operands has constant value null and the other has
        /// a null constant value other than null, then they are definitely not equal
        /// and we can give a constant value for either == or !=.  This is a spec violation
        /// that we retain from Dev10.
        /// </summary>
        /// <param name="kind">The operator kind.  Nothing will happen if it is not a lifted equality operator.</param>
        /// <param name="left">The left-hand operand of the operation (possibly wrapped in a conversion).</param>
        /// <param name="right">The right-hand operand of the operation (possibly wrapped in a conversion).</param>
        /// <returns>
        /// If the operator represents lifted equality, then constant value true if both arguments have constant
        /// value null, constant value false if exactly one argument has constant value null, and null otherwise.
        /// If the operator represents lifted inequality, then constant value false if both arguments have constant
        /// value null, constant value true if exactly one argument has constant value null, and null otherwise.
        /// </returns>
        /// <remarks>
        /// SPEC VIOLATION: according to the spec (section 7.19) constant expressions cannot
        /// include implicit nullable conversions or nullable subexpressions.  However, Dev10
        /// specifically folds over lifted == and != (see ExpressionBinder::TryFoldingNullableEquality).
        /// Dev 10 does do compile-time evaluation of simple lifted operators, but it does so
        /// in a rewriting pass (see NullableRewriter) - they are not treated as constant values.
        /// </remarks>
        private static ConstantValue TryFoldingNullableEquality(BinaryOperatorKind kind, BoundExpression left, BoundExpression right)
        {
            if (kind.IsLifted())
            {
                BinaryOperatorKind op = kind.Operator();
                if (op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual)
                {
                    if (left.Kind == BoundKind.Conversion && right.Kind == BoundKind.Conversion)
                    {
                        BoundConversion leftConv = (BoundConversion)left;
                        BoundConversion rightConv = (BoundConversion)right;
                        ConstantValue leftConstant = leftConv.Operand.ConstantValue;
                        ConstantValue rightConstant = rightConv.Operand.ConstantValue;

                        if (leftConstant != null && rightConstant != null)
                        {
                            bool leftIsNull = leftConstant.IsNull;
                            bool rightIsNull = rightConstant.IsNull;
                            if (leftIsNull || rightIsNull)
                            {
                                // IMPL CHANGE: Dev10 raises WRN_NubExprIsConstBool in some cases, but that really doesn't
                                // make sense (why warn that a constant has a constant value?).
                                return (leftIsNull == rightIsNull) == (op == BinaryOperatorKind.Equal) ? ConstantValue.True : ConstantValue.False;
                            }
                        }
                    }
                }
            }

            return null;
        }
Ejemplo n.º 14
0
        // Returns null if the operator can't be evaluated at compile time.
        private ConstantValue FoldBinaryOperator(
            CSharpSyntaxNode syntax,
            BinaryOperatorKind kind,
            BoundExpression left,
            BoundExpression right,
            SpecialType resultType,
            DiagnosticBag diagnostics,
            ref int compoundStringLength)
        {
            Debug.Assert(left != null);
            Debug.Assert(right != null);

            if (left.HasAnyErrors || right.HasAnyErrors)
            {
                return null;
            }

            // SPEC VIOLATION: see method definition for details
            ConstantValue nullableEqualityResult = TryFoldingNullableEquality(kind, left, right);
            if (nullableEqualityResult != null)
            {
                return nullableEqualityResult;
            }

            var valueLeft = left.ConstantValue;
            var valueRight = right.ConstantValue;
            if (valueLeft == null || valueRight == null)
            {
                return null;
            }

            if (valueLeft.IsBad || valueRight.IsBad)
            {
                return ConstantValue.Bad;
            }

            if (kind.IsEnum() && !kind.IsLifted())
            {
                return FoldEnumBinaryOperator(syntax, kind, left, right, diagnostics);
            }

            // Divisions by zero on integral types and decimal always fail even in an unchecked context.
            if (IsDivisionByZero(kind, valueRight))
            {
                Error(diagnostics, ErrorCode.ERR_IntDivByZero, syntax);
                return ConstantValue.Bad;
            }

            object newValue = null;

            // Certain binary operations never fail; bool & bool, for example. If we are in one of those
            // cases, simply fold the operation and return.
            //
            // Although remainder and division always overflow at runtime with arguments int.MinValue/long.MinValue and -1 
            // (regardless of checked context) the constant folding behavior is different. 
            // Remainder never overflows at compile time while division does.
            newValue = FoldNeverOverflowBinaryOperators(kind, valueLeft, valueRight);
            if (newValue != null)
            {
                return ConstantValue.Create(newValue, resultType);
            }

            ConstantValue concatResult = FoldStringConcatenation(kind, valueLeft, valueRight, ref compoundStringLength);
            if (concatResult != null)
            {
                if (concatResult.IsBad)
                {
                    Error(diagnostics, ErrorCode.ERR_ConstantStringTooLong, syntax);
                }

                return concatResult;
            }

            // Certain binary operations always fail if they overflow even when in an unchecked context;
            // decimal + decimal, for example. If we are in one of those cases, make the attempt. If it
            // succeeds, return the result. If not, give a compile-time error regardless of context.
            try
            {
                newValue = FoldDecimalBinaryOperators(kind, valueLeft, valueRight);
            }
            catch (OverflowException)
            {
                Error(diagnostics, ErrorCode.ERR_DecConstError, syntax);
                return ConstantValue.Bad;
            }

            if (newValue != null)
            {
                return ConstantValue.Create(newValue, resultType);
            }

            if (CheckOverflowAtCompileTime)
            {
                try
                {
                    newValue = FoldCheckedIntegralBinaryOperator(kind, valueLeft, valueRight);
                }
                catch (OverflowException)
                {
                    Error(diagnostics, ErrorCode.ERR_CheckedOverflow, syntax);
                    return ConstantValue.Bad;
                }
            }
            else
            {
                newValue = FoldUncheckedIntegralBinaryOperator(kind, valueLeft, valueRight);
            }

            if (newValue != null)
            {
                return ConstantValue.Create(newValue, resultType);
            }

            return null;
        }
Ejemplo n.º 15
0
        private ConstantValue FoldEnumBinaryOperator(
            CSharpSyntaxNode syntax,
            BinaryOperatorKind kind,
            BoundExpression left,
            BoundExpression right,
            DiagnosticBag diagnostics)
        {
            Debug.Assert(left != null);
            Debug.Assert(right != null);
            Debug.Assert(kind.IsEnum());
            Debug.Assert(!kind.IsLifted());

            // A built-in binary operation on constant enum operands is evaluated into an operation on 
            // constants of the underlying type U of the enum type E. Comparison operators are lowered as
            // simply computing U<U. All other operators are computed as (E)(U op U) or in the case of 
            // E-E, (U)(U-U).  

            TypeSymbol enumType = GetEnumType(kind, left, right);
            TypeSymbol underlyingType = enumType.GetEnumUnderlyingType();

            BoundExpression newLeftOperand = CreateConversion(left, underlyingType, diagnostics);
            BoundExpression newRightOperand = CreateConversion(right, underlyingType, diagnostics);

            // If the underlying type is byte, sbyte, short, ushort or nullables of those then we'll need
            // to convert it up to int or int? because there are no + - * & | ^ < > <= >= == != operators
            // on byte, sbyte, short or ushort. They all convert to int.

            SpecialType operandSpecialType = GetEnumPromotedType(underlyingType.SpecialType);
            TypeSymbol operandType = (operandSpecialType == underlyingType.SpecialType) ?
                underlyingType :
                GetSpecialType(operandSpecialType, diagnostics, syntax);

            newLeftOperand = CreateConversion(newLeftOperand, operandType, diagnostics);
            newRightOperand = CreateConversion(newRightOperand, operandType, diagnostics);

            BinaryOperatorKind newKind = kind.Operator().WithType(newLeftOperand.Type.SpecialType);

            SpecialType operatorType = SpecialType.None;

            switch (newKind.Operator())
            {
                case BinaryOperatorKind.Addition:
                case BinaryOperatorKind.Subtraction:
                case BinaryOperatorKind.And:
                case BinaryOperatorKind.Or:
                case BinaryOperatorKind.Xor:
                    operatorType = operandType.SpecialType;
                    break;

                case BinaryOperatorKind.LessThan:
                case BinaryOperatorKind.LessThanOrEqual:
                case BinaryOperatorKind.GreaterThan:
                case BinaryOperatorKind.GreaterThanOrEqual:
                case BinaryOperatorKind.Equal:
                case BinaryOperatorKind.NotEqual:
                    operatorType = SpecialType.System_Boolean;
                    break;

                default:
                    throw ExceptionUtilities.UnexpectedValue(newKind.Operator());
            }

            var constantValue = FoldBinaryOperator(syntax, newKind, newLeftOperand, newRightOperand, operatorType, diagnostics);

            if (operatorType != SpecialType.System_Boolean && constantValue != null && !constantValue.IsBad)
            {
                TypeSymbol resultType = kind == BinaryOperatorKind.EnumSubtraction ? underlyingType : enumType;

                // We might need to convert back to the underlying type.
                return FoldConstantNumericConversion(syntax, constantValue, resultType, diagnostics);
            }

            return constantValue;
        }
        private BoundExpression LowerUserDefinedBinaryOperator(
            CSharpSyntaxNode syntax,
            BinaryOperatorKind operatorKind,
            BoundExpression loweredLeft,
            BoundExpression loweredRight,
            TypeSymbol type,
            MethodSymbol method)
        {
            if (operatorKind.IsLogical())
            {
                // Yes, we could have a lifted, logical, user-defined operator:
                //
                // struct C { 
                //   public static C operator &(C x, C y) {...}
                //   public static bool operator true(C? c) { ... }
                //   public static bool operator false(C? c) { ... }
                // }
                //
                // If we have C? q, r and we say q && r then this gets bound as 
                // C? tempQ = q ;
                // C.false(tempQ) ? 
                //     tempQ : 
                //     ( 
                //         C? tempR = r ; 
                //         tempQ.HasValue & tempR.HasValue ? 
                //           new C?(C.&(tempQ.GetValueOrDefault(), tempR.GetValueOrDefault())) :
                //           default C?()
                //     )
                //
                // Note that the native compiler does not allow q && r. However, the native compiler
                // *does* allow q && r if C is defined as:
                //
                // struct C { 
                //   public static C? operator &(C? x, C? y) {...}
                //   public static bool operator true(C? c) { ... }
                //   public static bool operator false(C? c) { ... }
                // }
                //
                // It seems unusual and wrong that an & operator should be allowed to become
                // a && operator if there is a "manually lifted" operator in source, but not
                // if there is a "synthesized" lifted operator.  Roslyn fixes this bug.
                //
                // Anyway, in this case we must lower this to its non-logical form, and then
                // lower the interior of that to its non-lifted form.

                return LowerUserDefinedLogicalOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method);
            }
            else if (operatorKind.IsLifted())
            {
                if (operatorKind.IsComparison())
                {
                    return LowerLiftedUserDefinedComparisonOperator(syntax, operatorKind, loweredLeft, loweredRight, method);
                }
                else
                {
                    return LowerLiftedBinaryArithmeticOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method);
                }
            }

            // Otherwise, nothing special here.
            Debug.Assert((object)method != null);
            Debug.Assert(method.ReturnType == type);
            return BoundCall.Synthesized(syntax, null, method, loweredLeft, loweredRight);
        }
Ejemplo n.º 17
0
        internal BinaryOperatorSignature GetSignature(BinaryOperatorKind kind)
        {
            var left = LeftType(kind);
            switch (kind.Operator())
            {
                case BinaryOperatorKind.Multiplication:
                case BinaryOperatorKind.Division:
                case BinaryOperatorKind.Subtraction:
                case BinaryOperatorKind.Remainder:
                case BinaryOperatorKind.And:
                case BinaryOperatorKind.Or:
                case BinaryOperatorKind.Xor:
                    return new BinaryOperatorSignature(kind, left, left, left);
                case BinaryOperatorKind.Addition:
                    return new BinaryOperatorSignature(kind, LeftType(kind), RightType(kind), ReturnType(kind));
                case BinaryOperatorKind.LeftShift:

                case BinaryOperatorKind.RightShift:
                    TypeSymbol returnType = Compilation.GetSpecialType(SpecialType.System_Int32);

                    if (kind.IsLifted())
                    {
                        returnType = Compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(returnType);
                    }

                    return new BinaryOperatorSignature(kind, left, returnType, left);

                case BinaryOperatorKind.Equal:
                case BinaryOperatorKind.NotEqual:
                case BinaryOperatorKind.GreaterThan:
                case BinaryOperatorKind.LessThan:
                case BinaryOperatorKind.GreaterThanOrEqual:
                case BinaryOperatorKind.LessThanOrEqual:
                    return new BinaryOperatorSignature(kind, left, left, Compilation.GetSpecialType(SpecialType.System_Boolean));
            }
            return new BinaryOperatorSignature(kind, LeftType(kind), RightType(kind), ReturnType(kind));
        }
Ejemplo n.º 18
0
 private TypeSymbol ReturnType(BinaryOperatorKind kind)
 {
     if (kind.IsLifted())
     {
         return LiftedType(kind);
     }
     else
     {
         switch (kind.OperandTypes())
         {
             case BinaryOperatorKind.Int: return Compilation.GetSpecialType(SpecialType.System_Int32);
             case BinaryOperatorKind.UInt: return Compilation.GetSpecialType(SpecialType.System_UInt32);
             case BinaryOperatorKind.Long: return Compilation.GetSpecialType(SpecialType.System_Int64);
             case BinaryOperatorKind.ULong: return Compilation.GetSpecialType(SpecialType.System_UInt64);
             case BinaryOperatorKind.Float: return Compilation.GetSpecialType(SpecialType.System_Single);
             case BinaryOperatorKind.Double: return Compilation.GetSpecialType(SpecialType.System_Double);
             case BinaryOperatorKind.Decimal: return Compilation.GetSpecialType(SpecialType.System_Decimal);
             case BinaryOperatorKind.Bool: return Compilation.GetSpecialType(SpecialType.System_Boolean);
             case BinaryOperatorKind.Object: return Compilation.GetSpecialType(SpecialType.System_Object);
             case BinaryOperatorKind.ObjectAndString:
             case BinaryOperatorKind.StringAndObject:
             case BinaryOperatorKind.String:
                 return Compilation.GetSpecialType(SpecialType.System_String);
         }
     }
     Debug.Assert(false, "Bad operator kind in return type");
     return null;
 }
Ejemplo n.º 19
0
        private TypeSymbol LiftedType(BinaryOperatorKind kind)
        {
            Debug.Assert(kind.IsLifted());

            var nullable = Compilation.GetSpecialType(SpecialType.System_Nullable_T);

            switch (kind.OperandTypes())
            {
                case BinaryOperatorKind.Int: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Int32));
                case BinaryOperatorKind.UInt: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_UInt32));
                case BinaryOperatorKind.Long: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Int64));
                case BinaryOperatorKind.ULong: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_UInt64));
                case BinaryOperatorKind.Float: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Single));
                case BinaryOperatorKind.Double: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Double));
                case BinaryOperatorKind.Decimal: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Decimal));
                case BinaryOperatorKind.Bool: return nullable.Construct(Compilation.GetSpecialType(SpecialType.System_Boolean));
            }
            Debug.Assert(false, "Bad operator kind in lifted type");
            return null;
        }