Esempio n. 1
0
        /// <summary>
        /// Handle the case where an enum value is compared with another enum value
        /// bool operator op(E x, E y);
        /// </summary>
        Expression HandleEnumComparison(ResolveContext rc, BinaryOperatorType op, IType enumType, bool isNullable, Expression lhs, Expression rhs)
        {
            // evaluate as ((U)x op (U)y)
            IType elementType = ResolveContext.GetEnumUnderlyingType(enumType);

            if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable && elementType.Kind != TypeKind.Enum)
            {
                var rr = ResolveBinaryOperator(rc, op, new CastExpression(elementType, lhs).DoResolve(rc), new CastExpression(elementType, rhs).DoResolve(rc));
                if (rr.IsCompileTimeConstant)
                {
                    return(rr);
                }
            }
            IType resultType = rc.compilation.FindType(KnownTypeCode.Boolean);

            return(SetOperationInformations(rc, resultType, lhs, op, rhs, isNullable));
        }
Esempio n. 2
0
        /// <summary>
        /// Handle the case where an enum value is subtracted from another enum value
        /// U operator –(E x, E y);
        /// </summary>
        Expression HandleEnumSubtraction(ResolveContext rc, bool isNullable, IType enumType, Expression lhs, Expression rhs)
        {
            // evaluate as (U)((U)x – (U)y)
            IType elementType = ResolveContext.GetEnumUnderlyingType(enumType);

            if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable && elementType.Kind != TypeKind.Enum)
            {
                var rr = ResolveBinaryOperator(rc, BinaryOperatorType.Subtraction, new CastExpression(elementType, lhs).DoResolve(rc), new CastExpression(elementType, rhs).DoResolve(rc));
                rr = new CastExpression(elementType, rr).DoResolve(rc.WithCheckForOverflow(false));
                if (rr.IsCompileTimeConstant)
                {
                    return(rr);
                }
            }
            IType resultType = MakeNullable(rc, elementType, isNullable);

            return(SetOperationInformations(rc, resultType, lhs, BinaryOperatorType.Subtraction, rhs, isNullable));
        }
Esempio n. 3
0
        /// <summary>
        /// Handle the following enum operators:
        /// E operator +(E x, U y);
        /// E operator +(U x, E y);
        /// E operator –(E x, U y);
        /// E operator &amp;(E x, E y);
        /// E operator |(E x, E y);
        /// E operator ^(E x, E y);
        /// </summary>
        Expression HandleEnumOperator(ResolveContext rc, bool isNullable, IType enumType, BinaryOperatorType op, Expression lhs, Expression rhs)
        {
            // evaluate as (E)((U)x op (U)y)
            if (lhs.IsCompileTimeConstant && rhs.IsCompileTimeConstant && !isNullable)
            {
                IType elementType = ResolveContext.GetEnumUnderlyingType(enumType);
                if (elementType.Kind != TypeKind.Enum)
                {
                    var rr = ResolveBinaryOperator(rc, op, new CastExpression(elementType, lhs).DoResolve(rc), new CastExpression(elementType, rhs).DoResolve(rc));
                    rr = new CastExpression(enumType, rr).DoResolve(rc.WithCheckForOverflow(false));
                    if (rr.IsCompileTimeConstant) // only report result if it's a constant; use the regular OperatorResolveResult codepath otherwise
                    {
                        return(rr);
                    }
                }
            }
            IType resultType = MakeNullable(rc, enumType, isNullable);

            return(SetOperationInformations(rc, resultType, lhs, op, rhs, isNullable));
        }
Esempio n. 4
0
        public override Expression DoResolve(ResolveContext rc)
        {
            referencedType = (texpr as ITypeReference).Resolve(rc);
            if (referencedType == null)
            {
                return(null);
            }

            if (referencedType.Kind == TypeKind.Enum)
            {
                referencedType = ResolveContext.GetEnumUnderlyingType(referencedType);
            }

            int size_of = ResolveSizeOfBuiltin(rc, referencedType);

            if (size_of > 0)
            {
                return(new IntConstant(size_of, loc).DoResolve(rc));
            }

            _resolved = true;
            eclass    = ExprClass.Value;
            return(this);
        }
