2004-05-24 Anders Carlsson <andersca@gnome.org>
[mono.git] / mcs / mcs / cfold.cs
index 1c564ce3e4caea4cfd952da1c5ef418c9a8f9473..8ffdfb7503002850322c73e74cf5581dfbd94ffe 100755 (executable)
@@ -4,7 +4,7 @@
 // Author:
 //   Miguel de Icaza (miguel@ximian.com)
 //
-// (C) 2002 Ximian, Inc.
+// (C) 2002, 2003 Ximian, Inc.
 //
 
 using System;
@@ -26,8 +26,10 @@ namespace Mono.CSharp {
                //   (long, long)
                //   (uint, uint)
                //   (int, int)
+               //   (short, short)   (Happens with enumerations with underlying short type)
+               //   (ushort, ushort) (Happens with enumerations with underlying short type)
                //
-               static void DoConstantNumericPromotions (Binary.Operator oper,
+               static void DoConstantNumericPromotions (EmitContext ec, Binary.Operator oper,
                                                         ref Constant left, ref Constant right,
                                                         Location loc)
                {
@@ -57,16 +59,22 @@ namespace Mono.CSharp {
                                // converted to type ulong.  or an error ocurrs if the other
                                // operand is of type sbyte, short, int or long
                                //
+#if WRONG
                                Constant match, other;
+#endif
                                        
                                if (left is ULongConstant){
+#if WRONG
                                        other = right;
                                        match = left;
+#endif
                                        if (!(right is ULongConstant))
                                                right = right.ToULong (loc);
                                } else {
+#if WRONG
                                        other = left;
                                        match = right;
+#endif
                                        left = left.ToULong (loc);
                                }
 
@@ -93,14 +101,69 @@ namespace Mono.CSharp {
                        } else if (left is UIntConstant || right is UIntConstant){
                                //
                                // If either operand is of type uint, and the other
-                               // operand is of type sbyte, short or int, othe operands are
+                               // operand is of type sbyte, short or int, the operands are
                                // converted to type long.
                                //
-                               if (!(left is UIntConstant))
-                                       left = left.ToUInt (loc);
-                               else if (!(right is UIntConstant))
-                                       right = right.ToUInt (loc);
+                               Constant other;
+                               if (left is UIntConstant)
+                                       other = right;
+                               else
+                                       other = left;
+
+                               // Nothing to do.
+                               if (other is UIntConstant)
+                                       return;
+
+                               IntConstant ic = other as IntConstant;
+                               if (ic != null){
+                                       if (ic.Value >= 0){
+                                               if (left == other)
+                                                       left = new UIntConstant ((uint) ic.Value);
+                                               else
+                                                       right = new UIntConstant ((uint) ic.Value);
+                                               return;
+                                       }
+                               }
+                               
+                               if (other is SByteConstant || other is ShortConstant || ic != null){
+                                       left = left.ToLong (loc);
+                                       right = right.ToLong (loc);
+                               }
+
+                               return;
+                       } else if (left is EnumConstant || right is EnumConstant){
+                               //
+                               // If either operand is an enum constant, the other one must
+                               // be implicitly convertable to that enum's underlying type.
+                               //
+                               EnumConstant match;
+                               Constant other;
+                               if (left is EnumConstant){
+                                       other = right;
+                                       match = (EnumConstant) left;
+                               } else {
+                                       other = left;
+                                       match = (EnumConstant) right;
+                               }
+
+                               bool need_check = (other is EnumConstant) ||
+                                       ((oper != Binary.Operator.Addition) &&
+                                        (oper != Binary.Operator.Subtraction));
+
+                               if (need_check &&
+                                   !Convert.ImplicitConversionExists (ec, match, other.Type)) {
+                                       Convert.Error_CannotImplicitConversion (loc, match.Type, other.Type);
+                                       left = null;
+                                       right = null;
+                                       return;
+                               }
+
+                               if (left is EnumConstant)
+                                       left = ((EnumConstant) left).Child;
+                               if (right is EnumConstant)
+                                       right = ((EnumConstant) right).Child;
                                return;
+
                        } else {
                                //
                                // Force conversions to int32
@@ -130,7 +193,7 @@ namespace Mono.CSharp {
                        Type rt = right.Type;
                        Type result_type = null;
                        bool bool_res;
-                       
+
                        //
                        // Enumerator folding
                        //
@@ -147,10 +210,12 @@ namespace Mono.CSharp {
                                if (right is EnumConstant)
                                        right = ((EnumConstant) right).Child;
                        }
-                       
+
+                       Type wrap_as;
+                       Constant result = null;
                        switch (oper){
                        case Binary.Operator.BitwiseOr:
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
                                
@@ -191,11 +256,31 @@ namespace Mono.CSharp {
                                                return v;
                                        else
                                                return new EnumConstant (v, result_type);
+                               } else if (left is UShortConstant){
+                                       UShortConstant v;
+                                       ushort res = (ushort) (((UShortConstant)left).Value |
+                                                              ((UShortConstant)right).Value);
+                                       
+                                       v = new UShortConstant (res);
+                                       if (result_type == null)
+                                               return v;
+                                       else
+                                               return new EnumConstant (v, result_type);
+                               } else if (left is ShortConstant){
+                                       ShortConstant v;
+                                       short res = (short) (((ShortConstant)left).Value |
+                                                            ((ShortConstant)right).Value);
+                                       
+                                       v = new ShortConstant (res);
+                                       if (result_type == null)
+                                               return v;
+                                       else
+                                               return new EnumConstant (v, result_type);
                                }
                                break;
                                
                        case Binary.Operator.BitwiseAnd:
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
                                
@@ -236,11 +321,31 @@ namespace Mono.CSharp {
                                                return v;
                                        else
                                                return new EnumConstant (v, result_type);
+                               } else if (left is UShortConstant){
+                                       UShortConstant v;
+                                       ushort res = (ushort) (((UShortConstant)left).Value &
+                                                              ((UShortConstant)right).Value);
+                                       
+                                       v = new UShortConstant (res);
+                                       if (result_type == null)
+                                               return v;
+                                       else
+                                               return new EnumConstant (v, result_type);
+                               } else if (left is ShortConstant){
+                                       ShortConstant v;
+                                       short res = (short) (((ShortConstant)left).Value &
+                                                            ((ShortConstant)right).Value);
+                                       
+                                       v = new ShortConstant (res);
+                                       if (result_type == null)
+                                               return v;
+                                       else
+                                               return new EnumConstant (v, result_type);
                                }
                                break;
 
                        case Binary.Operator.ExclusiveOr:
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
                                
@@ -281,11 +386,30 @@ namespace Mono.CSharp {
                                                return v;
                                        else
                                                return new EnumConstant (v, result_type);
+                               } else if (left is UShortConstant){
+                                       UShortConstant v;
+                                       ushort res = (ushort) (((UShortConstant)left).Value ^
+                                                              ((UShortConstant)right).Value);
+                                       
+                                       v = new UShortConstant (res);
+                                       if (result_type == null)
+                                               return v;
+                                       else
+                                               return new EnumConstant (v, result_type);
+                               } else if (left is ShortConstant){
+                                       ShortConstant v;
+                                       short res = (short)(((ShortConstant)left).Value ^
+                                                           ((ShortConstant)right).Value);
+                                       
+                                       v = new ShortConstant (res);
+                                       if (result_type == null)
+                                               return v;
+                                       else
+                                               return new EnumConstant (v, result_type);
                                }
                                break;
 
                        case Binary.Operator.Addition:
-                               Constant result;
                                bool left_is_string = left is StringConstant;
                                bool right_is_string = right is StringConstant;
 
@@ -294,6 +418,7 @@ namespace Mono.CSharp {
                                // one is a string, and the other is not, then defer
                                // to runtime concatenation
                                //
+                               wrap_as = null;
                                if (left_is_string || right_is_string){
                                        if (left_is_string && right_is_string)
                                                return new StringConstant (
@@ -309,7 +434,6 @@ namespace Mono.CSharp {
                                //
                                // note that E operator + (E x, E y) is invalid
                                //
-                               Type wrap_as = null;
                                if (left is EnumConstant){
                                        if (right is EnumConstant){
                                                return null;
@@ -325,7 +449,7 @@ namespace Mono.CSharp {
                                }
 
                                result = null;
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
 
@@ -409,7 +533,30 @@ namespace Mono.CSharp {
                                        return result;
 
                        case Binary.Operator.Subtraction:
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               //
+                               // handle "E operator - (E x, U y)"
+                               // handle "E operator - (Y y, E x)"
+                               // handle "U operator - (E x, E y)"
+                               //
+                               wrap_as = null;
+                               if (left is EnumConstant){
+                                       if (right is EnumConstant){
+                                               if (left.Type == right.Type)
+                                                       wrap_as = TypeManager.EnumToUnderlying (left.Type);
+                                               else
+                                                       return null;
+                                       }
+                                       if (((EnumConstant) left).Child.Type != right.Type)
+                                               return null;
+
+                                       wrap_as = left.Type;
+                               } else if (right is EnumConstant){
+                                       if (((EnumConstant) right).Child.Type != left.Type)
+                                               return null;
+                                       wrap_as = right.Type;
+                               }
+
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
 
@@ -424,7 +571,7 @@ namespace Mono.CSharp {
                                                        res = unchecked (((DoubleConstant) left).Value -
                                                                         ((DoubleConstant) right).Value);
                                                
-                                               return new DoubleConstant (res);
+                                               result = new DoubleConstant (res);
                                        } else if (left is FloatConstant){
                                                float res;
                                                
@@ -435,7 +582,7 @@ namespace Mono.CSharp {
                                                        res = unchecked (((FloatConstant) left).Value -
                                                                         ((FloatConstant) right).Value);
                                                
-                                               return new FloatConstant (res);
+                                               result = new FloatConstant (res);
                                        } else if (left is ULongConstant){
                                                ulong res;
                                                
@@ -446,7 +593,7 @@ namespace Mono.CSharp {
                                                        res = unchecked (((ULongConstant) left).Value -
                                                                         ((ULongConstant) right).Value);
                                                
-                                               return new ULongConstant (res);
+                                               result = new ULongConstant (res);
                                        } else if (left is LongConstant){
                                                long res;
                                                
@@ -457,7 +604,7 @@ namespace Mono.CSharp {
                                                        res = unchecked (((LongConstant) left).Value -
                                                                         ((LongConstant) right).Value);
                                                
-                                               return new LongConstant (res);
+                                               result = new LongConstant (res);
                                        } else if (left is UIntConstant){
                                                uint res;
                                                
@@ -468,7 +615,7 @@ namespace Mono.CSharp {
                                                        res = unchecked (((UIntConstant) left).Value -
                                                                         ((UIntConstant) right).Value);
                                                
-                                               return new UIntConstant (res);
+                                               result = new UIntConstant (res);
                                        } else if (left is IntConstant){
                                                int res;
 
@@ -479,17 +626,20 @@ namespace Mono.CSharp {
                                                        res = unchecked (((IntConstant) left).Value -
                                                                         ((IntConstant) right).Value);
 
-                                               return new IntConstant (res);
+                                               result = new IntConstant (res);
                                        } else {
                                                throw new Exception ( "Unexepected input: " + left);
                                        }
                                } catch (OverflowException){
                                        Error_CompileTimeOverflow (loc);
                                }
-                               break;
+                               if (wrap_as != null)
+                                       return new EnumConstant (result, wrap_as);
+                               else
+                                       return result;
                                
                        case Binary.Operator.Multiply:
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
 
@@ -569,7 +719,7 @@ namespace Mono.CSharp {
                                break;
 
                        case Binary.Operator.Division:
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
 
@@ -653,7 +803,7 @@ namespace Mono.CSharp {
                                break;
                                
                        case Binary.Operator.Modulus:
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
 
@@ -727,6 +877,8 @@ namespace Mono.CSharp {
                                        } else {
                                                throw new Exception ( "Unexepected input: " + left);
                                        }
+                               } catch (DivideByZeroException){
+                                       Report.Error (020, loc, "Division by constant zero");
                                } catch (OverflowException){
                                        Error_CompileTimeOverflow (loc);
                                }
@@ -815,14 +967,27 @@ namespace Mono.CSharp {
                                                ((BoolConstant) right).Value);
                                
                                }
+                               if (left is NullLiteral){
+                                       if (right is NullLiteral)
+                                               return new BoolConstant (true);
+                                       else if (right is StringConstant)
+                                               return new BoolConstant (
+                                                       ((StringConstant) right).Value == null);
+                               } else if (right is NullLiteral){
+                                       if (left is NullLiteral)
+                                               return new BoolConstant (true);
+                                       else if (left is StringConstant)
+                                               return new BoolConstant (
+                                                       ((StringConstant) left).Value == null);
+                               }
                                if (left is StringConstant && right is StringConstant){
                                        return new BoolConstant (
                                                ((StringConstant) left).Value ==
                                                ((StringConstant) right).Value);
                                        
                                }
-                               
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
 
@@ -856,13 +1021,26 @@ namespace Mono.CSharp {
                                                ((BoolConstant) left).Value !=
                                                ((BoolConstant) right).Value);
                                }
+                               if (left is NullLiteral){
+                                       if (right is NullLiteral)
+                                               return new BoolConstant (false);
+                                       else if (right is StringConstant)
+                                               return new BoolConstant (
+                                                       ((StringConstant) right).Value != null);
+                               } else if (right is NullLiteral){
+                                       if (left is NullLiteral)
+                                               return new BoolConstant (false);
+                                       else if (left is StringConstant)
+                                               return new BoolConstant (
+                                                       ((StringConstant) left).Value != null);
+                               }
                                if (left is StringConstant && right is StringConstant){
                                        return new BoolConstant (
                                                ((StringConstant) left).Value !=
                                                ((StringConstant) right).Value);
                                        
                                }
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
 
@@ -891,7 +1069,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res);
 
                        case Binary.Operator.LessThan:
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
 
@@ -920,7 +1098,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res);
                                
                        case Binary.Operator.GreaterThan:
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
 
@@ -949,7 +1127,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res);
 
                        case Binary.Operator.GreaterThanOrEqual:
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;
 
@@ -978,7 +1156,7 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res);
 
                        case Binary.Operator.LessThanOrEqual:
-                               DoConstantNumericPromotions (oper, ref left, ref right, loc);
+                               DoConstantNumericPromotions (ec, oper, ref left, ref right, loc);
                                if (left == null || right == null)
                                        return null;