2008-11-24 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / mcs / cfold.cs
index a8775a54f93ee53e749b0d1a3dede4b27711416e..47c76af3ed8226bbd8f83f21404ba0cb4aecb1d1 100644 (file)
@@ -5,16 +5,16 @@
 //   Miguel de Icaza (miguel@ximian.com)
 //   Marek Safar (marek.safar@seznam.cz)
 //
-// (C) 2002, 2003 Ximian, Inc.
-//
-
+// Copyright 2002, 2003 Ximian, Inc.
+// Copyright 2003-2008, Novell, Inc.
+// 
 using System;
 
 namespace Mono.CSharp {
 
        public class ConstantFold {
 
-               static Type[] binary_promotions = new Type[] { 
+               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 };
 
@@ -25,21 +25,40 @@ namespace Mono.CSharp {
                // On success, the types of `lc' and `rc' on output will always match,
                // and the pair will be one of:
                //
-               static void DoBinaryNumericPromotions (ref Constant left, ref Constant right)
+               static bool DoBinaryNumericPromotions (ref Constant left, ref Constant right)
                {
                        Type ltype = left.Type;
                        Type rtype = right.Type;
 
                        foreach (Type t in binary_promotions) {
-                               if (t == ltype || t == rtype) {
-                                       left = left.ConvertImplicitly (t);
-                                       right = right.ConvertImplicitly (t);
-                                       return;
-                               }
+                               if (t == ltype)
+                                       return t == rtype || ConvertPromotion (ref right, ref left, t);
+
+                               if (t == rtype)
+                                       return t == ltype || ConvertPromotion (ref left, ref right, t);
                        }
 
                        left = left.ConvertImplicitly (TypeManager.int32_type);
                        right = right.ConvertImplicitly (TypeManager.int32_type);
+                       return left != null && right != null;
+               }
+
+               static bool ConvertPromotion (ref Constant prim, ref Constant second, Type type)
+               {
+                       Constant c = prim.ConvertImplicitly (type);
+                       if (c != null) {
+                               prim = c;
+                               return true;
+                       }
+
+                       if (type == TypeManager.uint32_type) {
+                               type = TypeManager.int64_type;
+                               prim = prim.ConvertImplicitly (type);
+                               second = second.ConvertImplicitly (type);
+                               return prim != null && second != null;
+                       }
+
+                       return false;
                }
 
                internal static void Error_CompileTimeOverflow (Location loc)
@@ -55,20 +74,35 @@ namespace Mono.CSharp {
                static public Constant BinaryFold (EmitContext 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);
+                       }
+
                        Type lt = left.Type;
                        Type rt = right.Type;
                        bool bool_res;
-                       Constant result = null;
 
                        if (lt == TypeManager.bool_type && lt == rt) {
-                               bool lv = ((BoolConstant) left ).Value;
-                               bool rv = ((BoolConstant) right).Value;
+                               bool lv = (bool) left.GetValue ();
+                               bool rv = (bool) right.GetValue ();                     
                                switch (oper) {
                                case Binary.Operator.BitwiseAnd:
                                case Binary.Operator.LogicalAnd:
@@ -137,8 +171,7 @@ namespace Mono.CSharp {
 
                        switch (oper){
                        case Binary.Operator.BitwiseOr:
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                if (left is IntConstant){
@@ -165,8 +198,7 @@ namespace Mono.CSharp {
                                break;
                                
                        case Binary.Operator.BitwiseAnd:
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
                                
                                ///
@@ -196,8 +228,7 @@ namespace Mono.CSharp {
                                break;
 
                        case Binary.Operator.ExclusiveOr:
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
                                
                                if (left is IntConstant){
@@ -237,6 +268,9 @@ namespace Mono.CSharp {
                                        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)"
@@ -266,8 +300,7 @@ namespace Mono.CSharp {
                                        return new EnumConstant (result, lt);
                                }
 
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                try {
@@ -388,8 +421,7 @@ namespace Mono.CSharp {
                                        return new EnumConstant (result, lt);
                                }
 
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                try {
@@ -480,8 +512,7 @@ namespace Mono.CSharp {
                                return result;
                                
                        case Binary.Operator.Multiply:
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                try {
@@ -571,8 +602,7 @@ namespace Mono.CSharp {
                                break;
 
                        case Binary.Operator.Division:
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                try {
@@ -666,8 +696,7 @@ namespace Mono.CSharp {
                                break;
                                
                        case Binary.Operator.Modulus:
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                try {
@@ -753,7 +782,7 @@ namespace Mono.CSharp {
                        case Binary.Operator.LeftShift:
                                IntConstant ic = right.ConvertImplicitly (TypeManager.int32_type) as IntConstant;
                                if (ic == null){
-                                       Binary.Error_OperatorCannotBeApplied (loc, "<<", lt, rt);
+                                       Binary.Error_OperatorCannotBeApplied (left, right, oper, loc);
                                        return null;
                                }
 
@@ -769,7 +798,7 @@ namespace Mono.CSharp {
                                if (left.Type == TypeManager.int32_type)
                                        return new IntConstant (((IntConstant)left).Value << lshift_val, left.Location);
 
-                               Binary.Error_OperatorCannotBeApplied (loc, "<<", lt, rt);
+                               Binary.Error_OperatorCannotBeApplied (left, right, oper, loc);
                                break;
 
                                //
@@ -778,7 +807,7 @@ namespace Mono.CSharp {
                        case Binary.Operator.RightShift:
                                IntConstant sic = right.ConvertImplicitly (TypeManager.int32_type) as IntConstant;
                                if (sic == null){
-                                       Binary.Error_OperatorCannotBeApplied (loc, ">>", lt, rt);
+                                       Binary.Error_OperatorCannotBeApplied (left, right, oper, loc); ;
                                        return null;
                                }
                                int rshift_val = sic.Value;
@@ -793,18 +822,18 @@ namespace Mono.CSharp {
                                if (left.Type == TypeManager.int32_type)
                                        return new IntConstant (((IntConstant)left).Value >> rshift_val, left.Location);
 
-                               Binary.Error_OperatorCannotBeApplied (loc, ">>", lt, rt);
+                               Binary.Error_OperatorCannotBeApplied (left, right, oper, loc);
                                break;
 
                        case Binary.Operator.Equality:
-                               if (left is NullConstant){
-                                       if (right is NullConstant)
+                               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 NullConstant){
-                                       if (left is NullConstant)
+                               } else if (right is NullLiteral) {
+                                       if (left is NullLiteral)
                                                return new BoolConstant (true, left.Location);
                                        else if (left is StringConstant)
                                                return new BoolConstant (
@@ -817,8 +846,7 @@ namespace Mono.CSharp {
                                        
                                }
 
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                bool_res = false;
@@ -846,14 +874,14 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res, left.Location);
 
                        case Binary.Operator.Inequality:
-                               if (left is NullConstant){
-                                       if (right is NullConstant)
+                               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 NullConstant){
-                                       if (left is NullConstant)
+                               } else if (right is NullLiteral) {
+                                       if (left is NullLiteral)
                                                return new BoolConstant (false, left.Location);
                                        else if (left is StringConstant)
                                                return new BoolConstant (
@@ -866,8 +894,7 @@ namespace Mono.CSharp {
                                        
                                }
 
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                bool_res = false;
@@ -895,8 +922,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res, left.Location);
 
                        case Binary.Operator.LessThan:
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                bool_res = false;
@@ -924,8 +950,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res, left.Location);
                                
                        case Binary.Operator.GreaterThan:
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                bool_res = false;
@@ -953,8 +978,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res, left.Location);
 
                        case Binary.Operator.GreaterThanOrEqual:
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                bool_res = false;
@@ -982,8 +1006,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res, left.Location);
 
                        case Binary.Operator.LessThanOrEqual:
-                               DoBinaryNumericPromotions (ref left, ref right);
-                               if (left == null || right == null)
+                               if (!DoBinaryNumericPromotions (ref left, ref right))
                                        return null;
 
                                bool_res = false;