// // constant.cs: Constants. // // Author: // Miguel de Icaza (miguel@ximian.com) // Marek Safar (marek.safar@gmail.com) // // Copyright 2001-2003 Ximian, Inc. // Copyright 2003-2008 Novell, Inc. // Copyright 2011-2013 Xamarin Inc // using System; using System.Globalization; #if STATIC using IKVM.Reflection.Emit; #else using System.Reflection.Emit; #endif namespace Mono.CSharp { /// /// Base class for constants and literals. /// public abstract class Constant : Expression { static readonly NumberFormatInfo nfi = CultureInfo.InvariantCulture.NumberFormat; protected Constant (Location loc) { this.loc = loc; } override public string ToString () { return this.GetType ().Name + " (" + GetValueAsLiteral () + ")"; } /// /// This is used to obtain the actual value of the literal /// cast into an object. /// public abstract object GetValue (); public abstract long GetValueAsLong (); public abstract string GetValueAsLiteral (); #if !STATIC // // Returns an object value which is typed to contant type // public virtual object GetTypedValue () { return GetValue (); } #endif public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) { if (!expl && IsLiteral && type.BuiltinType != BuiltinTypeSpec.Type.Double && BuiltinTypeSpec.IsPrimitiveTypeOrDecimal (target) && BuiltinTypeSpec.IsPrimitiveTypeOrDecimal (type)) { ec.Report.Error (31, loc, "Constant value `{0}' cannot be converted to a `{1}'", GetValueAsLiteral (), target.GetSignatureForError ()); } else { base.Error_ValueCannotBeConverted (ec, target, expl); } } public Constant ImplicitConversionRequired (ResolveContext ec, TypeSpec type) { Constant c = ConvertImplicitly (type); if (c == null) Error_ValueCannotBeConverted (ec, type, false); return c; } public override bool ContainsEmitWithAwait () { return false; } public virtual Constant ConvertImplicitly (TypeSpec type) { if (this.type == type) return this; if (!Convert.ImplicitNumericConversionExists (this.type, type)) return null; bool fail; object constant_value = ChangeType (GetValue (), type, out fail); if (fail){ // // We should always catch the error before this is ever // reached, by calling Convert.ImplicitStandardConversionExists // throw new InternalErrorException ("Missing constant conversion between `{0}' and `{1}'", Type.GetSignatureForError (), type.GetSignatureForError ()); } return CreateConstantFromValue (type, constant_value, loc); } // // Returns a constant instance based on Type // public static Constant CreateConstantFromValue (TypeSpec t, object v, Location loc) { switch (t.BuiltinType) { case BuiltinTypeSpec.Type.Int: return new IntConstant (t, (int) v, loc); case BuiltinTypeSpec.Type.String: return new StringConstant (t, (string) v, loc); case BuiltinTypeSpec.Type.UInt: return new UIntConstant (t, (uint) v, loc); case BuiltinTypeSpec.Type.Long: return new LongConstant (t, (long) v, loc); case BuiltinTypeSpec.Type.ULong: return new ULongConstant (t, (ulong) v, loc); case BuiltinTypeSpec.Type.Float: return new FloatConstant (t, (float) v, loc); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (t, (double) v, loc); case BuiltinTypeSpec.Type.Short: return new ShortConstant (t, (short) v, loc); case BuiltinTypeSpec.Type.UShort: return new UShortConstant (t, (ushort) v, loc); case BuiltinTypeSpec.Type.SByte: return new SByteConstant (t, (sbyte) v, loc); case BuiltinTypeSpec.Type.Byte: return new ByteConstant (t, (byte) v, loc); case BuiltinTypeSpec.Type.Char: return new CharConstant (t, (char) v, loc); case BuiltinTypeSpec.Type.Bool: return new BoolConstant (t, (bool) v, loc); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (t, (decimal) v, loc); } if (t.IsEnum) { var real_type = EnumSpec.GetUnderlyingType (t); return new EnumConstant (CreateConstantFromValue (real_type, v, loc), t); } if (v == null) { if (t.IsNullableType) return Nullable.LiftedNull.Create (t, loc); if (TypeSpec.IsReferenceType (t)) return new NullConstant (t, loc); } #if STATIC throw new InternalErrorException ("Constant value `{0}' has unexpected underlying type `{1}'", v, t.GetSignatureForError ()); #else return null; #endif } // // Returns a constant instance based on value and type. This is probing version of // CreateConstantFromValue // public static Constant ExtractConstantFromValue (TypeSpec t, object v, Location loc) { switch (t.BuiltinType) { case BuiltinTypeSpec.Type.Int: if (v is int) return new IntConstant (t, (int) v, loc); break; case BuiltinTypeSpec.Type.String: if (v is string) return new StringConstant (t, (string) v, loc); break; case BuiltinTypeSpec.Type.UInt: if (v is uint) return new UIntConstant (t, (uint) v, loc); break; case BuiltinTypeSpec.Type.Long: if (v is long) return new LongConstant (t, (long) v, loc); break; case BuiltinTypeSpec.Type.ULong: if (v is ulong) return new ULongConstant (t, (ulong) v, loc); break; case BuiltinTypeSpec.Type.Float: if (v is float) return new FloatConstant (t, (float) v, loc); break; case BuiltinTypeSpec.Type.Double: if (v is double) return new DoubleConstant (t, (double) v, loc); break; case BuiltinTypeSpec.Type.Short: if (v is short) return new ShortConstant (t, (short) v, loc); break; case BuiltinTypeSpec.Type.UShort: if (v is ushort) return new UShortConstant (t, (ushort) v, loc); break; case BuiltinTypeSpec.Type.SByte: if (v is sbyte) return new SByteConstant (t, (sbyte) v, loc); break; case BuiltinTypeSpec.Type.Byte: if (v is byte) return new ByteConstant (t, (byte) v, loc); break; case BuiltinTypeSpec.Type.Char: if (v is char) return new CharConstant (t, (char) v, loc); break; case BuiltinTypeSpec.Type.Bool: if (v is bool) return new BoolConstant (t, (bool) v, loc); break; case BuiltinTypeSpec.Type.Decimal: if (v is decimal) return new DecimalConstant (t, (decimal) v, loc); break; } if (t.IsEnum) { var real_type = EnumSpec.GetUnderlyingType (t); return new EnumConstant (CreateConstantFromValue (real_type, v, loc), t); } if (v == null) { if (t.IsNullableType) return Nullable.LiftedNull.Create (t, loc); if (TypeSpec.IsReferenceType (t)) return new NullConstant (t, loc); } return null; } public override Expression CreateExpressionTree (ResolveContext ec) { Arguments args = new Arguments (2); args.Add (new Argument (this)); args.Add (new Argument (new TypeOf (type, loc))); return CreateExpressionFactoryCall (ec, "Constant", args); } /// /// Maybe ConvertTo name is better. It tries to convert `this' constant to target_type. /// It throws OverflowException /// // DON'T CALL THIS METHOD DIRECTLY AS IT DOES NOT HANDLE ENUMS public abstract Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type); // This is a custom version of Convert.ChangeType() which works // with the TypeBuilder defined types when compiling corlib. static object ChangeType (object value, TypeSpec targetType, out bool error) { IConvertible convert_value = value as IConvertible; if (convert_value == null) { error = true; return null; } // // We cannot rely on build-in type conversions as they are // more limited than what C# supports. // See char -> float/decimal/double conversion // error = false; try { switch (targetType.BuiltinType) { case BuiltinTypeSpec.Type.Bool: return convert_value.ToBoolean (nfi); case BuiltinTypeSpec.Type.Byte: return convert_value.ToByte (nfi); case BuiltinTypeSpec.Type.Char: return convert_value.ToChar (nfi); case BuiltinTypeSpec.Type.Short: return convert_value.ToInt16 (nfi); case BuiltinTypeSpec.Type.Int: return convert_value.ToInt32 (nfi); case BuiltinTypeSpec.Type.Long: return convert_value.ToInt64 (nfi); case BuiltinTypeSpec.Type.SByte: return convert_value.ToSByte (nfi); case BuiltinTypeSpec.Type.Decimal: if (convert_value.GetType () == typeof (char)) return (decimal) convert_value.ToInt32 (nfi); return convert_value.ToDecimal (nfi); case BuiltinTypeSpec.Type.Double: if (convert_value.GetType () == typeof (char)) return (double) convert_value.ToInt32 (nfi); return convert_value.ToDouble (nfi); case BuiltinTypeSpec.Type.Float: if (convert_value.GetType () == typeof (char)) return (float) convert_value.ToInt32 (nfi); return convert_value.ToSingle (nfi); case BuiltinTypeSpec.Type.String: return convert_value.ToString (nfi); case BuiltinTypeSpec.Type.UShort: return convert_value.ToUInt16 (nfi); case BuiltinTypeSpec.Type.UInt: return convert_value.ToUInt32 (nfi); case BuiltinTypeSpec.Type.ULong: return convert_value.ToUInt64 (nfi); case BuiltinTypeSpec.Type.Object: return value; } } catch { } error = true; return null; } protected override Expression DoResolve (ResolveContext rc) { return this; } // // Attempts to do a compile-time folding of a constant cast and handles // error reporting for constant overlows only, on normal conversion // errors returns null // public Constant Reduce (ResolveContext ec, TypeSpec target_type) { try { return TryReduceConstant (ec, target_type); } catch (OverflowException) { if (ec.ConstantCheckState && Type.BuiltinType != BuiltinTypeSpec.Type.Decimal) { ec.Report.Error (221, loc, "Constant value `{0}' cannot be converted to a `{1}' (use `unchecked' syntax to override)", GetValueAsLiteral (), target_type.GetSignatureForError ()); } else { Error_ValueCannotBeConverted (ec, target_type, false); } return New.Constantify (target_type, loc); } } public Constant TryReduce (ResolveContext rc, TypeSpec targetType) { try { return TryReduceConstant (rc, targetType); } catch (OverflowException) { return null; } } Constant TryReduceConstant (ResolveContext ec, TypeSpec target_type) { if (Type == target_type) { // // Reducing literal value produces a new constant. Syntactically 10 is not same as (int)10 // if (IsLiteral) return CreateConstantFromValue (target_type, GetValue (), loc); return this; } Constant c; if (target_type.IsEnum) { c = TryReduceConstant (ec, EnumSpec.GetUnderlyingType (target_type)); if (c == null) return null; return new EnumConstant (c, target_type); } return ConvertExplicitly (ec.ConstantCheckState, target_type); } /// /// Need to pass type as the constant can require a boxing /// and in such case no optimization is possible /// public bool IsDefaultInitializer (TypeSpec type) { if (type == Type) return IsDefaultValue; return this is NullLiteral; } public abstract bool IsDefaultValue { get; } public abstract bool IsNegative { get; } // // When constant is declared as literal // public virtual bool IsLiteral { get { return false; } } public virtual bool IsOneInteger { get { return false; } } public override bool IsSideEffectFree { get { return true; } } // // Returns true iff 1) the stack type of this is one of Object, // int32, int64 and 2) this == 0 or this == null. // public virtual bool IsZeroInteger { get { return false; } } public override void EmitSideEffect (EmitContext ec) { // do nothing } public sealed override Expression Clone (CloneContext clonectx) { // No cloning is not needed for constants return this; } protected override void CloneTo (CloneContext clonectx, Expression target) { throw new NotSupportedException ("should not be reached"); } public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) { #if STATIC return base.MakeExpression (ctx); #else return System.Linq.Expressions.Expression.Constant (GetTypedValue (), type.GetMetaInfo ()); #endif } public new bool Resolve (ResolveContext rc) { // It exists only as hint not to call Resolve on constants return true; } } public abstract class IntegralConstant : Constant { protected IntegralConstant (TypeSpec type, Location loc) : base (loc) { this.type = type; eclass = ExprClass.Value; } public override void Error_ValueCannotBeConverted (ResolveContext ec, TypeSpec target, bool expl) { try { ConvertExplicitly (true, target); base.Error_ValueCannotBeConverted (ec, target, expl); } catch { ec.Report.Error (31, loc, "Constant value `{0}' cannot be converted to a `{1}'", GetValue ().ToString (), target.GetSignatureForError ()); } } public override string GetValueAsLiteral () { return GetValue ().ToString (); } public abstract Constant Increment (); } public class BoolConstant : Constant { public readonly bool Value; public BoolConstant (BuiltinTypes types, bool val, Location loc) : this (types.Bool, val, loc) { } public BoolConstant (TypeSpec type, bool val, Location loc) : base (loc) { eclass = ExprClass.Value; this.type = type; Value = val; } public override object GetValue () { return (object) Value; } public override string GetValueAsLiteral () { return Value ? "true" : "false"; } public override long GetValueAsLong () { return Value ? 1 : 0; } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode (Value); } public override void Emit (EmitContext ec) { if (Value) ec.EmitInt (1); else ec.EmitInt (0); } public override bool IsDefaultValue { get { return !Value; } } public override bool IsNegative { get { return false; } } public override bool IsZeroInteger { get { return Value == false; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { return null; } } public class ByteConstant : IntegralConstant { public readonly byte Value; public ByteConstant (BuiltinTypes types, byte v, Location loc) : this (types.Byte, v, loc) { } public ByteConstant (TypeSpec type, byte v, Location loc) : base (type, loc) { Value = v; } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode (Value); } public override void Emit (EmitContext ec) { ec.EmitInt (Value); } public override object GetValue () { return Value; } public override long GetValueAsLong () { return Value; } public override Constant Increment () { return new ByteConstant (type, checked ((byte)(Value + 1)), loc); } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsOneInteger { get { return Value == 1; } } public override bool IsNegative { get { return false; } } public override bool IsZeroInteger { get { return Value == 0; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.SByte: if (in_checked_context){ if (Value > SByte.MaxValue) throw new OverflowException (); } return new SByteConstant (target_type, (sbyte) Value, Location); case BuiltinTypeSpec.Type.Short: return new ShortConstant (target_type, (short) Value, Location); case BuiltinTypeSpec.Type.UShort: return new UShortConstant (target_type, (ushort) Value, Location); case BuiltinTypeSpec.Type.Int: return new IntConstant (target_type, (int) Value, Location); case BuiltinTypeSpec.Type.UInt: return new UIntConstant (target_type, (uint) Value, Location); case BuiltinTypeSpec.Type.Long: return new LongConstant (target_type, (long) Value, Location); case BuiltinTypeSpec.Type.ULong: return new ULongConstant (target_type, (ulong) Value, Location); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, Location); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, (double) Value, Location); case BuiltinTypeSpec.Type.Char: return new CharConstant (target_type, (char) Value, Location); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (target_type, (decimal) Value, Location); } return null; } } public class CharConstant : Constant { public readonly char Value; public CharConstant (BuiltinTypes types, char v, Location loc) : this (types.Char, v, loc) { } public CharConstant (TypeSpec type, char v, Location loc) : base (loc) { this.type = type; eclass = ExprClass.Value; Value = v; } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode ((ushort) Value); } public override void Emit (EmitContext ec) { ec.EmitInt (Value); } static string descape (char c) { switch (c){ case '\a': return "\\a"; case '\b': return "\\b"; case '\n': return "\\n"; case '\t': return "\\t"; case '\v': return "\\v"; case '\r': return "\\r"; case '\\': return "\\\\"; case '\f': return "\\f"; case '\0': return "\\0"; case '"': return "\\\""; case '\'': return "\\\'"; } return c.ToString (); } public override object GetValue () { return Value; } public override long GetValueAsLong () { return Value; } public override string GetValueAsLiteral () { return "\"" + descape (Value) + "\""; } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsNegative { get { return false; } } public override bool IsZeroInteger { get { return Value == '\0'; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.Byte: if (in_checked_context) { if (Value < Byte.MinValue || Value > Byte.MaxValue) throw new OverflowException (); } return new ByteConstant (target_type, (byte) Value, Location); case BuiltinTypeSpec.Type.SByte: if (in_checked_context) { if (Value > SByte.MaxValue) throw new OverflowException (); } return new SByteConstant (target_type, (sbyte) Value, Location); case BuiltinTypeSpec.Type.Short: if (in_checked_context) { if (Value > Int16.MaxValue) throw new OverflowException (); } return new ShortConstant (target_type, (short) Value, Location); case BuiltinTypeSpec.Type.Int: return new IntConstant (target_type, (int) Value, Location); case BuiltinTypeSpec.Type.UInt: return new UIntConstant (target_type, (uint) Value, Location); case BuiltinTypeSpec.Type.Long: return new LongConstant (target_type, (long) Value, Location); case BuiltinTypeSpec.Type.ULong: return new ULongConstant (target_type, (ulong) Value, Location); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, Location); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, (double) Value, Location); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (target_type, (decimal) Value, Location); } return null; } } public class SByteConstant : IntegralConstant { public readonly sbyte Value; public SByteConstant (BuiltinTypes types, sbyte v, Location loc) : this (types.SByte, v, loc) { } public SByteConstant (TypeSpec type, sbyte v, Location loc) : base (type, loc) { Value = v; } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode (Value); } public override void Emit (EmitContext ec) { ec.EmitInt (Value); } public override object GetValue () { return Value; } public override long GetValueAsLong () { return Value; } public override Constant Increment () { return new SByteConstant (type, checked((sbyte)(Value + 1)), loc); } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsNegative { get { return Value < 0; } } public override bool IsOneInteger { get { return Value == 1; } } public override bool IsZeroInteger { get { return Value == 0; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.Byte: if (in_checked_context && Value < 0) throw new OverflowException (); return new ByteConstant (target_type, (byte) Value, Location); case BuiltinTypeSpec.Type.Short: return new ShortConstant (target_type, (short) Value, Location); case BuiltinTypeSpec.Type.UShort: if (in_checked_context && Value < 0) throw new OverflowException (); return new UShortConstant (target_type, (ushort) Value, Location); case BuiltinTypeSpec.Type.Int: return new IntConstant (target_type, (int) Value, Location); case BuiltinTypeSpec.Type.UInt: if (in_checked_context && Value < 0) throw new OverflowException (); return new UIntConstant (target_type, (uint) Value, Location); case BuiltinTypeSpec.Type.Long: return new LongConstant (target_type, (long) Value, Location); case BuiltinTypeSpec.Type.ULong: if (in_checked_context && Value < 0) throw new OverflowException (); return new ULongConstant (target_type, (ulong) Value, Location); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, Location); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, (double) Value, Location); case BuiltinTypeSpec.Type.Char: if (in_checked_context && Value < 0) throw new OverflowException (); return new CharConstant (target_type, (char) Value, Location); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (target_type, (decimal) Value, Location); } return null; } } public class ShortConstant : IntegralConstant { public readonly short Value; public ShortConstant (BuiltinTypes types, short v, Location loc) : this (types.Short, v, loc) { } public ShortConstant (TypeSpec type, short v, Location loc) : base (type, loc) { Value = v; } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode (Value); } public override void Emit (EmitContext ec) { ec.EmitInt (Value); } public override object GetValue () { return Value; } public override long GetValueAsLong () { return Value; } public override Constant Increment () { return new ShortConstant (type, checked((short)(Value + 1)), loc); } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsZeroInteger { get { return Value == 0; } } public override bool IsNegative { get { return Value < 0; } } public override bool IsOneInteger { get { return Value == 1; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.Byte: if (in_checked_context) { if (Value < Byte.MinValue || Value > Byte.MaxValue) throw new OverflowException (); } return new ByteConstant (target_type, (byte) Value, Location); case BuiltinTypeSpec.Type.SByte: if (in_checked_context) { if (Value < SByte.MinValue || Value > SByte.MaxValue) throw new OverflowException (); } return new SByteConstant (target_type, (sbyte) Value, Location); case BuiltinTypeSpec.Type.UShort: if (in_checked_context && Value < 0) throw new OverflowException (); return new UShortConstant (target_type, (ushort) Value, Location); case BuiltinTypeSpec.Type.Int: return new IntConstant (target_type, (int) Value, Location); case BuiltinTypeSpec.Type.UInt: if (in_checked_context && Value < 0) throw new OverflowException (); return new UIntConstant (target_type, (uint) Value, Location); case BuiltinTypeSpec.Type.Long: return new LongConstant (target_type, (long) Value, Location); case BuiltinTypeSpec.Type.ULong: if (in_checked_context && Value < 0) throw new OverflowException (); return new ULongConstant (target_type, (ulong) Value, Location); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, Location); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, (double) Value, Location); case BuiltinTypeSpec.Type.Char: if (in_checked_context) { if (Value < Char.MinValue) throw new OverflowException (); } return new CharConstant (target_type, (char) Value, Location); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (target_type, (decimal) Value, Location); } return null; } } public class UShortConstant : IntegralConstant { public readonly ushort Value; public UShortConstant (BuiltinTypes types, ushort v, Location loc) : this (types.UShort, v, loc) { } public UShortConstant (TypeSpec type, ushort v, Location loc) : base (type, loc) { Value = v; } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode (Value); } public override void Emit (EmitContext ec) { ec.EmitInt (Value); } public override object GetValue () { return Value; } public override long GetValueAsLong () { return Value; } public override Constant Increment () { return new UShortConstant (type, checked((ushort)(Value + 1)), loc); } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsNegative { get { return false; } } public override bool IsOneInteger { get { return Value == 1; } } public override bool IsZeroInteger { get { return Value == 0; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.Byte: if (in_checked_context) { if (Value > Byte.MaxValue) throw new OverflowException (); } return new ByteConstant (target_type, (byte) Value, Location); case BuiltinTypeSpec.Type.SByte: if (in_checked_context) { if (Value > SByte.MaxValue) throw new OverflowException (); } return new SByteConstant (target_type, (sbyte) Value, Location); case BuiltinTypeSpec.Type.Short: if (in_checked_context) { if (Value > Int16.MaxValue) throw new OverflowException (); } return new ShortConstant (target_type, (short) Value, Location); case BuiltinTypeSpec.Type.Int: return new IntConstant (target_type, (int) Value, Location); case BuiltinTypeSpec.Type.UInt: return new UIntConstant (target_type, (uint) Value, Location); case BuiltinTypeSpec.Type.Long: return new LongConstant (target_type, (long) Value, Location); case BuiltinTypeSpec.Type.ULong: return new ULongConstant (target_type, (ulong) Value, Location); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, Location); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, (double) Value, Location); case BuiltinTypeSpec.Type.Char: if (in_checked_context) { if (Value > Char.MaxValue) throw new OverflowException (); } return new CharConstant (target_type, (char) Value, Location); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (target_type, (decimal) Value, Location); } return null; } } public class IntConstant : IntegralConstant { public readonly int Value; public IntConstant (BuiltinTypes types, int v, Location loc) : this (types.Int, v, loc) { } public IntConstant (TypeSpec type, int v, Location loc) : base (type, loc) { Value = v; } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode (Value); } public override void Emit (EmitContext ec) { ec.EmitInt (Value); } public override object GetValue () { return Value; } public override long GetValueAsLong () { return Value; } public override Constant Increment () { return new IntConstant (type, checked(Value + 1), loc); } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsNegative { get { return Value < 0; } } public override bool IsOneInteger { get { return Value == 1; } } public override bool IsZeroInteger { get { return Value == 0; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.Byte: if (in_checked_context) { if (Value < Byte.MinValue || Value > Byte.MaxValue) throw new OverflowException (); } return new ByteConstant (target_type, (byte) Value, Location); case BuiltinTypeSpec.Type.SByte: if (in_checked_context) { if (Value < SByte.MinValue || Value > SByte.MaxValue) throw new OverflowException (); } return new SByteConstant (target_type, (sbyte) Value, Location); case BuiltinTypeSpec.Type.Short: if (in_checked_context) { if (Value < Int16.MinValue || Value > Int16.MaxValue) throw new OverflowException (); } return new ShortConstant (target_type, (short) Value, Location); case BuiltinTypeSpec.Type.UShort: if (in_checked_context) { if (Value < UInt16.MinValue || Value > UInt16.MaxValue) throw new OverflowException (); } return new UShortConstant (target_type, (ushort) Value, Location); case BuiltinTypeSpec.Type.UInt: if (in_checked_context) { if (Value < UInt32.MinValue) throw new OverflowException (); } return new UIntConstant (target_type, (uint) Value, Location); case BuiltinTypeSpec.Type.Long: return new LongConstant (target_type, (long) Value, Location); case BuiltinTypeSpec.Type.ULong: if (in_checked_context && Value < 0) throw new OverflowException (); return new ULongConstant (target_type, (ulong) Value, Location); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, Location); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, (double) Value, Location); case BuiltinTypeSpec.Type.Char: if (in_checked_context) { if (Value < Char.MinValue || Value > Char.MaxValue) throw new OverflowException (); } return new CharConstant (target_type, (char) Value, Location); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (target_type, (decimal) Value, Location); } return null; } public override Constant ConvertImplicitly (TypeSpec type) { if (this.type == type) return this; Constant c = TryImplicitIntConversion (type); if (c != null) return c; //.Resolve (rc); return base.ConvertImplicitly (type); } /// /// Attempts to perform an implicit constant conversion of the IntConstant /// into a different data type using casts (See Implicit Constant /// Expression Conversions) /// Constant TryImplicitIntConversion (TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.SByte: if (Value >= SByte.MinValue && Value <= SByte.MaxValue) return new SByteConstant (target_type, (sbyte) Value, loc); break; case BuiltinTypeSpec.Type.Byte: if (Value >= Byte.MinValue && Value <= Byte.MaxValue) return new ByteConstant (target_type, (byte) Value, loc); break; case BuiltinTypeSpec.Type.Short: if (Value >= Int16.MinValue && Value <= Int16.MaxValue) return new ShortConstant (target_type, (short) Value, loc); break; case BuiltinTypeSpec.Type.UShort: if (Value >= UInt16.MinValue && Value <= UInt16.MaxValue) return new UShortConstant (target_type, (ushort) Value, loc); break; case BuiltinTypeSpec.Type.UInt: if (Value >= 0) return new UIntConstant (target_type, (uint) Value, loc); break; case BuiltinTypeSpec.Type.ULong: // // we can optimize this case: a positive int32 // always fits on a uint64. But we need an opcode // to do it. // if (Value >= 0) return new ULongConstant (target_type, (ulong) Value, loc); break; case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, (double) Value, loc); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, loc); } return null; } } public class UIntConstant : IntegralConstant { public readonly uint Value; public UIntConstant (BuiltinTypes types, uint v, Location loc) : this (types.UInt, v, loc) { } public UIntConstant (TypeSpec type, uint v, Location loc) : base (type, loc) { Value = v; } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode (Value); } public override void Emit (EmitContext ec) { ec.EmitInt (unchecked ((int) Value)); } public override object GetValue () { return Value; } public override long GetValueAsLong () { return Value; } public override Constant Increment () { return new UIntConstant (type, checked(Value + 1), loc); } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsNegative { get { return false; } } public override bool IsOneInteger { get { return Value == 1; } } public override bool IsZeroInteger { get { return Value == 0; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.Byte: if (in_checked_context) { if (Value < 0 || Value > byte.MaxValue) throw new OverflowException (); } return new ByteConstant (target_type, (byte) Value, Location); case BuiltinTypeSpec.Type.SByte: if (in_checked_context) { if (Value > SByte.MaxValue) throw new OverflowException (); } return new SByteConstant (target_type, (sbyte) Value, Location); case BuiltinTypeSpec.Type.Short: if (in_checked_context) { if (Value > Int16.MaxValue) throw new OverflowException (); } return new ShortConstant (target_type, (short) Value, Location); case BuiltinTypeSpec.Type.UShort: if (in_checked_context) { if (Value < UInt16.MinValue || Value > UInt16.MaxValue) throw new OverflowException (); } return new UShortConstant (target_type, (ushort) Value, Location); case BuiltinTypeSpec.Type.Int: if (in_checked_context) { if (Value > Int32.MaxValue) throw new OverflowException (); } return new IntConstant (target_type, (int) Value, Location); case BuiltinTypeSpec.Type.Long: return new LongConstant (target_type, (long) Value, Location); case BuiltinTypeSpec.Type.ULong: return new ULongConstant (target_type, (ulong) Value, Location); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, Location); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, (double) Value, Location); case BuiltinTypeSpec.Type.Char: if (in_checked_context) { if (Value < Char.MinValue || Value > Char.MaxValue) throw new OverflowException (); } return new CharConstant (target_type, (char) Value, Location); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (target_type, (decimal) Value, Location); } return null; } } public class LongConstant : IntegralConstant { public readonly long Value; public LongConstant (BuiltinTypes types, long v, Location loc) : this (types.Long, v, loc) { } public LongConstant (TypeSpec type, long v, Location loc) : base (type, loc) { Value = v; } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode (Value); } public override void Emit (EmitContext ec) { ec.EmitLong (Value); } public override object GetValue () { return Value; } public override long GetValueAsLong () { return Value; } public override Constant Increment () { return new LongConstant (type, checked(Value + 1), loc); } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsNegative { get { return Value < 0; } } public override bool IsOneInteger { get { return Value == 1; } } public override bool IsZeroInteger { get { return Value == 0; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.Byte: if (in_checked_context) { if (Value < Byte.MinValue || Value > Byte.MaxValue) throw new OverflowException (); } return new ByteConstant (target_type, (byte) Value, Location); case BuiltinTypeSpec.Type.SByte: if (in_checked_context) { if (Value < SByte.MinValue || Value > SByte.MaxValue) throw new OverflowException (); } return new SByteConstant (target_type, (sbyte) Value, Location); case BuiltinTypeSpec.Type.Short: if (in_checked_context) { if (Value < Int16.MinValue || Value > Int16.MaxValue) throw new OverflowException (); } return new ShortConstant (target_type, (short) Value, Location); case BuiltinTypeSpec.Type.UShort: if (in_checked_context) { if (Value < UInt16.MinValue || Value > UInt16.MaxValue) throw new OverflowException (); } return new UShortConstant (target_type, (ushort) Value, Location); case BuiltinTypeSpec.Type.Int: if (in_checked_context) { if (Value < Int32.MinValue || Value > Int32.MaxValue) throw new OverflowException (); } return new IntConstant (target_type, (int) Value, Location); case BuiltinTypeSpec.Type.UInt: if (in_checked_context) { if (Value < UInt32.MinValue || Value > UInt32.MaxValue) throw new OverflowException (); } return new UIntConstant (target_type, (uint) Value, Location); case BuiltinTypeSpec.Type.ULong: if (in_checked_context && Value < 0) throw new OverflowException (); return new ULongConstant (target_type, (ulong) Value, Location); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, Location); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, (double) Value, Location); case BuiltinTypeSpec.Type.Char: if (in_checked_context) { if (Value < Char.MinValue || Value > Char.MaxValue) throw new OverflowException (); } return new CharConstant (target_type, (char) Value, Location); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (target_type, (decimal) Value, Location); } return null; } public override Constant ConvertImplicitly (TypeSpec type) { if (Value >= 0 && type.BuiltinType == BuiltinTypeSpec.Type.ULong) { return new ULongConstant (type, (ulong) Value, loc); } return base.ConvertImplicitly (type); } } public class ULongConstant : IntegralConstant { public readonly ulong Value; public ULongConstant (BuiltinTypes types, ulong v, Location loc) : this (types.ULong, v, loc) { } public ULongConstant (TypeSpec type, ulong v, Location loc) : base (type, loc) { Value = v; } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode (Value); } public override void Emit (EmitContext ec) { ec.EmitLong (unchecked ((long) Value)); } public override object GetValue () { return Value; } public override long GetValueAsLong () { return (long) Value; } public override Constant Increment () { return new ULongConstant (type, checked(Value + 1), loc); } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsNegative { get { return false; } } public override bool IsOneInteger { get { return Value == 1; } } public override bool IsZeroInteger { get { return Value == 0; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.Byte: if (in_checked_context && Value > Byte.MaxValue) throw new OverflowException (); return new ByteConstant (target_type, (byte) Value, Location); case BuiltinTypeSpec.Type.SByte: if (in_checked_context && Value > ((ulong) SByte.MaxValue)) throw new OverflowException (); return new SByteConstant (target_type, (sbyte) Value, Location); case BuiltinTypeSpec.Type.Short: if (in_checked_context && Value > ((ulong) Int16.MaxValue)) throw new OverflowException (); return new ShortConstant (target_type, (short) Value, Location); case BuiltinTypeSpec.Type.UShort: if (in_checked_context && Value > UInt16.MaxValue) throw new OverflowException (); return new UShortConstant (target_type, (ushort) Value, Location); case BuiltinTypeSpec.Type.Int: if (in_checked_context && Value > UInt32.MaxValue) throw new OverflowException (); return new IntConstant (target_type, (int) Value, Location); case BuiltinTypeSpec.Type.UInt: if (in_checked_context && Value > UInt32.MaxValue) throw new OverflowException (); return new UIntConstant (target_type, (uint) Value, Location); case BuiltinTypeSpec.Type.Long: if (in_checked_context && Value > Int64.MaxValue) throw new OverflowException (); return new LongConstant (target_type, (long) Value, Location); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, Location); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, (double) Value, Location); case BuiltinTypeSpec.Type.Char: if (in_checked_context && Value > Char.MaxValue) throw new OverflowException (); return new CharConstant (target_type, (char) Value, Location); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (target_type, (decimal) Value, Location); } return null; } } public class FloatConstant : Constant { // // Store constant value as double because float constant operations // need to work on double value to match JIT // public readonly double DoubleValue; public FloatConstant (BuiltinTypes types, double v, Location loc) : this (types.Float, v, loc) { } public FloatConstant (TypeSpec type, double v, Location loc) : base (loc) { this.type = type; eclass = ExprClass.Value; DoubleValue = v; } public override Constant ConvertImplicitly (TypeSpec type) { if (type.BuiltinType == BuiltinTypeSpec.Type.Double) return new DoubleConstant (type, DoubleValue, loc); return base.ConvertImplicitly (type); } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode (Value); } public override void Emit (EmitContext ec) { ec.Emit (OpCodes.Ldc_R4, Value); } public float Value { get { return (float) DoubleValue; } } public override object GetValue () { return Value; } public override string GetValueAsLiteral () { return Value.ToString (); } public override long GetValueAsLong () { throw new NotSupportedException (); } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsNegative { get { return Value < 0; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.Byte: if (in_checked_context) { if (Value < byte.MinValue || Value > byte.MaxValue || float.IsNaN (Value)) throw new OverflowException (); } return new ByteConstant (target_type, (byte) DoubleValue, Location); case BuiltinTypeSpec.Type.SByte: if (in_checked_context) { if (Value < sbyte.MinValue || Value > sbyte.MaxValue || float.IsNaN (Value)) throw new OverflowException (); } return new SByteConstant (target_type, (sbyte) DoubleValue, Location); case BuiltinTypeSpec.Type.Short: if (in_checked_context) { if (Value < short.MinValue || Value > short.MaxValue || float.IsNaN (Value)) throw new OverflowException (); } return new ShortConstant (target_type, (short) DoubleValue, Location); case BuiltinTypeSpec.Type.UShort: if (in_checked_context) { if (Value < ushort.MinValue || Value > ushort.MaxValue || float.IsNaN (Value)) throw new OverflowException (); } return new UShortConstant (target_type, (ushort) DoubleValue, Location); case BuiltinTypeSpec.Type.Int: if (in_checked_context) { if (Value < int.MinValue || Value > int.MaxValue || float.IsNaN (Value)) throw new OverflowException (); } return new IntConstant (target_type, (int) DoubleValue, Location); case BuiltinTypeSpec.Type.UInt: if (in_checked_context) { if (Value < uint.MinValue || Value > uint.MaxValue || float.IsNaN (Value)) throw new OverflowException (); } return new UIntConstant (target_type, (uint) DoubleValue, Location); case BuiltinTypeSpec.Type.Long: if (in_checked_context) { if (Value < long.MinValue || Value > long.MaxValue || float.IsNaN (Value)) throw new OverflowException (); } return new LongConstant (target_type, (long) DoubleValue, Location); case BuiltinTypeSpec.Type.ULong: if (in_checked_context) { if (Value < ulong.MinValue || Value > ulong.MaxValue || float.IsNaN (Value)) throw new OverflowException (); } return new ULongConstant (target_type, (ulong) DoubleValue, Location); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, DoubleValue, Location); case BuiltinTypeSpec.Type.Char: if (in_checked_context) { if (Value < (float) char.MinValue || Value > (float) char.MaxValue || float.IsNaN (Value)) throw new OverflowException (); } return new CharConstant (target_type, (char) DoubleValue, Location); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (target_type, (decimal) DoubleValue, Location); } return null; } } public class DoubleConstant : Constant { public readonly double Value; public DoubleConstant (BuiltinTypes types, double v, Location loc) : this (types.Double, v, loc) { } public DoubleConstant (TypeSpec type, double v, Location loc) : base (loc) { this.type = type; eclass = ExprClass.Value; Value = v; } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { enc.Encode (Value); } public override void Emit (EmitContext ec) { ec.Emit (OpCodes.Ldc_R8, Value); } public override object GetValue () { return Value; } public override string GetValueAsLiteral () { return Value.ToString (); } public override long GetValueAsLong () { throw new NotSupportedException (); } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsNegative { get { return Value < 0; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.Byte: if (in_checked_context) { if (Value < Byte.MinValue || Value > Byte.MaxValue || double.IsNaN (Value)) throw new OverflowException (); } return new ByteConstant (target_type, (byte) Value, Location); case BuiltinTypeSpec.Type.SByte: if (in_checked_context) { if (Value < SByte.MinValue || Value > SByte.MaxValue || double.IsNaN (Value)) throw new OverflowException (); } return new SByteConstant (target_type, (sbyte) Value, Location); case BuiltinTypeSpec.Type.Short: if (in_checked_context) { if (Value < short.MinValue || Value > short.MaxValue || double.IsNaN (Value)) throw new OverflowException (); } return new ShortConstant (target_type, (short) Value, Location); case BuiltinTypeSpec.Type.UShort: if (in_checked_context) { if (Value < ushort.MinValue || Value > ushort.MaxValue || double.IsNaN (Value)) throw new OverflowException (); } return new UShortConstant (target_type, (ushort) Value, Location); case BuiltinTypeSpec.Type.Int: if (in_checked_context) { if (Value < int.MinValue || Value > int.MaxValue || double.IsNaN (Value)) throw new OverflowException (); } return new IntConstant (target_type, (int) Value, Location); case BuiltinTypeSpec.Type.UInt: if (in_checked_context) { if (Value < uint.MinValue || Value > uint.MaxValue || double.IsNaN (Value)) throw new OverflowException (); } return new UIntConstant (target_type, (uint) Value, Location); case BuiltinTypeSpec.Type.Long: if (in_checked_context) { if (Value < long.MinValue || Value > long.MaxValue || double.IsNaN (Value)) throw new OverflowException (); } return new LongConstant (target_type, (long) Value, Location); case BuiltinTypeSpec.Type.ULong: if (in_checked_context) { if (Value < ulong.MinValue || Value > ulong.MaxValue || double.IsNaN (Value)) throw new OverflowException (); } return new ULongConstant (target_type, (ulong) Value, Location); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, Location); case BuiltinTypeSpec.Type.Char: if (in_checked_context) { if (Value < (double) char.MinValue || Value > (double) char.MaxValue || double.IsNaN (Value)) throw new OverflowException (); } return new CharConstant (target_type, (char) Value, Location); case BuiltinTypeSpec.Type.Decimal: return new DecimalConstant (target_type, (decimal) Value, Location); } return null; } } public class DecimalConstant : Constant { public readonly decimal Value; public DecimalConstant (BuiltinTypes types, decimal d, Location loc) : this (types.Decimal, d, loc) { } public DecimalConstant (TypeSpec type, decimal d, Location loc) : base (loc) { this.type = type; eclass = ExprClass.Value; Value = d; } public override void Emit (EmitContext ec) { MethodSpec m; int [] words = decimal.GetBits (Value); int power = (words [3] >> 16) & 0xff; if (power == 0) { if (Value <= int.MaxValue && Value >= int.MinValue) { m = ec.Module.PredefinedMembers.DecimalCtorInt.Resolve (loc); if (m == null) { return; } ec.EmitInt ((int) Value); ec.Emit (OpCodes.Newobj, m); return; } if (Value <= long.MaxValue && Value >= long.MinValue) { m = ec.Module.PredefinedMembers.DecimalCtorLong.Resolve (loc); if (m == null) { return; } ec.EmitLong ((long) Value); ec.Emit (OpCodes.Newobj, m); return; } } ec.EmitInt (words [0]); ec.EmitInt (words [1]); ec.EmitInt (words [2]); // sign ec.EmitInt (words [3] >> 31); // power ec.EmitInt (power); m = ec.Module.PredefinedMembers.DecimalCtor.Resolve (loc); if (m != null) { ec.Emit (OpCodes.Newobj, m); } } public override bool IsDefaultValue { get { return Value == 0; } } public override bool IsNegative { get { return Value < 0; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { switch (target_type.BuiltinType) { case BuiltinTypeSpec.Type.SByte: return new SByteConstant (target_type, (sbyte) Value, loc); case BuiltinTypeSpec.Type.Byte: return new ByteConstant (target_type, (byte) Value, loc); case BuiltinTypeSpec.Type.Short: return new ShortConstant (target_type, (short) Value, loc); case BuiltinTypeSpec.Type.UShort: return new UShortConstant (target_type, (ushort) Value, loc); case BuiltinTypeSpec.Type.Int: return new IntConstant (target_type, (int) Value, loc); case BuiltinTypeSpec.Type.UInt: return new UIntConstant (target_type, (uint) Value, loc); case BuiltinTypeSpec.Type.Long: return new LongConstant (target_type, (long) Value, loc); case BuiltinTypeSpec.Type.ULong: return new ULongConstant (target_type, (ulong) Value, loc); case BuiltinTypeSpec.Type.Char: return new CharConstant (target_type, (char) Value, loc); case BuiltinTypeSpec.Type.Float: return new FloatConstant (target_type, (float) Value, loc); case BuiltinTypeSpec.Type.Double: return new DoubleConstant (target_type, (double) Value, loc); } return null; } public override object GetValue () { return Value; } public override string GetValueAsLiteral () { return Value.ToString () + "M"; } public override long GetValueAsLong () { throw new NotSupportedException (); } } public class StringConstant : Constant { public StringConstant (BuiltinTypes types, string s, Location loc) : this (types.String, s, loc) { } public StringConstant (TypeSpec type, string s, Location loc) : base (loc) { this.type = type; eclass = ExprClass.Value; Value = s; } protected StringConstant (Location loc) : base (loc) { } public string Value { get; protected set; } public override object GetValue () { return Value; } public override string GetValueAsLiteral () { // FIXME: Escape the string. return "\"" + Value + "\""; } public override long GetValueAsLong () { throw new NotSupportedException (); } public override void Emit (EmitContext ec) { if (Value == null) { ec.EmitNull (); return; } // // Use string.Empty for both literals and constants even if // it's not allowed at language level // if (Value.Length == 0 && ec.Module.Compiler.Settings.Optimize) { var string_type = ec.BuiltinTypes.String; if (ec.CurrentType != string_type) { var m = ec.Module.PredefinedMembers.StringEmpty.Get (); if (m != null) { ec.Emit (OpCodes.Ldsfld, m); return; } } } ec.Emit (OpCodes.Ldstr, Value); } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { // cast to object if (type != targetType) enc.Encode (type); enc.Encode (Value); } public override bool IsDefaultValue { get { return Value == null; } } public override bool IsNegative { get { return false; } } public override bool IsNull { get { return IsDefaultValue; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { return null; } public override Constant ConvertImplicitly (TypeSpec type) { if (IsDefaultValue && type.BuiltinType == BuiltinTypeSpec.Type.Object) return new NullConstant (type, loc); return base.ConvertImplicitly (type); } } class NameOf : StringConstant { readonly SimpleName name; public NameOf (SimpleName name) : base (name.Location) { this.name = name; } static void Error_MethodGroupWithTypeArguments (ResolveContext rc, Location loc) { rc.Report.Error (8084, loc, "An argument to nameof operator cannot be method group with type arguments"); } protected override Expression DoResolve (ResolveContext rc) { throw new NotSupportedException (); } bool ResolveArgumentExpression (ResolveContext rc, Expression expr) { var sn = expr as SimpleName; if (sn != null) { Value = sn.Name; if (rc.Module.Compiler.Settings.Version < LanguageVersion.V_6) rc.Report.FeatureIsNotAvailable (rc.Module.Compiler, Location, "nameof operator"); var res = sn.LookupNameExpression (rc, MemberLookupRestrictions.IgnoreAmbiguity | MemberLookupRestrictions.NameOfExcluded); if (sn.HasTypeArguments && res is MethodGroupExpr) { Error_MethodGroupWithTypeArguments (rc, expr.Location); } return true; } var ma = expr as MemberAccess; if (ma != null) { var lexpr = ma.LeftExpression; var res = ma.LookupNameExpression (rc, MemberLookupRestrictions.IgnoreAmbiguity); if (res == null) { return false; } if (rc.Module.Compiler.Settings.Version < LanguageVersion.V_6) rc.Report.FeatureIsNotAvailable (rc.Module.Compiler, Location, "nameof operator"); if (ma is QualifiedAliasMember) { rc.Report.Error (8083, loc, "An alias-qualified name is not an expression"); return false; } if (!IsLeftExpressionValid (lexpr)) { rc.Report.Error (8082, lexpr.Location, "An argument to nameof operator cannot include sub-expression"); return false; } var mg = res as MethodGroupExpr; if (mg != null) { var emg = res as ExtensionMethodGroupExpr; if (emg != null && !emg.ResolveNameOf (rc, ma)) { return true; } if (!mg.HasAccessibleCandidate (rc)) { ErrorIsInaccesible (rc, ma.GetSignatureForError (), loc); } if (ma.HasTypeArguments) { Error_MethodGroupWithTypeArguments (rc, ma.Location); } } Value = ma.Name; return true; } rc.Report.Error (8081, loc, "Expression does not have a name"); return false; } static bool IsLeftExpressionValid (Expression expr) { if (expr is SimpleName) return true; if (expr is This) return true; if (expr is NamespaceExpression) return true; if (expr is TypeExpr) return true; var ma = expr as MemberAccess; if (ma != null) { // TODO: Will conditional access be allowed? return IsLeftExpressionValid (ma.LeftExpression); } return false; } public Expression ResolveOverload (ResolveContext rc, Arguments args) { if (args == null || args.Count != 1) { name.Error_NameDoesNotExist (rc); return null; } var arg = args [0]; var res = ResolveArgumentExpression (rc, arg.Expr); if (!res) { return null; } type = rc.BuiltinTypes.String; eclass = ExprClass.Value; return this; } } // // Null constant can have its own type, think of `default (Foo)' // public class NullConstant : Constant { public NullConstant (TypeSpec type, Location loc) : base (loc) { eclass = ExprClass.Value; this.type = type; } public override Expression CreateExpressionTree (ResolveContext ec) { if (type == InternalType.NullLiteral || type.BuiltinType == BuiltinTypeSpec.Type.Object) { // Optimized version, also avoids referencing literal internal type Arguments args = new Arguments (1); args.Add (new Argument (this)); return CreateExpressionFactoryCall (ec, "Constant", args); } return base.CreateExpressionTree (ec); } public override void EncodeAttributeValue (IMemberContext rc, AttributeEncoder enc, TypeSpec targetType, TypeSpec parameterType) { switch (targetType.BuiltinType) { case BuiltinTypeSpec.Type.Object: // Type it as string cast enc.Encode (rc.Module.Compiler.BuiltinTypes.String); goto case BuiltinTypeSpec.Type.String; case BuiltinTypeSpec.Type.String: case BuiltinTypeSpec.Type.Type: enc.Encode (byte.MaxValue); return; default: var ac = targetType as ArrayContainer; if (ac != null && ac.Rank == 1 && !ac.Element.IsArray) { enc.Encode (uint.MaxValue); return; } break; } base.EncodeAttributeValue (rc, enc, targetType, parameterType); } public override void Emit (EmitContext ec) { ec.EmitNull (); // Only to make verifier happy if (type.IsGenericParameter) ec.Emit (OpCodes.Unbox_Any, type); } public override string ExprClassName { get { return GetSignatureForError (); } } public override Constant ConvertExplicitly (bool inCheckedContext, TypeSpec targetType) { if (targetType.IsPointer) { if (IsLiteral || this is NullPointer) return new NullPointer (targetType, loc); return null; } // Exlude internal compiler types if (targetType.Kind == MemberKind.InternalCompilerType && targetType.BuiltinType != BuiltinTypeSpec.Type.Dynamic) return null; if (!IsLiteral && !Convert.ImplicitStandardConversionExists (this, targetType)) return null; if (TypeSpec.IsReferenceType (targetType)) return new NullConstant (targetType, loc); if (targetType.IsNullableType) return Nullable.LiftedNull.Create (targetType, loc); return null; } public override Constant ConvertImplicitly (TypeSpec targetType) { return ConvertExplicitly (false, targetType); } public override string GetSignatureForError () { return "null"; } public override object GetValue () { return null; } public override string GetValueAsLiteral () { return GetSignatureForError (); } public override long GetValueAsLong () { throw new NotSupportedException (); } public override bool IsDefaultValue { get { return true; } } public override bool IsNegative { get { return false; } } public override bool IsNull { get { return true; } } public override bool IsZeroInteger { get { return true; } } } // // A null constant in a pointer context // class NullPointer : NullConstant { public NullPointer (TypeSpec type, Location loc) : base (type, loc) { } public override Expression CreateExpressionTree (ResolveContext ec) { Error_PointerInsideExpressionTree (ec); return base.CreateExpressionTree (ec); } public override void Emit (EmitContext ec) { // // Emits null pointer // ec.EmitInt (0); ec.Emit (OpCodes.Conv_U); } } /// /// 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. /// public class SideEffectConstant : Constant { public readonly Constant value; Expression side_effect; public SideEffectConstant (Constant value, Expression side_effect, Location loc) : base (loc) { this.value = value; type = value.Type; eclass = ExprClass.Value; while (side_effect is SideEffectConstant) side_effect = ((SideEffectConstant) side_effect).side_effect; this.side_effect = side_effect; } public override bool IsSideEffectFree { get { return false; } } public override bool ContainsEmitWithAwait () { return side_effect.ContainsEmitWithAwait (); } public override object GetValue () { return value.GetValue (); } public override string GetValueAsLiteral () { return value.GetValueAsLiteral (); } public override long GetValueAsLong () { return value.GetValueAsLong (); } 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 void FlowAnalysis (FlowAnalysisContext fc) { side_effect.FlowAnalysis (fc); } public override bool IsDefaultValue { get { return value.IsDefaultValue; } } public override bool IsNegative { get { return value.IsNegative; } } public override bool IsZeroInteger { get { return value.IsZeroInteger; } } public override Constant ConvertExplicitly (bool in_checked_context, TypeSpec target_type) { Constant new_value = value.ConvertExplicitly (in_checked_context, target_type); if (new_value == null) return null; var c = new SideEffectConstant (new_value, side_effect, new_value.Location); c.type = target_type; c.eclass = eclass; return c; } } }