예제 #1
0
        Expression UnaryNumericPromotion(ResolveContext rc, UnaryOperatorType op, ref IType type, bool isNullable, Expression expression)
        {
            // V# 4.0 spec: §7.3.6.1
            TypeCode code = ReflectionHelper.GetTypeCode(type);

            if (isNullable && type.Kind == TypeKind.Null)
            {
                code = TypeCode.SByte; // cause promotion of null to int32
            }
            switch (op)
            {
            case UnaryOperatorType.UnaryNegation:
                if (code == TypeCode.UInt32)
                {
                    type = rc.compilation.FindType(KnownTypeCode.Int64);
                    return(rc.Convert(expression, MakeNullable(rc, type, isNullable),
                                      isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion));
                }
                goto case UnaryOperatorType.UnaryPlus;

            case UnaryOperatorType.UnaryPlus:
            case UnaryOperatorType.OnesComplement:
                if (code >= TypeCode.Char && code <= TypeCode.UInt16)
                {
                    type = rc.compilation.FindType(KnownTypeCode.Int32);
                    return(rc.Convert(expression, MakeNullable(rc, type, isNullable),
                                      isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion));
                }
                break;
            }
            return(expression);
        }
예제 #2
0
        //
        // Converts `source' to an int, uint, long or ulong.
        //
        protected Expression ConvertExpressionToArrayIndex(ResolveContext ec, Expression source, bool pointerArray = false)
        {
            Expression converted;

            using (ec.Set(ResolveContext.Options.CheckedScope))
            {
                converted = ec.Convert(source, KnownTypeReference.Int32.Resolve(ec));
                if (converted == null)
                {
                    converted = ec.Convert(source, KnownTypeReference.UInt32.Resolve(ec));
                }
                if (converted == null)
                {
                    converted = ec.Convert(source, KnownTypeReference.Int64.Resolve(ec));
                }
                if (converted == null)
                {
                    converted = ec.Convert(source, KnownTypeReference.UInt64.Resolve(ec));
                }

                if (converted == null)
                {
                    ec.Report.Error(0, loc, "Cannot convert type `{0}' to `{1}'",
                                    source.Type.ToString(), KnownTypeReference.Int32.ToString());
                    return(null);
                }
            }

            if (pointerArray)
            {
                return(converted);
            }

            //
            // Only positive constants are allowed at compile time
            //
            Constant c = converted as Constant;

            if (c != null && c.IsNegative)
            {
                ec.Report.Error(0, source.Location, "Cannot create an array with a negative size");
            }

            // No conversion needed to array index
            if (converted.Type.IsKnownType(KnownTypeCode.Int32))
            {
                return(converted);
            }

            return(new CastExpression(KnownTypeReference.Int32.Resolve(ec), converted).DoResolve(ec));
        }
예제 #3
0
        Expression CastTo(ResolveContext rc, TypeCode targetType, bool isNullable, Expression expression, bool allowNullableConstants)
        {
            IType elementType  = rc.compilation.FindType(targetType);
            IType nullableType = MakeNullable(rc, elementType, isNullable);

            if (nullableType.Equals(expression.Type))
            {
                return(expression);
            }
            if (allowNullableConstants && expression.IsCompileTimeConstant)
            {
                if (expression.ConstantValue == null)
                {
                    return(Constant.CreateConstantFromValue(rc, nullableType, null, loc));
                }
                Expression rr = new CastExpression(elementType, expression).DoResolve(rc);
                if (rr.IsError)
                {
                    return(rr);
                }
                Debug.Assert(rr.IsCompileTimeConstant);
                return(Constant.CreateConstantFromValue(rc, nullableType, rr.ConstantValue, loc));
            }
            else
            {
                return(rc.Convert(expression, nullableType,
                                  isNullable ? Conversion.ImplicitNullableConversion : Conversion.ImplicitNumericConversion));
            }
        }
 //
 // Converts static initializer only
 //
 void UnifyInitializerElement(ResolveContext ec)
 {
     for (int i = 0; i < array_data.Count; ++i)
     {
         Expression e = array_data[i];
         if (e != null)
         {
             array_data[i] = ec.Convert(e, array_element_type);
         }
     }
 }
