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());
            }
        }
        private BoundExpression RewriteLiftedBinaryOperator(CSharpSyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, MethodSymbol method)
        {
            var conditionalLeft = loweredLeft as BoundLoweredConditionalAccess;

            // NOTE: we could in theory handle side-effecting loweredRight here too
            //       by including it as a part of whenNull, but there is a concern 
            //       that it can lead to code duplication
            var optimize = conditionalLeft != null &&
                !ReadIsSideeffecting(loweredRight) &&
                (conditionalLeft.WhenNullOpt == null || conditionalLeft.WhenNullOpt.IsDefaultValue());

            if (optimize)
            {
                loweredLeft = conditionalLeft.WhenNotNull;
            }

            var result = operatorKind.IsComparison() ?
                            operatorKind.IsUserDefined() ?
                                LowerLiftedUserDefinedComparisonOperator(syntax, operatorKind, loweredLeft, loweredRight, method) :
                                LowerLiftedBuiltInComparisonOperator(syntax, operatorKind, loweredLeft, loweredRight) :
                            LowerLiftedBinaryArithmeticOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method);

            if (optimize)
            {
                BoundExpression whenNullOpt = null;

                // for all operators null-in means null-out
                // except for the Equal/NotEqual since null == null ==> true
                if (operatorKind.Operator() == BinaryOperatorKind.NotEqual ||
                    operatorKind.Operator() == BinaryOperatorKind.Equal)
                {
                    whenNullOpt = RewriteLiftedBinaryOperator(syntax, operatorKind, _factory.Default(loweredLeft.Type), loweredRight, type, method);
                }

                result = conditionalLeft.Update(
                    conditionalLeft.Receiver,
                    conditionalLeft.HasValueMethodOpt,
                    whenNotNull: result,
                    whenNullOpt: whenNullOpt,
                    id: conditionalLeft.Id,
                    type: result.Type
                );
            }

            return result;
        }
        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());
            }
        }
        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 if (operatorKind.IsDynamic())
            {
                Debug.Assert((object)method == null);
                Debug.Assert(!isPointerElementAccess);

                if (operatorKind.IsLogical())
                {
                    return MakeDynamicLogicalBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, type, isCompoundAssignment, applyParentUnaryOperator);
                }
                else
                {
                    return dynamicFactory.MakeDynamicBinaryOperator(operatorKind, loweredLeft, loweredRight, isCompoundAssignment, type).ToExpression();
                }
            }
            else if (operatorKind.IsUserDefined())
            {
                return LowerUserDefinedBinaryOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method);
            }
            else if (operatorKind.IsLifted())
            {
                if (operatorKind.IsComparison())
                {
                    return LowerLiftedBuiltInComparisonOperator(syntax, operatorKind, loweredLeft, loweredRight);
                }
                else
                {
                    return LowerLiftedBinaryArithmeticOperator(syntax, operatorKind, loweredLeft, loweredRight, type, method);
                }
            }
            else
            {
                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:
                        return RewritePointerNumericOperator(syntax, operatorKind, loweredLeft, loweredRight, type, isPointerElementAccess, isLeftPointer: true);

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

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

                    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);
        }
        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);
        }
Beispiel #6
0
 public static bool RequiresNumericTypes(this BinaryOperatorKind op)
 {
     return(op.IsArithmetic() || op.IsLogical() || op.IsComparison());
 }
        private BoundExpression LowerUserDefinedBinaryOperator(
            CSharpSyntaxNode syntax,
            BinaryOperatorKind operatorKind,
            BoundExpression loweredLeft,
            BoundExpression loweredRight,
            TypeSymbol type,
            MethodSymbol method)
        {
            Debug.Assert(!operatorKind.IsLogical());

            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);
        }