Esempio n. 5
0
        public override Expression DoResolve(ResolveContext rc)
        {
            if (_resolved)
            {
                return(this);
            }

            expr = expr.DoResolve(rc);
            if (expr == null)
            {
                return(null);
            }

            eclass = ExprClass.Value;
            if (ResolvedType == null)
            {
                ResolvedType = target_type.ResolveAsType(rc);


                if (ResolvedType == null)
                {
                    return(null);
                }
            }

            if (rc.IsStaticType(ResolvedType))
            {
                rc.Report.Error(246, loc, "Cannot convert to static type `{0}'", ResolvedType.ToString());
                return(null);
            }


            // V# 4.0 spec: §7.7.6 Cast expressions
            Conversion c = rc.conversions.ExplicitConversion(expr, ResolvedType);

            if (!c.IsValid)
            {
                rc.Report.Error(196, Location, "Cannot convert source type `{0}' to target type `{1}'", expr.Type.ToString(), ResolvedType.ToString());
                return(ErrorResult);
            }

            if (expr.IsCompileTimeConstant && !c.IsUserDefined)
            {
                TypeCode code = ReflectionHelper.GetTypeCode(ResolvedType);
                if (code >= TypeCode.Boolean && code <= TypeCode.Decimal && expr.ConstantValue != null)
                {
                    try
                    {
                        return(Constant.CreateConstantFromValue(rc, ResolvedType, rc.VSharpPrimitiveCast(code, expr.ConstantValue), loc));
                    }
                    catch (OverflowException)
                    {
                        return(new ErrorExpression(ResolvedType, loc));
                    }
                    catch (InvalidCastException)
                    {
                        return(new ErrorExpression(ResolvedType, loc));
                    }
                }
                else if (code == TypeCode.String)
                {
                    if (expr.ConstantValue == null || expr.ConstantValue is string)
                    {
                        return(Constant.CreateConstantFromValue(rc, ResolvedType, expr.ConstantValue, loc));
                    }
                    else
                    {
                        return(new ErrorExpression(ResolvedType, loc));
                    }
                }
                else if (ResolvedType.Kind == TypeKind.Enum)
                {
                    code = ReflectionHelper.GetTypeCode(ResolveContext.GetEnumUnderlyingType(ResolvedType));
                    if (code >= TypeCode.SByte && code <= TypeCode.UInt64 && expr.ConstantValue != null)
                    {
                        try
                        {
                            return(Constant.CreateConstantFromValue(rc, ResolvedType, rc.VSharpPrimitiveCast(code, expr.ConstantValue), loc));
                        }
                        catch (OverflowException)
                        {
                            return(new ErrorExpression(ResolvedType, loc));
                        }
                        catch (InvalidCastException)
                        {
                            return(new ErrorExpression(ResolvedType, loc));
                        }
                    }
                }
            }

            return(new CastExpression(ResolvedType, expr, c, rc.checkForOverflow));
        }