예제 #5
0
        /// <summary>
        /// Resolves an object creation.
        /// </summary>
        /// <param name="type">Type of the object to create.</param>
        /// <param name="arguments">
        /// Arguments passed to the constructor.
        /// The resolver may mutate this array to wrap elements in <see cref="CastExpression"/>s!
        /// </param>
        /// <param name="argumentNames">
        /// The argument names. Pass the null string for positional arguments.
        /// </param>
        /// <param name="allowProtectedAccess">
        /// Whether to allow calling protected constructors.
        /// This should be false except when resolving constructor initializers.
        /// </param>
        /// <param name="initializerStatements">
        /// Statements for Objects/Collections initializer.
        /// <see cref="InvocationExpression.InitializerStatements"/>
        /// </param>
        /// <returns>InvocationResolveResult or ErrorResolveResult</returns>
        public static Expression ResolveObjectCreation(ResolveContext rc, Location l, IType type, Expression[] arguments, string[] argumentNames = null, bool allowProtectedAccess = false, IList <Expression> initializerStatements = null)
        {
            if (type.Kind == TypeKind.Delegate)
            {
                if (arguments == null || arguments.Length != 1)
                {
                    rc.Report.Error(0, l, "Method name expected");
                    return(null);
                }
                Expression input  = arguments[0];
                IMethod    invoke = input.Type.GetDelegateInvokeMethod();
                if (invoke != null)
                {
                    input = new MethodGroupExpression(
                        input, invoke.Name,
                        methods: new[] { new MethodListWithDeclaringType(input.Type)
                                         {
                                             invoke
                                         } },
                        typeArguments: EmptyList <IType> .Instance
                        );
                }
                return(rc.Convert(input, type));
            }
            OverloadResolution or            = rc.CreateOverloadResolution(arguments, argumentNames);
            MemberLookup       lookup        = rc.CreateMemberLookup();
            List <IMethod>     allApplicable = null;

            foreach (IMethod ctor in type.GetConstructors())
            {
                if (lookup.IsAccessible(ctor, allowProtectedAccess))
                {
                    or.AddCandidate(ctor);
                }
                else
                {
                    or.AddCandidate(ctor, OverloadResolutionErrors.Inaccessible);
                }
            }
            if (or.BestCandidate != null)
            {
                return(or.CreateInvocation(null, initializerStatements));
            }
            else
            {
                rc.Report.Error(0, l,
                                "The type `{0}' does not contain a constructor that takes `{1}' arguments",
                                type.ToString(), arguments != null ? arguments.Length.ToString() : "0");
                return(ErrorResult);
            }
        }
예제 #6
0
 void AdjustArrayAccessArguments(ResolveContext rc, Expression[] arguments)
 {
     for (int i = 0; i < arguments.Length; i++)
     {
         if (!(rc.TryConvert(ref arguments[i], rc.compilation.FindType(KnownTypeCode.Int32)) ||
               rc.TryConvert(ref arguments[i], rc.compilation.FindType(KnownTypeCode.UInt32)) ||
               rc.TryConvert(ref arguments[i], rc.compilation.FindType(KnownTypeCode.Int64)) ||
               rc.TryConvert(ref arguments[i], rc.compilation.FindType(KnownTypeCode.UInt64))))
         {
             // conversion failed
             arguments[i] = rc.Convert(arguments[i], rc.compilation.FindType(KnownTypeCode.Int32), Conversion.None);
         }
     }
 }
