File: Binder\Semantics\Operators\OperatorKindExtensions.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System.Diagnostics;
using System.Linq.Expressions;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    internal static partial class OperatorKindExtensions
    {
        public static int OperatorIndex(this UnaryOperatorKind kind)
        {
            return ((int)kind.Operator() >> 8) - 16;
        }
 
        public static UnaryOperatorKind Operator(this UnaryOperatorKind kind)
        {
            return kind & UnaryOperatorKind.OpMask;
        }
 
        public static UnaryOperatorKind Unlifted(this UnaryOperatorKind kind)
        {
            return kind & ~UnaryOperatorKind.Lifted;
        }
 
        public static bool IsLifted(this UnaryOperatorKind kind)
        {
            return 0 != (kind & UnaryOperatorKind.Lifted);
        }
 
        public static bool IsChecked(this UnaryOperatorKind kind)
        {
            return 0 != (kind & UnaryOperatorKind.Checked);
        }
 
        public static bool IsUserDefined(this UnaryOperatorKind kind)
        {
            return (kind & UnaryOperatorKind.TypeMask) == UnaryOperatorKind.UserDefined;
        }
 
        public static UnaryOperatorKind OverflowChecks(this UnaryOperatorKind kind)
        {
            return kind & UnaryOperatorKind.Checked;
        }
 
        public static UnaryOperatorKind WithOverflowChecksIfApplicable(this UnaryOperatorKind kind, bool enabled)
        {
            if (enabled)
            {
                // If it's dynamic and we're in a checked context then just mark it as checked,
                // regardless of whether it is +x -x !x ~x ++x --x x++ or x--. Let the lowering 
                // pass sort out what to do with it.
 
                if (kind.IsDynamic())
                {
                    return kind | UnaryOperatorKind.Checked;
                }
 
                if (kind.IsIntegral())
                {
                    switch (kind.Operator())
                    {
                        case UnaryOperatorKind.PrefixIncrement:
                        case UnaryOperatorKind.PostfixIncrement:
                        case UnaryOperatorKind.PrefixDecrement:
                        case UnaryOperatorKind.PostfixDecrement:
                        case UnaryOperatorKind.UnaryMinus:
                            return kind | UnaryOperatorKind.Checked;
                    }
                }
                return kind;
            }
            else
            {
                return kind & ~UnaryOperatorKind.Checked;
            }
        }
 
        public static UnaryOperatorKind OperandTypes(this UnaryOperatorKind kind)
        {
            return kind & UnaryOperatorKind.TypeMask;
        }
 
        public static bool IsDynamic(this UnaryOperatorKind kind)
        {
            return kind.OperandTypes() == UnaryOperatorKind.Dynamic;
        }
 
        public static bool IsIntegral(this UnaryOperatorKind kind)
        {
            switch (kind.OperandTypes())
            {
                case UnaryOperatorKind.SByte:
                case UnaryOperatorKind.Byte:
                case UnaryOperatorKind.Short:
                case UnaryOperatorKind.UShort:
                case UnaryOperatorKind.Int:
                case UnaryOperatorKind.UInt:
                case UnaryOperatorKind.Long:
                case UnaryOperatorKind.ULong:
                case UnaryOperatorKind.NInt:
                case UnaryOperatorKind.NUInt:
                case UnaryOperatorKind.Char:
                case UnaryOperatorKind.Enum:
                case UnaryOperatorKind.Pointer:
                    return true;
            }
 
            return false;
        }
 
        public static UnaryOperatorKind WithType(this UnaryOperatorKind kind, UnaryOperatorKind type)
        {
            Debug.Assert(kind == (kind & ~UnaryOperatorKind.TypeMask));
            Debug.Assert(type == (type & UnaryOperatorKind.TypeMask));
            return kind | type;
        }
 
        public static int OperatorIndex(this BinaryOperatorKind kind)
        {
            return ((int)kind.Operator() >> 8) - 16;
        }
 
        public static BinaryOperatorKind Operator(this BinaryOperatorKind kind)
        {
            return kind & BinaryOperatorKind.OpMask;
        }
 
        public static BinaryOperatorKind Unlifted(this BinaryOperatorKind kind)
        {
            return kind & ~BinaryOperatorKind.Lifted;
        }
 
        public static BinaryOperatorKind OperatorWithLogical(this BinaryOperatorKind kind)
        {
            return kind & (BinaryOperatorKind.OpMask | BinaryOperatorKind.Logical);
        }
 
        public static BinaryOperatorKind WithType(this BinaryOperatorKind kind, SpecialType type)
        {
            Debug.Assert(kind == (kind & ~BinaryOperatorKind.TypeMask));
            switch (type)
            {
                case SpecialType.System_Int32:
                    return kind | BinaryOperatorKind.Int;
                case SpecialType.System_UInt32:
                    return kind | BinaryOperatorKind.UInt;
                case SpecialType.System_Int64:
                    return kind | BinaryOperatorKind.Long;
                case SpecialType.System_UInt64:
                    return kind | BinaryOperatorKind.ULong;
                default:
                    throw ExceptionUtilities.UnexpectedValue(type);
            }
        }
 
        public static UnaryOperatorKind WithType(this UnaryOperatorKind kind, SpecialType type)
        {
            Debug.Assert(kind == (kind & ~UnaryOperatorKind.TypeMask));
            switch (type)
            {
                case SpecialType.System_Int32:
                    return kind | UnaryOperatorKind.Int;
                case SpecialType.System_UInt32:
                    return kind | UnaryOperatorKind.UInt;
                case SpecialType.System_Int64:
                    return kind | UnaryOperatorKind.Long;
                case SpecialType.System_UInt64:
                    return kind | UnaryOperatorKind.ULong;
                default:
                    throw ExceptionUtilities.UnexpectedValue(type);
            }
        }
 
        public static BinaryOperatorKind WithType(this BinaryOperatorKind kind, BinaryOperatorKind type)
        {
            Debug.Assert(kind == (kind & ~BinaryOperatorKind.TypeMask));
            Debug.Assert(type == (type & BinaryOperatorKind.TypeMask));
            return kind | type;
        }
 
        public static bool IsLifted(this BinaryOperatorKind kind)
        {
            return 0 != (kind & BinaryOperatorKind.Lifted);
        }
 
        public static bool IsDynamic(this BinaryOperatorKind kind)
        {
            return kind.OperandTypes() == BinaryOperatorKind.Dynamic;
        }
 
        public static bool IsComparison(this BinaryOperatorKind kind)
        {
            switch (kind.Operator())
            {
                case BinaryOperatorKind.Equal:
                case BinaryOperatorKind.NotEqual:
                case BinaryOperatorKind.GreaterThan:
                case BinaryOperatorKind.GreaterThanOrEqual:
                case BinaryOperatorKind.LessThan:
                case BinaryOperatorKind.LessThanOrEqual:
                    return true;
            }
            return false;
        }
 
        public static bool IsChecked(this BinaryOperatorKind kind)
        {
            return 0 != (kind & BinaryOperatorKind.Checked);
        }
 
        public static bool EmitsAsCheckedInstruction(this BinaryOperatorKind kind)
        {
            if (!kind.IsChecked())
            {
                return false;
            }
 
            switch (kind.Operator())
            {
                case BinaryOperatorKind.Addition:
                case BinaryOperatorKind.Subtraction:
                case BinaryOperatorKind.Multiplication:
                    return true;
            }
 
            return false;
        }
 
        public static BinaryOperatorKind WithOverflowChecksIfApplicable(this BinaryOperatorKind kind, bool enabled)
        {
            if (enabled)
            {
                // If it's a dynamic binop then make it checked. Let the lowering
                // pass sort out what to do with it.
                if (kind.IsDynamic())
                {
                    return kind | BinaryOperatorKind.Checked;
                }
 
                if (kind.IsIntegral())
                {
                    switch (kind.Operator())
                    {
                        case BinaryOperatorKind.Addition:
                        case BinaryOperatorKind.Subtraction:
                        case BinaryOperatorKind.Multiplication:
                        case BinaryOperatorKind.Division:
                            return kind | BinaryOperatorKind.Checked;
                    }
                }
                return kind;
            }
            else
            {
                return kind & ~BinaryOperatorKind.Checked;
            }
        }
 
        public static bool IsEnum(this BinaryOperatorKind kind)
        {
            switch (kind.OperandTypes())
            {
                case BinaryOperatorKind.Enum:
                case BinaryOperatorKind.EnumAndUnderlying:
                case BinaryOperatorKind.UnderlyingAndEnum:
                    return true;
            }
 
            return false;
        }
 
        public static bool IsEnum(this UnaryOperatorKind kind)
        {
            return kind.OperandTypes() == UnaryOperatorKind.Enum;
        }
 
        public static bool IsIntegral(this BinaryOperatorKind kind)
        {
            switch (kind.OperandTypes())
            {
                case BinaryOperatorKind.Int:
                case BinaryOperatorKind.UInt:
                case BinaryOperatorKind.Long:
                case BinaryOperatorKind.ULong:
                case BinaryOperatorKind.NInt:
                case BinaryOperatorKind.NUInt:
                case BinaryOperatorKind.Char:
                case BinaryOperatorKind.Enum:
                case BinaryOperatorKind.EnumAndUnderlying:
                case BinaryOperatorKind.UnderlyingAndEnum:
                case BinaryOperatorKind.Pointer:
                case BinaryOperatorKind.PointerAndInt:
                case BinaryOperatorKind.PointerAndUInt:
                case BinaryOperatorKind.PointerAndLong:
                case BinaryOperatorKind.PointerAndULong:
                case BinaryOperatorKind.IntAndPointer:
                case BinaryOperatorKind.UIntAndPointer:
                case BinaryOperatorKind.LongAndPointer:
                case BinaryOperatorKind.ULongAndPointer:
                    return true;
            }
 
            return false;
        }
 
        public static bool IsLogical(this BinaryOperatorKind kind)
        {
            return 0 != (kind & BinaryOperatorKind.Logical);
        }
 
        public static BinaryOperatorKind OperandTypes(this BinaryOperatorKind kind)
        {
            return kind & BinaryOperatorKind.TypeMask;
        }
 
        public static bool IsUserDefined(this BinaryOperatorKind kind)
        {
            return (kind & BinaryOperatorKind.TypeMask) == BinaryOperatorKind.UserDefined;
        }
 
        public static bool IsShift(this BinaryOperatorKind kind)
        {
            BinaryOperatorKind type = kind.Operator();
            return type == BinaryOperatorKind.LeftShift || type == BinaryOperatorKind.RightShift || type == BinaryOperatorKind.UnsignedRightShift;
        }
 
        public static ExpressionType ToExpressionType(this BinaryOperatorKind kind, bool isCompoundAssignment)
        {
            if (isCompoundAssignment)
            {
                switch (kind.Operator())
                {
                    case BinaryOperatorKind.Multiplication: return ExpressionType.MultiplyAssign;
                    case BinaryOperatorKind.Addition: return ExpressionType.AddAssign;
                    case BinaryOperatorKind.Subtraction: return ExpressionType.SubtractAssign;
                    case BinaryOperatorKind.Division: return ExpressionType.DivideAssign;
                    case BinaryOperatorKind.Remainder: return ExpressionType.ModuloAssign;
                    case BinaryOperatorKind.LeftShift: return ExpressionType.LeftShiftAssign;
                    case BinaryOperatorKind.RightShift: return ExpressionType.RightShiftAssign;
                    case BinaryOperatorKind.And: return ExpressionType.AndAssign;
                    case BinaryOperatorKind.Xor: return ExpressionType.ExclusiveOrAssign;
                    case BinaryOperatorKind.Or: return ExpressionType.OrAssign;
                }
            }
            else
            {
                switch (kind.Operator())
                {
                    case BinaryOperatorKind.Multiplication: return ExpressionType.Multiply;
                    case BinaryOperatorKind.Addition: return ExpressionType.Add;
                    case BinaryOperatorKind.Subtraction: return ExpressionType.Subtract;
                    case BinaryOperatorKind.Division: return ExpressionType.Divide;
                    case BinaryOperatorKind.Remainder: return ExpressionType.Modulo;
                    case BinaryOperatorKind.LeftShift: return ExpressionType.LeftShift;
                    case BinaryOperatorKind.RightShift: return ExpressionType.RightShift;
                    case BinaryOperatorKind.Equal: return ExpressionType.Equal;
                    case BinaryOperatorKind.NotEqual: return ExpressionType.NotEqual;
                    case BinaryOperatorKind.GreaterThan: return ExpressionType.GreaterThan;
                    case BinaryOperatorKind.LessThan: return ExpressionType.LessThan;
                    case BinaryOperatorKind.GreaterThanOrEqual: return ExpressionType.GreaterThanOrEqual;
                    case BinaryOperatorKind.LessThanOrEqual: return ExpressionType.LessThanOrEqual;
                    case BinaryOperatorKind.And: return ExpressionType.And;
                    case BinaryOperatorKind.Xor: return ExpressionType.ExclusiveOr;
                    case BinaryOperatorKind.Or: return ExpressionType.Or;
                }
            }
 
            throw ExceptionUtilities.UnexpectedValue(kind.Operator());
        }
 
        public static ExpressionType ToExpressionType(this UnaryOperatorKind kind)
        {
            switch (kind.Operator())
            {
                case UnaryOperatorKind.PrefixIncrement:
                case UnaryOperatorKind.PostfixIncrement:
                    return ExpressionType.Increment;
 
                case UnaryOperatorKind.PostfixDecrement:
                case UnaryOperatorKind.PrefixDecrement:
                    return ExpressionType.Decrement;
 
                case UnaryOperatorKind.UnaryPlus: return ExpressionType.UnaryPlus;
                case UnaryOperatorKind.UnaryMinus: return ExpressionType.Negate;
                case UnaryOperatorKind.LogicalNegation: return ExpressionType.Not;
                case UnaryOperatorKind.BitwiseComplement: return ExpressionType.OnesComplement;
                case UnaryOperatorKind.True: return ExpressionType.IsTrue;
                case UnaryOperatorKind.False: return ExpressionType.IsFalse;
                default:
                    throw ExceptionUtilities.UnexpectedValue(kind.Operator());
            }
        }
 
#if DEBUG
        public static string Dump(this BinaryOperatorKind kind)
        {
            var b = new StringBuilder();
            if ((kind & BinaryOperatorKind.Lifted) != 0) b.Append("Lifted");
            if ((kind & BinaryOperatorKind.Logical) != 0) b.Append("Logical");
            if ((kind & BinaryOperatorKind.Checked) != 0) b.Append("Checked");
            var type = kind & BinaryOperatorKind.TypeMask;
            if (type != 0) b.Append(type.ToString());
            var op = kind & BinaryOperatorKind.OpMask;
            if (op != 0) b.Append(op.ToString());
            return b.ToString();
        }
 
        public static string Dump(this UnaryOperatorKind kind)
        {
            var b = new StringBuilder();
            if ((kind & UnaryOperatorKind.Lifted) != 0) b.Append("Lifted");
            if ((kind & UnaryOperatorKind.Checked) != 0) b.Append("Checked");
            var type = kind & UnaryOperatorKind.TypeMask;
            if (type != 0) b.Append(type.ToString());
            var op = kind & UnaryOperatorKind.OpMask;
            if (op != 0) b.Append(op.ToString());
            return b.ToString();
        }
#endif
    }
}