Esempio n. 6
0
        public override Expression DoResolve(ResolveContext rc)
        {
            if (_resolved)
            {
                return(this);
            }

            expr = expr.DoResolve(rc);
            if (expr == null)
            {
                return(null);
            }

            eclass = ExprClass.Value;
            if (ResolvedType == null)
            {
                ResolvedType = target_type.ResolveAsType(rc);


                if (ResolvedType == null)
                {
                    return(null);
                }
            }

            if (rc.IsStaticType(ResolvedType))
            {
                rc.Report.Error(246, loc, "Cannot convert to static type `{0}'", ResolvedType.ToString());
                return(null);
            }
            Conversion c = rc.conversions.ExplicitConversion(expr, ResolvedType);

            if (c.IsValid)
            {
                var cv = rc.conversions.ImplicitConversion(expr, rc.CurrentTypeDefinition.EnumUnderlyingType);
                if (!cv.IsValid)
                {
                    rc.Report.Error(0, loc,
                                    "Cannot implicitly convert type `{0}' to enum underlying `{1}'. An explicit conversion exists (are you missing a cast?)",
                                    expr.Type.ToString(), ResolvedType.ToString());
                    return(ErrorResult);
                }
            }

            if (!c.IsValid && !c.IsNumericConversion)
            {
                c = rc.conversions.ExplicitConversion(expr, ResolvedType);
                if (c.IsValid)
                {
                    rc.Report.Error(0, loc,
                                    "Cannot implicitly convert type `{0}' to enum underlying `{1}'. An explicit conversion exists (are you missing a cast?)",
                                    expr.Type.ToString(), ResolvedType.ToString());
                }
                else
                {
                    rc.Report.Error(0, loc, "Cannot implicitly convert type `{0}' to enum underlying type `{1}'",
                                    expr.Type.ToString(), ResolvedType.ToString());
                }

                return(ErrorResult);
            }
            //else if (!c.IsValid && c.IsNumericConversion)
            //    rc.Report.Warning(0, 4 ,loc, "Implicit conversion from type `{0}' to `{1}' was performed",
            //           expr.Type.ToString(), ResolvedType.ToString());


            if (expr.IsCompileTimeConstant && !c.IsUserDefined)
            {
                TypeCode code = ReflectionHelper.GetTypeCode(ResolvedType);
                if (code >= TypeCode.Boolean && code <= TypeCode.Decimal && expr.ConstantValue != null)
                {
                    try
                    {
                        return(Constant.CreateConstantFromValue(rc, ResolvedType, rc.VSharpPrimitiveCast(code, expr.ConstantValue), loc));
                    }
                    catch (OverflowException)
                    {
                        return(new ErrorExpression(ResolvedType, loc));
                    }
                    catch (InvalidCastException)
                    {
                        return(new ErrorExpression(ResolvedType, loc));
                    }
                }
                else if (code == TypeCode.String)
                {
                    if (expr.ConstantValue == null || expr.ConstantValue is string)
                    {
                        return(Constant.CreateConstantFromValue(rc, ResolvedType, expr.ConstantValue, loc));
                    }
                    else
                    {
                        return(new ErrorExpression(ResolvedType, loc));
                    }
                }
                else if (ResolvedType.Kind == TypeKind.Enum)
                {
                    code = ReflectionHelper.GetTypeCode(ResolveContext.GetEnumUnderlyingType(ResolvedType));
                    if (code >= TypeCode.SByte && code <= TypeCode.UInt64 && expr.ConstantValue != null)
                    {
                        try
                        {
                            return(Constant.CreateConstantFromValue(rc, ResolvedType, rc.VSharpPrimitiveCast(code, expr.ConstantValue), loc));
                        }
                        catch (OverflowException)
                        {
                            return(new ErrorExpression(ResolvedType, loc));
                        }
                        catch (InvalidCastException)
                        {
                            return(new ErrorExpression(ResolvedType, loc));
                        }
                    }
                }
            }

            return(new CastExpression(ResolvedType, expr, c, rc.checkForOverflow));
        }
Esempio n. 7
0
        public static Constant CreateConstantFromValue(ICompilation rc, IType t, object v, Location loc)
        {
            Constant c = null;

            switch ((t as ITypeDefinition).KnownTypeCode)
            {
            case KnownTypeCode.Int32:
                c = new IntConstant((int)v, loc);
                break;

            case KnownTypeCode.String:
                c = new StringConstant((string)v, loc); break;

            case KnownTypeCode.UInt32:
                c = new UIntConstant((uint)v, loc); break;

            case KnownTypeCode.Int64:
                c = new LongConstant((long)v, loc); break;

            case KnownTypeCode.UInt64:
                c = new ULongConstant((ulong)v, loc); break;

            case KnownTypeCode.Single:
                c = new FloatConstant((float)v, loc); break;

            case KnownTypeCode.Double:
                c = new DoubleConstant((double)v, loc); break;

            case KnownTypeCode.Int16:
                c = new ShortConstant((short)v, loc); break;

            case KnownTypeCode.UInt16:
                c = new UShortConstant((ushort)v, loc); break;

            case KnownTypeCode.SByte:
                c = new SByteConstant((sbyte)v, loc); break;

            case KnownTypeCode.Byte:
                c = new ByteConstant((byte)v, loc); break;

            case KnownTypeCode.Char:
                c = new CharConstant((char)v, loc); break;

            case KnownTypeCode.Boolean:
                c = new BoolConstant((bool)v, loc); break;
            }


            if (t.Kind == TypeKind.Enum)
            {
                var real_type = ResolveContext.GetEnumUnderlyingType(t);
                return(CreateConstantFromValue(rc, real_type, v, loc));
            }

            if (v == null)
            {
                // TODO:Support nullable constant ?

                if (t.IsReferenceType.HasValue && t.IsReferenceType.Value)
                {
                    c = new NullConstant(t, loc);
                }
            }

            if (c != null)
            {
                c = ((Constant)c).ResolveType(rc);
            }

            return(c);
        }
Esempio n. 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));
            }
        }