예제 #7
0
        public Expression ResolveUnaryOperator(ResolveContext rc, UnaryOperatorType op, Expression expression)
        {
            // V# 4.0 spec: §7.3.3 Unary operator overload resolution
            string overloadableOperatorName = GetOverloadableOperatorName(op);

            if (overloadableOperatorName == null)
            {
                switch (op)
                {
                case UnaryOperatorType.Dereference:
                    PointerTypeSpec p = expression.Type as PointerTypeSpec;
                    if (p != null)
                    {
                        return(SetOperationInformations(rc, p.ElementType, op, expression));
                    }
                    else
                    {
                        return(ErrorResult);
                    }

                case UnaryOperatorType.AddressOf:
                    return(SetOperationInformations(rc, new PointerTypeSpec(expression.Type), op, expression));


                default:
                    return(ErrorExpression.UnknownError);
                }
            }
            // If the type is nullable, get the underlying type:
            IType type       = NullableType.GetUnderlyingType(expression.Type);
            bool  isNullable = NullableType.IsNullable(expression.Type);

            // the operator is overloadable:
            OverloadResolution userDefinedOperatorOR = rc.CreateOverloadResolution(new[] { expression });

            foreach (var candidate in rc.GetUserDefinedOperatorCandidates(type, overloadableOperatorName))
            {
                userDefinedOperatorOR.AddCandidate(candidate);
            }
            if (userDefinedOperatorOR.FoundApplicableCandidate)
            {
                return(SetUserDefinedOperationInformations(rc, userDefinedOperatorOR));
            }

            expression = UnaryNumericPromotion(rc, op, ref type, isNullable, expression);
            VSharpOperators.OperatorMethod[] methodGroup;
            VSharpOperators operators = VSharpOperators.Get(rc.compilation);

            switch (op)
            {
            case UnaryOperatorType.PreIncrement:
            case UnaryOperatorType.Decrement:
            case UnaryOperatorType.PostIncrement:
            case UnaryOperatorType.PostDecrement:
                // V# 4.0 spec: §7.6.9 Postfix increment and decrement operators
                // V# 4.0 spec: §7.7.5 Prefix increment and decrement operators
                TypeCode code = ReflectionHelper.GetTypeCode(type);
                if ((code >= TypeCode.Char && code <= TypeCode.Decimal) || type.Kind == TypeKind.Enum || type.Kind == TypeKind.Pointer)
                {
                    return(SetOperationInformations(rc, expression.Type, op, expression, isNullable));
                }
                else
                {
                    return(new ErrorExpression(expression.Type));
                }

            case UnaryOperatorType.UnaryPlus:
                methodGroup = operators.UnaryPlusOperators;
                break;

            case UnaryOperatorType.UnaryNegation:
                methodGroup = rc.checkForOverflow ? operators.CheckedUnaryMinusOperators : operators.UncheckedUnaryMinusOperators;
                break;

            case UnaryOperatorType.LogicalNot:
                methodGroup = operators.LogicalNegationOperators;
                break;

            case UnaryOperatorType.OnesComplement:
                if (type.Kind == TypeKind.Enum)
                {
                    if (expression.IsCompileTimeConstant && !isNullable && expression.ConstantValue != null)
                    {
                        // evaluate as (E)(~(U)x);
                        var            U            = rc.compilation.FindType(expression.ConstantValue.GetType());
                        var            unpackedEnum = Constant.CreateConstantFromValue(rc, U, expression.ConstantValue, loc);
                        var            rr           = ResolveUnaryOperator(rc, op, unpackedEnum);
                        ResolveContext ovfrc        = rc.WithCheckForOverflow(false);
                        rr = new CastExpression(type, rr).DoResolve(ovfrc);
                        if (rr.IsCompileTimeConstant)
                        {
                            return(rr);
                        }
                    }
                    return(SetOperationInformations(rc, expression.Type, op, expression, isNullable));
                }
                else
                {
                    methodGroup = operators.BitwiseComplementOperators;
                    break;
                }

            default:
                throw new InvalidOperationException();
            }
            OverloadResolution builtinOperatorOR = rc.CreateOverloadResolution(new[] { expression });

            foreach (var candidate in methodGroup)
            {
                builtinOperatorOR.AddCandidate(candidate);
            }
            VSharpOperators.UnaryOperatorMethod m = (VSharpOperators.UnaryOperatorMethod)builtinOperatorOR.BestCandidate;
            IType resultType = m.ReturnType;

            if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None)
            {
                if (userDefinedOperatorOR.BestCandidate != null)
                {
                    // If there are any user-defined operators, prefer those over the built-in operators.
                    // It'll be a more informative error.
                    return(SetUserDefinedOperationInformations(rc, userDefinedOperatorOR));
                }
                else if (builtinOperatorOR.BestCandidateAmbiguousWith != null)
                {
                    // If the best candidate is ambiguous, just use the input type instead
                    // of picking one of the ambiguous overloads.
                    return(new ErrorExpression(expression.Type));
                }
                else
                {
                    return(new ErrorExpression(resultType));
                }
            }
            else if (expression.IsCompileTimeConstant && m.CanEvaluateAtCompileTime)
            {
                object val;
                try
                {
                    val = m.Invoke(rc, expression.ConstantValue);
                }
                catch (ArithmeticException)
                {
                    return(new ErrorExpression(resultType));
                }
                return(Constant.CreateConstantFromValue(rc, resultType, val, loc));
            }
            else
            {
                expression = rc.Convert(expression, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]);
                return(SetOperationInformations(rc, resultType, op, expression,
                                                builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator));
            }
        }
