2008-09-24 Miguel de Icaza <miguel@novell.com>
[mono.git] / mcs / mcs / constant.cs
index 5dae2a6a55050d96ee35abcaf159addffbb21001..dd4b3e993701e9e1087a19e6c0145ec15d7eb22e 100644 (file)
@@ -5,14 +5,15 @@
 //   Miguel de Icaza (miguel@ximian.com)
 //   Marek Safar (marek.safar@seznam.cz)
 //
-// (C) 2001 Ximian, Inc.
-//
+// Copyright 2001-2003 Ximian, Inc.
+// Copyright 2003-2008 Novell, Inc.
 //
 
 namespace Mono.CSharp {
 
        using System;
        using System.Reflection.Emit;
+       using System.Collections;
 
        /// <summary>
        ///   Base class for constants and literals.
@@ -39,14 +40,14 @@ namespace Mono.CSharp {
                        return this.GetType ().Name + " (" + AsString () + ")";
                }
 
-               public override bool GetAttributableValue (Type value_type, out object value)
+               public override bool GetAttributableValue (EmitContext ec, Type value_type, out object value)
                {
                        if (value_type == TypeManager.object_type) {
                                value = GetTypedValue ();
                                return true;
                        }
 
-                       Constant c = ImplicitConversionRequired (value_type, loc);
+                       Constant c = ImplicitConversionRequired (ec, value_type, loc);
                        if (c == null) {
                                value = null;
                                return false;
@@ -75,11 +76,23 @@ namespace Mono.CSharp {
                        return this;
                }
 
-               public Constant ImplicitConversionRequired (Type type, Location loc)
+               public override void Error_ValueCannotBeConverted (EmitContext ec, Location loc, Type target, bool expl)
+               {
+                       if (!expl && IsLiteral && 
+                               (TypeManager.IsPrimitiveType (target) || type == TypeManager.decimal_type) &&
+                               (TypeManager.IsPrimitiveType (type) || type == TypeManager.decimal_type)) {
+                               Report.Error (31, loc, "Constant value `{0}' cannot be converted to a `{1}'",
+                                       AsString (), TypeManager.CSharpName (target));
+                       } else {
+                               base.Error_ValueCannotBeConverted (ec, loc, target, expl);
+                       }
+               }
+
+               public Constant ImplicitConversionRequired (EmitContext ec, Type type, Location loc)
                {
                        Constant c = ConvertImplicitly (type);
                        if (c == null)
-                               Error_ValueCannotBeConverted (null, loc, type, false);
+                               Error_ValueCannotBeConverted (ec, loc, type, false);
                        return c;
                }
 
@@ -138,17 +151,27 @@ namespace Mono.CSharp {
                        if (t == TypeManager.decimal_type)
                                return new DecimalConstant ((decimal) v, loc);
                        if (TypeManager.IsEnumType (t)) {
-                               Type real_type = TypeManager.TypeToCoreType (v.GetType ());
-                               if (real_type == t)
-                                       real_type = System.Enum.GetUnderlyingType (real_type);
+                               Type real_type = TypeManager.GetEnumUnderlyingType (t);
                                return new EnumConstant (CreateConstant (real_type, v, loc), t);
                        } 
                        if (v == null && !TypeManager.IsValueType (t))
-                               return new EmptyConstantCast (new NullConstant (loc), t);
+                               return new EmptyConstantCast (new NullLiteral (loc), t);
 
                        throw new Exception ("Unknown type for constant (" + t +
                                        "), details: " + v);
                }
+
+               public override Expression CreateExpressionTree (EmitContext ec)
+               {
+                       ArrayList args = new ArrayList (2);
+                       args.Add (new Argument (this));
+                       args.Add (new Argument (
+                               new TypeOf (new TypeExpression (type, loc), loc)));
+
+                       return CreateExpressionFactoryCall ("Constant", args);
+               }
+
+
                /// <summary>
                /// Maybe ConvertTo name is better. It tries to convert `this' constant to target_type.
                /// It throws OverflowException 
@@ -165,9 +188,14 @@ namespace Mono.CSharp {
                                return TryReduce (ec, target_type);
                        }
                        catch (OverflowException) {
-                               Report.Error (221, loc, "Constant value `{0}' cannot be converted to a `{1}' (use `unchecked' syntax to override)",
-                                       GetValue ().ToString (), TypeManager.CSharpName (target_type));
-                               return null;
+                               if (ec.ConstantCheckState) {                            
+                                       Report.Error (221, loc, "Constant value `{0}' cannot be converted to a `{1}' (use `unchecked' syntax to override)",
+                                               GetValue ().ToString (), TypeManager.CSharpName (target_type));
+                               } else {
+                                       Error_ValueCannotBeConverted (ec, loc, target_type, false);
+                               }
+
+                               return New.Constantify (target_type);
                        }
                }
 
@@ -177,7 +205,7 @@ namespace Mono.CSharp {
                                return this;
 
                        if (TypeManager.IsEnumType (target_type)) {
-                               Constant c = TryReduce (ec, TypeManager.EnumToUnderlying (target_type));
+                               Constant c = TryReduce (ec, TypeManager.GetEnumUnderlyingType (target_type));
                                if (c == null)
                                        return null;
 
@@ -209,6 +237,13 @@ namespace Mono.CSharp {
                        get;
                }
 
+               //
+               // When constant is declared as literal
+               //
+               public virtual bool IsLiteral {
+                       get { return false; }
+               }
+
                //
                // Returns true iff 1) the stack type of this is one of Object, 
                // int32, int64 and 2) this == 0 or this == null.
@@ -217,10 +252,20 @@ namespace Mono.CSharp {
                        get { return false; }
                }
 
+               public override void EmitSideEffect (EmitContext ec)
+               {
+                       // do nothing
+               }
+
                protected override void CloneTo (CloneContext clonectx, Expression target)
                {
                        // CloneTo: Nothing, we do not keep any state on this expression
                }
+
+               public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
+               {
+                       // A constant cannot be of generic type
+               }
        }
 
        public abstract class IntegralConstant : Constant {
@@ -1138,6 +1183,13 @@ namespace Mono.CSharp {
                                ig.Emit (OpCodes.Conv_I8);
                                return;
                        }
+
+                       if (l >= 0 && l <= uint.MaxValue) {
+                               IntLiteral.EmitInt (ig, unchecked ((int) l));
+                               ig.Emit (OpCodes.Conv_U8);
+                               return;
+                       }
+                       
                        ig.Emit (OpCodes.Ldc_I8, l);
                }
 
@@ -1398,56 +1450,56 @@ namespace Mono.CSharp {
                {
                        if (target_type == TypeManager.byte_type) {
                                if (in_checked_context){
-                                       if (Value < byte.MinValue || Value > byte.MaxValue)
+                                       if (Value < byte.MinValue || Value > byte.MaxValue || float.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new ByteConstant ((byte) Value, Location);
                        }
                        if (target_type == TypeManager.sbyte_type) {
                                if (in_checked_context){
-                                       if (Value <  sbyte.MinValue || Value > sbyte.MaxValue)
+                                       if (Value < sbyte.MinValue || Value > sbyte.MaxValue || float.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new SByteConstant ((sbyte) Value, Location);
                        }
                        if (target_type == TypeManager.short_type) {
                                if (in_checked_context){
-                                       if (Value < short.MinValue || Value > short.MaxValue)
+                                       if (Value < short.MinValue || Value > short.MaxValue || float.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new ShortConstant ((short) Value, Location);
                        }
                        if (target_type == TypeManager.ushort_type) {
                                if (in_checked_context){
-                                       if (Value < ushort.MinValue || Value > ushort.MaxValue)
+                                       if (Value < ushort.MinValue || Value > ushort.MaxValue || float.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new UShortConstant ((ushort) Value, Location);
                        }
                        if (target_type == TypeManager.int32_type) {
                                if (in_checked_context){
-                                       if (Value < int.MinValue || Value > int.MaxValue)
+                                       if (Value < int.MinValue || Value > int.MaxValue || float.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new IntConstant ((int) Value, Location);
                        }
                        if (target_type == TypeManager.uint32_type) {
                                if (in_checked_context){
-                                       if (Value < uint.MinValue || Value > uint.MaxValue)
+                                       if (Value < uint.MinValue || Value > uint.MaxValue || float.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new UIntConstant ((uint) Value, Location);
                        }
                        if (target_type == TypeManager.int64_type) {
                                if (in_checked_context){
-                                       if (Value < long.MinValue || Value > long.MaxValue)
+                                       if (Value < long.MinValue || Value > long.MaxValue || float.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new LongConstant ((long) Value, Location);
                        }
                        if (target_type == TypeManager.uint64_type) {
                                if (in_checked_context){
-                                       if (Value < ulong.MinValue || Value > ulong.MaxValue)
+                                       if (Value < ulong.MinValue || Value > ulong.MaxValue || float.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new ULongConstant ((ulong) Value, Location);
@@ -1456,7 +1508,7 @@ namespace Mono.CSharp {
                                return new DoubleConstant ((double) Value, Location);
                        if (target_type == TypeManager.char_type) {
                                if (in_checked_context){
-                                       if (Value < (float) char.MinValue || Value > (float) char.MaxValue)
+                                       if (Value < (float) char.MinValue || Value > (float) char.MaxValue || float.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new CharConstant ((char) Value, Location);
@@ -1516,56 +1568,56 @@ namespace Mono.CSharp {
                {
                        if (target_type == TypeManager.byte_type) {
                                if (in_checked_context){
-                                       if (Value < Byte.MinValue || Value > Byte.MaxValue)
+                                       if (Value < Byte.MinValue || Value > Byte.MaxValue || double.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new ByteConstant ((byte) Value, Location);
                        }
                        if (target_type == TypeManager.sbyte_type) {
                                if (in_checked_context){
-                                       if (Value < SByte.MinValue || Value > SByte.MaxValue)
+                                       if (Value < SByte.MinValue || Value > SByte.MaxValue || double.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new SByteConstant ((sbyte) Value, Location);
                        }
                        if (target_type == TypeManager.short_type) {
                                if (in_checked_context){
-                                       if (Value < short.MinValue || Value > short.MaxValue)
+                                       if (Value < short.MinValue || Value > short.MaxValue || double.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new ShortConstant ((short) Value, Location);
                        }
                        if (target_type == TypeManager.ushort_type) {
                                if (in_checked_context){
-                                       if (Value < ushort.MinValue || Value > ushort.MaxValue)
+                                       if (Value < ushort.MinValue || Value > ushort.MaxValue || double.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new UShortConstant ((ushort) Value, Location);
                        }
                        if (target_type == TypeManager.int32_type) {
                                if (in_checked_context){
-                                       if (Value < int.MinValue || Value > int.MaxValue)
+                                       if (Value < int.MinValue || Value > int.MaxValue || double.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new IntConstant ((int) Value, Location);
                        }
                        if (target_type == TypeManager.uint32_type) {
                                if (in_checked_context){
-                                       if (Value < uint.MinValue || Value > uint.MaxValue)
+                                       if (Value < uint.MinValue || Value > uint.MaxValue || double.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new UIntConstant ((uint) Value, Location);
                        }
                        if (target_type == TypeManager.int64_type) {
                                if (in_checked_context){
-                                       if (Value < long.MinValue || Value > long.MaxValue)
+                                       if (Value < long.MinValue || Value > long.MaxValue || double.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new LongConstant ((long) Value, Location);
                        }
                        if (target_type == TypeManager.uint64_type) {
                                if (in_checked_context){
-                                       if (Value < ulong.MinValue || Value > ulong.MaxValue)
+                                       if (Value < ulong.MinValue || Value > ulong.MaxValue || double.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new ULongConstant ((ulong) Value, Location);
@@ -1574,7 +1626,7 @@ namespace Mono.CSharp {
                                return new FloatConstant ((float) Value, Location);
                        if (target_type == TypeManager.char_type) {
                                if (in_checked_context){
-                                       if (Value < (double) char.MinValue || Value > (double) char.MaxValue)
+                                       if (Value < (double) char.MinValue || Value > (double) char.MaxValue || double.IsNaN (Value))
                                                throw new OverflowException ();
                                }
                                return new CharConstant ((char) Value, Location);
@@ -1600,7 +1652,7 @@ namespace Mono.CSharp {
 
                override public string AsString ()
                {
-                       return Value.ToString ();
+                       return Value.ToString () + "M";
                }
 
                public override object GetValue ()
@@ -1617,6 +1669,14 @@ namespace Mono.CSharp {
 
                        if (power == 0 && Value <= int.MaxValue && Value >= int.MinValue)
                        {
+                               if (TypeManager.void_decimal_ctor_int_arg == null) {
+                                       TypeManager.void_decimal_ctor_int_arg = TypeManager.GetPredefinedConstructor (
+                                               TypeManager.decimal_type, loc, TypeManager.int32_type);
+
+                                       if (TypeManager.void_decimal_ctor_int_arg == null)
+                                               return;
+                               }
+
                                IntConstant.EmitInt (ig, (int)Value);
                                ig.Emit (OpCodes.Newobj, TypeManager.void_decimal_ctor_int_arg);
                                return;
@@ -1638,6 +1698,15 @@ namespace Mono.CSharp {
                        // power
                        IntConstant.EmitInt (ig, power);
 
+                       if (TypeManager.void_decimal_ctor_five_args == null) {
+                               TypeManager.void_decimal_ctor_five_args = TypeManager.GetPredefinedConstructor (
+                                       TypeManager.decimal_type, loc, TypeManager.int32_type, TypeManager.int32_type,
+                                       TypeManager.int32_type, TypeManager.bool_type, TypeManager.byte_type);
+
+                               if (TypeManager.void_decimal_ctor_five_args == null)
+                                       return;
+                       }
+
                        ig.Emit (OpCodes.Newobj, TypeManager.void_decimal_ctor_five_args);
                }
 
@@ -1712,10 +1781,26 @@ namespace Mono.CSharp {
                
                public override void Emit (EmitContext ec)
                {
-                       if (Value == null)
+                       if (Value == null) {
                                ec.ig.Emit (OpCodes.Ldnull);
-                       else
-                               ec.ig.Emit (OpCodes.Ldstr, Value);
+                               return;
+                       }
+
+                       //
+                       // Use string.Empty for both literals and constants even if
+                       // it's not allowed at language level
+                       //
+                       if (Value.Length == 0 && RootContext.Optimize && ec.TypeContainer.TypeBuilder != TypeManager.string_type) {                     
+                               if (TypeManager.string_empty == null)
+                                       TypeManager.string_empty = TypeManager.GetPredefinedField (TypeManager.string_type, "Empty", loc);
+
+                               if (TypeManager.string_empty != null) {
+                                       ec.ig.Emit (OpCodes.Ldsfld, TypeManager.string_empty);
+                                       return;
+                               }
+                       }
+
+                       ec.ig.Emit (OpCodes.Ldstr, Value);
                }
 
                public override Constant Increment ()
@@ -1741,6 +1826,69 @@ namespace Mono.CSharp {
                }
        }
 
-}
+       /// <summary>
+       ///   The value is constant, but when emitted has a side effect.  This is
+       ///   used by BitwiseAnd to ensure that the second expression is invoked
+       ///   regardless of the value of the left side.  
+       /// </summary>
+       
+       public class SideEffectConstant : Constant {
+               public Constant value;
+               Expression side_effect;
+               
+               public SideEffectConstant (Constant value, Expression side_effect, Location loc) : base (loc)
+               {
+                       this.value = value;
+                       while (side_effect is SideEffectConstant)
+                               side_effect = ((SideEffectConstant) side_effect).side_effect;
+                       this.side_effect = side_effect;
+                       eclass = ExprClass.Value;
+                       type = value.Type;
+               }
+
+               public override string AsString ()
+               {
+                       return value.AsString ();
+               }
+
+               public override object GetValue ()
+               {
+                       return value.GetValue ();
+               }
+
+               public override void Emit (EmitContext ec)
+               {
+                       side_effect.EmitSideEffect (ec);
+                       value.Emit (ec);
+               }
+
+               public override void EmitSideEffect (EmitContext ec)
+               {
+                       side_effect.EmitSideEffect (ec);
+                       value.EmitSideEffect (ec);
+               }
 
+               public override bool IsDefaultValue {
+                       get { return value.IsDefaultValue; }
+               }
 
+               public override Constant Increment ()
+               {
+                       throw new NotSupportedException ();
+               }
+               
+               public override bool IsNegative {
+                       get { return value.IsNegative; }
+               }
+
+               public override bool IsZeroInteger {
+                       get { return value.IsZeroInteger; }
+               }
+
+               public override Constant ConvertExplicitly (bool in_checked_context, Type target_type)
+               {
+                       Constant new_value = value.ConvertExplicitly (in_checked_context, target_type);
+                       return new_value == null ? null : new SideEffectConstant (new_value, side_effect, new_value.Location);
+               }
+       }
+}