context: //
  // cfold.cs: Constant Folding
  //
  // Author:
  //   Miguel de Icaza (miguel@ximian.com)
  //   Marek Safar (marek.safar@seznam.cz)
  //
  // Copyright 2002, 2003 Ximian, Inc.
  // Copyright 2003-2008, Novell, Inc.
  // 
  using System;
  
  namespace Mono.CSharp {
  
  	public static class ConstantFold
  	{
  		public static TypeSpec[] CreateBinaryPromotionsTypes (BuiltinTypes types)
  		{
  			return new TypeSpec[] { 
  				types.Decimal, types.Double, types.Float,
  				types.ULong, types.Long, types.UInt
  			};
  		}
  
  		//
  		// Performs the numeric promotions on the left and right expresions
  		// and deposits the results on `lc' and `rc'.
  		//
  		// On success, the types of `lc' and `rc' on output will always match,
  		// and the pair will be one of:
  		//
  		// TODO: BinaryFold should be called as an optimization step only,
  		// error checking here is weak
  		//		
  		static bool DoBinaryNumericPromotions (ResolveContext rc, ref Constant left, ref Constant right)
  		{
  			TypeSpec ltype = left.Type;
  			TypeSpec rtype = right.Type;
  
  			foreach (TypeSpec t in rc.BuiltinTypes.BinaryPromotionsTypes) {
  				if (t == ltype)
  					return t == rtype || ConvertPromotion (rc, ref right, ref left, t);
  
  				if (t == rtype)
  					return t == ltype || ConvertPromotion (rc, ref left, ref right, t);
  			}
  
  			left = left.ConvertImplicitly (rc.BuiltinTypes.Int);
  			right = right.ConvertImplicitly (rc.BuiltinTypes.Int);
  			return left != null && right != null;
  		}
  
  		static bool ConvertPromotion (ResolveContext rc, ref Constant prim, ref Constant second, TypeSpec type)
  		{
  			Constant c = prim.ConvertImplicitly (type);
  			if (c != null) {
  				prim = c;
  				return true;
  			}
  
  			if (type.BuiltinType == BuiltinTypeSpec.Type.UInt) {
  				type = rc.BuiltinTypes.Long;
  				prim = prim.ConvertImplicitly (type);
  				second = second.ConvertImplicitly (type);
  				return prim != null && second != null;
  			}
  
  			return false;
  		}
  
  		internal static void Error_CompileTimeOverflow (ResolveContext rc, Location loc)
  		{
  			rc.Report.Error (220, loc, "The operation overflows at compile time in checked mode");
  		}
  		
  		/// <summary>
  		///   Constant expression folder for binary operations.
  		///
  		///   Returns null if the expression can not be folded.
  		/// </summary>
  		static public Constant BinaryFold (ResolveContext ec, Binary.Operator oper,
  						     Constant left, Constant right, Location loc)
  		{
  			Constant result = null;
  
  			if (left is EmptyConstantCast)
  				return BinaryFold (ec, oper, ((EmptyConstantCast)left).child, right, loc);
  
  			if (left is SideEffectConstant) {
  				result = BinaryFold (ec, oper, ((SideEffectConstant) left).value, right, loc);
  				if (result == null)
  					return null;
  				return new SideEffectConstant (result, left, loc);
  			}
  
  			if (right is EmptyConstantCast)
  				return BinaryFold (ec, oper, left, ((EmptyConstantCast)right).child, loc);
  
  			if (right is SideEffectConstant) {
  				result = BinaryFold (ec, oper, left, ((SideEffectConstant) right).value, loc);
  				if (result == null)
  					return null;
  				return new SideEffectConstant (result, right, loc);
  			}
  
  			TypeSpec lt = left.Type;
  			TypeSpec rt = right.Type;
  			bool bool_res;
  
  			if (lt.BuiltinType == BuiltinTypeSpec.Type.Bool && lt == rt) {
  				bool lv = (bool) left.GetValue ();
  				bool rv = (bool) right.GetValue ();			
  				switch (oper) {
  				case Binary.Operator.BitwiseAnd:
  				case Binary.Operator.LogicalAnd:
  					return new BoolConstant (ec.BuiltinTypes, lv && rv, left.Location);
  				case Binary.Operator.BitwiseOr:
  				case Binary.Operator.LogicalOr:
  					return new BoolConstant (ec.BuiltinTypes, lv || rv, left.Location);
  				case Binary.Operator.ExclusiveOr:
  					return new BoolConstant (ec.BuiltinTypes, lv ^ rv, left.Location);
  				case Binary.Operator.Equality:
  					return new BoolConstant (ec.BuiltinTypes, lv == rv, left.Location);
  				case Binary.Operator.Inequality:
  					return new BoolConstant (ec.BuiltinTypes, lv != rv, left.Location);
  				}
  				return null;
  			}
  
  			//
  			// During an enum evaluation, none of the rules are valid
  			// Not sure whether it is bug in csc or in documentation
  			//
  			if (ec.HasSet (ResolveContext.Options.EnumScope)){
  				if (left is EnumConstant)
  					left = ((EnumConstant) left).Child;
  				
  				if (right is EnumConstant)
  					right = ((EnumConstant) right).Child;
  			} else if (left is EnumConstant && rt == lt) {
  				switch (oper){
  					///
  					/// E operator |(E x, E y);
  					/// E operator &(E x, E y);
  					/// E operator ^(E x, E y);
  					/// 
  					case Binary.Operator.BitwiseOr:
  					case Binary.Operator.BitwiseAnd:
  					case Binary.Operator.ExclusiveOr:
  						result = BinaryFold (ec, oper, ((EnumConstant)left).Child, ((EnumConstant)right).Child, loc);
  						if (result != null)
  							result = result.TryReduce (ec, lt, loc);
  						return result;
  
  					///
  					/// U operator -(E x, E y);
  					/// 
  					case Binary.Operator.Subtraction:
  						result = BinaryFold (ec, oper, ((EnumConstant)left).Child, ((EnumConstant)right).Child, loc);
  						if (result != null)
  							result = result.TryReduce (ec, EnumSpec.GetUnderlyingType (lt), loc);
  						return result;
  
  					///
  					/// bool operator ==(E x, E y);
  					/// bool operator !=(E x, E y);
  					/// bool operator <(E x, E y);
  					/// bool operator >(E x, E y);
  					/// bool operator <=(E x, E y);
  					/// bool operator >=(E x, E y);
  					/// 
  					case Binary.Operator.Equality:				
  					case Binary.Operator.Inequality:
  					case Binary.Operator.LessThan:				
  					case Binary.Operator.GreaterThan:
  					case Binary.Operator.LessThanOrEqual:				
  					case Binary.Operator.GreaterThanOrEqual:
  						return BinaryFold(ec, oper, ((EnumConstant)left).Child, ((EnumConstant)right).Child, loc);
  				}
  				return null;
  			}
  
  			switch (oper){
  			case Binary.Operator.BitwiseOr:
  				//
  				// bool? operator &(bool? x, bool? y);
  				//
  				if ((lt.BuiltinType == BuiltinTypeSpec.Type.Bool && right is NullLiteral) ||
  					(rt.BuiltinType == BuiltinTypeSpec.Type.Bool && left is NullLiteral)) {
  					var b = new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
  
  					// false | null => null
  					// null | false => null
  					if ((right is NullLiteral && left.IsDefaultValue) || (left is NullLiteral && right.IsDefaultValue))
  						return Nullable.LiftedNull.CreateFromExpression (ec, b);
  
  					// true | null => true
  					// null | true => true
  					return ReducedExpression.Create (new BoolConstant (ec.BuiltinTypes, true, loc), b);					
  				}
  
  				if (!DoBinaryNumericPromotions (ec, ref left, ref right))
  					return null;
  
  				if (left is IntConstant){
  					int res = ((IntConstant) left).Value | ((IntConstant) right).Value;
  
  					return new IntConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				if (left is UIntConstant){
  					uint res = ((UIntConstant)left).Value | ((UIntConstant)right).Value;
  
  					return new UIntConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				if (left is LongConstant){
  					long res = ((LongConstant)left).Value | ((LongConstant)right).Value;
  
  					return new LongConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				if (left is ULongConstant){
  					ulong res = ((ULongConstant)left).Value |
  						((ULongConstant)right).Value;
  
  					return new ULongConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				break;
  				
  			case Binary.Operator.BitwiseAnd:
  				//
  				// bool? operator &(bool? x, bool? y);
  				//
  				if ((lt.BuiltinType == BuiltinTypeSpec.Type.Bool && right is NullLiteral) ||
  					(rt.BuiltinType == BuiltinTypeSpec.Type.Bool && left is NullLiteral)) {
  					var b = new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
  
  					// false & null => false
  					// null & false => false
  					if ((right is NullLiteral && left.IsDefaultValue) || (left is NullLiteral && right.IsDefaultValue))
  						return ReducedExpression.Create (new BoolConstant (ec.BuiltinTypes, false, loc), b);
  
  					// true & null => null
  					// null & true => null
  					return Nullable.LiftedNull.CreateFromExpression (ec, b);
  				}
  
  				if (!DoBinaryNumericPromotions (ec, ref left, ref right))
  					return null;
  				
  				///
  				/// int operator &(int x, int y);
  				/// uint operator &(uint x, uint y);
  				/// long operator &(long x, long y);
  				/// ulong operator &(ulong x, ulong y);
  				///
  				if (left is IntConstant){
  					int res = ((IntConstant) left).Value & ((IntConstant) right).Value;
  					return new IntConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				if (left is UIntConstant){
  					uint res = ((UIntConstant)left).Value & ((UIntConstant)right).Value;
  					return new UIntConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				if (left is LongConstant){
  					long res = ((LongConstant)left).Value & ((LongConstant)right).Value;
  					return new LongConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				if (left is ULongConstant){
  					ulong res = ((ULongConstant)left).Value &
  						((ULongConstant)right).Value;
  
  					return new ULongConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				break;
  
  			case Binary.Operator.ExclusiveOr:
  				if (!DoBinaryNumericPromotions (ec, ref left, ref right))
  					return null;
  				
  				if (left is IntConstant){
  					int res = ((IntConstant) left).Value ^ ((IntConstant) right).Value;
  					return new IntConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				if (left is UIntConstant){
  					uint res = ((UIntConstant)left).Value ^ ((UIntConstant)right).Value;
  
  					return new UIntConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				if (left is LongConstant){
  					long res = ((LongConstant)left).Value ^ ((LongConstant)right).Value;
  
  					return new LongConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				if (left is ULongConstant){
  					ulong res = ((ULongConstant)left).Value ^
  						((ULongConstant)right).Value;
  
  					return new ULongConstant (ec.BuiltinTypes, res, left.Location);
  				}
  				break;
  
  			case Binary.Operator.Addition:
  				if (lt == InternalType.NullLiteral)
  					return right;
  
  				if (rt == InternalType.NullLiteral)
  					return left;
  
  				//
  				// If both sides are strings, then concatenate, if
  				// one is a string, and the other is not, then defer
  				// to runtime concatenation
  				//
  				if (lt.BuiltinType == BuiltinTypeSpec.Type.String || rt.BuiltinType == BuiltinTypeSpec.Type.String){
  					if (lt == rt)
  						return new StringConstant (ec.BuiltinTypes, (string)left.GetValue () + (string)right.GetValue (),
  							left.Location);
  					
  					return null;
  				}
  
  				//
  				// handle "E operator + (E x, U y)"
  				// handle "E operator + (Y y, E x)"
  				//
  				EnumConstant lc = left as EnumConstant;
  				EnumConstant rc = right as EnumConstant;
  				if (lc != null || rc != null){
  					if (lc == null) {
  						lc = rc;
  						lt = lc.Type;
  						right = left;
  					}
  
  					// U has to be implicitly convetible to E.base
  					right = right.ConvertImplicitly (lc.Child.Type);
  					if (right == null)
  						return null;
  
  					result = BinaryFold (ec, oper, lc.Child, right, loc);
  					if (result == null)
  						return null;
  
  					result = result.TryReduce (ec, lt, loc);
  					if (result == null)
  						return null;
  
  					return new EnumConstant (result, lt);
  				}
  
  				if (!DoBinaryNumericPromotions (ec, ref left, ref right))
  					return null;
  
  				try {
  					if (left is DoubleConstant){
  						double res;
  						
  						if (ec.ConstantCheckState)
  							res = checked (((DoubleConstant) left).Value +
  								       ((DoubleConstant) right).Value);
  						else
  							res = unchecked (((DoubleConstant) left).Value +
  									 ((DoubleConstant) right).Value);
  
  						return new DoubleConstant (ec.BuiltinTypes, res, left.Location);
  					}
  					if (left is FloatConstant){
  						float res;
  						
  						if (ec.ConstantCheckState)
  							res = checked (((FloatConstant) left).Value +
  								       ((FloatConstant) right).Value);
  						else
  							res = unchecked (((FloatConstant) left).Value +
  									 ((FloatConstant) right).Value);
  
  						result = new FloatConstant (ec.BuiltinTypes, res, left.Location);
  					} else if (left is ULongConstant){
  						ulong res;
  						
  						if (ec.ConstantCheckState)
  							res = checked (((ULongConstant) left).Value +
  								       ((ULongConstant) right).Value);
  						else
  							res = unchecked (((ULongConstant) left).Value +
  									 ((ULongConstant) right).Value);
  
  						result = new ULongConstant (ec.BuiltinTypes, res, left.Location);
  					} else if (left is LongConstant){
  						long res;
  						
  						if (ec.ConstantCheckState)
  							res = checked (((LongConstant) left).Value +
  								       ((LongConstant) right).Value);
  						else
  							res = unchecked (((LongConstant) left).Value +
  									 ((LongConstant) right).Value);
  
  						result = new LongConstant (ec.BuiltinTypes, res, left.Location);
  					} else if (left is UIntConstant){
  						uint res;
  						
  						if (ec.ConstantCheckState)
  							res = checked (((UIntConstant) left).Value +
  								       ((UIntConstant) right).Value);
  						else
  							res = unchecked (((UIntConstant) left).Value +
  									 ((UIntConstant) right).Value);
  
  						result = new UIntConstant (ec.BuiltinTypes, res, left.Location);
  					} else if (left is IntConstant){
  						int res;
  
  						if (ec.ConstantCheckState)
  							res = checked (((IntConstant) left).Value +
  								       ((IntConstant) right).Value);
  						else
  
gt: 							res = unchecked (((IntConstant) left).Value +