예제 #8
0
        public Expression ResolveBinaryOperator(ResolveContext rc, BinaryOperatorType op, Expression lhs, Expression rhs)
        {
            // V# 4.0 spec: §7.3.4 Binary operator overload resolution
            string overloadableOperatorName = ResolveContext.GetOverloadableOperatorName(op);

            if (overloadableOperatorName == null)
            {
                // Handle logical and/or exactly as bitwise and/or:
                // - If the user overloads a bitwise operator, that implicitly creates the corresponding logical operator.
                // - If both inputs are compile-time constants, it doesn't matter that we don't short-circuit.
                // - If inputs aren't compile-time constants, we don't evaluate anything, so again it doesn't matter that we don't short-circuit
                if (op == BinaryOperatorType.LogicalAnd)
                {
                    overloadableOperatorName = ResolveContext.GetOverloadableOperatorName(BinaryOperatorType.BitwiseAnd);
                }
                else if (op == BinaryOperatorType.LogicalOr)
                {
                    overloadableOperatorName = ResolveContext.GetOverloadableOperatorName(BinaryOperatorType.BitwiseOr);
                }
                else if (op == BinaryOperatorType.NullCoalescing)
                {
                    // null coalescing operator is not overloadable and needs to be handled separately
                    return(ResolveNullCoalescingOperator(rc, lhs, rhs));
                }
                else
                {
                    return(ErrorExpression.UnknownError);
                }
            }

            // If the type is nullable, get the underlying type:
            bool  isNullable = NullableType.IsNullable(lhs.Type) || NullableType.IsNullable(rhs.Type);
            IType lhsType    = NullableType.GetUnderlyingType(lhs.Type);
            IType rhsType    = NullableType.GetUnderlyingType(rhs.Type);

            // the operator is overloadable:
            OverloadResolution             userDefinedOperatorOR  = rc.CreateOverloadResolution(new[] { lhs, rhs });
            HashSet <IParameterizedMember> userOperatorCandidates = new HashSet <IParameterizedMember>();

            userOperatorCandidates.UnionWith(rc.GetUserDefinedOperatorCandidates(lhsType, overloadableOperatorName));
            userOperatorCandidates.UnionWith(rc.GetUserDefinedOperatorCandidates(rhsType, overloadableOperatorName));
            foreach (var candidate in userOperatorCandidates)
            {
                userDefinedOperatorOR.AddCandidate(candidate);
            }
            if (userDefinedOperatorOR.FoundApplicableCandidate)
            {
                return(SetUserDefinedOperationInformations(rc, userDefinedOperatorOR));
            }

            if (lhsType.Kind == TypeKind.Null && rhsType.IsReferenceType == false ||
                lhsType.IsReferenceType == false && rhsType.Kind == TypeKind.Null)
            {
                isNullable = true;
            }
            if (op == BinaryOperatorType.LeftShift || op == BinaryOperatorType.RightShift)
            {
                // special case: the shift operators allow "var x = null << null", producing int?.
                if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
                {
                    isNullable = true;
                }
                // for shift operators, do unary promotion independently on both arguments
                lhs = UnaryNumericPromotion(rc, UnaryOperatorType.UnaryPlus, ref lhsType, isNullable, lhs);
                rhs = UnaryNumericPromotion(rc, UnaryOperatorType.UnaryPlus, ref rhsType, isNullable, rhs);
            }
            else
            {
                bool allowNullableConstants = op == BinaryOperatorType.Equality || op == BinaryOperatorType.Inequality;
                if (!BinaryNumericPromotion(rc, isNullable, ref lhs, ref rhs, allowNullableConstants))
                {
                    return(new ErrorExpression(lhs.Type));
                }
            }
            // re-read underlying types after numeric promotion
            lhsType = NullableType.GetUnderlyingType(lhs.Type);
            rhsType = NullableType.GetUnderlyingType(rhs.Type);

            IEnumerable <VSharpOperators.OperatorMethod> methodGroup;
            VSharpOperators operators = VSharpOperators.Get(rc.compilation);

            switch (op)
            {
            case BinaryOperatorType.Multiply:
                methodGroup = operators.MultiplicationOperators;
                break;

            case BinaryOperatorType.Division:
                methodGroup = operators.DivisionOperators;
                break;

            case BinaryOperatorType.Modulus:
                methodGroup = operators.RemainderOperators;
                break;

            case BinaryOperatorType.Addition:
                methodGroup = operators.AdditionOperators;
                {
                    if (lhsType.Kind == TypeKind.Enum)
                    {
                        // E operator +(E x, U y);
                        IType underlyingType = MakeNullable(rc, ResolveContext.GetEnumUnderlyingType(lhsType), isNullable);
                        if (rc.TryConvertEnum(ref rhs, underlyingType, ref isNullable, ref lhs))
                        {
                            return(HandleEnumOperator(rc, isNullable, lhsType, op, lhs, rhs));
                        }
                    }
                    if (rhsType.Kind == TypeKind.Enum)
                    {
                        // E operator +(U x, E y);
                        IType underlyingType = MakeNullable(rc, ResolveContext.GetEnumUnderlyingType(rhsType), isNullable);
                        if (rc.TryConvertEnum(ref lhs, underlyingType, ref isNullable, ref rhs))
                        {
                            return(HandleEnumOperator(rc, isNullable, rhsType, op, lhs, rhs));
                        }
                    }

                    if (lhsType.Kind == TypeKind.Delegate && rc.TryConvert(ref rhs, lhsType))
                    {
                        return(SetOperationInformations(rc, lhsType, lhs, op, rhs));
                    }
                    else if (rhsType.Kind == TypeKind.Delegate && rc.TryConvert(ref lhs, rhsType))
                    {
                        return(SetOperationInformations(rc, rhsType, lhs, op, rhs));
                    }

                    if (lhsType is PointerTypeSpec)
                    {
                        methodGroup = new[] {
                            PointerArithmeticOperator(rc, lhsType, lhsType, KnownTypeCode.Int32),
                            PointerArithmeticOperator(rc, lhsType, lhsType, KnownTypeCode.UInt32),
                            PointerArithmeticOperator(rc, lhsType, lhsType, KnownTypeCode.Int64),
                            PointerArithmeticOperator(rc, lhsType, lhsType, KnownTypeCode.UInt64)
                        };
                    }
                    else if (rhsType is PointerTypeSpec)
                    {
                        methodGroup = new[] {
                            PointerArithmeticOperator(rc, rhsType, KnownTypeCode.Int32, rhsType),
                            PointerArithmeticOperator(rc, rhsType, KnownTypeCode.UInt32, rhsType),
                            PointerArithmeticOperator(rc, rhsType, KnownTypeCode.Int64, rhsType),
                            PointerArithmeticOperator(rc, rhsType, KnownTypeCode.UInt64, rhsType)
                        };
                    }
                    if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
                    {
                        return(new ErrorExpression(SpecialTypeSpec.NullType));
                    }
                }
                break;

            case BinaryOperatorType.Subtraction:
                methodGroup = operators.SubtractionOperators;
                {
                    if (lhsType.Kind == TypeKind.Enum)
                    {
                        // U operator –(E x, E y);
                        if (rc.TryConvertEnum(ref rhs, lhs.Type, ref isNullable, ref lhs, allowConversionFromConstantZero: false))
                        {
                            return(HandleEnumSubtraction(rc, isNullable, lhsType, lhs, rhs));
                        }

                        // E operator –(E x, U y);
                        IType underlyingType = MakeNullable(rc, ResolveContext.GetEnumUnderlyingType(lhsType), isNullable);
                        if (rc.TryConvertEnum(ref rhs, underlyingType, ref isNullable, ref lhs))
                        {
                            return(HandleEnumOperator(rc, isNullable, lhsType, op, lhs, rhs));
                        }
                    }
                    if (rhsType.Kind == TypeKind.Enum)
                    {
                        // U operator –(E x, E y);
                        if (rc.TryConvertEnum(ref lhs, rhs.Type, ref isNullable, ref rhs, allowConversionFromConstantZero: false))
                        {
                            return(HandleEnumSubtraction(rc, isNullable, rhsType, lhs, rhs));
                        }

                        // E operator -(U x, E y);
                        IType underlyingType = MakeNullable(rc, ResolveContext.GetEnumUnderlyingType(rhsType), isNullable);
                        if (rc.TryConvertEnum(ref lhs, underlyingType, ref isNullable, ref rhs))
                        {
                            return(HandleEnumOperator(rc, isNullable, rhsType, op, lhs, rhs));
                        }
                    }

                    if (lhsType.Kind == TypeKind.Delegate && rc.TryConvert(ref rhs, lhsType))
                    {
                        return(SetOperationInformations(rc, lhsType, lhs, op, rhs));
                    }
                    else if (rhsType.Kind == TypeKind.Delegate && rc.TryConvert(ref lhs, rhsType))
                    {
                        return(SetOperationInformations(rc, rhsType, lhs, op, rhs));
                    }

                    if (lhsType is PointerTypeSpec)
                    {
                        if (rhsType is PointerTypeSpec)
                        {
                            IType int64 = rc.compilation.FindType(KnownTypeCode.Int64);
                            if (lhsType.Equals(rhsType))
                            {
                                return(SetOperationInformations(rc, int64, lhs, op, rhs));
                            }
                            else
                            {
                                return(new ErrorExpression(int64));
                            }
                        }
                        methodGroup = new[] {
                            PointerArithmeticOperator(rc, lhsType, lhsType, KnownTypeCode.Int32),
                            PointerArithmeticOperator(rc, lhsType, lhsType, KnownTypeCode.UInt32),
                            PointerArithmeticOperator(rc, lhsType, lhsType, KnownTypeCode.Int64),
                            PointerArithmeticOperator(rc, lhsType, lhsType, KnownTypeCode.UInt64)
                        };
                    }

                    if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
                    {
                        return(new ErrorExpression(SpecialTypeSpec.NullType));
                    }
                }
                break;

            case BinaryOperatorType.LeftShift:
                methodGroup = operators.ShiftLeftOperators;
                break;

            case BinaryOperatorType.RightShift:
                methodGroup = operators.ShiftRightOperators;
                break;

            case BinaryOperatorType.RotateRight:
                methodGroup = operators.RotateRightOperators;
                break;

            case BinaryOperatorType.RotateLeft:
                methodGroup = operators.RotateLeftOperators;
                break;


            case BinaryOperatorType.Equality:
            case BinaryOperatorType.Inequality:
            case BinaryOperatorType.LessThan:
            case BinaryOperatorType.GreaterThan:
            case BinaryOperatorType.LessThanOrEqual:
            case BinaryOperatorType.GreaterThanOrEqual:
            {
                if (lhsType.Kind == TypeKind.Enum && rc.TryConvert(ref rhs, lhs.Type))
                {
                    // bool operator op(E x, E y);
                    return(HandleEnumComparison(rc, op, lhsType, isNullable, lhs, rhs));
                }
                else if (rhsType.Kind == TypeKind.Enum && rc.TryConvert(ref lhs, rhs.Type))
                {
                    // bool operator op(E x, E y);
                    return(HandleEnumComparison(rc, op, rhsType, isNullable, lhs, rhs));
                }
                else if (lhsType is PointerTypeSpec && rhsType is PointerTypeSpec)
                {
                    return(SetOperationInformations(rc, rc.compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs));
                }
                if (op == BinaryOperatorType.Equality || op == BinaryOperatorType.Inequality)
                {
                    if (lhsType.IsReferenceType == true && rhsType.IsReferenceType == true)
                    {
                        // If it's a reference comparison
                        if (op == BinaryOperatorType.Equality)
                        {
                            methodGroup = operators.ReferenceEqualityOperators;
                        }
                        else
                        {
                            methodGroup = operators.ReferenceInequalityOperators;
                        }
                        break;
                    }
                    else if (lhsType.Kind == TypeKind.Null && IsNullableTypeOrNonValueType(rhs.Type) ||
                             IsNullableTypeOrNonValueType(lhs.Type) && rhsType.Kind == TypeKind.Null)
                    {
                        // compare type parameter or nullable type with the null literal
                        return(SetOperationInformations(rc, rc.compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs));
                    }
                }
                switch (op)
                {
                case BinaryOperatorType.Equality:
                    methodGroup = operators.ValueEqualityOperators;
                    break;

                case BinaryOperatorType.Inequality:
                    methodGroup = operators.ValueInequalityOperators;
                    break;

                case BinaryOperatorType.LessThan:
                    methodGroup = operators.LessThanOperators;
                    break;

                case BinaryOperatorType.GreaterThan:
                    methodGroup = operators.GreaterThanOperators;
                    break;

                case BinaryOperatorType.LessThanOrEqual:
                    methodGroup = operators.LessThanOrEqualOperators;
                    break;

                case BinaryOperatorType.GreaterThanOrEqual:
                    methodGroup = operators.GreaterThanOrEqualOperators;
                    break;

                default:
                    throw new InvalidOperationException();
                }
            }
            break;

            case BinaryOperatorType.BitwiseAnd:
            case BinaryOperatorType.BitwiseOr:
            case BinaryOperatorType.ExclusiveOr:
            {
                if (lhsType.Kind == TypeKind.Enum)
                {
                    // bool operator op(E x, E y);
                    if (rc.TryConvertEnum(ref rhs, lhs.Type, ref isNullable, ref lhs))
                    {
                        return(HandleEnumOperator(rc, isNullable, lhsType, op, lhs, rhs));
                    }
                }

                if (rhsType.Kind == TypeKind.Enum)
                {
                    // bool operator op(E x, E y);
                    if (rc.TryConvertEnum(ref lhs, rhs.Type, ref isNullable, ref rhs))
                    {
                        return(HandleEnumOperator(rc, isNullable, rhsType, op, lhs, rhs));
                    }
                }

                switch (op)
                {
                case BinaryOperatorType.BitwiseAnd:
                    methodGroup = operators.BitwiseAndOperators;
                    break;

                case BinaryOperatorType.BitwiseOr:
                    methodGroup = operators.BitwiseOrOperators;
                    break;

                case BinaryOperatorType.ExclusiveOr:
                    methodGroup = operators.BitwiseXorOperators;
                    break;

                default:
                    throw new InvalidOperationException();
                }
            }
            break;

            case BinaryOperatorType.LogicalAnd:
                methodGroup = operators.LogicalAndOperators;
                break;

            case BinaryOperatorType.LogicalOr:
                methodGroup = operators.LogicalOrOperators;
                break;

            default:
                throw new InvalidOperationException();
            }
            OverloadResolution builtinOperatorOR = rc.CreateOverloadResolution(new[] { lhs, rhs });

            foreach (var candidate in methodGroup)
            {
                builtinOperatorOR.AddCandidate(candidate);
            }
            VSharpOperators.BinaryOperatorMethod m = (VSharpOperators.BinaryOperatorMethod)builtinOperatorOR.BestCandidate;
            IType resultType = m.ReturnType;

            if (builtinOperatorOR.BestCandidateErrors != OverloadResolutionErrors.None)
            {
                // If there are any user-defined operators, prefer those over the built-in operators.
                // It'll be a more informative error.
                if (userDefinedOperatorOR.BestCandidate != null)
                {
                    return(SetUserDefinedOperationInformations(rc, userDefinedOperatorOR));
                }
                else
                {
                    return(new ErrorExpression(resultType));
                }
            }
            else if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && m.CanEvaluateAtCompileTime)
            {
                object val;
                try
                {
                    val = m.Invoke(rc, lhs.ConstantValue, rhs.ConstantValue);
                }
                catch (ArithmeticException)
                {
                    return(new ErrorExpression(resultType));
                }
                return(Constant.CreateConstantFromValue(rc, resultType, val, loc));
            }
            else
            {
                lhs = rc.Convert(lhs, m.Parameters[0].Type, builtinOperatorOR.ArgumentConversions[0]);
                rhs = rc.Convert(rhs, m.Parameters[1].Type, builtinOperatorOR.ArgumentConversions[1]);
                return(SetOperationInformations(rc, resultType, lhs, op, rhs,
                                                builtinOperatorOR.BestCandidate is OverloadResolution.ILiftedOperator));
            }
        }