2010-05-17 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / mcs / cfold.cs
index 47c76af3ed8226bbd8f83f21404ba0cb4aecb1d1..b8b82ffe573f99a60cec30aaf0444ebb22f00810 100644 (file)
@@ -14,38 +14,56 @@ namespace Mono.CSharp {
 
        public class ConstantFold {
 
-               public static readonly Type[] binary_promotions = new Type[] { 
-                       TypeManager.decimal_type, TypeManager.double_type, TypeManager.float_type,
-                       TypeManager.uint64_type, TypeManager.int64_type, TypeManager.uint32_type };
+               static TypeSpec[] binary_promotions;
+
+               public static TypeSpec[] BinaryPromotionsTypes {
+                       get {
+                               if (binary_promotions == null) {
+                                        binary_promotions = new TypeSpec[] { 
+                                               TypeManager.decimal_type, TypeManager.double_type, TypeManager.float_type,
+                                               TypeManager.uint64_type, TypeManager.int64_type, TypeManager.uint32_type };
+                               }
+
+                               return binary_promotions;
+                       }
+               }
+
+               public static void Reset ()
+               {
+                       binary_promotions = null;
+               }
 
                //
                // Performs the numeric promotions on the left and right expresions
-               // and desposits the results on `lc' and `rc'.
+               // 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:
                //
-               static bool DoBinaryNumericPromotions (ref Constant left, ref Constant right)
+               // 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)
                {
-                       Type ltype = left.Type;
-                       Type rtype = right.Type;
+                       TypeSpec ltype = left.Type;
+                       TypeSpec rtype = right.Type;
 
-                       foreach (Type t in binary_promotions) {
+                       foreach (TypeSpec t in BinaryPromotionsTypes) {
                                if (t == ltype)
-                                       return t == rtype || ConvertPromotion (ref right, ref left, t);
+                                       return t == rtype || ConvertPromotion (rc, ref right, ref left, t);
 
                                if (t == rtype)
-                                       return t == ltype || ConvertPromotion (ref left, ref right, t);
+                                       return t == ltype || ConvertPromotion (rc, ref left, ref right, t);
                        }
 
-                       left = left.ConvertImplicitly (TypeManager.int32_type);
-                       right = right.ConvertImplicitly (TypeManager.int32_type);
+                       left = left.ConvertImplicitly (rc, TypeManager.int32_type);
+                       right = right.ConvertImplicitly (rc, TypeManager.int32_type);
                        return left != null && right != null;
                }
 
-               static bool ConvertPromotion (ref Constant prim, ref Constant second, Type type)
+               static bool ConvertPromotion (ResolveContext rc, ref Constant prim, ref Constant second, TypeSpec type)
                {
-                       Constant c = prim.ConvertImplicitly (type);
+                       Constant c = prim.ConvertImplicitly (rc, type);
                        if (c != null) {
                                prim = c;
                                return true;
@@ -53,17 +71,17 @@ namespace Mono.CSharp {
 
                        if (type == TypeManager.uint32_type) {
                                type = TypeManager.int64_type;
-                               prim = prim.ConvertImplicitly (type);
-                               second = second.ConvertImplicitly (type);
+                               prim = prim.ConvertImplicitly (rc, type);
+                               second = second.ConvertImplicitly (rc, type);
                                return prim != null && second != null;
                        }
 
                        return false;
                }
 
-               internal static void Error_CompileTimeOverflow (Location loc)
+               internal static void Error_CompileTimeOverflow (ResolveContext rc, Location loc)
                {
-                       Report.Error (220, loc, "The operation overflows at compile time in checked mode");
+                       rc.Report.Error (220, loc, "The operation overflows at compile time in checked mode");
                }
                
                /// <summary>
@@ -71,7 +89,7 @@ namespace Mono.CSharp {
                ///
                ///   Returns null if the expression can not be folded.
                /// </summary>
-               static public Constant BinaryFold (EmitContext ec, Binary.Operator oper,
+               static public Constant BinaryFold (ResolveContext ec, Binary.Operator oper,
                                                     Constant left, Constant right, Location loc)
                {
                        Constant result = null;
@@ -96,8 +114,8 @@ namespace Mono.CSharp {
                                return new SideEffectConstant (result, right, loc);
                        }
 
-                       Type lt = left.Type;
-                       Type rt = right.Type;
+                       TypeSpec lt = left.Type;
+                       TypeSpec rt = right.Type;
                        bool bool_res;
 
                        if (lt == TypeManager.bool_type && lt == rt) {
@@ -124,7 +142,7 @@ namespace Mono.CSharp {
                        // During an enum evaluation, none of the rules are valid
                        // Not sure whether it is bug in csc or in documentation
                        //
-                       if (ec.InEnumContext){
+                       if (ec.HasSet (ResolveContext.Options.EnumScope)){
                                if (left is EnumConstant)
                                        left = ((EnumConstant) left).Child;
                                
@@ -140,15 +158,19 @@ namespace Mono.CSharp {
                                        case Binary.Operator.BitwiseOr:
                                        case Binary.Operator.BitwiseAnd:
                                        case Binary.Operator.ExclusiveOr:
-                                               return BinaryFold (ec, oper, ((EnumConstant)left).Child,
-                                                               ((EnumConstant)right).Child, loc).TryReduce (ec, lt, loc);
+                                               result = BinaryFold (ec, oper, ((EnumConstant)left).Child, ((EnumConstant)right).Child, loc);
+                                               if (result != null)
+                                                       result = result.Resolve (ec).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);
-                                               return result.TryReduce (ec, ((EnumConstant)left).Child.Type, loc);
+                                               if (result != null)
+                                                       result = result.Resolve (ec).TryReduce (ec, ((EnumConstant)left).Child.Type, loc);
+                                               return result;
 
                                        ///
                                        /// bool operator ==(E x, E y);
@@ -171,7 +193,7 @@ namespace Mono.CSharp {
 
                        switch (oper){
                        case Binary.Operator.BitwiseOr:
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                if (left is IntConstant){
@@ -198,7 +220,7 @@ namespace Mono.CSharp {
                                break;
                                
                        case Binary.Operator.BitwiseAnd:
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
                                
                                ///
@@ -228,7 +250,7 @@ namespace Mono.CSharp {
                                break;
 
                        case Binary.Operator.ExclusiveOr:
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
                                
                                if (left is IntConstant){
@@ -254,23 +276,25 @@ namespace Mono.CSharp {
                                break;
 
                        case Binary.Operator.Addition:
+                               if (lt == TypeManager.null_type)
+                                       return right;
+
+                               if (rt == TypeManager.null_type)
+                                       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 == TypeManager.string_type || rt == TypeManager.string_type){
-                                       if (lt == TypeManager.string_type && rt == TypeManager.string_type)
-                                               return new StringConstant (
-                                                       ((StringConstant) left).Value +
-                                                       ((StringConstant) right).Value, left.Location);
+                                       if (lt == rt)
+                                               return new StringConstant ((string)left.GetValue () + (string)right.GetValue (),
+                                                       left.Location);
                                        
                                        return null;
                                }
 
-                               if (lt == TypeManager.null_type && lt == rt)
-                                       return left;
-
                                //
                                // handle "E operator + (E x, U y)"
                                // handle "E operator + (Y y, E x)"
@@ -285,7 +309,7 @@ namespace Mono.CSharp {
                                        }
 
                                        // U has to be implicitly convetible to E.base
-                                       right = right.ConvertImplicitly (lc.Child.Type);
+                                       right = right.ConvertImplicitly (ec, lc.Child.Type);
                                        if (right == null)
                                                return null;
 
@@ -293,14 +317,14 @@ namespace Mono.CSharp {
                                        if (result == null)
                                                return null;
 
-                                       result = result.TryReduce (ec, lt, loc);
+                                       result = result.Resolve (ec).TryReduce (ec, lt, loc);
                                        if (result == null)
                                                return null;
 
                                        return new EnumConstant (result, lt);
                                }
 
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                try {
@@ -382,11 +406,9 @@ namespace Mono.CSharp {
                                                                ((DecimalConstant) right).Value);
 
                                                result = new DecimalConstant (res, left.Location);
-                                       } else {
-                                               throw new Exception ( "Unexepected addition input: " + left);
                                        }
                                } catch (OverflowException){
-                                       Error_CompileTimeOverflow (loc);
+                                       Error_CompileTimeOverflow (ec, loc);
                                }
 
                                return result;
@@ -406,7 +428,7 @@ namespace Mono.CSharp {
                                        }
 
                                        // U has to be implicitly convetible to E.base
-                                       right = right.ConvertImplicitly (lc.Child.Type);
+                                       right = right.ConvertImplicitly (ec, lc.Child.Type);
                                        if (right == null)
                                                return null;
 
@@ -414,14 +436,14 @@ namespace Mono.CSharp {
                                        if (result == null)
                                                return null;
 
-                                       result = result.TryReduce (ec, lt, loc);
+                                       result = result.Resolve (ec).TryReduce (ec, lt, loc);
                                        if (result == null)
                                                return null;
 
                                        return new EnumConstant (result, lt);
                                }
 
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                try {
@@ -506,13 +528,13 @@ namespace Mono.CSharp {
                                                throw new Exception ( "Unexepected subtraction input: " + left);
                                        }
                                } catch (OverflowException){
-                                       Error_CompileTimeOverflow (loc);
+                                       Error_CompileTimeOverflow (ec, loc);
                                }
 
                                return result;
                                
                        case Binary.Operator.Multiply:
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                try {
@@ -597,12 +619,12 @@ namespace Mono.CSharp {
                                                throw new Exception ( "Unexepected multiply input: " + left);
                                        }
                                } catch (OverflowException){
-                                       Error_CompileTimeOverflow (loc);
+                                       Error_CompileTimeOverflow (ec, loc);
                                }
                                break;
 
                        case Binary.Operator.Division:
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                try {
@@ -687,16 +709,16 @@ namespace Mono.CSharp {
                                                throw new Exception ( "Unexepected division input: " + left);
                                        }
                                } catch (OverflowException){
-                                       Error_CompileTimeOverflow (loc);
+                                       Error_CompileTimeOverflow (ec, loc);
 
                                } catch (DivideByZeroException) {
-                                       Report.Error (020, loc, "Division by constant zero");
+                                       ec.Report.Error (20, loc, "Division by constant zero");
                                }
                                
                                break;
                                
                        case Binary.Operator.Modulus:
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                try {
@@ -770,9 +792,9 @@ namespace Mono.CSharp {
                                                throw new Exception ( "Unexepected modulus input: " + left);
                                        }
                                } catch (DivideByZeroException){
-                                       Report.Error (020, loc, "Division by constant zero");
+                                       ec.Report.Error (20, loc, "Division by constant zero");
                                } catch (OverflowException){
-                                       Error_CompileTimeOverflow (loc);
+                                       Error_CompileTimeOverflow (ec, loc);
                                }
                                break;
 
@@ -780,9 +802,9 @@ namespace Mono.CSharp {
                                // There is no overflow checking on left shift
                                //
                        case Binary.Operator.LeftShift:
-                               IntConstant ic = right.ConvertImplicitly (TypeManager.int32_type) as IntConstant;
+                               IntConstant ic = right.ConvertImplicitly (ec, TypeManager.int32_type) as IntConstant;
                                if (ic == null){
-                                       Binary.Error_OperatorCannotBeApplied (left, right, oper, loc);
+                                       Binary.Error_OperatorCannotBeApplied (ec, left, right, oper, loc);
                                        return null;
                                }
 
@@ -794,20 +816,20 @@ namespace Mono.CSharp {
                                if (left.Type == TypeManager.uint32_type)
                                        return new UIntConstant (((UIntConstant)left).Value << lshift_val, left.Location);
 
-                               left = left.ConvertImplicitly (TypeManager.int32_type);
+                               left = left.ConvertImplicitly (ec, TypeManager.int32_type);
                                if (left.Type == TypeManager.int32_type)
                                        return new IntConstant (((IntConstant)left).Value << lshift_val, left.Location);
 
-                               Binary.Error_OperatorCannotBeApplied (left, right, oper, loc);
+                               Binary.Error_OperatorCannotBeApplied (ec, left, right, oper, loc);
                                break;
 
                                //
                                // There is no overflow checking on right shift
                                //
                        case Binary.Operator.RightShift:
-                               IntConstant sic = right.ConvertImplicitly (TypeManager.int32_type) as IntConstant;
+                               IntConstant sic = right.ConvertImplicitly (ec, TypeManager.int32_type) as IntConstant;
                                if (sic == null){
-                                       Binary.Error_OperatorCannotBeApplied (left, right, oper, loc); ;
+                                       Binary.Error_OperatorCannotBeApplied (ec, left, right, oper, loc); ;
                                        return null;
                                }
                                int rshift_val = sic.Value;
@@ -818,35 +840,29 @@ namespace Mono.CSharp {
                                if (left.Type == TypeManager.uint32_type)
                                        return new UIntConstant (((UIntConstant)left).Value >> rshift_val, left.Location);
 
-                               left = left.ConvertImplicitly (TypeManager.int32_type);
+                               left = left.ConvertImplicitly (ec, TypeManager.int32_type);
                                if (left.Type == TypeManager.int32_type)
                                        return new IntConstant (((IntConstant)left).Value >> rshift_val, left.Location);
 
-                               Binary.Error_OperatorCannotBeApplied (left, right, oper, loc);
+                               Binary.Error_OperatorCannotBeApplied (ec, left, right, oper, loc);
                                break;
 
                        case Binary.Operator.Equality:
-                               if (left is NullLiteral){
-                                       if (right is NullLiteral)
-                                               return new BoolConstant (true, left.Location);
-                                       else if (right is StringConstant)
-                                               return new BoolConstant (
-                                                       ((StringConstant) right).Value == null, left.Location);
-                               } else if (right is NullLiteral) {
-                                       if (left is NullLiteral)
-                                               return new BoolConstant (true, left.Location);
-                                       else if (left is StringConstant)
+                               if (TypeManager.IsReferenceType (lt) && TypeManager.IsReferenceType (lt)) {
+                                       if (left.IsNull || right.IsNull) {
+                                               return ReducedExpression.Create (
+                                                       new BoolConstant (left.IsNull == right.IsNull, left.Location).Resolve (ec),
+                                                       new Binary (oper, left, right, loc));
+                                       }
+
+                                       if (left is StringConstant && right is StringConstant)
                                                return new BoolConstant (
-                                                       ((StringConstant) left).Value == null, left.Location);
-                               }
-                               if (left is StringConstant && right is StringConstant){
-                                       return new BoolConstant (
-                                               ((StringConstant) left).Value ==
-                                               ((StringConstant) right).Value, left.Location);
-                                       
+                                                       ((StringConstant) left).Value == ((StringConstant) right).Value, left.Location);
+
+                                       return null;
                                }
 
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                bool_res = false;
@@ -874,27 +890,21 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res, left.Location);
 
                        case Binary.Operator.Inequality:
-                               if (left is NullLiteral) {
-                                       if (right is NullLiteral)
-                                               return new BoolConstant (false, left.Location);
-                                       else if (right is StringConstant)
-                                               return new BoolConstant (
-                                                       ((StringConstant) right).Value != null, left.Location);
-                               } else if (right is NullLiteral) {
-                                       if (left is NullLiteral)
-                                               return new BoolConstant (false, left.Location);
-                                       else if (left is StringConstant)
+                               if (TypeManager.IsReferenceType (lt) && TypeManager.IsReferenceType (lt)) {
+                                       if (left.IsNull || right.IsNull) {
+                                               return ReducedExpression.Create (
+                                                       new BoolConstant (left.IsNull != right.IsNull, left.Location).Resolve (ec),
+                                                       new Binary (oper, left, right, loc));
+                                       }
+
+                                       if (left is StringConstant && right is StringConstant)
                                                return new BoolConstant (
-                                                       ((StringConstant) left).Value != null, left.Location);
-                               }
-                               if (left is StringConstant && right is StringConstant){
-                                       return new BoolConstant (
-                                               ((StringConstant) left).Value !=
-                                               ((StringConstant) right).Value, left.Location);
-                                       
+                                                       ((StringConstant) left).Value != ((StringConstant) right).Value, left.Location);
+
+                                       return null;
                                }
 
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                bool_res = false;
@@ -922,7 +932,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res, left.Location);
 
                        case Binary.Operator.LessThan:
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                bool_res = false;
@@ -950,7 +960,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res, left.Location);
                                
                        case Binary.Operator.GreaterThan:
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                bool_res = false;
@@ -978,7 +988,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res, left.Location);
 
                        case Binary.Operator.GreaterThanOrEqual:
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                bool_res = false;
@@ -1006,7 +1016,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res, left.Location);
 
                        case Binary.Operator.LessThanOrEqual:
-                               if (!DoBinaryNumericPromotions (ref left, ref right))
+                               if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
                                bool_res = false;