-
- // <remarks>
- // The ExprClass class contains the is used to pass the
- // classification of an expression (value, variable, namespace,
- // type, method group, property access, event access, indexer access,
- // nothing).
- // </remarks>
- public enum ExprClass {
- Invalid,
-
- Value,
- Variable, // Every Variable should implement LValue
- Namespace,
- Type,
- MethodGroup,
- PropertyAccess,
- EventAccess,
- IndexerAccess,
- Nothing,
- }
-
- // <remarks>
- // Base class for expressions
- // </remarks>
- public abstract class Expression {
- protected ExprClass eclass;
- protected Type type;
-
- public Type Type {
- get {
- return type;
- }
-
- set {
- type = value;
- }
- }
-
- public ExprClass ExprClass {
- get {
- return eclass;
- }
-
- set {
- eclass = value;
- }
- }
-
- // <summary>
- // Utility wrapper routine for Error, just to beautify the code
- // </summary>
- static protected void Error (TypeContainer tc, int error, string s)
- {
- tc.RootContext.Report.Error (error, s);
- }
-
- static protected void Error (TypeContainer tc, int error, Location l, string s)
- {
- tc.RootContext.Report.Error (error, l, s);
- }
-
- // <summary>
- // Utility wrapper routine for Warning, just to beautify the code
- // </summary>
- static protected void Warning (TypeContainer tc, int warning, string s)
- {
- tc.RootContext.Report.Warning (warning, s);
- }
-
- // <summary>
- // Performs semantic analysis on the Expression
- // </summary>
- //
- // <remarks>
- // The Resolve method is invoked to perform the semantic analysis
- // on the node.
- //
- // The return value is an expression (it can be the
- // same expression in some cases) or a new
- // expression that better represents this node.
- //
- // For example, optimizations of Unary (LiteralInt)
- // would return a new LiteralInt with a negated
- // value.
- //
- // If there is an error during semantic analysis,
- // then an error should
- // be reported (using TypeContainer.RootContext.Report) and a null
- // value should be returned.
- //
- // There are two side effects expected from calling
- // Resolve(): the the field variable "eclass" should
- // be set to any value of the enumeration
- // `ExprClass' and the type variable should be set
- // to a valid type (this is the type of the
- // expression).
- // </remarks>
-
- public abstract Expression DoResolve (TypeContainer tc);
-
-
- //
- // Currently Resolve wraps DoResolve to perform sanity
- // checking and assertion checking on what we expect from Resolve
- //
-
- public Expression Resolve (TypeContainer tc)
- {
- Expression e = DoResolve (tc);
-
- if (e != null){
- if (e.ExprClass == ExprClass.Invalid)
- throw new Exception ("Expression " + e +
- " ExprClass is Invalid after resolve");
-
- if (e.ExprClass != ExprClass.MethodGroup)
- if (e.type == null)
- throw new Exception ("Expression " + e +
- " did not set its type after Resolve");
- }
-
- return e;
- }
-
- // <summary>
- // Emits the code for the expression
- // </summary>
- //
- // <remarks>
- //
- // The Emit method is invoked to generate the code
- // for the expression.
- //
- // </remarks>
- public abstract void Emit (EmitContext ec);
-
- // <summary>
- // Protected constructor. Only derivate types should
- // be able to be created
- // </summary>
-
- protected Expression ()
- {
- eclass = ExprClass.Invalid;
- type = null;
- }
-
- // <summary>
- // Returns a literalized version of a literal FieldInfo
- // </summary>
- static Expression Literalize (FieldInfo fi)
- {
- Type t = fi.FieldType;
- object v = fi.GetValue (fi);
-
- if (t == TypeManager.int32_type)
- return new IntLiteral ((int) v);
- else if (t == TypeManager.uint32_type)
- return new UIntLiteral ((uint) v);
- else if (t == TypeManager.int64_type)
- return new LongLiteral ((long) v);
- else if (t == TypeManager.uint64_type)
- return new ULongLiteral ((ulong) v);
- else if (t == TypeManager.float_type)
- return new FloatLiteral ((float) v);
- else if (t == TypeManager.double_type)
- return new DoubleLiteral ((double) v);
- else if (t == TypeManager.string_type)
- return new StringLiteral ((string) v);
- else if (t == TypeManager.short_type)
- return new IntLiteral ((int) ((short)v));
- else if (t == TypeManager.ushort_type)
- return new IntLiteral ((int) ((ushort)v));
- else if (t == TypeManager.sbyte_type)
- return new IntLiteral ((int) ((sbyte)v));
- else if (t == TypeManager.byte_type)
- return new IntLiteral ((int) ((byte)v));
- else if (t == TypeManager.char_type)
- return new IntLiteral ((int) ((char)v));
- else
- throw new Exception ("Unknown type for literal (" + v.GetType () +
- "), details: " + fi);
- }
-
- //
- // Returns a fully formed expression after a MemberLookup
- //
- static Expression ExprClassFromMemberInfo (TypeContainer tc, MemberInfo mi)
- {
- if (mi is EventInfo){
- return new EventExpr ((EventInfo) mi);
- } else if (mi is FieldInfo){
- FieldInfo fi = (FieldInfo) mi;
-
- if (fi.IsLiteral){
- Expression e = Literalize (fi);
- e.Resolve (tc);
-
- return e;
- } else
- return new FieldExpr (fi);
- } else if (mi is PropertyInfo){
- return new PropertyExpr ((PropertyInfo) mi);
- } else if (mi is Type)
- return new TypeExpr ((Type) mi);
-
- return null;
- }
-
- //
- // FIXME: Probably implement a cache for (t,name,current_access_set)?
- //
- // FIXME: We need to cope with access permissions here, or this wont
- // work!
- //
- // This code could use some optimizations, but we need to do some
- // measurements. For example, we could use a delegate to `flag' when
- // something can not any longer be a method-group (because it is something
- // else).
- //
- // Return values:
- // If the return value is an Array, then it is an array of
- // MethodBases
- //
- // If the return value is an MemberInfo, it is anything, but a Method
- //
- // null on error.
- //
- // FIXME: When calling MemberLookup inside an `Invocation', we should pass
- // the arguments here and have MemberLookup return only the methods that
- // match the argument count/type, unlike we are doing now (we delay this
- // decision).
- //
- // This is so we can catch correctly attempts to invoke instance methods
- // from a static body (scan for error 120 in ResolveSimpleName).
- //
- public static Expression MemberLookup (TypeContainer tc, Type t, string name,
- bool same_type, MemberTypes mt, BindingFlags bf)
- {
- if (same_type)
- bf |= BindingFlags.NonPublic;
-
- MemberInfo [] mi = tc.RootContext.TypeManager.FindMembers (
- t, mt, bf, Type.FilterName, name);
-
- if (mi == null)
- return null;
-
- if (mi.Length == 1 && !(mi [0] is MethodBase))
- return Expression.ExprClassFromMemberInfo (tc, mi [0]);
-
- for (int i = 0; i < mi.Length; i++)
- if (!(mi [i] is MethodBase)){
- Error (tc,
- -5, "Do not know how to reproduce this case: " +
- "Methods and non-Method with the same name, " +
- "report this please");
-
- for (i = 0; i < mi.Length; i++){
- Type tt = mi [i].GetType ();
-
- Console.WriteLine (i + ": " + mi [i]);
- while (tt != TypeManager.object_type){
- Console.WriteLine (tt);
- tt = tt.BaseType;
- }
- }
- }
-
- return new MethodGroupExpr (mi);
- }
-
- public const MemberTypes AllMemberTypes =
- MemberTypes.Constructor |
- MemberTypes.Event |
- MemberTypes.Field |
- MemberTypes.Method |
- MemberTypes.NestedType |
- MemberTypes.Property;
-
- public const BindingFlags AllBindingsFlags =
- BindingFlags.Public |
- BindingFlags.Static |
- BindingFlags.Instance;
-
- public static Expression MemberLookup (TypeContainer tc, Type t, string name,
- bool same_type)
- {
- return MemberLookup (tc, t, name, same_type, AllMemberTypes, AllBindingsFlags);
- }
-
- //
- // I am in general unhappy with this implementation.
- //
- // I need to revise this.
- //
- static public Expression ResolveMemberAccess (TypeContainer tc, string name)
- {
- Expression left_e = null;
- int dot_pos = name.LastIndexOf (".");
- string left = name.Substring (0, dot_pos);
- string right = name.Substring (dot_pos + 1);
- Type t;
-
- if ((t = tc.LookupType (left, false)) != null)
- left_e = new TypeExpr (t);
- else {
- //
- // FIXME: IMplement:
-
- // Handle here:
- // T.P Static property access (P) on Type T.
- // e.P instance property access on instance e for P.
- // p
- //
- }
-
- if (left_e == null){
- Error (tc, 246, "Can not find type or namespace `"+left+"'");
- return null;
- }
-
- switch (left_e.ExprClass){
- case ExprClass.Type:
- return MemberLookup (tc,
- left_e.Type, right,
- left_e.Type == tc.TypeBuilder);
-
- case ExprClass.Namespace:
- case ExprClass.PropertyAccess:
- case ExprClass.IndexerAccess:
- case ExprClass.Variable:
- case ExprClass.Value:
- case ExprClass.Nothing:
- case ExprClass.EventAccess:
- case ExprClass.MethodGroup:
- case ExprClass.Invalid:
- throw new Exception ("Should have got the " + left_e.ExprClass +
- " handled before");
- }
-
- return null;
- }
-
- static public Expression ImplicitReferenceConversion (Expression expr, Type target_type)
- {
- Type expr_type = expr.Type;
-
- if (target_type == TypeManager.object_type) {
- if (expr_type.IsClass)
- return new EmptyCast (expr, target_type);
- if (expr_type.IsValueType)
- return new BoxedCast (expr);
- } else if (expr_type.IsSubclassOf (target_type))
- return new EmptyCast (expr, target_type);
- else
- // FIXME: missing implicit reference conversions:
- //
- // from any class-type S to any interface-type T.
- // from any interface type S to interface-type T.
- // from an array-type S to an array-type of type T
- // from an array-type to System.Array
- // from any delegate type to System.Delegate
- // from any array-type or delegate type into System.ICloneable.
- // from the null type to any reference-type.
-
- return null;
-
- return null;
- }
-
- // <summary>
- // Handles expressions like this: decimal d; d = 1;
- // and changes them into: decimal d; d = new System.Decimal (1);
- // </summary>
- static Expression InternalTypeConstructor (TypeContainer tc, Expression expr, Type target)
- {
- ArrayList args = new ArrayList ();
-
- args.Add (new Argument (expr, Argument.AType.Expression));
-
- Console.WriteLine ("The InternalTypeConstructor is: " + expr);
- Expression ne = new New (target.FullName, args,
- new Location ("FIXME", 1, 1));
-
- return ne.Resolve (tc);
- }
-
- // <summary>
- // Implicit Numeric Conversions.
- //
- // expr is the expression to convert, returns a new expression of type
- // target_type or null if an implicit conversion is not possible.
- //
- // </summary>
- static public Expression ImplicitNumericConversion (TypeContainer tc, Expression expr,
- Type target_type, Location l)
- {
- Type expr_type = expr.Type;
-
- //
- // Attempt to do the implicit constant expression conversions
-
- if (expr is IntLiteral){
- Expression e;
-
- e = TryImplicitIntConversion (target_type, (IntLiteral) expr);
- if (e != null)
- return e;
- } else if (expr is LongLiteral){
- //
- // Try the implicit constant expression conversion
- // from long to ulong, instead of a nice routine,
- // we just inline it
- //
- if (((LongLiteral) expr).Value > 0)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_I8);
- }
-
- if (expr_type == TypeManager.sbyte_type){
- //
- // From sbyte to short, int, long, float, double.
- //
- if (target_type == TypeManager.int32_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_I4);
- if (target_type == TypeManager.int64_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_I8);
- if (target_type == TypeManager.double_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R8);
- if (target_type == TypeManager.float_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R4);
- if (target_type == TypeManager.short_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_I2);
- if (target_type == TypeManager.decimal_type)
- return InternalTypeConstructor (tc, expr, target_type);
- } else if (expr_type == TypeManager.byte_type){
- //
- // From byte to short, ushort, int, uint, long, ulong, float, double
- //
- if ((target_type == TypeManager.short_type) ||
- (target_type == TypeManager.ushort_type) ||
- (target_type == TypeManager.int32_type) ||
- (target_type == TypeManager.uint32_type))
- return new EmptyCast (expr, target_type);
-
- if (target_type == TypeManager.uint64_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_U8);
- if (target_type == TypeManager.int64_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_I8);
-
- if (target_type == TypeManager.float_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R4);
- if (target_type == TypeManager.double_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R8);
- if (target_type == TypeManager.decimal_type)
- return InternalTypeConstructor (tc, expr, target_type);
- } else if (expr_type == TypeManager.short_type){
- //
- // From short to int, long, float, double
- //
- if (target_type == TypeManager.int32_type)
- return new EmptyCast (expr, target_type);
- if (target_type == TypeManager.int64_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_I8);
- if (target_type == TypeManager.double_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R8);
- if (target_type == TypeManager.float_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R4);
- if (target_type == TypeManager.decimal_type)
- return InternalTypeConstructor (tc, expr, target_type);
- } else if (expr_type == TypeManager.ushort_type){
- //
- // From ushort to int, uint, long, ulong, float, double
- //
- if (target_type == TypeManager.uint32_type)
- return new EmptyCast (expr, target_type);
-
- if (target_type == TypeManager.uint64_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_U8);
- if (target_type == TypeManager.int32_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_I4);
- if (target_type == TypeManager.int64_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_I8);
- if (target_type == TypeManager.double_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R8);
- if (target_type == TypeManager.float_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R4);
- if (target_type == TypeManager.decimal_type)
- return InternalTypeConstructor (tc, expr, target_type);
- } else if (expr_type == TypeManager.int32_type){
- //
- // From int to long, float, double
- //
- if (target_type == TypeManager.int64_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_I8);
- if (target_type == TypeManager.double_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R8);
- if (target_type == TypeManager.float_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R4);
- if (target_type == TypeManager.decimal_type)
- return InternalTypeConstructor (tc, expr, target_type);
- } else if (expr_type == TypeManager.uint32_type){
- //
- // From uint to long, ulong, float, double
- //
- if (target_type == TypeManager.int64_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_U8);
- if (target_type == TypeManager.uint64_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_U8);
- if (target_type == TypeManager.double_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un,
- OpCodes.Conv_R8);
- if (target_type == TypeManager.float_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un,
- OpCodes.Conv_R4);
- if (target_type == TypeManager.decimal_type)
- return InternalTypeConstructor (tc, expr, target_type);
- } else if ((expr_type == TypeManager.uint64_type) ||
- (expr_type == TypeManager.int64_type)){
- //
- // From long/ulong to float, double
- //
- if (target_type == TypeManager.double_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un,
- OpCodes.Conv_R8);
- if (target_type == TypeManager.float_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R_Un,
- OpCodes.Conv_R4);
- if (target_type == TypeManager.decimal_type)
- return InternalTypeConstructor (tc, expr, target_type);
- } else if (expr_type == TypeManager.char_type){
- //
- // From char to ushort, int, uint, long, ulong, float, double
- //
- if ((target_type == TypeManager.ushort_type) ||
- (target_type == TypeManager.int32_type) ||
- (target_type == TypeManager.uint32_type))
- return new EmptyCast (expr, target_type);
- if (target_type == TypeManager.uint64_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_U8);
- if (target_type == TypeManager.int64_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_I8);
- if (target_type == TypeManager.float_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R4);
- if (target_type == TypeManager.double_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R8);
- if (target_type == TypeManager.decimal_type)
- return InternalTypeConstructor (tc, expr, target_type);
- } else if (expr_type == TypeManager.float_type){
- //
- // float to double
- //
- if (target_type == TypeManager.double_type)
- return new OpcodeCast (expr, target_type, OpCodes.Conv_R8);
- }
-
- return null;
- }
-
- // <summary>
- // User-defined implicit conversions
- // </summary>
- static public Expression ImplicitUserConversion (TypeContainer tc, Expression source,
- Type target, Location l)
- {
- Expression mg1, mg2;
- MethodBase method;
- ArrayList arguments;
-
- mg1 = MemberLookup (tc, source.Type, "op_Implicit", false);
- mg2 = MemberLookup (tc, target, "op_Implicit", false);
-
- MethodGroupExpr union = Invocation.MakeUnionSet (mg1, mg2);
-
- if (union != null) {
- arguments = new ArrayList ();
- arguments.Add (new Argument (source, Argument.AType.Expression));
-
- method = Invocation.OverloadResolve (tc, union, arguments, l, true);
-
- if (method != null) {
- MethodInfo mi = (MethodInfo) method;
-
- if (mi.ReturnType == target)
- return new UserImplicitCast (mi, arguments);
- }
- }
-
- // If we have a boolean type, we need to check for the True
- // and False operators too.
-
- if (target == TypeManager.bool_type) {
-
- mg1 = MemberLookup (tc, source.Type, "op_True", false);
- mg2 = MemberLookup (tc, target, "op_True", false);
-
- union = Invocation.MakeUnionSet (mg1, mg2);
-
- if (union == null)
- return null;
-
- arguments = new ArrayList ();
- arguments.Add (new Argument (source, Argument.AType.Expression));
-
- method = Invocation.OverloadResolve (tc, union, arguments,
- new Location ("FIXME", 1, 1), true);
- if (method != null) {
- MethodInfo mi = (MethodInfo) method;
-
- if (mi.ReturnType == target)
- return new UserImplicitCast (mi, arguments);
- }
- }
-
- return null;
- }
-
- // <summary>
- // Converts implicitly the resolved expression `expr' into the
- // `target_type'. It returns a new expression that can be used
- // in a context that expects a `target_type'.
- // </summary>
- static public Expression ConvertImplicit (TypeContainer tc, Expression expr,
- Type target_type, Location l)
- {
- Type expr_type = expr.Type;
- Expression e;
-
- if (expr_type == target_type)
- return expr;
-
- e = ImplicitNumericConversion (tc, expr, target_type, l);
- if (e != null)
- return e;
-
- e = ImplicitReferenceConversion (expr, target_type);
- if (e != null)
- return e;
-
- e = ImplicitUserConversion (tc, expr, target_type, l);
- if (e != null)
- return e;
-
- if (target_type.IsSubclassOf (TypeManager.enum_type) && expr is IntLiteral){
- IntLiteral i = (IntLiteral) expr;
-
- if (i.Value == 0)
- return new EmptyCast (expr, target_type);
- }
- return null;
- }
-
-
- // <summary>
- // Attempts to apply the `Standard Implicit
- // Conversion' rules to the expression `expr' into
- // the `target_type'. It returns a new expression
- // that can be used in a context that expects a
- // `target_type'.
- //
- // This is different from `ConvertImplicit' in that the
- // user defined implicit conversions are excluded.
- // </summary>
- static public Expression ConvertImplicitStandard (TypeContainer tc, Expression expr,
- Type target_type, Location l)
- {
- Type expr_type = expr.Type;
- Expression e;
-
- if (expr_type == target_type)
- return expr;
-
- e = ImplicitNumericConversion (tc, expr, target_type, l);
- if (e != null)
- return e;
-
- e = ImplicitReferenceConversion (expr, target_type);
- if (e != null)
- return e;
-
- if (target_type.IsSubclassOf (TypeManager.enum_type) && expr is IntLiteral){
- IntLiteral i = (IntLiteral) expr;
-
- if (i.Value == 0)
- return new EmptyCast (expr, target_type);
- }
- return null;
- }
- // <summary>
- // Attemps to perform an implict constant conversion of the IntLiteral
- // into a different data type using casts (See Implicit Constant
- // Expression Conversions)
- // </summary>
- static protected Expression TryImplicitIntConversion (Type target_type, IntLiteral il)
- {
- int value = il.Value;
-
- if (target_type == TypeManager.sbyte_type){
- if (value >= SByte.MinValue && value <= SByte.MaxValue)
- return il;
- } else if (target_type == TypeManager.byte_type){
- if (Byte.MinValue >= 0 && value <= Byte.MaxValue)
- return il;
- } else if (target_type == TypeManager.short_type){
- if (value >= Int16.MinValue && value <= Int16.MaxValue)
- return il;
- } else if (target_type == TypeManager.ushort_type){
- if (value >= UInt16.MinValue && value <= UInt16.MaxValue)
- return il;
- } else if (target_type == TypeManager.uint32_type){
- //
- // we can optimize this case: a positive int32
- // always fits on a uint32
- //
- if (value >= 0)
- return il;
- } else if (target_type == TypeManager.uint64_type){
- //
- // 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 OpcodeCast (il, target_type, OpCodes.Conv_I8);
- }