/// <summary>
 /// If supplied expression is of Nullable(T) type, invokes its GetValueOrDefault method.
 /// </summary>
 /// <param name="expression"></param>
 /// <returns></returns>
 public static Expression RemoveNullability(this Expression expression)
 {
     return
         (expression.IsVoid()
         ? expression
         : expression.IsNullableType()
                ? ConstantHelper.TryEvalConst(null, expression.Type.GetField("Value"), expression)
                : expression);
 }
        private static bool TryAdjustNumericType(ParseTreeNode root, Expression expr, Type targetType, out Expression adjusted)
        {
            if (expr.IsNumeric() && Numerics.Contains(targetType))
            {
                var mySize     = Marshal.SizeOf(expr.Type);
                var targetSize = Marshal.SizeOf(targetType);

                if (mySize < targetSize)
                {
                    adjusted = ConstantHelper.TryEvalConst(root, expr, ExpressionType.Convert, targetType);
                    return(true);
                }

                if (mySize == targetSize)
                {
                    if (ReferenceEquals(targetType, typeof(Single)) ||
                        ReferenceEquals(targetType, typeof(Double)))
                    {
                        adjusted = ConstantHelper.TryEvalConst(root, expr, ExpressionType.Convert, targetType);
                        return(true);
                    }
                }

                // attempt overflow-checked autocast between integer constants,
                // or from integer to real constants (but never from reals to integers)
                if (!expr.IsRealNumeric() || !targetType.IsInteger())
                {
                    // only attempt to auto-convert constants
                    var constExpr = expr as ConstantExpression;
                    if (constExpr != null)
                    {
                        try
                        {
                            adjusted = ConstantHelper.EvalConst(root, constExpr, ExpressionType.ConvertChecked, targetType);
                            return(true);
                        }
                        catch (Exception e)
                        {
                            if (!(e is TargetInvocationException || e is OverflowException))
                            {
                                // something unexpected happened
                                throw;
                            }

                            // we'll get here if a conversion to target type causes overflow
                        }
                    }
                }
            }

            adjusted = null;
            return(false);
        }
        /// <summary>
        /// Attempts to auto-adjust the size of arguments for a binary arithmetic operation.
        /// Integers grow up to Int64, Double or Decimal, floats grow up to Decimal.
        /// </summary>
        /// <param name="leftExpr">Left argument</param>
        /// <param name="rightExpr">Right argument</param>
        /// <param name="root">Corresponding parse tree node</param>
        /// <exception cref="ArgumentNullException"><paramref name="root"/> is null</exception>
        /// <exception cref="ArgumentNullException"><paramref name="leftExpr"/> is null</exception>
        /// <exception cref="ArgumentNullException"><paramref name="rightExpr"/> is null</exception>
        /// <exception cref="CompilationException">Argument types cannot be adjusted, try conversion</exception>
        public static void AdjustArgumentsForBinaryOperation(ref Expression leftExpr, ref Expression rightExpr, ParseTreeNode root)
        {
            if (root == null)
            {
                throw new ArgumentNullException("root");
            }

            if (leftExpr == null)
            {
                throw new ArgumentNullException("leftExpr");
            }

            if (rightExpr == null)
            {
                throw new ArgumentNullException("rightExpr");
            }

            // do we actually have to adjust them?
            if (ReferenceEquals(leftExpr.Type, rightExpr.Type))
            {
                return;
            }

            if (leftExpr.IsNumeric() && rightExpr.IsNumeric())
            {
                var leftSize  = Marshal.SizeOf(leftExpr.Type);
                var rightSize = Marshal.SizeOf(rightExpr.Type);

                if (leftSize < rightSize)
                {
                    leftExpr = ConstantHelper.TryEvalConst(root, leftExpr, ExpressionType.Convert, rightExpr.Type);
                }
                else if (leftSize > rightSize)
                {
                    rightExpr = ConstantHelper.TryEvalConst(root, rightExpr, ExpressionType.Convert, leftExpr.Type);
                }
                else if (ReferenceEquals(leftExpr.Type, typeof(Single)))
                {
                    // sizes are equal, so the other guy is probably Int32 or UInt32
                    rightExpr = ConstantHelper.TryEvalConst(root, rightExpr, ExpressionType.Convert, leftExpr.Type);
                }
                else if (ReferenceEquals(rightExpr.Type, typeof(Single)))
                {
                    // sizes are equal, so the other guy is probably Int32 or UInt32
                    leftExpr = ConstantHelper.TryEvalConst(root, leftExpr, ExpressionType.Convert, rightExpr.Type);
                }
                else if (ReferenceEquals(leftExpr.Type, typeof(Double)))
                {
                    // sizes are equal, so the other guy is probably Int64 or UInt64
                    rightExpr = ConstantHelper.TryEvalConst(root, rightExpr, ExpressionType.Convert, leftExpr.Type);
                }
                else if (ReferenceEquals(rightExpr.Type, typeof(Double)))
                {
                    // sizes are equal, so the other guy is probably Int64 or UInt64
                    leftExpr = ConstantHelper.TryEvalConst(root, leftExpr, ExpressionType.Convert, rightExpr.Type);
                }
            }
            else
            {
                throw new CompilationException(
                          String.Format(
                              "Argument types {0} and {1} cannot be auto-adjusted, try conversion",
                              leftExpr.Type.FullName, rightExpr.Type.FullName), root);
            }
        }