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

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

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

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

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

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

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

            object newValue = null;

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

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

                return concatResult;
            }

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

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

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

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

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

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

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

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

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

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

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

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

            SpecialType operatorType = SpecialType.None;

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

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

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

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

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

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

            return constantValue;
        }