2 // expression.cs: Expression representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@seznam.cz)
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2003, 2004 Novell, Inc.
13 namespace Mono.CSharp {
15 using System.Collections;
16 using System.Reflection;
17 using System.Reflection.Emit;
21 /// This is just a helper class, it is generated by Unary, UnaryMutator
22 /// when an overloaded method has been found. It just emits the code for a
25 public class StaticCallExpr : ExpressionStatement {
29 public StaticCallExpr (MethodInfo m, ArrayList a, Location l)
35 eclass = ExprClass.Value;
39 public override Expression DoResolve (EmitContext ec)
42 // We are born fully resolved
47 public override void Emit (EmitContext ec)
50 Invocation.EmitArguments (ec, mi, args, false, null);
52 ec.ig.Emit (OpCodes.Call, mi);
56 static public StaticCallExpr MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
57 Expression e, Location loc)
62 args = new ArrayList (1);
63 Argument a = new Argument (e, Argument.AType.Expression);
65 // We need to resolve the arguments before sending them in !
66 if (!a.Resolve (ec, loc))
70 method = Invocation.OverloadResolve (
71 ec, (MethodGroupExpr) mg, args, false, loc);
76 return new StaticCallExpr ((MethodInfo) method, args, loc);
79 public override void EmitStatement (EmitContext ec)
82 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
83 ec.ig.Emit (OpCodes.Pop);
86 public MethodInfo Method {
91 public class ParenthesizedExpression : Expression
93 public Expression Expr;
95 public ParenthesizedExpression (Expression expr)
100 public override Expression DoResolve (EmitContext ec)
102 Expr = Expr.Resolve (ec);
106 public override void Emit (EmitContext ec)
108 throw new Exception ("Should not happen");
111 public override Location Location
114 return Expr.Location;
120 /// Unary expressions.
124 /// Unary implements unary expressions. It derives from
125 /// ExpressionStatement becuase the pre/post increment/decrement
126 /// operators can be used in a statement context.
128 public class Unary : Expression {
129 public enum Operator : byte {
130 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
131 Indirection, AddressOf, TOP
134 public Operator Oper;
135 public Expression Expr;
137 public Unary (Operator op, Expression expr, Location loc)
145 /// Returns a stringified representation of the Operator
147 static public string OperName (Operator oper)
150 case Operator.UnaryPlus:
152 case Operator.UnaryNegation:
154 case Operator.LogicalNot:
156 case Operator.OnesComplement:
158 case Operator.AddressOf:
160 case Operator.Indirection:
164 return oper.ToString ();
167 public static readonly string [] oper_names;
171 oper_names = new string [(int)Operator.TOP];
173 oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus";
174 oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation";
175 oper_names [(int) Operator.LogicalNot] = "op_LogicalNot";
176 oper_names [(int) Operator.OnesComplement] = "op_OnesComplement";
177 oper_names [(int) Operator.Indirection] = "op_Indirection";
178 oper_names [(int) Operator.AddressOf] = "op_AddressOf";
181 void Error23 (Type t)
183 Report.Error (23, loc, "Operator `{0}' cannot be applied to operand of type `{1}'",
184 OperName (Oper), TypeManager.CSharpName (t));
188 /// The result has been already resolved:
190 /// FIXME: a minus constant -128 sbyte cant be turned into a
193 static Expression TryReduceNegative (Constant expr)
197 if (expr is IntConstant)
198 e = new IntConstant (-((IntConstant) expr).Value, expr.Location);
199 else if (expr is UIntConstant){
200 uint value = ((UIntConstant) expr).Value;
202 if (value < 2147483649)
203 return new IntConstant (-(int)value, expr.Location);
205 e = new LongConstant (-value, expr.Location);
207 else if (expr is LongConstant)
208 e = new LongConstant (-((LongConstant) expr).Value, expr.Location);
209 else if (expr is ULongConstant){
210 ulong value = ((ULongConstant) expr).Value;
212 if (value < 9223372036854775809)
213 return new LongConstant(-(long)value, expr.Location);
215 else if (expr is FloatConstant)
216 e = new FloatConstant (-((FloatConstant) expr).Value, expr.Location);
217 else if (expr is DoubleConstant)
218 e = new DoubleConstant (-((DoubleConstant) expr).Value, expr.Location);
219 else if (expr is DecimalConstant)
220 e = new DecimalConstant (-((DecimalConstant) expr).Value, expr.Location);
221 else if (expr is ShortConstant)
222 e = new IntConstant (-((ShortConstant) expr).Value, expr.Location);
223 else if (expr is UShortConstant)
224 e = new IntConstant (-((UShortConstant) expr).Value, expr.Location);
225 else if (expr is SByteConstant)
226 e = new IntConstant (-((SByteConstant) expr).Value, expr.Location);
227 else if (expr is ByteConstant)
228 e = new IntConstant (-((ByteConstant) expr).Value, expr.Location);
233 // This routine will attempt to simplify the unary expression when the
234 // argument is a constant. The result is returned in `result' and the
235 // function returns true or false depending on whether a reduction
236 // was performed or not
238 bool Reduce (EmitContext ec, Constant e, out Expression result)
240 Type expr_type = e.Type;
243 case Operator.UnaryPlus:
244 if (expr_type == TypeManager.bool_type){
253 case Operator.UnaryNegation:
254 result = TryReduceNegative (e);
255 return result != null;
257 case Operator.LogicalNot:
258 if (expr_type != TypeManager.bool_type) {
264 BoolConstant b = (BoolConstant) e;
265 result = new BoolConstant (!(b.Value), b.Location);
268 case Operator.OnesComplement:
269 if (!((expr_type == TypeManager.int32_type) ||
270 (expr_type == TypeManager.uint32_type) ||
271 (expr_type == TypeManager.int64_type) ||
272 (expr_type == TypeManager.uint64_type) ||
273 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
276 if (Convert.ImplicitConversionExists (ec, e, TypeManager.int32_type)){
277 result = new Cast (new TypeExpression (TypeManager.int32_type, loc), e, loc);
278 result = result.Resolve (ec);
279 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.uint32_type)){
280 result = new Cast (new TypeExpression (TypeManager.uint32_type, loc), e, loc);
281 result = result.Resolve (ec);
282 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.int64_type)){
283 result = new Cast (new TypeExpression (TypeManager.int64_type, loc), e, loc);
284 result = result.Resolve (ec);
285 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.uint64_type)){
286 result = new Cast (new TypeExpression (TypeManager.uint64_type, loc), e, loc);
287 result = result.Resolve (ec);
290 if (result == null || !(result is Constant)){
296 expr_type = result.Type;
297 e = (Constant) result;
300 if (e is EnumConstant){
301 EnumConstant enum_constant = (EnumConstant) e;
304 if (Reduce (ec, enum_constant.Child, out reduced)){
305 result = new EnumConstant ((Constant) reduced, enum_constant.Type);
313 if (expr_type == TypeManager.int32_type){
314 result = new IntConstant (~ ((IntConstant) e).Value, e.Location);
315 } else if (expr_type == TypeManager.uint32_type){
316 result = new UIntConstant (~ ((UIntConstant) e).Value, e.Location);
317 } else if (expr_type == TypeManager.int64_type){
318 result = new LongConstant (~ ((LongConstant) e).Value, e.Location);
319 } else if (expr_type == TypeManager.uint64_type){
320 result = new ULongConstant (~ ((ULongConstant) e).Value, e.Location);
328 case Operator.AddressOf:
332 case Operator.Indirection:
336 throw new Exception ("Can not constant fold: " + Oper.ToString());
339 Expression ResolveOperator (EmitContext ec)
342 // Step 1: Default operations on CLI native types.
345 // Attempt to use a constant folding operation.
346 if (Expr is Constant){
349 if (Reduce (ec, (Constant) Expr, out result))
354 // Step 2: Perform Operator Overload location
356 Type expr_type = Expr.Type;
360 op_name = oper_names [(int) Oper];
362 mg = MemberLookup (ec, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
365 Expression e = StaticCallExpr.MakeSimpleCall (
366 ec, (MethodGroupExpr) mg, Expr, loc);
376 // Only perform numeric promotions on:
379 if (expr_type == null)
383 case Operator.LogicalNot:
384 if (expr_type != TypeManager.bool_type) {
385 Expr = ResolveBoolean (ec, Expr, loc);
392 type = TypeManager.bool_type;
395 case Operator.OnesComplement:
396 if (!((expr_type == TypeManager.int32_type) ||
397 (expr_type == TypeManager.uint32_type) ||
398 (expr_type == TypeManager.int64_type) ||
399 (expr_type == TypeManager.uint64_type) ||
400 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
403 e = Convert.ImplicitConversion (ec, Expr, TypeManager.int32_type, loc);
406 e = Convert.ImplicitConversion (ec, Expr, TypeManager.uint32_type, loc);
409 e = Convert.ImplicitConversion (ec, Expr, TypeManager.int64_type, loc);
412 e = Convert.ImplicitConversion (ec, Expr, TypeManager.uint64_type, loc);
425 case Operator.AddressOf:
431 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){
435 IVariable variable = Expr as IVariable;
436 bool is_fixed = variable != null && variable.VerifyFixed ();
438 if (!ec.InFixedInitializer && !is_fixed) {
439 Error (212, "You can only take the address of unfixed expression inside " +
440 "of a fixed statement initializer");
444 if (ec.InFixedInitializer && is_fixed) {
445 Error (213, "You cannot use the fixed statement to take the address of an already fixed expression");
449 LocalVariableReference lr = Expr as LocalVariableReference;
451 if (lr.local_info.IsCaptured){
452 AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc);
455 lr.local_info.AddressTaken = true;
456 lr.local_info.Used = true;
459 // According to the specs, a variable is considered definitely assigned if you take
461 if ((variable != null) && (variable.VariableInfo != null)){
462 variable.VariableInfo.SetAssigned (ec);
465 type = TypeManager.GetPointerType (Expr.Type);
468 case Operator.Indirection:
474 if (!expr_type.IsPointer){
475 Error (193, "The * or -> operator must be applied to a pointer");
480 // We create an Indirection expression, because
481 // it can implement the IMemoryLocation.
483 return new Indirection (Expr, loc);
485 case Operator.UnaryPlus:
487 // A plus in front of something is just a no-op, so return the child.
491 case Operator.UnaryNegation:
493 // Deals with -literals
494 // int operator- (int x)
495 // long operator- (long x)
496 // float operator- (float f)
497 // double operator- (double d)
498 // decimal operator- (decimal d)
500 Expression expr = null;
503 // transform - - expr into expr
506 Unary unary = (Unary) Expr;
508 if (unary.Oper == Operator.UnaryNegation)
513 // perform numeric promotions to int,
517 // The following is inneficient, because we call
518 // ImplicitConversion too many times.
520 // It is also not clear if we should convert to Float
521 // or Double initially.
523 if (expr_type == TypeManager.uint32_type){
525 // FIXME: handle exception to this rule that
526 // permits the int value -2147483648 (-2^31) to
527 // bt wrote as a decimal interger literal
529 type = TypeManager.int64_type;
530 Expr = Convert.ImplicitConversion (ec, Expr, type, loc);
534 if (expr_type == TypeManager.uint64_type){
536 // FIXME: Handle exception of `long value'
537 // -92233720368547758087 (-2^63) to be wrote as
538 // decimal integer literal.
544 if (expr_type == TypeManager.float_type){
549 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int32_type, loc);
556 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int64_type, loc);
563 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.double_type, loc);
574 Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" +
575 TypeManager.CSharpName (expr_type) + "'");
579 public override Expression DoResolve (EmitContext ec)
581 if (Oper == Operator.AddressOf) {
582 Expr = Expr.DoResolveLValue (ec, new EmptyExpression ());
584 if (Expr == null || Expr.eclass != ExprClass.Variable){
585 Error (211, "Cannot take the address of the given expression");
590 Expr = Expr.Resolve (ec);
595 eclass = ExprClass.Value;
596 return ResolveOperator (ec);
599 public override Expression DoResolveLValue (EmitContext ec, Expression right)
601 if (Oper == Operator.Indirection)
602 return DoResolve (ec);
607 public override void Emit (EmitContext ec)
609 ILGenerator ig = ec.ig;
612 case Operator.UnaryPlus:
613 throw new Exception ("This should be caught by Resolve");
615 case Operator.UnaryNegation:
617 ig.Emit (OpCodes.Ldc_I4_0);
618 if (type == TypeManager.int64_type)
619 ig.Emit (OpCodes.Conv_U8);
621 ig.Emit (OpCodes.Sub_Ovf);
624 ig.Emit (OpCodes.Neg);
629 case Operator.LogicalNot:
631 ig.Emit (OpCodes.Ldc_I4_0);
632 ig.Emit (OpCodes.Ceq);
635 case Operator.OnesComplement:
637 ig.Emit (OpCodes.Not);
640 case Operator.AddressOf:
641 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
645 throw new Exception ("This should not happen: Operator = "
650 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
652 if (Oper == Operator.LogicalNot)
653 Expr.EmitBranchable (ec, target, !onTrue);
655 base.EmitBranchable (ec, target, onTrue);
658 public override string ToString ()
660 return "Unary (" + Oper + ", " + Expr + ")";
666 // Unary operators are turned into Indirection expressions
667 // after semantic analysis (this is so we can take the address
668 // of an indirection).
670 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
672 LocalTemporary temporary;
675 public Indirection (Expression expr, Location l)
678 type = TypeManager.HasElementType (expr.Type) ? TypeManager.GetElementType (expr.Type) : expr.Type;
679 eclass = ExprClass.Variable;
683 public override void Emit (EmitContext ec)
688 LoadFromPtr (ec.ig, Type);
691 public void Emit (EmitContext ec, bool leave_copy)
695 ec.ig.Emit (OpCodes.Dup);
696 temporary = new LocalTemporary (ec, expr.Type);
697 temporary.Store (ec);
701 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
703 prepared = prepare_for_load;
707 if (prepare_for_load)
708 ec.ig.Emit (OpCodes.Dup);
712 ec.ig.Emit (OpCodes.Dup);
713 temporary = new LocalTemporary (ec, expr.Type);
714 temporary.Store (ec);
717 StoreFromPtr (ec.ig, type);
719 if (temporary != null)
723 public void AddressOf (EmitContext ec, AddressOp Mode)
728 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
730 return DoResolve (ec);
733 public override Expression DoResolve (EmitContext ec)
736 // Born fully resolved
741 public override string ToString ()
743 return "*(" + expr + ")";
746 #region IVariable Members
748 public VariableInfo VariableInfo {
754 public bool VerifyFixed ()
756 // A pointer-indirection is always fixed.
764 /// Unary Mutator expressions (pre and post ++ and --)
768 /// UnaryMutator implements ++ and -- expressions. It derives from
769 /// ExpressionStatement becuase the pre/post increment/decrement
770 /// operators can be used in a statement context.
772 /// FIXME: Idea, we could split this up in two classes, one simpler
773 /// for the common case, and one with the extra fields for more complex
774 /// classes (indexers require temporary access; overloaded require method)
777 public class UnaryMutator : ExpressionStatement {
779 public enum Mode : byte {
786 PreDecrement = IsDecrement,
787 PostIncrement = IsPost,
788 PostDecrement = IsPost | IsDecrement
792 bool is_expr = false;
793 bool recurse = false;
798 // This is expensive for the simplest case.
800 StaticCallExpr method;
802 public UnaryMutator (Mode m, Expression e, Location l)
809 static string OperName (Mode mode)
811 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
816 /// Returns whether an object of type `t' can be incremented
817 /// or decremented with add/sub (ie, basically whether we can
818 /// use pre-post incr-decr operations on it, but it is not a
819 /// System.Decimal, which we require operator overloading to catch)
821 static bool IsIncrementableNumber (Type t)
823 return (t == TypeManager.sbyte_type) ||
824 (t == TypeManager.byte_type) ||
825 (t == TypeManager.short_type) ||
826 (t == TypeManager.ushort_type) ||
827 (t == TypeManager.int32_type) ||
828 (t == TypeManager.uint32_type) ||
829 (t == TypeManager.int64_type) ||
830 (t == TypeManager.uint64_type) ||
831 (t == TypeManager.char_type) ||
832 (t.IsSubclassOf (TypeManager.enum_type)) ||
833 (t == TypeManager.float_type) ||
834 (t == TypeManager.double_type) ||
835 (t.IsPointer && t != TypeManager.void_ptr_type);
838 Expression ResolveOperator (EmitContext ec)
840 Type expr_type = expr.Type;
843 // Step 1: Perform Operator Overload location
848 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
849 op_name = "op_Increment";
851 op_name = "op_Decrement";
853 mg = MemberLookup (ec, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
856 method = StaticCallExpr.MakeSimpleCall (
857 ec, (MethodGroupExpr) mg, expr, loc);
860 } else if (!IsIncrementableNumber (expr_type)) {
861 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
862 TypeManager.CSharpName (expr_type) + "'");
867 // The operand of the prefix/postfix increment decrement operators
868 // should be an expression that is classified as a variable,
869 // a property access or an indexer access
872 if (expr.eclass == ExprClass.Variable){
873 LocalVariableReference var = expr as LocalVariableReference;
874 if ((var != null) && var.IsReadOnly) {
875 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
878 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
879 expr = expr.ResolveLValue (ec, this, Location);
883 expr.Error_UnexpectedKind (ec, "variable, indexer or property access", loc);
890 public override Expression DoResolve (EmitContext ec)
892 expr = expr.Resolve (ec);
897 eclass = ExprClass.Value;
898 return ResolveOperator (ec);
901 static int PtrTypeSize (Type t)
903 return GetTypeSize (TypeManager.GetElementType (t));
907 // Loads the proper "1" into the stack based on the type, then it emits the
908 // opcode for the operation requested
910 void LoadOneAndEmitOp (EmitContext ec, Type t)
913 // Measure if getting the typecode and using that is more/less efficient
914 // that comparing types. t.GetTypeCode() is an internal call.
916 ILGenerator ig = ec.ig;
918 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
919 LongConstant.EmitLong (ig, 1);
920 else if (t == TypeManager.double_type)
921 ig.Emit (OpCodes.Ldc_R8, 1.0);
922 else if (t == TypeManager.float_type)
923 ig.Emit (OpCodes.Ldc_R4, 1.0F);
924 else if (t.IsPointer){
925 int n = PtrTypeSize (t);
928 ig.Emit (OpCodes.Sizeof, t);
930 IntConstant.EmitInt (ig, n);
932 ig.Emit (OpCodes.Ldc_I4_1);
935 // Now emit the operation
938 if (t == TypeManager.int32_type ||
939 t == TypeManager.int64_type){
940 if ((mode & Mode.IsDecrement) != 0)
941 ig.Emit (OpCodes.Sub_Ovf);
943 ig.Emit (OpCodes.Add_Ovf);
944 } else if (t == TypeManager.uint32_type ||
945 t == TypeManager.uint64_type){
946 if ((mode & Mode.IsDecrement) != 0)
947 ig.Emit (OpCodes.Sub_Ovf_Un);
949 ig.Emit (OpCodes.Add_Ovf_Un);
951 if ((mode & Mode.IsDecrement) != 0)
952 ig.Emit (OpCodes.Sub_Ovf);
954 ig.Emit (OpCodes.Add_Ovf);
957 if ((mode & Mode.IsDecrement) != 0)
958 ig.Emit (OpCodes.Sub);
960 ig.Emit (OpCodes.Add);
963 if (t == TypeManager.sbyte_type){
965 ig.Emit (OpCodes.Conv_Ovf_I1);
967 ig.Emit (OpCodes.Conv_I1);
968 } else if (t == TypeManager.byte_type){
970 ig.Emit (OpCodes.Conv_Ovf_U1);
972 ig.Emit (OpCodes.Conv_U1);
973 } else if (t == TypeManager.short_type){
975 ig.Emit (OpCodes.Conv_Ovf_I2);
977 ig.Emit (OpCodes.Conv_I2);
978 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
980 ig.Emit (OpCodes.Conv_Ovf_U2);
982 ig.Emit (OpCodes.Conv_U2);
987 void EmitCode (EmitContext ec, bool is_expr)
990 this.is_expr = is_expr;
991 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
995 public override void Emit (EmitContext ec)
998 // We use recurse to allow ourselfs to be the source
999 // of an assignment. This little hack prevents us from
1000 // having to allocate another expression
1003 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1005 LoadOneAndEmitOp (ec, expr.Type);
1007 ec.ig.Emit (OpCodes.Call, method.Method);
1012 EmitCode (ec, true);
1015 public override void EmitStatement (EmitContext ec)
1017 EmitCode (ec, false);
1022 /// Base class for the `Is' and `As' classes.
1026 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1029 public abstract class Probe : Expression {
1030 public Expression ProbeType;
1031 protected Expression expr;
1032 protected Type probe_type;
1034 public Probe (Expression expr, Expression probe_type, Location l)
1036 ProbeType = probe_type;
1041 public Expression Expr {
1047 public override Expression DoResolve (EmitContext ec)
1049 TypeExpr texpr = ProbeType.ResolveAsTypeTerminal (ec, false);
1052 probe_type = texpr.ResolveType (ec);
1054 expr = expr.Resolve (ec);
1058 if (expr.Type.IsPointer) {
1059 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1067 /// Implementation of the `is' operator.
1069 public class Is : Probe {
1070 public Is (Expression expr, Expression probe_type, Location l)
1071 : base (expr, probe_type, l)
1076 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1081 public override void Emit (EmitContext ec)
1083 ILGenerator ig = ec.ig;
1088 case Action.AlwaysFalse:
1089 ig.Emit (OpCodes.Pop);
1090 IntConstant.EmitInt (ig, 0);
1092 case Action.AlwaysTrue:
1093 ig.Emit (OpCodes.Pop);
1094 IntConstant.EmitInt (ig, 1);
1096 case Action.LeaveOnStack:
1097 // the `e != null' rule.
1098 ig.Emit (OpCodes.Ldnull);
1099 ig.Emit (OpCodes.Ceq);
1100 ig.Emit (OpCodes.Ldc_I4_0);
1101 ig.Emit (OpCodes.Ceq);
1104 ig.Emit (OpCodes.Isinst, probe_type);
1105 ig.Emit (OpCodes.Ldnull);
1106 ig.Emit (OpCodes.Cgt_Un);
1109 throw new Exception ("never reached");
1112 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1114 ILGenerator ig = ec.ig;
1117 case Action.AlwaysFalse:
1119 ig.Emit (OpCodes.Br, target);
1122 case Action.AlwaysTrue:
1124 ig.Emit (OpCodes.Br, target);
1127 case Action.LeaveOnStack:
1128 // the `e != null' rule.
1130 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1134 ig.Emit (OpCodes.Isinst, probe_type);
1135 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1138 throw new Exception ("never reached");
1141 public override Expression DoResolve (EmitContext ec)
1143 Expression e = base.DoResolve (ec);
1145 if ((e == null) || (expr == null))
1148 Type etype = expr.Type;
1149 bool warning_always_matches = false;
1150 bool warning_never_matches = false;
1152 type = TypeManager.bool_type;
1153 eclass = ExprClass.Value;
1156 // First case, if at compile time, there is an implicit conversion
1157 // then e != null (objects) or true (value types)
1159 e = Convert.ImplicitConversionStandard (ec, expr, probe_type, loc);
1160 if (e != null && !(e is NullCast)){
1162 if (etype.IsValueType)
1163 action = Action.AlwaysTrue;
1165 action = Action.LeaveOnStack;
1167 warning_always_matches = true;
1168 } else if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1170 // Second case: explicit reference convresion
1172 if (expr is NullLiteral)
1173 action = Action.AlwaysFalse;
1175 action = Action.Probe;
1177 action = Action.AlwaysFalse;
1178 warning_never_matches = true;
1181 if (warning_always_matches)
1182 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1183 else if (warning_never_matches){
1184 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1185 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1193 /// Implementation of the `as' operator.
1195 public class As : Probe {
1196 public As (Expression expr, Expression probe_type, Location l)
1197 : base (expr, probe_type, l)
1201 bool do_isinst = false;
1202 Expression resolved_type;
1204 public override void Emit (EmitContext ec)
1206 ILGenerator ig = ec.ig;
1211 ig.Emit (OpCodes.Isinst, probe_type);
1214 static void Error_CannotConvertType (Type source, Type target, Location loc)
1216 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1217 TypeManager.CSharpName (source),
1218 TypeManager.CSharpName (target));
1221 public override Expression DoResolve (EmitContext ec)
1223 if (resolved_type == null) {
1224 resolved_type = base.DoResolve (ec);
1226 if (resolved_type == null)
1231 eclass = ExprClass.Value;
1232 Type etype = expr.Type;
1234 if (TypeManager.IsValueType (probe_type)){
1235 Report.Error (77, loc, "The as operator must be used with a reference type (`" +
1236 TypeManager.CSharpName (probe_type) + "' is a value type)");
1241 Expression e = Convert.ImplicitConversion (ec, expr, probe_type, loc);
1248 if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1253 Error_CannotConvertType (etype, probe_type, loc);
1259 /// This represents a typecast in the source language.
1261 /// FIXME: Cast expressions have an unusual set of parsing
1262 /// rules, we need to figure those out.
1264 public class Cast : Expression {
1265 Expression target_type;
1268 public Cast (Expression cast_type, Expression expr)
1269 : this (cast_type, expr, cast_type.Location)
1273 public Cast (Expression cast_type, Expression expr, Location loc)
1275 this.target_type = cast_type;
1280 public Expression TargetType {
1286 public Expression Expr {
1295 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
1297 expr = expr.DoResolveLValue (ec, right_side);
1301 return ResolveRest (ec);
1304 public override Expression DoResolve (EmitContext ec)
1306 expr = expr.Resolve (ec);
1310 return ResolveRest (ec);
1313 Expression ResolveRest (EmitContext ec)
1315 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1319 type = target.ResolveType (ec);
1321 if (type.IsAbstract && type.IsSealed) {
1322 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1326 eclass = ExprClass.Value;
1328 Constant c = expr as Constant;
1330 c = c.TryReduce (ec, type, loc);
1335 if (type.IsPointer && !ec.InUnsafe) {
1339 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1343 public override void Emit (EmitContext ec)
1346 // This one will never happen
1348 throw new Exception ("Should not happen");
1353 /// Binary operators
1355 public class Binary : Expression {
1356 public enum Operator : byte {
1357 Multiply, Division, Modulus,
1358 Addition, Subtraction,
1359 LeftShift, RightShift,
1360 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1361 Equality, Inequality,
1371 Expression left, right;
1373 // This must be kept in sync with Operator!!!
1374 public static readonly string [] oper_names;
1378 oper_names = new string [(int) Operator.TOP];
1380 oper_names [(int) Operator.Multiply] = "op_Multiply";
1381 oper_names [(int) Operator.Division] = "op_Division";
1382 oper_names [(int) Operator.Modulus] = "op_Modulus";
1383 oper_names [(int) Operator.Addition] = "op_Addition";
1384 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1385 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1386 oper_names [(int) Operator.RightShift] = "op_RightShift";
1387 oper_names [(int) Operator.LessThan] = "op_LessThan";
1388 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1389 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1390 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1391 oper_names [(int) Operator.Equality] = "op_Equality";
1392 oper_names [(int) Operator.Inequality] = "op_Inequality";
1393 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1394 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1395 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1396 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1397 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1400 public Binary (Operator oper, Expression left, Expression right)
1405 this.loc = left.Location;
1408 public Operator Oper {
1417 public Expression Left {
1426 public Expression Right {
1437 /// Returns a stringified representation of the Operator
1439 public static string OperName (Operator oper)
1442 case Operator.Multiply:
1444 case Operator.Division:
1446 case Operator.Modulus:
1448 case Operator.Addition:
1450 case Operator.Subtraction:
1452 case Operator.LeftShift:
1454 case Operator.RightShift:
1456 case Operator.LessThan:
1458 case Operator.GreaterThan:
1460 case Operator.LessThanOrEqual:
1462 case Operator.GreaterThanOrEqual:
1464 case Operator.Equality:
1466 case Operator.Inequality:
1468 case Operator.BitwiseAnd:
1470 case Operator.BitwiseOr:
1472 case Operator.ExclusiveOr:
1474 case Operator.LogicalOr:
1476 case Operator.LogicalAnd:
1480 return oper.ToString ();
1483 public override string ToString ()
1485 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1486 right.ToString () + ")";
1489 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1491 if (expr.Type == target_type)
1494 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1497 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1500 34, loc, "Operator `" + OperName (oper)
1501 + "' is ambiguous on operands of type `"
1502 + TypeManager.CSharpName (l) + "' "
1503 + "and `" + TypeManager.CSharpName (r)
1507 bool IsOfType (EmitContext ec, Type l, Type r, Type t, bool check_user_conversions)
1509 if ((l == t) || (r == t))
1512 if (!check_user_conversions)
1515 if (Convert.ImplicitUserConversionExists (ec, l, t))
1517 else if (Convert.ImplicitUserConversionExists (ec, r, t))
1524 // Note that handling the case l == Decimal || r == Decimal
1525 // is taken care of by the Step 1 Operator Overload resolution.
1527 // If `check_user_conv' is true, we also check whether a user-defined conversion
1528 // exists. Note that we only need to do this if both arguments are of a user-defined
1529 // type, otherwise ConvertImplict() already finds the user-defined conversion for us,
1530 // so we don't explicitly check for performance reasons.
1532 bool DoNumericPromotions (EmitContext ec, Type l, Type r, Expression lexpr, Expression rexpr, bool check_user_conv)
1534 if (IsOfType (ec, l, r, TypeManager.double_type, check_user_conv)){
1536 // If either operand is of type double, the other operand is
1537 // conveted to type double.
1539 if (r != TypeManager.double_type)
1540 right = Convert.ImplicitConversion (ec, right, TypeManager.double_type, loc);
1541 if (l != TypeManager.double_type)
1542 left = Convert.ImplicitConversion (ec, left, TypeManager.double_type, loc);
1544 type = TypeManager.double_type;
1545 } else if (IsOfType (ec, l, r, TypeManager.float_type, check_user_conv)){
1547 // if either operand is of type float, the other operand is
1548 // converted to type float.
1550 if (r != TypeManager.double_type)
1551 right = Convert.ImplicitConversion (ec, right, TypeManager.float_type, loc);
1552 if (l != TypeManager.double_type)
1553 left = Convert.ImplicitConversion (ec, left, TypeManager.float_type, loc);
1554 type = TypeManager.float_type;
1555 } else if (IsOfType (ec, l, r, TypeManager.uint64_type, check_user_conv)){
1559 // If either operand is of type ulong, the other operand is
1560 // converted to type ulong. or an error ocurrs if the other
1561 // operand is of type sbyte, short, int or long
1563 if (l == TypeManager.uint64_type){
1564 if (r != TypeManager.uint64_type){
1565 if (right is IntConstant){
1566 IntConstant ic = (IntConstant) right;
1568 e = Convert.TryImplicitIntConversion (l, ic);
1571 } else if (right is LongConstant){
1572 long ll = ((LongConstant) right).Value;
1575 right = new ULongConstant ((ulong) ll, right.Location);
1577 e = Convert.ImplicitNumericConversion (ec, right, l);
1584 if (left is IntConstant){
1585 e = Convert.TryImplicitIntConversion (r, (IntConstant) left);
1588 } else if (left is LongConstant){
1589 long ll = ((LongConstant) left).Value;
1592 left = new ULongConstant ((ulong) ll, right.Location);
1594 e = Convert.ImplicitNumericConversion (ec, left, r);
1601 if ((other == TypeManager.sbyte_type) ||
1602 (other == TypeManager.short_type) ||
1603 (other == TypeManager.int32_type) ||
1604 (other == TypeManager.int64_type))
1605 Error_OperatorAmbiguous (loc, oper, l, r);
1607 left = ForceConversion (ec, left, TypeManager.uint64_type);
1608 right = ForceConversion (ec, right, TypeManager.uint64_type);
1610 type = TypeManager.uint64_type;
1611 } else if (IsOfType (ec, l, r, TypeManager.int64_type, check_user_conv)){
1613 // If either operand is of type long, the other operand is converted
1616 if (l != TypeManager.int64_type)
1617 left = Convert.ImplicitConversion (ec, left, TypeManager.int64_type, loc);
1618 if (r != TypeManager.int64_type)
1619 right = Convert.ImplicitConversion (ec, right, TypeManager.int64_type, loc);
1621 type = TypeManager.int64_type;
1622 } else if (IsOfType (ec, l, r, TypeManager.uint32_type, check_user_conv)){
1624 // If either operand is of type uint, and the other
1625 // operand is of type sbyte, short or int, othe operands are
1626 // converted to type long (unless we have an int constant).
1630 if (l == TypeManager.uint32_type){
1631 if (right is IntConstant){
1632 IntConstant ic = (IntConstant) right;
1636 right = new UIntConstant ((uint) val, ic.Location);
1643 } else if (r == TypeManager.uint32_type){
1644 if (left is IntConstant){
1645 IntConstant ic = (IntConstant) left;
1649 left = new UIntConstant ((uint) val, ic.Location);
1658 if ((other == TypeManager.sbyte_type) ||
1659 (other == TypeManager.short_type) ||
1660 (other == TypeManager.int32_type)){
1661 left = ForceConversion (ec, left, TypeManager.int64_type);
1662 right = ForceConversion (ec, right, TypeManager.int64_type);
1663 type = TypeManager.int64_type;
1666 // if either operand is of type uint, the other
1667 // operand is converd to type uint
1669 left = ForceConversion (ec, left, TypeManager.uint32_type);
1670 right = ForceConversion (ec, right, TypeManager.uint32_type);
1671 type = TypeManager.uint32_type;
1673 } else if (l == TypeManager.decimal_type || r == TypeManager.decimal_type){
1674 if (l != TypeManager.decimal_type)
1675 left = Convert.ImplicitConversion (ec, left, TypeManager.decimal_type, loc);
1677 if (r != TypeManager.decimal_type)
1678 right = Convert.ImplicitConversion (ec, right, TypeManager.decimal_type, loc);
1679 type = TypeManager.decimal_type;
1681 left = ForceConversion (ec, left, TypeManager.int32_type);
1682 right = ForceConversion (ec, right, TypeManager.int32_type);
1685 Convert.ImplicitConversionExists (ec, lexpr, TypeManager.string_type) &&
1686 Convert.ImplicitConversionExists (ec, rexpr, TypeManager.string_type);
1687 if (strConv && left != null && right != null)
1688 Error_OperatorAmbiguous (loc, oper, l, r);
1690 type = TypeManager.int32_type;
1693 return (left != null) && (right != null);
1696 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1698 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1701 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1703 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1707 void Error_OperatorCannotBeApplied ()
1709 Error_OperatorCannotBeApplied (Location, OperName (oper), left.GetSignatureForError (), right.GetSignatureForError ());
1712 static bool is_unsigned (Type t)
1714 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1715 t == TypeManager.short_type || t == TypeManager.byte_type);
1718 static bool is_user_defined (Type t)
1720 if (t.IsSubclassOf (TypeManager.value_type) &&
1721 (!TypeManager.IsBuiltinType (t) || t == TypeManager.decimal_type))
1727 Expression Make32or64 (EmitContext ec, Expression e)
1731 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1732 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1734 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1737 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1740 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1743 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1749 Expression CheckShiftArguments (EmitContext ec)
1753 e = ForceConversion (ec, right, TypeManager.int32_type);
1755 Error_OperatorCannotBeApplied ();
1760 if (((e = Convert.ImplicitConversion (ec, left, TypeManager.int32_type, loc)) != null) ||
1761 ((e = Convert.ImplicitConversion (ec, left, TypeManager.uint32_type, loc)) != null) ||
1762 ((e = Convert.ImplicitConversion (ec, left, TypeManager.int64_type, loc)) != null) ||
1763 ((e = Convert.ImplicitConversion (ec, left, TypeManager.uint64_type, loc)) != null)){
1767 if (type == TypeManager.int32_type || type == TypeManager.uint32_type){
1768 right = new Binary (Binary.Operator.BitwiseAnd, right, new IntConstant (31, loc));
1769 right = right.DoResolve (ec);
1771 right = new Binary (Binary.Operator.BitwiseAnd, right, new IntConstant (63, loc));
1772 right = right.DoResolve (ec);
1777 Error_OperatorCannotBeApplied ();
1782 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1783 // i.e., not invoke op_Equality.
1785 static bool EqualsNullIsReferenceEquals (Type t)
1787 return t == TypeManager.object_type || t == TypeManager.string_type ||
1788 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1791 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1793 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1794 "Possible unintended reference comparison; to get a value comparison, " +
1795 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1798 Expression ResolveOperator (EmitContext ec)
1801 Type r = right.Type;
1803 if (oper == Operator.Equality || oper == Operator.Inequality){
1805 // Optimize out call to op_Equality in a few cases.
1807 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1808 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1810 Type = TypeManager.bool_type;
1816 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1817 Type = TypeManager.bool_type;
1824 // Do not perform operator overload resolution when both sides are
1827 Expression left_operators = null, right_operators = null;
1828 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))){
1830 // Step 1: Perform Operator Overload location
1832 string op = oper_names [(int) oper];
1834 MethodGroupExpr union;
1835 left_operators = MemberLookup (ec, l, op, MemberTypes.Method, AllBindingFlags, loc);
1837 right_operators = MemberLookup (
1838 ec, r, op, MemberTypes.Method, AllBindingFlags, loc);
1839 union = Invocation.MakeUnionSet (left_operators, right_operators, loc);
1841 union = (MethodGroupExpr) left_operators;
1843 if (union != null) {
1844 ArrayList args = new ArrayList (2);
1845 args.Add (new Argument (left, Argument.AType.Expression));
1846 args.Add (new Argument (right, Argument.AType.Expression));
1848 MethodBase method = Invocation.OverloadResolve (
1849 ec, union, args, true, Location.Null);
1851 if (method != null) {
1852 MethodInfo mi = (MethodInfo) method;
1854 return new BinaryMethod (mi.ReturnType, method, args);
1860 // Step 0: String concatenation (because overloading will get this wrong)
1862 if (oper == Operator.Addition){
1864 // If any of the arguments is a string, cast to string
1867 // Simple constant folding
1868 if (left is StringConstant && right is StringConstant)
1869 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1871 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1873 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1874 Error_OperatorCannotBeApplied ();
1878 // try to fold it in on the left
1879 if (left is StringConcat) {
1882 // We have to test here for not-null, since we can be doubly-resolved
1883 // take care of not appending twice
1886 type = TypeManager.string_type;
1887 ((StringConcat) left).Append (ec, right);
1888 return left.Resolve (ec);
1894 // Otherwise, start a new concat expression
1895 return new StringConcat (ec, loc, left, right).Resolve (ec);
1899 // Transform a + ( - b) into a - b
1901 if (right is Unary){
1902 Unary right_unary = (Unary) right;
1904 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1905 oper = Operator.Subtraction;
1906 right = right_unary.Expr;
1912 if (oper == Operator.Equality || oper == Operator.Inequality){
1913 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1914 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1915 Error_OperatorCannotBeApplied ();
1919 type = TypeManager.bool_type;
1923 if (l.IsPointer || r.IsPointer) {
1924 if (l.IsPointer && r.IsPointer) {
1925 type = TypeManager.bool_type;
1929 if (l.IsPointer && r == TypeManager.null_type) {
1930 right = new EmptyCast (NullPointer.Null, l);
1931 type = TypeManager.bool_type;
1935 if (r.IsPointer && l == TypeManager.null_type) {
1936 left = new EmptyCast (NullPointer.Null, r);
1937 type = TypeManager.bool_type;
1943 // operator != (object a, object b)
1944 // operator == (object a, object b)
1946 // For this to be used, both arguments have to be reference-types.
1947 // Read the rationale on the spec (14.9.6)
1949 if (!(l.IsValueType || r.IsValueType)){
1950 type = TypeManager.bool_type;
1956 // Also, a standard conversion must exist from either one
1958 bool left_to_right =
1959 Convert.ImplicitStandardConversionExists (ec, left, r);
1960 bool right_to_left = !left_to_right &&
1961 Convert.ImplicitStandardConversionExists (ec, right, l);
1963 if (!left_to_right && !right_to_left) {
1964 Error_OperatorCannotBeApplied ();
1968 if (left_to_right && left_operators != null &&
1969 RootContext.WarningLevel >= 2) {
1970 ArrayList args = new ArrayList (2);
1971 args.Add (new Argument (left, Argument.AType.Expression));
1972 args.Add (new Argument (left, Argument.AType.Expression));
1973 MethodBase method = Invocation.OverloadResolve (
1974 ec, (MethodGroupExpr) left_operators, args, true, Location.Null);
1976 Warning_UnintendedReferenceComparison (loc, "right", l);
1979 if (right_to_left && right_operators != null &&
1980 RootContext.WarningLevel >= 2) {
1981 ArrayList args = new ArrayList (2);
1982 args.Add (new Argument (right, Argument.AType.Expression));
1983 args.Add (new Argument (right, Argument.AType.Expression));
1984 MethodBase method = Invocation.OverloadResolve (
1985 ec, (MethodGroupExpr) right_operators, args, true, Location.Null);
1987 Warning_UnintendedReferenceComparison (loc, "left", r);
1991 // We are going to have to convert to an object to compare
1993 if (l != TypeManager.object_type)
1994 left = new EmptyCast (left, TypeManager.object_type);
1995 if (r != TypeManager.object_type)
1996 right = new EmptyCast (right, TypeManager.object_type);
1999 // FIXME: CSC here catches errors cs254 and cs252
2005 // One of them is a valuetype, but the other one is not.
2007 if (!l.IsValueType || !r.IsValueType) {
2008 Error_OperatorCannotBeApplied ();
2013 // Only perform numeric promotions on:
2014 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
2016 if (oper == Operator.Addition || oper == Operator.Subtraction) {
2017 if (l.IsSubclassOf (TypeManager.delegate_type)){
2018 if (((right.eclass == ExprClass.MethodGroup) ||
2019 (r == TypeManager.anonymous_method_type))){
2020 if ((RootContext.Version != LanguageVersion.ISO_1)){
2021 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
2029 if (r.IsSubclassOf (TypeManager.delegate_type)){
2031 ArrayList args = new ArrayList (2);
2033 args = new ArrayList (2);
2034 args.Add (new Argument (left, Argument.AType.Expression));
2035 args.Add (new Argument (right, Argument.AType.Expression));
2037 if (oper == Operator.Addition)
2038 method = TypeManager.delegate_combine_delegate_delegate;
2040 method = TypeManager.delegate_remove_delegate_delegate;
2043 Error_OperatorCannotBeApplied ();
2047 return new BinaryDelegate (l, method, args);
2052 // Pointer arithmetic:
2054 // T* operator + (T* x, int y);
2055 // T* operator + (T* x, uint y);
2056 // T* operator + (T* x, long y);
2057 // T* operator + (T* x, ulong y);
2059 // T* operator + (int y, T* x);
2060 // T* operator + (uint y, T *x);
2061 // T* operator + (long y, T *x);
2062 // T* operator + (ulong y, T *x);
2064 // T* operator - (T* x, int y);
2065 // T* operator - (T* x, uint y);
2066 // T* operator - (T* x, long y);
2067 // T* operator - (T* x, ulong y);
2069 // long operator - (T* x, T *y)
2072 if (r.IsPointer && oper == Operator.Subtraction){
2074 return new PointerArithmetic (
2075 false, left, right, TypeManager.int64_type,
2078 Expression t = Make32or64 (ec, right);
2080 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
2082 } else if (r.IsPointer && oper == Operator.Addition){
2083 Expression t = Make32or64 (ec, left);
2085 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
2090 // Enumeration operators
2092 bool lie = TypeManager.IsEnumType (l);
2093 bool rie = TypeManager.IsEnumType (r);
2097 // U operator - (E e, E f)
2099 if (oper == Operator.Subtraction){
2101 type = TypeManager.EnumToUnderlying (l);
2104 Error_OperatorCannotBeApplied ();
2110 // operator + (E e, U x)
2111 // operator - (E e, U x)
2113 if (oper == Operator.Addition || oper == Operator.Subtraction){
2114 Type enum_type = lie ? l : r;
2115 Type other_type = lie ? r : l;
2116 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2118 if (underlying_type != other_type){
2119 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2129 Error_OperatorCannotBeApplied ();
2138 temp = Convert.ImplicitConversion (ec, right, l, loc);
2142 Error_OperatorCannotBeApplied ();
2146 temp = Convert.ImplicitConversion (ec, left, r, loc);
2151 Error_OperatorCannotBeApplied ();
2156 if (oper == Operator.Equality || oper == Operator.Inequality ||
2157 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2158 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2159 if (left.Type != right.Type){
2160 Error_OperatorCannotBeApplied ();
2163 type = TypeManager.bool_type;
2167 if (oper == Operator.BitwiseAnd ||
2168 oper == Operator.BitwiseOr ||
2169 oper == Operator.ExclusiveOr){
2170 if (left.Type != right.Type){
2171 Error_OperatorCannotBeApplied ();
2177 Error_OperatorCannotBeApplied ();
2181 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2182 return CheckShiftArguments (ec);
2184 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2185 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2186 type = TypeManager.bool_type;
2191 Error_OperatorCannotBeApplied ();
2195 Expression e = new ConditionalLogicalOperator (
2196 oper == Operator.LogicalAnd, left, right, l, loc);
2197 return e.Resolve (ec);
2201 // operator & (bool x, bool y)
2202 // operator | (bool x, bool y)
2203 // operator ^ (bool x, bool y)
2205 if (l == TypeManager.bool_type && r == TypeManager.bool_type){
2206 if (oper == Operator.BitwiseAnd ||
2207 oper == Operator.BitwiseOr ||
2208 oper == Operator.ExclusiveOr){
2215 // Pointer comparison
2217 if (l.IsPointer && r.IsPointer){
2218 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2219 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2220 type = TypeManager.bool_type;
2226 // This will leave left or right set to null if there is an error
2228 bool check_user_conv = is_user_defined (l) && is_user_defined (r);
2229 DoNumericPromotions (ec, l, r, left, right, check_user_conv);
2230 if (left == null || right == null){
2231 Error_OperatorCannotBeApplied (loc, OperName (oper), l, r);
2236 // reload our cached types if required
2241 if (oper == Operator.BitwiseAnd ||
2242 oper == Operator.BitwiseOr ||
2243 oper == Operator.ExclusiveOr){
2245 if (((l == TypeManager.int32_type) ||
2246 (l == TypeManager.uint32_type) ||
2247 (l == TypeManager.short_type) ||
2248 (l == TypeManager.ushort_type) ||
2249 (l == TypeManager.int64_type) ||
2250 (l == TypeManager.uint64_type))){
2253 Error_OperatorCannotBeApplied ();
2257 Error_OperatorCannotBeApplied ();
2262 if (oper == Operator.Equality ||
2263 oper == Operator.Inequality ||
2264 oper == Operator.LessThanOrEqual ||
2265 oper == Operator.LessThan ||
2266 oper == Operator.GreaterThanOrEqual ||
2267 oper == Operator.GreaterThan){
2268 type = TypeManager.bool_type;
2274 Constant EnumLiftUp (EmitContext ec, Constant left, Constant right)
2277 case Operator.BitwiseOr:
2278 case Operator.BitwiseAnd:
2279 case Operator.ExclusiveOr:
2280 case Operator.Equality:
2281 case Operator.Inequality:
2282 case Operator.LessThan:
2283 case Operator.LessThanOrEqual:
2284 case Operator.GreaterThan:
2285 case Operator.GreaterThanOrEqual:
2286 if (left is EnumConstant)
2289 if (left.IsZeroInteger)
2290 return new EnumConstant (left, right.Type);
2294 case Operator.Addition:
2295 case Operator.Subtraction:
2298 case Operator.Multiply:
2299 case Operator.Division:
2300 case Operator.Modulus:
2301 case Operator.LeftShift:
2302 case Operator.RightShift:
2303 if (right is EnumConstant || left is EnumConstant)
2307 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2311 public override Expression DoResolve (EmitContext ec)
2313 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2314 left = ((ParenthesizedExpression) left).Expr;
2315 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2319 if (left.eclass == ExprClass.Type) {
2320 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2324 left = left.Resolve (ec);
2329 Constant lc = left as Constant;
2330 if (lc != null && lc.Type == TypeManager.bool_type &&
2331 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2332 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2334 // TODO: make a sense to resolve unreachable expression as we do for statement
2335 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2339 right = right.Resolve (ec);
2343 eclass = ExprClass.Value;
2344 Constant rc = right as Constant;
2346 // The conversion rules are ignored in enum context but why
2347 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2348 left = lc = EnumLiftUp (ec, lc, rc);
2352 right = rc = EnumLiftUp (ec, rc, lc);
2357 if (oper == Operator.BitwiseAnd) {
2358 if (rc != null && rc.IsZeroInteger) {
2359 return lc is EnumConstant ?
2360 new EnumConstant (rc, lc.Type):
2364 if (lc != null && lc.IsZeroInteger) {
2365 return rc is EnumConstant ?
2366 new EnumConstant (lc, rc.Type):
2370 else if (oper == Operator.BitwiseOr) {
2371 if (lc is EnumConstant &&
2372 rc != null && rc.IsZeroInteger)
2374 if (rc is EnumConstant &&
2375 lc != null && lc.IsZeroInteger)
2377 } else if (oper == Operator.LogicalAnd) {
2378 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2380 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2384 if (rc != null && lc != null){
2385 int prev_e = Report.Errors;
2386 Expression e = ConstantFold.BinaryFold (
2387 ec, oper, lc, rc, loc);
2388 if (e != null || Report.Errors != prev_e)
2392 // Comparison warnings
2393 if (oper == Operator.Equality || oper == Operator.Inequality ||
2394 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2395 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2396 if (left.Equals (right)) {
2397 Report.Warning (1718, 3, loc, "Comparison made to same variable; did you mean to compare something else?");
2399 CheckUselessComparison (lc, right.Type);
2400 CheckUselessComparison (rc, left.Type);
2403 return ResolveOperator (ec);
2406 private void CheckUselessComparison (Constant c, Type type)
2408 if (c == null || !IsTypeIntegral (type)
2409 || c is StringConstant
2410 || c is BoolConstant
2411 || c is CharConstant
2412 || c is FloatConstant
2413 || c is DoubleConstant
2414 || c is DecimalConstant
2420 if (c is ULongConstant) {
2421 ulong uvalue = ((ULongConstant) c).Value;
2422 if (uvalue > long.MaxValue) {
2423 if (type == TypeManager.byte_type ||
2424 type == TypeManager.sbyte_type ||
2425 type == TypeManager.short_type ||
2426 type == TypeManager.ushort_type ||
2427 type == TypeManager.int32_type ||
2428 type == TypeManager.uint32_type ||
2429 type == TypeManager.int64_type)
2430 WarnUselessComparison (type);
2433 value = (long) uvalue;
2435 else if (c is ByteConstant)
2436 value = ((ByteConstant) c).Value;
2437 else if (c is SByteConstant)
2438 value = ((SByteConstant) c).Value;
2439 else if (c is ShortConstant)
2440 value = ((ShortConstant) c).Value;
2441 else if (c is UShortConstant)
2442 value = ((UShortConstant) c).Value;
2443 else if (c is IntConstant)
2444 value = ((IntConstant) c).Value;
2445 else if (c is UIntConstant)
2446 value = ((UIntConstant) c).Value;
2447 else if (c is LongConstant)
2448 value = ((LongConstant) c).Value;
2451 if (IsValueOutOfRange (value, type))
2452 WarnUselessComparison (type);
2457 private bool IsValueOutOfRange (long value, Type type)
2459 if (IsTypeUnsigned (type) && value < 0)
2461 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2462 type == TypeManager.byte_type && value >= 0x100 ||
2463 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2464 type == TypeManager.ushort_type && value >= 0x10000 ||
2465 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2466 type == TypeManager.uint32_type && value >= 0x100000000;
2469 private static bool IsTypeIntegral (Type type)
2471 return type == TypeManager.uint64_type ||
2472 type == TypeManager.int64_type ||
2473 type == TypeManager.uint32_type ||
2474 type == TypeManager.int32_type ||
2475 type == TypeManager.ushort_type ||
2476 type == TypeManager.short_type ||
2477 type == TypeManager.sbyte_type ||
2478 type == TypeManager.byte_type;
2481 private static bool IsTypeUnsigned (Type type)
2483 return type == TypeManager.uint64_type ||
2484 type == TypeManager.uint32_type ||
2485 type == TypeManager.ushort_type ||
2486 type == TypeManager.byte_type;
2489 private void WarnUselessComparison (Type type)
2491 Report.Warning (652, 2, loc, "Comparison to integral constant is useless; the constant is outside the range of type `{0}'",
2492 TypeManager.CSharpName (type));
2496 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2497 /// context of a conditional bool expression. This function will return
2498 /// false if it is was possible to use EmitBranchable, or true if it was.
2500 /// The expression's code is generated, and we will generate a branch to `target'
2501 /// if the resulting expression value is equal to isTrue
2503 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2505 ILGenerator ig = ec.ig;
2508 // This is more complicated than it looks, but its just to avoid
2509 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2510 // but on top of that we want for == and != to use a special path
2511 // if we are comparing against null
2513 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2514 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2517 // put the constant on the rhs, for simplicity
2519 if (left is Constant) {
2520 Expression swap = right;
2525 if (((Constant) right).IsZeroInteger) {
2528 ig.Emit (OpCodes.Brtrue, target);
2530 ig.Emit (OpCodes.Brfalse, target);
2533 } else if (right is BoolConstant) {
2535 if (my_on_true != ((BoolConstant) right).Value)
2536 ig.Emit (OpCodes.Brtrue, target);
2538 ig.Emit (OpCodes.Brfalse, target);
2543 } else if (oper == Operator.LogicalAnd) {
2546 Label tests_end = ig.DefineLabel ();
2548 left.EmitBranchable (ec, tests_end, false);
2549 right.EmitBranchable (ec, target, true);
2550 ig.MarkLabel (tests_end);
2552 left.EmitBranchable (ec, target, false);
2553 right.EmitBranchable (ec, target, false);
2558 } else if (oper == Operator.LogicalOr){
2560 left.EmitBranchable (ec, target, true);
2561 right.EmitBranchable (ec, target, true);
2564 Label tests_end = ig.DefineLabel ();
2565 left.EmitBranchable (ec, tests_end, true);
2566 right.EmitBranchable (ec, target, false);
2567 ig.MarkLabel (tests_end);
2572 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2573 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2574 oper == Operator.Equality || oper == Operator.Inequality)) {
2575 base.EmitBranchable (ec, target, onTrue);
2583 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2586 case Operator.Equality:
2588 ig.Emit (OpCodes.Beq, target);
2590 ig.Emit (OpCodes.Bne_Un, target);
2593 case Operator.Inequality:
2595 ig.Emit (OpCodes.Bne_Un, target);
2597 ig.Emit (OpCodes.Beq, target);
2600 case Operator.LessThan:
2603 ig.Emit (OpCodes.Blt_Un, target);
2605 ig.Emit (OpCodes.Blt, target);
2608 ig.Emit (OpCodes.Bge_Un, target);
2610 ig.Emit (OpCodes.Bge, target);
2613 case Operator.GreaterThan:
2616 ig.Emit (OpCodes.Bgt_Un, target);
2618 ig.Emit (OpCodes.Bgt, target);
2621 ig.Emit (OpCodes.Ble_Un, target);
2623 ig.Emit (OpCodes.Ble, target);
2626 case Operator.LessThanOrEqual:
2629 ig.Emit (OpCodes.Ble_Un, target);
2631 ig.Emit (OpCodes.Ble, target);
2634 ig.Emit (OpCodes.Bgt_Un, target);
2636 ig.Emit (OpCodes.Bgt, target);
2640 case Operator.GreaterThanOrEqual:
2643 ig.Emit (OpCodes.Bge_Un, target);
2645 ig.Emit (OpCodes.Bge, target);
2648 ig.Emit (OpCodes.Blt_Un, target);
2650 ig.Emit (OpCodes.Blt, target);
2653 Console.WriteLine (oper);
2654 throw new Exception ("what is THAT");
2658 public override void Emit (EmitContext ec)
2660 ILGenerator ig = ec.ig;
2665 // Handle short-circuit operators differently
2668 if (oper == Operator.LogicalAnd) {
2669 Label load_zero = ig.DefineLabel ();
2670 Label end = ig.DefineLabel ();
2672 left.EmitBranchable (ec, load_zero, false);
2674 ig.Emit (OpCodes.Br, end);
2676 ig.MarkLabel (load_zero);
2677 ig.Emit (OpCodes.Ldc_I4_0);
2680 } else if (oper == Operator.LogicalOr) {
2681 Label load_one = ig.DefineLabel ();
2682 Label end = ig.DefineLabel ();
2684 left.EmitBranchable (ec, load_one, true);
2686 ig.Emit (OpCodes.Br, end);
2688 ig.MarkLabel (load_one);
2689 ig.Emit (OpCodes.Ldc_I4_1);
2697 bool isUnsigned = is_unsigned (left.Type);
2700 case Operator.Multiply:
2702 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2703 opcode = OpCodes.Mul_Ovf;
2704 else if (isUnsigned)
2705 opcode = OpCodes.Mul_Ovf_Un;
2707 opcode = OpCodes.Mul;
2709 opcode = OpCodes.Mul;
2713 case Operator.Division:
2715 opcode = OpCodes.Div_Un;
2717 opcode = OpCodes.Div;
2720 case Operator.Modulus:
2722 opcode = OpCodes.Rem_Un;
2724 opcode = OpCodes.Rem;
2727 case Operator.Addition:
2729 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2730 opcode = OpCodes.Add_Ovf;
2731 else if (isUnsigned)
2732 opcode = OpCodes.Add_Ovf_Un;
2734 opcode = OpCodes.Add;
2736 opcode = OpCodes.Add;
2739 case Operator.Subtraction:
2741 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2742 opcode = OpCodes.Sub_Ovf;
2743 else if (isUnsigned)
2744 opcode = OpCodes.Sub_Ovf_Un;
2746 opcode = OpCodes.Sub;
2748 opcode = OpCodes.Sub;
2751 case Operator.RightShift:
2753 opcode = OpCodes.Shr_Un;
2755 opcode = OpCodes.Shr;
2758 case Operator.LeftShift:
2759 opcode = OpCodes.Shl;
2762 case Operator.Equality:
2763 opcode = OpCodes.Ceq;
2766 case Operator.Inequality:
2767 ig.Emit (OpCodes.Ceq);
2768 ig.Emit (OpCodes.Ldc_I4_0);
2770 opcode = OpCodes.Ceq;
2773 case Operator.LessThan:
2775 opcode = OpCodes.Clt_Un;
2777 opcode = OpCodes.Clt;
2780 case Operator.GreaterThan:
2782 opcode = OpCodes.Cgt_Un;
2784 opcode = OpCodes.Cgt;
2787 case Operator.LessThanOrEqual:
2788 Type lt = left.Type;
2790 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2791 ig.Emit (OpCodes.Cgt_Un);
2793 ig.Emit (OpCodes.Cgt);
2794 ig.Emit (OpCodes.Ldc_I4_0);
2796 opcode = OpCodes.Ceq;
2799 case Operator.GreaterThanOrEqual:
2800 Type le = left.Type;
2802 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2803 ig.Emit (OpCodes.Clt_Un);
2805 ig.Emit (OpCodes.Clt);
2807 ig.Emit (OpCodes.Ldc_I4_0);
2809 opcode = OpCodes.Ceq;
2812 case Operator.BitwiseOr:
2813 opcode = OpCodes.Or;
2816 case Operator.BitwiseAnd:
2817 opcode = OpCodes.And;
2820 case Operator.ExclusiveOr:
2821 opcode = OpCodes.Xor;
2825 throw new Exception ("This should not happen: Operator = "
2826 + oper.ToString ());
2834 // Object created by Binary when the binary operator uses an method instead of being
2835 // a binary operation that maps to a CIL binary operation.
2837 public class BinaryMethod : Expression {
2838 public MethodBase method;
2839 public ArrayList Arguments;
2841 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2846 eclass = ExprClass.Value;
2849 public override Expression DoResolve (EmitContext ec)
2854 public override void Emit (EmitContext ec)
2856 ILGenerator ig = ec.ig;
2858 if (Arguments != null)
2859 Invocation.EmitArguments (ec, method, Arguments, false, null);
2861 if (method is MethodInfo)
2862 ig.Emit (OpCodes.Call, (MethodInfo) method);
2864 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2869 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2870 // b, c, d... may be strings or objects.
2872 public class StringConcat : Expression {
2874 bool invalid = false;
2875 bool emit_conv_done = false;
2877 // Are we also concating objects?
2879 bool is_strings_only = true;
2881 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2884 type = TypeManager.string_type;
2885 eclass = ExprClass.Value;
2887 operands = new ArrayList (2);
2892 public override Expression DoResolve (EmitContext ec)
2900 public void Append (EmitContext ec, Expression operand)
2905 if (operand is StringConstant && operands.Count != 0) {
2906 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2907 if (last_operand != null) {
2908 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2914 // Conversion to object
2916 if (operand.Type != TypeManager.string_type) {
2917 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2920 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2926 operands.Add (operand);
2929 public override void Emit (EmitContext ec)
2931 MethodInfo concat_method = null;
2934 // Do conversion to arguments; check for strings only
2937 // This can get called multiple times, so we have to deal with that.
2938 if (!emit_conv_done) {
2939 emit_conv_done = true;
2940 for (int i = 0; i < operands.Count; i ++) {
2941 Expression e = (Expression) operands [i];
2942 is_strings_only &= e.Type == TypeManager.string_type;
2945 for (int i = 0; i < operands.Count; i ++) {
2946 Expression e = (Expression) operands [i];
2948 if (! is_strings_only && e.Type == TypeManager.string_type) {
2949 // need to make sure this is an object, because the EmitParams
2950 // method might look at the type of this expression, see it is a
2951 // string and emit a string [] when we want an object [];
2953 e = new EmptyCast (e, TypeManager.object_type);
2955 operands [i] = new Argument (e, Argument.AType.Expression);
2960 // Find the right method
2962 switch (operands.Count) {
2965 // This should not be possible, because simple constant folding
2966 // is taken care of in the Binary code.
2968 throw new Exception ("how did you get here?");
2971 concat_method = is_strings_only ?
2972 TypeManager.string_concat_string_string :
2973 TypeManager.string_concat_object_object ;
2976 concat_method = is_strings_only ?
2977 TypeManager.string_concat_string_string_string :
2978 TypeManager.string_concat_object_object_object ;
2982 // There is not a 4 param overlaod for object (the one that there is
2983 // is actually a varargs methods, and is only in corlib because it was
2984 // introduced there before.).
2986 if (!is_strings_only)
2989 concat_method = TypeManager.string_concat_string_string_string_string;
2992 concat_method = is_strings_only ?
2993 TypeManager.string_concat_string_dot_dot_dot :
2994 TypeManager.string_concat_object_dot_dot_dot ;
2998 Invocation.EmitArguments (ec, concat_method, operands, false, null);
2999 ec.ig.Emit (OpCodes.Call, concat_method);
3004 // Object created with +/= on delegates
3006 public class BinaryDelegate : Expression {
3010 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
3015 eclass = ExprClass.Value;
3018 public override Expression DoResolve (EmitContext ec)
3023 public override void Emit (EmitContext ec)
3025 ILGenerator ig = ec.ig;
3027 Invocation.EmitArguments (ec, method, args, false, null);
3029 ig.Emit (OpCodes.Call, (MethodInfo) method);
3030 ig.Emit (OpCodes.Castclass, type);
3033 public Expression Right {
3035 Argument arg = (Argument) args [1];
3040 public bool IsAddition {
3042 return method == TypeManager.delegate_combine_delegate_delegate;
3048 // User-defined conditional logical operator
3049 public class ConditionalLogicalOperator : Expression {
3050 Expression left, right;
3053 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
3056 eclass = ExprClass.Value;
3060 this.is_and = is_and;
3063 protected void Error19 ()
3065 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3068 protected void Error218 ()
3070 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3071 "declarations of operator true and operator false");
3074 Expression op_true, op_false, op;
3075 LocalTemporary left_temp;
3077 public override Expression DoResolve (EmitContext ec)
3080 Expression operator_group;
3082 operator_group = MethodLookup (ec, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
3083 if (operator_group == null) {
3088 left_temp = new LocalTemporary (ec, type);
3090 ArrayList arguments = new ArrayList ();
3091 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3092 arguments.Add (new Argument (right, Argument.AType.Expression));
3093 method = Invocation.OverloadResolve (
3094 ec, (MethodGroupExpr) operator_group, arguments, false, loc)
3096 if (method == null) {
3101 if (method.ReturnType != type) {
3102 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3103 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3107 op = new StaticCallExpr (method, arguments, loc);
3109 op_true = GetOperatorTrue (ec, left_temp, loc);
3110 op_false = GetOperatorFalse (ec, left_temp, loc);
3111 if ((op_true == null) || (op_false == null)) {
3119 public override void Emit (EmitContext ec)
3121 ILGenerator ig = ec.ig;
3122 Label false_target = ig.DefineLabel ();
3123 Label end_target = ig.DefineLabel ();
3126 left_temp.Store (ec);
3128 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3129 left_temp.Emit (ec);
3130 ig.Emit (OpCodes.Br, end_target);
3131 ig.MarkLabel (false_target);
3133 ig.MarkLabel (end_target);
3137 public class PointerArithmetic : Expression {
3138 Expression left, right;
3142 // We assume that `l' is always a pointer
3144 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3150 is_add = is_addition;
3153 public override Expression DoResolve (EmitContext ec)
3155 eclass = ExprClass.Variable;
3157 if (left.Type == TypeManager.void_ptr_type) {
3158 Error (242, "The operation in question is undefined on void pointers");
3165 public override void Emit (EmitContext ec)
3167 Type op_type = left.Type;
3168 ILGenerator ig = ec.ig;
3170 // It must be either array or fixed buffer
3171 Type element = TypeManager.HasElementType (op_type) ?
3172 element = TypeManager.GetElementType (op_type) :
3173 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3175 int size = GetTypeSize (element);
3176 Type rtype = right.Type;
3178 if (rtype.IsPointer){
3180 // handle (pointer - pointer)
3184 ig.Emit (OpCodes.Sub);
3188 ig.Emit (OpCodes.Sizeof, element);
3190 IntLiteral.EmitInt (ig, size);
3191 ig.Emit (OpCodes.Div);
3193 ig.Emit (OpCodes.Conv_I8);
3196 // handle + and - on (pointer op int)
3199 ig.Emit (OpCodes.Conv_I);
3201 Constant right_const = right as Constant;
3202 if (right_const != null && size != 0) {
3203 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3211 ig.Emit (OpCodes.Sizeof, element);
3213 IntLiteral.EmitInt (ig, size);
3214 if (rtype == TypeManager.int64_type)
3215 ig.Emit (OpCodes.Conv_I8);
3216 else if (rtype == TypeManager.uint64_type)
3217 ig.Emit (OpCodes.Conv_U8);
3218 ig.Emit (OpCodes.Mul);
3222 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3223 ig.Emit (OpCodes.Conv_I);
3226 ig.Emit (OpCodes.Add);
3228 ig.Emit (OpCodes.Sub);
3234 /// Implements the ternary conditional operator (?:)
3236 public class Conditional : Expression {
3237 Expression expr, trueExpr, falseExpr;
3239 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3242 this.trueExpr = trueExpr;
3243 this.falseExpr = falseExpr;
3244 this.loc = expr.Location;
3247 public Expression Expr {
3253 public Expression TrueExpr {
3259 public Expression FalseExpr {
3265 public override Expression DoResolve (EmitContext ec)
3267 expr = expr.Resolve (ec);
3272 if (expr.Type != TypeManager.bool_type){
3273 expr = Expression.ResolveBoolean (
3280 Assign ass = expr as Assign;
3281 if (ass != null && ass.Source is Constant) {
3282 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3285 trueExpr = trueExpr.Resolve (ec);
3286 falseExpr = falseExpr.Resolve (ec);
3288 if (trueExpr == null || falseExpr == null)
3291 eclass = ExprClass.Value;
3292 if (trueExpr.Type == falseExpr.Type)
3293 type = trueExpr.Type;
3296 Type true_type = trueExpr.Type;
3297 Type false_type = falseExpr.Type;
3300 // First, if an implicit conversion exists from trueExpr
3301 // to falseExpr, then the result type is of type falseExpr.Type
3303 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3306 // Check if both can convert implicitl to each other's type
3308 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3310 "Can not compute type of conditional expression " +
3311 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3312 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3313 "' convert implicitly to each other");
3318 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3322 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3323 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3328 // Dead code optimalization
3329 if (expr is BoolConstant){
3330 BoolConstant bc = (BoolConstant) expr;
3332 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3333 return bc.Value ? trueExpr : falseExpr;
3339 public override void Emit (EmitContext ec)
3341 ILGenerator ig = ec.ig;
3342 Label false_target = ig.DefineLabel ();
3343 Label end_target = ig.DefineLabel ();
3345 expr.EmitBranchable (ec, false_target, false);
3347 ig.Emit (OpCodes.Br, end_target);
3348 ig.MarkLabel (false_target);
3349 falseExpr.Emit (ec);
3350 ig.MarkLabel (end_target);
3358 public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3359 public readonly string Name;
3360 public readonly Block Block;
3361 public LocalInfo local_info;
3364 LocalTemporary temp;
3366 public LocalVariableReference (Block block, string name, Location l)
3371 eclass = ExprClass.Variable;
3375 // Setting `is_readonly' to false will allow you to create a writable
3376 // reference to a read-only variable. This is used by foreach and using.
3378 public LocalVariableReference (Block block, string name, Location l,
3379 LocalInfo local_info, bool is_readonly)
3380 : this (block, name, l)
3382 this.local_info = local_info;
3383 this.is_readonly = is_readonly;
3386 public VariableInfo VariableInfo {
3388 return local_info.VariableInfo;
3392 public bool IsReadOnly {
3398 public bool VerifyAssigned (EmitContext ec)
3400 VariableInfo variable_info = local_info.VariableInfo;
3401 return variable_info == null || variable_info.IsAssigned (ec, loc);
3404 protected Expression DoResolveBase (EmitContext ec, Expression lvalue_right_side)
3406 if (local_info == null) {
3407 local_info = Block.GetLocalInfo (Name);
3410 if (lvalue_right_side == EmptyExpression.Null)
3411 local_info.Used = true;
3413 is_readonly = local_info.ReadOnly;
3416 type = local_info.VariableType;
3418 VariableInfo variable_info = local_info.VariableInfo;
3419 if (lvalue_right_side != null){
3421 if (lvalue_right_side is LocalVariableReference || lvalue_right_side == EmptyExpression.Null)
3422 Report.Error (1657, loc, "Cannot pass `{0}' as a ref or out argument because it is a `{1}'",
3423 Name, local_info.GetReadOnlyContext ());
3425 Report.Error (1656, loc, "Cannot assign to `{0}' because it is a `{1}'",
3426 Name, local_info.GetReadOnlyContext ());
3430 if (variable_info != null)
3431 variable_info.SetAssigned (ec);
3434 Expression e = Block.GetConstantExpression (Name);
3436 local_info.Used = true;
3437 eclass = ExprClass.Value;
3438 return e.Resolve (ec);
3441 if (!VerifyAssigned (ec))
3444 if (lvalue_right_side == null)
3445 local_info.Used = true;
3447 if (ec.CurrentAnonymousMethod != null){
3449 // If we are referencing a variable from the external block
3450 // flag it for capturing
3452 if ((local_info.Block.Toplevel != ec.CurrentBlock.Toplevel) ||
3453 ec.CurrentAnonymousMethod.IsIterator)
3455 if (local_info.AddressTaken){
3456 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3459 ec.CaptureVariable (local_info);
3466 public override Expression DoResolve (EmitContext ec)
3468 return DoResolveBase (ec, null);
3471 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3473 return DoResolveBase (ec, right_side);
3476 public bool VerifyFixed ()
3478 // A local Variable is always fixed.
3482 public override int GetHashCode()
3484 return Name.GetHashCode ();
3487 public override bool Equals (object obj)
3489 LocalVariableReference lvr = obj as LocalVariableReference;
3493 return Name == lvr.Name && Block == lvr.Block;
3496 public override void Emit (EmitContext ec)
3498 ILGenerator ig = ec.ig;
3500 if (local_info.FieldBuilder == null){
3502 // A local variable on the local CLR stack
3504 ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
3507 // A local variable captured by anonymous methods.
3510 ec.EmitCapturedVariableInstance (local_info);
3512 ig.Emit (OpCodes.Ldfld, local_info.FieldBuilder);
3516 public void Emit (EmitContext ec, bool leave_copy)
3520 ec.ig.Emit (OpCodes.Dup);
3521 if (local_info.FieldBuilder != null){
3522 temp = new LocalTemporary (ec, Type);
3528 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3530 ILGenerator ig = ec.ig;
3531 prepared = prepare_for_load;
3533 if (local_info.FieldBuilder == null){
3535 // A local variable on the local CLR stack
3537 if (local_info.LocalBuilder == null)
3538 throw new Exception ("This should not happen: both Field and Local are null");
3542 ec.ig.Emit (OpCodes.Dup);
3543 ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
3546 // A local variable captured by anonymous methods or itereators.
3548 ec.EmitCapturedVariableInstance (local_info);
3550 if (prepare_for_load)
3551 ig.Emit (OpCodes.Dup);
3554 ig.Emit (OpCodes.Dup);
3555 temp = new LocalTemporary (ec, Type);
3558 ig.Emit (OpCodes.Stfld, local_info.FieldBuilder);
3564 public void AddressOf (EmitContext ec, AddressOp mode)
3566 ILGenerator ig = ec.ig;
3568 if (local_info.FieldBuilder == null){
3570 // A local variable on the local CLR stack
3572 ig.Emit (OpCodes.Ldloca, local_info.LocalBuilder);
3575 // A local variable captured by anonymous methods or iterators
3577 ec.EmitCapturedVariableInstance (local_info);
3578 ig.Emit (OpCodes.Ldflda, local_info.FieldBuilder);
3582 public override string ToString ()
3584 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3589 /// This represents a reference to a parameter in the intermediate
3592 public class ParameterReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3598 public bool is_ref, is_out, prepared;
3612 LocalTemporary temp;
3614 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3617 this.name = par.Name;
3621 eclass = ExprClass.Variable;
3624 public VariableInfo VariableInfo {
3628 public bool VerifyFixed ()
3630 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3631 return par.ModFlags == Parameter.Modifier.NONE;
3634 public bool IsAssigned (EmitContext ec, Location loc)
3636 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3639 Report.Error (269, loc,
3640 "Use of unassigned out parameter `{0}'", par.Name);
3644 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3646 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3649 Report.Error (170, loc,
3650 "Use of possibly unassigned field `" + field_name + "'");
3654 public void SetAssigned (EmitContext ec)
3656 if (is_out && ec.DoFlowAnalysis)
3657 ec.CurrentBranching.SetAssigned (vi);
3660 public void SetFieldAssigned (EmitContext ec, string field_name)
3662 if (is_out && ec.DoFlowAnalysis)
3663 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3666 protected void DoResolveBase (EmitContext ec)
3668 if (!par.Resolve (ec)) {
3672 type = par.ParameterType;
3673 Parameter.Modifier mod = par.ModFlags;
3674 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3675 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3676 eclass = ExprClass.Variable;
3679 vi = block.ParameterMap [idx];
3681 if (ec.CurrentAnonymousMethod != null){
3683 Report.Error (1628, Location, "Cannot use ref or out parameter `{0}' inside an anonymous method block",
3689 // If we are referencing the parameter from the external block
3690 // flag it for capturing
3692 //Console.WriteLine ("Is parameter `{0}' local? {1}", name, block.IsLocalParameter (name));
3693 if (!block.Toplevel.IsLocalParameter (name)){
3694 ec.CaptureParameter (name, type, idx);
3699 public override int GetHashCode()
3701 return name.GetHashCode ();
3704 public override bool Equals (object obj)
3706 ParameterReference pr = obj as ParameterReference;
3710 return name == pr.name && block == pr.block;
3714 // Notice that for ref/out parameters, the type exposed is not the
3715 // same type exposed externally.
3718 // externally we expose "int&"
3719 // here we expose "int".
3721 // We record this in "is_ref". This means that the type system can treat
3722 // the type as it is expected, but when we generate the code, we generate
3723 // the alternate kind of code.
3725 public override Expression DoResolve (EmitContext ec)
3729 if (is_out && ec.DoFlowAnalysis && (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3735 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3744 static public void EmitLdArg (ILGenerator ig, int x)
3748 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3749 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3750 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3751 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3752 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3755 ig.Emit (OpCodes.Ldarg, x);
3759 // This method is used by parameters that are references, that are
3760 // being passed as references: we only want to pass the pointer (that
3761 // is already stored in the parameter, not the address of the pointer,
3762 // and not the value of the variable).
3764 public void EmitLoad (EmitContext ec)
3766 ILGenerator ig = ec.ig;
3769 if (!ec.MethodIsStatic)
3772 EmitLdArg (ig, arg_idx);
3775 // FIXME: Review for anonymous methods
3779 public override void Emit (EmitContext ec)
3784 public void Emit (EmitContext ec, bool leave_copy)
3786 ILGenerator ig = ec.ig;
3789 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3791 throw new InternalErrorException ();
3793 ec.EmitParameter (name);
3797 if (!ec.MethodIsStatic)
3800 EmitLdArg (ig, arg_idx);
3804 ec.ig.Emit (OpCodes.Dup);
3807 // If we are a reference, we loaded on the stack a pointer
3808 // Now lets load the real value
3810 LoadFromPtr (ig, type);
3814 ec.ig.Emit (OpCodes.Dup);
3817 temp = new LocalTemporary (ec, type);
3823 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3825 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3826 ec.EmitAssignParameter (name, source, leave_copy, prepare_for_load);
3830 ILGenerator ig = ec.ig;
3833 prepared = prepare_for_load;
3835 if (!ec.MethodIsStatic)
3838 if (is_ref && !prepared)
3839 EmitLdArg (ig, arg_idx);
3844 ec.ig.Emit (OpCodes.Dup);
3848 temp = new LocalTemporary (ec, type);
3852 StoreFromPtr (ig, type);
3858 ig.Emit (OpCodes.Starg_S, (byte) arg_idx);
3860 ig.Emit (OpCodes.Starg, arg_idx);
3864 public void AddressOf (EmitContext ec, AddressOp mode)
3866 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3867 ec.EmitAddressOfParameter (name);
3873 if (!ec.MethodIsStatic)
3878 ec.ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx);
3880 ec.ig.Emit (OpCodes.Ldarg, arg_idx);
3883 ec.ig.Emit (OpCodes.Ldarga_S, (byte) arg_idx);
3885 ec.ig.Emit (OpCodes.Ldarga, arg_idx);
3889 public override string ToString ()
3891 return "ParameterReference[" + name + "]";
3896 /// Used for arguments to New(), Invocation()
3898 public class Argument {
3899 public enum AType : byte {
3906 public readonly AType ArgType;
3907 public Expression Expr;
3909 public Argument (Expression expr, AType type)
3912 this.ArgType = type;
3915 public Argument (Expression expr)
3918 this.ArgType = AType.Expression;
3923 if (ArgType == AType.Ref || ArgType == AType.Out)
3924 return TypeManager.GetReferenceType (Expr.Type);
3930 public Parameter.Modifier Modifier
3935 return Parameter.Modifier.OUT;
3938 return Parameter.Modifier.REF;
3941 return Parameter.Modifier.NONE;
3946 public static string FullDesc (Argument a)
3948 if (a.ArgType == AType.ArgList)
3951 return (a.ArgType == AType.Ref ? "ref " :
3952 (a.ArgType == AType.Out ? "out " : "")) +
3953 TypeManager.CSharpName (a.Expr.Type);
3956 public bool ResolveMethodGroup (EmitContext ec, Location loc)
3958 // FIXME: csc doesn't report any error if you try to use `ref' or
3959 // `out' in a delegate creation expression.
3960 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3967 void Error_LValueRequired (Location loc)
3969 Report.Error (1510, loc, "A ref or out argument must be an assignable variable");
3972 public bool Resolve (EmitContext ec, Location loc)
3974 bool old_do_flow_analysis = ec.DoFlowAnalysis;
3975 ec.DoFlowAnalysis = true;
3977 if (ArgType == AType.Ref) {
3978 ec.InRefOutArgumentResolving = true;
3979 Expr = Expr.Resolve (ec);
3980 ec.InRefOutArgumentResolving = false;
3982 ec.DoFlowAnalysis = old_do_flow_analysis;
3986 Expr = Expr.DoResolveLValue (ec, Expr);
3988 Error_LValueRequired (loc);
3989 } else if (ArgType == AType.Out) {
3990 ec.InRefOutArgumentResolving = true;
3991 Expr = Expr.DoResolveLValue (ec, EmptyExpression.Null);
3992 ec.InRefOutArgumentResolving = false;
3995 Error_LValueRequired (loc);
3998 Expr = Expr.Resolve (ec);
4000 ec.DoFlowAnalysis = old_do_flow_analysis;
4005 if (ArgType == AType.Expression)
4009 // Catch errors where fields of a MarshalByRefObject are passed as ref or out
4010 // This is only allowed for `this'
4012 FieldExpr fe = Expr as FieldExpr;
4013 if (fe != null && !fe.IsStatic){
4014 Expression instance = fe.InstanceExpression;
4016 if (instance.GetType () != typeof (This)){
4017 if (fe.InstanceExpression.Type.IsSubclassOf (TypeManager.mbr_type)){
4018 Report.SymbolRelatedToPreviousError (fe.InstanceExpression.Type);
4019 Report.Warning (197, 1, loc,
4020 "Passing `{0}' as ref or out or taking its address may cause a runtime exception because it is a field of a marshal-by-reference class",
4021 fe.GetSignatureForError ());
4028 if (Expr.eclass != ExprClass.Variable){
4030 // We just probe to match the CSC output
4032 if (Expr.eclass == ExprClass.PropertyAccess ||
4033 Expr.eclass == ExprClass.IndexerAccess){
4034 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
4035 Expr.GetSignatureForError ());
4037 Error_LValueRequired (loc);
4045 public void Emit (EmitContext ec)
4048 // Ref and Out parameters need to have their addresses taken.
4050 // ParameterReferences might already be references, so we want
4051 // to pass just the value
4053 if (ArgType == AType.Ref || ArgType == AType.Out){
4054 AddressOp mode = AddressOp.Store;
4056 if (ArgType == AType.Ref)
4057 mode |= AddressOp.Load;
4059 if (Expr is ParameterReference){
4060 ParameterReference pr = (ParameterReference) Expr;
4066 pr.AddressOf (ec, mode);
4069 if (Expr is IMemoryLocation)
4070 ((IMemoryLocation) Expr).AddressOf (ec, mode);
4072 Error_LValueRequired (Expr.Location);
4082 /// Invocation of methods or delegates.
4084 public class Invocation : ExpressionStatement {
4085 public readonly ArrayList Arguments;
4088 MethodBase method = null;
4091 // arguments is an ArrayList, but we do not want to typecast,
4092 // as it might be null.
4094 // FIXME: only allow expr to be a method invocation or a
4095 // delegate invocation (7.5.5)
4097 public Invocation (Expression expr, ArrayList arguments)
4100 Arguments = arguments;
4101 loc = expr.Location;
4104 public Expression Expr {
4111 /// Determines "better conversion" as specified in 14.4.2.3
4113 /// Returns : p if a->p is better,
4114 /// q if a->q is better,
4115 /// null if neither is better
4117 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q, Location loc)
4119 Type argument_type = a.Type;
4120 Expression argument_expr = a.Expr;
4122 if (argument_type == null)
4123 throw new Exception ("Expression of type " + a.Expr +
4124 " does not resolve its type");
4126 if (p == null || q == null)
4127 throw new InternalErrorException ("BetterConversion Got a null conversion");
4132 if (argument_expr is NullLiteral) {
4134 // If the argument is null and one of the types to compare is 'object' and
4135 // the other is a reference type, we prefer the other.
4137 // This follows from the usual rules:
4138 // * There is an implicit conversion from 'null' to type 'object'
4139 // * There is an implicit conversion from 'null' to any reference type
4140 // * There is an implicit conversion from any reference type to type 'object'
4141 // * There is no implicit conversion from type 'object' to other reference types
4142 // => Conversion of 'null' to a reference type is better than conversion to 'object'
4144 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
4145 // null type. I think it used to be 'object' and thus needed a special
4146 // case to avoid the immediately following two checks.
4148 if (!p.IsValueType && q == TypeManager.object_type)
4150 if (!q.IsValueType && p == TypeManager.object_type)
4154 if (argument_type == p)
4157 if (argument_type == q)
4160 Expression p_tmp = new EmptyExpression (p);
4161 Expression q_tmp = new EmptyExpression (q);
4163 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
4164 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
4166 if (p_to_q && !q_to_p)
4169 if (q_to_p && !p_to_q)
4172 if (p == TypeManager.sbyte_type)
4173 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
4174 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4176 if (q == TypeManager.sbyte_type)
4177 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
4178 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4181 if (p == TypeManager.short_type)
4182 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
4183 q == TypeManager.uint64_type)
4185 if (q == TypeManager.short_type)
4186 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
4187 p == TypeManager.uint64_type)
4190 if (p == TypeManager.int32_type)
4191 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4193 if (q == TypeManager.int32_type)
4194 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4197 if (p == TypeManager.int64_type)
4198 if (q == TypeManager.uint64_type)
4200 if (q == TypeManager.int64_type)
4201 if (p == TypeManager.uint64_type)
4208 /// Determines "Better function" between candidate
4209 /// and the current best match
4212 /// Returns an integer indicating :
4213 /// false if candidate ain't better
4214 /// true if candidate is better than the current best match
4216 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4217 MethodBase candidate, bool candidate_params,
4218 MethodBase best, bool best_params, Location loc)
4220 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4221 ParameterData best_pd = TypeManager.GetParameterData (best);
4223 bool better_at_least_one = false;
4225 for (int j = 0; j < argument_count; ++j) {
4226 Argument a = (Argument) args [j];
4228 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
4229 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
4231 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4232 if (candidate_params)
4233 ct = TypeManager.GetElementType (ct);
4235 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4237 bt = TypeManager.GetElementType (bt);
4243 Type better = BetterConversion (ec, a, ct, bt, loc);
4245 // for each argument, the conversion to 'ct' should be no worse than
4246 // the conversion to 'bt'.
4250 // for at least one argument, the conversion to 'ct' should be better than
4251 // the conversion to 'bt'.
4253 better_at_least_one = true;
4256 if (better_at_least_one)
4260 // This handles the case
4262 // Add (float f1, float f2, float f3);
4263 // Add (params decimal [] foo);
4265 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4266 // first candidate would've chosen as better.
4272 // This handles the following cases:
4274 // Trim () is better than Trim (params char[] chars)
4275 // Concat (string s1, string s2, string s3) is better than
4276 // Concat (string s1, params string [] srest)
4278 return !candidate_params && best_params;
4281 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4283 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4286 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4287 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4289 if (cand_pd.Count != base_pd.Count)
4292 for (int j = 0; j < cand_pd.Count; ++j) {
4293 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4294 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4295 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4296 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4298 if (cm != bm || ct != bt)
4305 public static string FullMethodDesc (MethodBase mb)
4311 if (mb is MethodInfo) {
4312 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4316 sb = new StringBuilder ();
4318 sb.Append (TypeManager.CSharpSignature (mb));
4319 return sb.ToString ();
4322 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4324 MemberInfo [] miset;
4325 MethodGroupExpr union;
4330 return (MethodGroupExpr) mg2;
4333 return (MethodGroupExpr) mg1;
4336 MethodGroupExpr left_set = null, right_set = null;
4337 int length1 = 0, length2 = 0;
4339 left_set = (MethodGroupExpr) mg1;
4340 length1 = left_set.Methods.Length;
4342 right_set = (MethodGroupExpr) mg2;
4343 length2 = right_set.Methods.Length;
4345 ArrayList common = new ArrayList ();
4347 foreach (MethodBase r in right_set.Methods){
4348 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4352 miset = new MemberInfo [length1 + length2 - common.Count];
4353 left_set.Methods.CopyTo (miset, 0);
4357 foreach (MethodBase r in right_set.Methods) {
4358 if (!common.Contains (r))
4362 union = new MethodGroupExpr (miset, loc);
4367 public static bool IsParamsMethodApplicable (EmitContext ec,
4368 ArrayList arguments, int arg_count,
4369 MethodBase candidate)
4371 return IsParamsMethodApplicable (
4372 ec, arguments, arg_count, candidate, false) ||
4373 IsParamsMethodApplicable (
4374 ec, arguments, arg_count, candidate, true);
4380 /// Determines if the candidate method, if a params method, is applicable
4381 /// in its expanded form to the given set of arguments
4383 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4384 int arg_count, MethodBase candidate,
4387 ParameterData pd = TypeManager.GetParameterData (candidate);
4389 int pd_count = pd.Count;
4393 int count = pd_count - 1;
4395 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4397 if (pd_count != arg_count)
4404 if (count > arg_count)
4407 if (pd_count == 1 && arg_count == 0)
4411 // If we have come this far, the case which
4412 // remains is when the number of parameters is
4413 // less than or equal to the argument count.
4415 for (int i = 0; i < count; ++i) {
4417 Argument a = (Argument) arguments [i];
4419 Parameter.Modifier a_mod = a.Modifier &
4420 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4421 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4422 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4424 if (a_mod == p_mod) {
4426 if (a_mod == Parameter.Modifier.NONE)
4427 if (!Convert.ImplicitConversionExists (ec,
4429 pd.ParameterType (i)))
4432 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4433 Type pt = pd.ParameterType (i);
4436 pt = TypeManager.GetReferenceType (pt);
4447 Argument a = (Argument) arguments [count];
4448 if (!(a.Expr is Arglist))
4454 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
4456 for (int i = pd_count - 1; i < arg_count; i++) {
4457 Argument a = (Argument) arguments [i];
4459 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4467 /// Determines if the candidate method is applicable (section 14.4.2.1)
4468 /// to the given set of arguments
4470 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4471 MethodBase candidate)
4473 ParameterData pd = TypeManager.GetParameterData (candidate);
4475 if (arg_count != pd.Count)
4478 for (int i = arg_count; i > 0; ) {
4481 Argument a = (Argument) arguments [i];
4483 Parameter.Modifier a_mod = a.Modifier &
4484 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4486 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4487 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4489 if (a_mod == p_mod) {
4490 Type pt = pd.ParameterType (i);
4492 if (a_mod == Parameter.Modifier.NONE) {
4493 if (!Convert.ImplicitConversionExists (ec, a.Expr, pt))
4507 static internal bool IsAncestralType (Type first_type, Type second_type)
4509 return first_type != second_type &&
4510 (second_type.IsSubclassOf (first_type) ||
4511 TypeManager.ImplementsInterface (second_type, first_type));
4515 /// Find the Applicable Function Members (7.4.2.1)
4517 /// me: Method Group expression with the members to select.
4518 /// it might contain constructors or methods (or anything
4519 /// that maps to a method).
4521 /// Arguments: ArrayList containing resolved Argument objects.
4523 /// loc: The location if we want an error to be reported, or a Null
4524 /// location for "probing" purposes.
4526 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4527 /// that is the best match of me on Arguments.
4530 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4531 ArrayList Arguments, bool may_fail,
4534 MethodBase method = null;
4535 bool method_params = false;
4536 Type applicable_type = null;
4538 ArrayList candidates = new ArrayList (2);
4539 ArrayList candidate_overrides = null;
4542 // Used to keep a map between the candidate
4543 // and whether it is being considered in its
4544 // normal or expanded form
4546 // false is normal form, true is expanded form
4548 Hashtable candidate_to_form = null;
4550 if (Arguments != null)
4551 arg_count = Arguments.Count;
4553 if ((me.Name == "Invoke") &&
4554 TypeManager.IsDelegateType (me.DeclaringType)) {
4555 Error_InvokeOnDelegate (loc);
4559 MethodBase[] methods = me.Methods;
4562 // First we construct the set of applicable methods
4564 bool is_sorted = true;
4565 for (int i = 0; i < methods.Length; i++){
4566 Type decl_type = methods [i].DeclaringType;
4569 // If we have already found an applicable method
4570 // we eliminate all base types (Section 14.5.5.1)
4572 if ((applicable_type != null) &&
4573 IsAncestralType (decl_type, applicable_type))
4577 // Methods marked 'override' don't take part in 'applicable_type'
4578 // computation, nor in the actual overload resolution.
4579 // However, they still need to be emitted instead of a base virtual method.
4580 // We avoid doing the 'applicable' test here, since it'll anyway be applied
4581 // to the base virtual function, and IsOverride is much faster than IsApplicable.
4583 if (!me.IsBase && TypeManager.IsOverride (methods [i])) {
4584 if (candidate_overrides == null)
4585 candidate_overrides = new ArrayList ();
4586 candidate_overrides.Add (methods [i]);
4591 // Check if candidate is applicable (section 14.4.2.1)
4592 // Is candidate applicable in normal form?
4594 bool is_applicable = IsApplicable (
4595 ec, Arguments, arg_count, methods [i]);
4597 if (!is_applicable &&
4598 (IsParamsMethodApplicable (
4599 ec, Arguments, arg_count, methods [i]))) {
4600 MethodBase candidate = methods [i];
4601 if (candidate_to_form == null)
4602 candidate_to_form = new PtrHashtable ();
4603 candidate_to_form [candidate] = candidate;
4604 // Candidate is applicable in expanded form
4605 is_applicable = true;
4611 candidates.Add (methods [i]);
4613 if (applicable_type == null)
4614 applicable_type = decl_type;
4615 else if (applicable_type != decl_type) {
4617 if (IsAncestralType (applicable_type, decl_type))
4618 applicable_type = decl_type;
4622 int candidate_top = candidates.Count;
4624 if (applicable_type == null) {
4626 // Okay so we have failed to find anything so we
4627 // return by providing info about the closest match
4629 int errors = Report.Errors;
4630 for (int i = 0; i < methods.Length; ++i) {
4631 MethodBase c = (MethodBase) methods [i];
4632 ParameterData pd = TypeManager.GetParameterData (c);
4634 if (pd.Count != arg_count)
4637 VerifyArgumentsCompat (ec, Arguments, arg_count,
4638 c, false, null, may_fail, loc);
4640 if (!may_fail && errors == Report.Errors)
4641 throw new InternalErrorException (
4642 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4643 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4648 if (!may_fail && errors == Report.Errors) {
4649 string report_name = me.Name;
4650 if (report_name == ".ctor")
4651 report_name = me.DeclaringType.ToString ();
4652 Error_WrongNumArguments (loc, report_name, arg_count);
4660 // At this point, applicable_type is _one_ of the most derived types
4661 // in the set of types containing the methods in this MethodGroup.
4662 // Filter the candidates so that they only contain methods from the
4663 // most derived types.
4666 int finalized = 0; // Number of finalized candidates
4669 // Invariant: applicable_type is a most derived type
4671 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4672 // eliminating all it's base types. At the same time, we'll also move
4673 // every unrelated type to the end of the array, and pick the next
4674 // 'applicable_type'.
4676 Type next_applicable_type = null;
4677 int j = finalized; // where to put the next finalized candidate
4678 int k = finalized; // where to put the next undiscarded candidate
4679 for (int i = finalized; i < candidate_top; ++i) {
4680 MethodBase candidate = (MethodBase) candidates [i];
4681 Type decl_type = candidate.DeclaringType;
4683 if (decl_type == applicable_type) {
4684 candidates [k++] = candidates [j];
4685 candidates [j++] = candidates [i];
4689 if (IsAncestralType (decl_type, applicable_type))
4692 if (next_applicable_type != null &&
4693 IsAncestralType (decl_type, next_applicable_type))
4696 candidates [k++] = candidates [i];
4698 if (next_applicable_type == null ||
4699 IsAncestralType (next_applicable_type, decl_type))
4700 next_applicable_type = decl_type;
4703 applicable_type = next_applicable_type;
4706 } while (applicable_type != null);
4710 // Now we actually find the best method
4713 method = (MethodBase) candidates [0];
4714 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4715 for (int ix = 1; ix < candidate_top; ix++){
4716 MethodBase candidate = (MethodBase) candidates [ix];
4718 if (candidate == method)
4721 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4723 if (BetterFunction (ec, Arguments, arg_count,
4724 candidate, cand_params,
4725 method, method_params, loc)) {
4727 method_params = cand_params;
4731 // Now check that there are no ambiguities i.e the selected method
4732 // should be better than all the others
4734 MethodBase ambiguous = null;
4735 for (int ix = 0; ix < candidate_top; ix++){
4736 MethodBase candidate = (MethodBase) candidates [ix];
4738 if (candidate == method)
4741 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4742 if (!BetterFunction (ec, Arguments, arg_count,
4743 method, method_params,
4744 candidate, cand_params,
4746 Report.SymbolRelatedToPreviousError (candidate);
4747 ambiguous = candidate;
4751 if (ambiguous != null) {
4752 Report.SymbolRelatedToPreviousError (method);
4753 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4754 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4759 // If the method is a virtual function, pick an override closer to the LHS type.
4761 if (!me.IsBase && method.IsVirtual) {
4762 if (TypeManager.IsOverride (method))
4763 throw new InternalErrorException (
4764 "Should not happen. An 'override' method took part in overload resolution: " + method);
4766 if (candidate_overrides != null)
4767 foreach (MethodBase candidate in candidate_overrides) {
4768 if (IsOverride (candidate, method))
4774 // And now check if the arguments are all
4775 // compatible, perform conversions if
4776 // necessary etc. and return if everything is
4779 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4780 method_params, null, may_fail, loc))
4783 if (method != null) {
4784 IMethodData data = TypeManager.GetMethod (method);
4786 data.SetMemberIsUsed ();
4791 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4793 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4794 name, arg_count.ToString ());
4797 static void Error_InvokeOnDelegate (Location loc)
4799 Report.Error (1533, loc,
4800 "Invoke cannot be called directly on a delegate");
4803 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4804 Type delegate_type, Argument a, ParameterData expected_par)
4806 if (delegate_type == null)
4807 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4808 TypeManager.CSharpSignature (method));
4810 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4811 TypeManager.CSharpName (delegate_type));
4813 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4815 string index = (idx + 1).ToString ();
4816 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4817 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4818 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4819 index, Parameter.GetModifierSignature (a.Modifier));
4821 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4822 index, Parameter.GetModifierSignature (mod));
4824 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4825 index, Argument.FullDesc (a), expected_par.ParameterDesc (idx));
4829 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4830 int arg_count, MethodBase method,
4831 bool chose_params_expanded,
4832 Type delegate_type, bool may_fail,
4835 ParameterData pd = TypeManager.GetParameterData (method);
4836 int pd_count = pd.Count;
4838 for (int j = 0; j < arg_count; j++) {
4839 Argument a = (Argument) Arguments [j];
4840 Expression a_expr = a.Expr;
4841 Type parameter_type = pd.ParameterType (j);
4842 Parameter.Modifier pm = pd.ParameterModifier (j);
4844 if (pm == Parameter.Modifier.PARAMS){
4845 if ((pm & ~Parameter.Modifier.PARAMS) != a.Modifier) {
4847 Error_InvalidArguments (loc, j, method, delegate_type, a, pd);
4851 if (chose_params_expanded)
4852 parameter_type = TypeManager.GetElementType (parameter_type);
4853 } else if (pm == Parameter.Modifier.ARGLIST) {
4854 if (!(a.Expr is Arglist)) {
4856 Error_InvalidArguments (loc, j, method, delegate_type, a, pd);
4864 if (pd.ParameterModifier (j) != a.Modifier){
4866 Error_InvalidArguments (loc, j, method, delegate_type, a, pd);
4874 if (!a.Type.Equals (parameter_type)){
4877 conv = Convert.ImplicitConversion (ec, a_expr, parameter_type, loc);
4881 Error_InvalidArguments (loc, j, method, delegate_type, a, pd);
4886 // Update the argument with the implicit conversion
4892 if (parameter_type.IsPointer){
4899 Parameter.Modifier a_mod = a.Modifier &
4900 unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF));
4901 Parameter.Modifier p_mod = pd.ParameterModifier (j) &
4902 unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF));
4904 if (a_mod != p_mod &&
4905 pd.ParameterModifier (pd_count - 1) != Parameter.Modifier.PARAMS) {
4907 Invocation.Error_InvalidArguments (loc, j, method, null, a, pd);
4917 public override Expression DoResolve (EmitContext ec)
4920 // First, resolve the expression that is used to
4921 // trigger the invocation
4923 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4927 if (!(expr is MethodGroupExpr)) {
4928 Type expr_type = expr.Type;
4930 if (expr_type != null){
4931 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4933 return (new DelegateInvocation (
4934 this.expr, Arguments, loc)).Resolve (ec);
4938 if (!(expr is MethodGroupExpr)){
4939 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4944 // Next, evaluate all the expressions in the argument list
4946 if (Arguments != null){
4947 foreach (Argument a in Arguments){
4948 if (!a.Resolve (ec, loc))
4953 MethodGroupExpr mg = (MethodGroupExpr) expr;
4954 method = OverloadResolve (ec, mg, Arguments, false, loc);
4959 MethodInfo mi = method as MethodInfo;
4961 type = TypeManager.TypeToCoreType (mi.ReturnType);
4962 Expression iexpr = mg.InstanceExpression;
4964 if (iexpr == null ||
4965 iexpr is This || iexpr is EmptyExpression ||
4966 mg.IdenticalTypeName) {
4967 mg.InstanceExpression = null;
4969 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
4973 if (iexpr == null || iexpr is EmptyExpression) {
4974 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
4980 if (type.IsPointer){
4988 // Only base will allow this invocation to happen.
4990 if (mg.IsBase && method.IsAbstract){
4991 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
4995 if (Arguments == null && method.Name == "Finalize") {
4997 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
4999 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
5003 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
5007 if (mg.InstanceExpression != null)
5008 mg.InstanceExpression.CheckMarshallByRefAccess (ec.ContainerType);
5010 eclass = ExprClass.Value;
5014 bool IsSpecialMethodInvocation (MethodBase method)
5016 IMethodData md = TypeManager.GetMethod (method);
5018 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
5021 if (!TypeManager.IsSpecialMethod (method))
5024 int args = TypeManager.GetParameterData (method).Count;
5025 if (method.Name.StartsWith ("get_") && args > 0)
5027 else if (method.Name.StartsWith ("set_") && args > 2)
5030 // TODO: check operators and events as well ?
5033 Report.SymbolRelatedToPreviousError (method);
5034 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
5035 TypeManager.CSharpSignature (method, true));
5041 // Emits the list of arguments as an array
5043 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
5045 ILGenerator ig = ec.ig;
5046 int count = arguments.Count - idx;
5047 Argument a = (Argument) arguments [idx];
5048 Type t = a.Expr.Type;
5050 IntConstant.EmitInt (ig, count);
5051 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
5053 int top = arguments.Count;
5054 for (int j = idx; j < top; j++){
5055 a = (Argument) arguments [j];
5057 ig.Emit (OpCodes.Dup);
5058 IntConstant.EmitInt (ig, j - idx);
5061 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj);
5063 ig.Emit (OpCodes.Ldelema, t);
5068 ig.Emit (OpCodes.Stobj, t);
5075 /// Emits a list of resolved Arguments that are in the arguments
5078 /// The MethodBase argument might be null if the
5079 /// emission of the arguments is known not to contain
5080 /// a `params' field (for example in constructors or other routines
5081 /// that keep their arguments in this structure)
5083 /// if `dup_args' is true, a copy of the arguments will be left
5084 /// on the stack. If `dup_args' is true, you can specify `this_arg'
5085 /// which will be duplicated before any other args. Only EmitCall
5086 /// should be using this interface.
5088 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
5090 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
5091 int top = arguments == null ? 0 : arguments.Count;
5092 LocalTemporary [] temps = null;
5094 if (dup_args && top != 0)
5095 temps = new LocalTemporary [top];
5097 for (int i = 0; i < top; i++){
5098 Argument a = (Argument) arguments [i];
5101 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
5103 // Special case if we are passing the same data as the
5104 // params argument, do not put it in an array.
5106 if (pd.ParameterType (i) == a.Type)
5109 EmitParams (ec, i, arguments);
5116 ec.ig.Emit (OpCodes.Dup);
5117 (temps [i] = new LocalTemporary (ec, a.Type)).Store (ec);
5122 if (this_arg != null)
5125 for (int i = 0; i < top; i ++)
5126 temps [i].Emit (ec);
5129 if (pd != null && pd.Count > top &&
5130 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
5131 ILGenerator ig = ec.ig;
5133 IntConstant.EmitInt (ig, 0);
5134 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
5138 static Type[] GetVarargsTypes (EmitContext ec, MethodBase mb,
5139 ArrayList arguments)
5141 ParameterData pd = TypeManager.GetParameterData (mb);
5143 if (arguments == null)
5144 return new Type [0];
5146 Argument a = (Argument) arguments [pd.Count - 1];
5147 Arglist list = (Arglist) a.Expr;
5149 return list.ArgumentTypes;
5153 /// This checks the ConditionalAttribute on the method
5155 static bool IsMethodExcluded (MethodBase method, EmitContext ec)
5157 if (method.IsConstructor)
5160 IMethodData md = TypeManager.GetMethod (method);
5162 return md.IsExcluded (ec);
5164 // For some methods (generated by delegate class) GetMethod returns null
5165 // because they are not included in builder_to_method table
5166 if (method.DeclaringType is TypeBuilder)
5169 return AttributeTester.IsConditionalMethodExcluded (method);
5173 /// is_base tells whether we want to force the use of the `call'
5174 /// opcode instead of using callvirt. Call is required to call
5175 /// a specific method, while callvirt will always use the most
5176 /// recent method in the vtable.
5178 /// is_static tells whether this is an invocation on a static method
5180 /// instance_expr is an expression that represents the instance
5181 /// it must be non-null if is_static is false.
5183 /// method is the method to invoke.
5185 /// Arguments is the list of arguments to pass to the method or constructor.
5187 public static void EmitCall (EmitContext ec, bool is_base,
5188 bool is_static, Expression instance_expr,
5189 MethodBase method, ArrayList Arguments, Location loc)
5191 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
5194 // `dup_args' leaves an extra copy of the arguments on the stack
5195 // `omit_args' does not leave any arguments at all.
5196 // So, basically, you could make one call with `dup_args' set to true,
5197 // and then another with `omit_args' set to true, and the two calls
5198 // would have the same set of arguments. However, each argument would
5199 // only have been evaluated once.
5200 public static void EmitCall (EmitContext ec, bool is_base,
5201 bool is_static, Expression instance_expr,
5202 MethodBase method, ArrayList Arguments, Location loc,
5203 bool dup_args, bool omit_args)
5205 ILGenerator ig = ec.ig;
5206 bool struct_call = false;
5207 bool this_call = false;
5208 LocalTemporary this_arg = null;
5210 Type decl_type = method.DeclaringType;
5212 if (!RootContext.StdLib) {
5213 // Replace any calls to the system's System.Array type with calls to
5214 // the newly created one.
5215 if (method == TypeManager.system_int_array_get_length)
5216 method = TypeManager.int_array_get_length;
5217 else if (method == TypeManager.system_int_array_get_rank)
5218 method = TypeManager.int_array_get_rank;
5219 else if (method == TypeManager.system_object_array_clone)
5220 method = TypeManager.object_array_clone;
5221 else if (method == TypeManager.system_int_array_get_length_int)
5222 method = TypeManager.int_array_get_length_int;
5223 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5224 method = TypeManager.int_array_get_lower_bound_int;
5225 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5226 method = TypeManager.int_array_get_upper_bound_int;
5227 else if (method == TypeManager.system_void_array_copyto_array_int)
5228 method = TypeManager.void_array_copyto_array_int;
5231 if (ec.TestObsoleteMethodUsage) {
5233 // This checks ObsoleteAttribute on the method and on the declaring type
5235 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5237 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5240 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5242 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5246 if (IsMethodExcluded (method, ec))
5250 if (instance_expr == EmptyExpression.Null) {
5251 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5255 this_call = instance_expr is This;
5256 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5262 // Push the instance expression
5264 if (instance_expr.Type.IsValueType) {
5266 // Special case: calls to a function declared in a
5267 // reference-type with a value-type argument need
5268 // to have their value boxed.
5269 if (decl_type.IsValueType) {
5271 // If the expression implements IMemoryLocation, then
5272 // we can optimize and use AddressOf on the
5275 // If not we have to use some temporary storage for
5277 if (instance_expr is IMemoryLocation) {
5278 ((IMemoryLocation)instance_expr).
5279 AddressOf (ec, AddressOp.LoadStore);
5281 LocalTemporary temp = new LocalTemporary (ec, instance_expr.Type);
5282 instance_expr.Emit (ec);
5284 temp.AddressOf (ec, AddressOp.Load);
5287 // avoid the overhead of doing this all the time.
5289 t = TypeManager.GetReferenceType (instance_expr.Type);
5291 instance_expr.Emit (ec);
5292 ig.Emit (OpCodes.Box, instance_expr.Type);
5293 t = TypeManager.object_type;
5296 instance_expr.Emit (ec);
5297 t = instance_expr.Type;
5301 ig.Emit (OpCodes.Dup);
5302 if (Arguments != null && Arguments.Count != 0) {
5303 this_arg = new LocalTemporary (ec, t);
5304 this_arg.Store (ec);
5311 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5314 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5315 call_op = OpCodes.Call;
5317 call_op = OpCodes.Callvirt;
5319 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5320 Type[] varargs_types = GetVarargsTypes (ec, method, Arguments);
5321 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5328 // and DoFoo is not virtual, you can omit the callvirt,
5329 // because you don't need the null checking behavior.
5331 if (method is MethodInfo)
5332 ig.Emit (call_op, (MethodInfo) method);
5334 ig.Emit (call_op, (ConstructorInfo) method);
5337 public override void Emit (EmitContext ec)
5339 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5341 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5344 public override void EmitStatement (EmitContext ec)
5349 // Pop the return value if there is one
5351 if (method is MethodInfo){
5352 Type ret = ((MethodInfo)method).ReturnType;
5353 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5354 ec.ig.Emit (OpCodes.Pop);
5359 public class InvocationOrCast : ExpressionStatement
5362 Expression argument;
5364 public InvocationOrCast (Expression expr, Expression argument)
5367 this.argument = argument;
5368 this.loc = expr.Location;
5371 public override Expression DoResolve (EmitContext ec)
5374 // First try to resolve it as a cast.
5376 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5378 Cast cast = new Cast (te, argument, loc);
5379 return cast.Resolve (ec);
5383 // This can either be a type or a delegate invocation.
5384 // Let's just resolve it and see what we'll get.
5386 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5391 // Ok, so it's a Cast.
5393 if (expr.eclass == ExprClass.Type) {
5394 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5395 return cast.Resolve (ec);
5399 // It's a delegate invocation.
5401 if (!TypeManager.IsDelegateType (expr.Type)) {
5402 Error (149, "Method name expected");
5406 ArrayList args = new ArrayList ();
5407 args.Add (new Argument (argument, Argument.AType.Expression));
5408 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5409 return invocation.Resolve (ec);
5414 Error (201, "Only assignment, call, increment, decrement and new object " +
5415 "expressions can be used as a statement");
5418 public override ExpressionStatement ResolveStatement (EmitContext ec)
5421 // First try to resolve it as a cast.
5423 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5430 // This can either be a type or a delegate invocation.
5431 // Let's just resolve it and see what we'll get.
5433 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5434 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5440 // It's a delegate invocation.
5442 if (!TypeManager.IsDelegateType (expr.Type)) {
5443 Error (149, "Method name expected");
5447 ArrayList args = new ArrayList ();
5448 args.Add (new Argument (argument, Argument.AType.Expression));
5449 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5450 return invocation.ResolveStatement (ec);
5453 public override void Emit (EmitContext ec)
5455 throw new Exception ("Cannot happen");
5458 public override void EmitStatement (EmitContext ec)
5460 throw new Exception ("Cannot happen");
5465 // This class is used to "disable" the code generation for the
5466 // temporary variable when initializing value types.
5468 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5469 public void AddressOf (EmitContext ec, AddressOp Mode)
5476 /// Implements the new expression
5478 public class New : ExpressionStatement, IMemoryLocation {
5479 public readonly ArrayList Arguments;
5482 // During bootstrap, it contains the RequestedType,
5483 // but if `type' is not null, it *might* contain a NewDelegate
5484 // (because of field multi-initialization)
5486 public Expression RequestedType;
5488 MethodBase method = null;
5491 // If set, the new expression is for a value_target, and
5492 // we will not leave anything on the stack.
5494 Expression value_target;
5495 bool value_target_set = false;
5497 public New (Expression requested_type, ArrayList arguments, Location l)
5499 RequestedType = requested_type;
5500 Arguments = arguments;
5504 public bool SetValueTypeVariable (Expression value)
5506 value_target = value;
5507 value_target_set = true;
5508 if (!(value_target is IMemoryLocation)){
5509 Error_UnexpectedKind (null, "variable", loc);
5516 // This function is used to disable the following code sequence for
5517 // value type initialization:
5519 // AddressOf (temporary)
5523 // Instead the provide will have provided us with the address on the
5524 // stack to store the results.
5526 static Expression MyEmptyExpression;
5528 public void DisableTemporaryValueType ()
5530 if (MyEmptyExpression == null)
5531 MyEmptyExpression = new EmptyAddressOf ();
5534 // To enable this, look into:
5535 // test-34 and test-89 and self bootstrapping.
5537 // For instance, we can avoid a copy by using `newobj'
5538 // instead of Call + Push-temp on value types.
5539 // value_target = MyEmptyExpression;
5544 /// Converts complex core type syntax like 'new int ()' to simple constant
5546 public static Constant Constantify (Type t)
5548 if (t == TypeManager.int32_type)
5549 return new IntConstant (0, Location.Null);
5550 if (t == TypeManager.uint32_type)
5551 return new UIntConstant (0, Location.Null);
5552 if (t == TypeManager.int64_type)
5553 return new LongConstant (0, Location.Null);
5554 if (t == TypeManager.uint64_type)
5555 return new ULongConstant (0, Location.Null);
5556 if (t == TypeManager.float_type)
5557 return new FloatConstant (0, Location.Null);
5558 if (t == TypeManager.double_type)
5559 return new DoubleConstant (0, Location.Null);
5560 if (t == TypeManager.short_type)
5561 return new ShortConstant (0, Location.Null);
5562 if (t == TypeManager.ushort_type)
5563 return new UShortConstant (0, Location.Null);
5564 if (t == TypeManager.sbyte_type)
5565 return new SByteConstant (0, Location.Null);
5566 if (t == TypeManager.byte_type)
5567 return new ByteConstant (0, Location.Null);
5568 if (t == TypeManager.char_type)
5569 return new CharConstant ('\0', Location.Null);
5570 if (t == TypeManager.bool_type)
5571 return new BoolConstant (false, Location.Null);
5572 if (t == TypeManager.decimal_type)
5573 return new DecimalConstant (0, Location.Null);
5579 // Checks whether the type is an interface that has the
5580 // [ComImport, CoClass] attributes and must be treated
5583 public Expression CheckComImport (EmitContext ec)
5585 if (!type.IsInterface)
5589 // Turn the call into:
5590 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5592 Type real_class = AttributeTester.GetCoClassAttribute (type);
5593 if (real_class == null)
5596 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5597 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5598 return cast.Resolve (ec);
5601 public override Expression DoResolve (EmitContext ec)
5604 // The New DoResolve might be called twice when initializing field
5605 // expressions (see EmitFieldInitializers, the call to
5606 // GetInitializerExpression will perform a resolve on the expression,
5607 // and later the assign will trigger another resolution
5609 // This leads to bugs (#37014)
5612 if (RequestedType is NewDelegate)
5613 return RequestedType;
5617 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5621 type = texpr.ResolveType (ec);
5623 if (Arguments == null) {
5624 Expression c = Constantify (type);
5629 if (TypeManager.IsDelegateType (type)) {
5630 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5631 if (RequestedType != null)
5632 if (!(RequestedType is DelegateCreation))
5633 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5634 return RequestedType;
5637 if (type.IsAbstract && type.IsSealed) {
5638 Report.SymbolRelatedToPreviousError (type);
5639 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5643 if (type.IsInterface || type.IsAbstract){
5644 RequestedType = CheckComImport (ec);
5645 if (RequestedType != null)
5646 return RequestedType;
5648 Report.SymbolRelatedToPreviousError (type);
5649 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5653 bool is_struct = type.IsValueType;
5654 eclass = ExprClass.Value;
5657 // SRE returns a match for .ctor () on structs (the object constructor),
5658 // so we have to manually ignore it.
5660 if (is_struct && Arguments == null)
5663 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5664 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5665 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5670 MethodGroupExpr mg = ml as MethodGroupExpr;
5673 ml.Error_UnexpectedKind (ec, "method group", loc);
5677 if (Arguments != null){
5678 foreach (Argument a in Arguments){
5679 if (!a.Resolve (ec, loc))
5684 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5685 if (method == null) {
5686 if (almostMatchedMembers.Count != 0)
5687 MemberLookupFailed (ec, type, type, ".ctor", null, true, loc);
5695 // This DoEmit can be invoked in two contexts:
5696 // * As a mechanism that will leave a value on the stack (new object)
5697 // * As one that wont (init struct)
5699 // You can control whether a value is required on the stack by passing
5700 // need_value_on_stack. The code *might* leave a value on the stack
5701 // so it must be popped manually
5703 // If we are dealing with a ValueType, we have a few
5704 // situations to deal with:
5706 // * The target is a ValueType, and we have been provided
5707 // the instance (this is easy, we are being assigned).
5709 // * The target of New is being passed as an argument,
5710 // to a boxing operation or a function that takes a
5713 // In this case, we need to create a temporary variable
5714 // that is the argument of New.
5716 // Returns whether a value is left on the stack
5718 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5720 bool is_value_type = type.IsValueType;
5721 ILGenerator ig = ec.ig;
5726 // Allow DoEmit() to be called multiple times.
5727 // We need to create a new LocalTemporary each time since
5728 // you can't share LocalBuilders among ILGeneators.
5729 if (!value_target_set)
5730 value_target = new LocalTemporary (ec, type);
5732 ml = (IMemoryLocation) value_target;
5733 ml.AddressOf (ec, AddressOp.Store);
5737 Invocation.EmitArguments (ec, method, Arguments, false, null);
5741 ig.Emit (OpCodes.Initobj, type);
5743 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5744 if (need_value_on_stack){
5745 value_target.Emit (ec);
5750 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5755 public override void Emit (EmitContext ec)
5760 public override void EmitStatement (EmitContext ec)
5762 if (DoEmit (ec, false))
5763 ec.ig.Emit (OpCodes.Pop);
5766 public void AddressOf (EmitContext ec, AddressOp Mode)
5768 if (!type.IsValueType){
5770 // We throw an exception. So far, I believe we only need to support
5772 // foreach (int j in new StructType ())
5775 throw new Exception ("AddressOf should not be used for classes");
5778 if (!value_target_set)
5779 value_target = new LocalTemporary (ec, type);
5781 IMemoryLocation ml = (IMemoryLocation) value_target;
5782 ml.AddressOf (ec, AddressOp.Store);
5784 Invocation.EmitArguments (ec, method, Arguments, false, null);
5787 ec.ig.Emit (OpCodes.Initobj, type);
5789 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5791 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5796 /// 14.5.10.2: Represents an array creation expression.
5800 /// There are two possible scenarios here: one is an array creation
5801 /// expression that specifies the dimensions and optionally the
5802 /// initialization data and the other which does not need dimensions
5803 /// specified but where initialization data is mandatory.
5805 public class ArrayCreation : Expression {
5806 Expression requested_base_type;
5807 ArrayList initializers;
5810 // The list of Argument types.
5811 // This is used to construct the `newarray' or constructor signature
5813 ArrayList arguments;
5816 // Method used to create the array object.
5818 MethodBase new_method = null;
5820 Type array_element_type;
5821 Type underlying_type;
5822 bool is_one_dimensional = false;
5823 bool is_builtin_type = false;
5824 bool expect_initializers = false;
5825 int num_arguments = 0;
5829 ArrayList array_data;
5834 // The number of array initializers that we can handle
5835 // via the InitializeArray method - through EmitStaticInitializers
5837 int num_automatic_initializers;
5839 const int max_automatic_initializers = 6;
5841 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5843 this.requested_base_type = requested_base_type;
5844 this.initializers = initializers;
5848 arguments = new ArrayList ();
5850 foreach (Expression e in exprs) {
5851 arguments.Add (new Argument (e, Argument.AType.Expression));
5856 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5858 this.requested_base_type = requested_base_type;
5859 this.initializers = initializers;
5863 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5865 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5867 //dimensions = tmp.Length - 1;
5868 expect_initializers = true;
5871 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5873 StringBuilder sb = new StringBuilder (rank);
5876 for (int i = 1; i < idx_count; i++)
5881 return new ComposedCast (base_type, sb.ToString (), loc);
5884 void Error_IncorrectArrayInitializer ()
5886 Error (178, "Invalid rank specifier: expected `,' or `]'");
5889 public bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5891 if (specified_dims) {
5892 Argument a = (Argument) arguments [idx];
5894 if (!a.Resolve (ec, loc))
5897 Constant c = a.Expr as Constant;
5899 c = c.ToType (TypeManager.int32_type, a.Expr.Location);
5903 Report.Error (150, a.Expr.Location, "A constant value is expected");
5907 int value = (int) c.GetValue ();
5909 if (value != probe.Count) {
5910 Error_IncorrectArrayInitializer ();
5914 bounds [idx] = value;
5917 int child_bounds = -1;
5918 for (int i = 0; i < probe.Count; ++i) {
5919 object o = probe [i];
5920 if (o is ArrayList) {
5921 ArrayList sub_probe = o as ArrayList;
5922 int current_bounds = sub_probe.Count;
5924 if (child_bounds == -1)
5925 child_bounds = current_bounds;
5927 else if (child_bounds != current_bounds){
5928 Error_IncorrectArrayInitializer ();
5931 if (specified_dims && (idx + 1 >= arguments.Count)){
5932 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
5936 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
5940 if (child_bounds != -1){
5941 Error_IncorrectArrayInitializer ();
5945 Expression tmp = (Expression) o;
5946 tmp = tmp.Resolve (ec);
5951 // Console.WriteLine ("I got: " + tmp);
5952 // Handle initialization from vars, fields etc.
5954 Expression conv = Convert.ImplicitConversionRequired (
5955 ec, tmp, underlying_type, loc);
5960 if (conv is StringConstant || conv is DecimalConstant || conv is NullCast) {
5961 // These are subclasses of Constant that can appear as elements of an
5962 // array that cannot be statically initialized (with num_automatic_initializers
5963 // > max_automatic_initializers), so num_automatic_initializers should be left as zero.
5964 array_data.Add (conv);
5965 } else if (conv is Constant) {
5966 // These are the types of Constant that can appear in arrays that can be
5967 // statically allocated.
5968 array_data.Add (conv);
5969 num_automatic_initializers++;
5971 array_data.Add (conv);
5978 public void UpdateIndices (EmitContext ec)
5981 for (ArrayList probe = initializers; probe != null;) {
5982 if (probe.Count > 0 && probe [0] is ArrayList) {
5983 Expression e = new IntConstant (probe.Count, Location.Null);
5984 arguments.Add (new Argument (e, Argument.AType.Expression));
5986 bounds [i++] = probe.Count;
5988 probe = (ArrayList) probe [0];
5991 Expression e = new IntConstant (probe.Count, Location.Null);
5992 arguments.Add (new Argument (e, Argument.AType.Expression));
5994 bounds [i++] = probe.Count;
6001 public bool ValidateInitializers (EmitContext ec, Type array_type)
6003 if (initializers == null) {
6004 if (expect_initializers)
6010 if (underlying_type == null)
6014 // We use this to store all the date values in the order in which we
6015 // will need to store them in the byte blob later
6017 array_data = new ArrayList ();
6018 bounds = new Hashtable ();
6022 if (arguments != null) {
6023 ret = CheckIndices (ec, initializers, 0, true);
6026 arguments = new ArrayList ();
6028 ret = CheckIndices (ec, initializers, 0, false);
6035 if (arguments.Count != dimensions) {
6036 Error_IncorrectArrayInitializer ();
6045 // Creates the type of the array
6047 bool LookupType (EmitContext ec)
6049 StringBuilder array_qualifier = new StringBuilder (rank);
6052 // `In the first form allocates an array instace of the type that results
6053 // from deleting each of the individual expression from the expression list'
6055 if (num_arguments > 0) {
6056 array_qualifier.Append ("[");
6057 for (int i = num_arguments-1; i > 0; i--)
6058 array_qualifier.Append (",");
6059 array_qualifier.Append ("]");
6065 TypeExpr array_type_expr;
6066 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
6067 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
6068 if (array_type_expr == null)
6071 type = array_type_expr.ResolveType (ec);
6073 if (!type.IsArray) {
6074 Error (622, "Can only use array initializer expressions to assign to array types. Try using a new expression instead.");
6077 underlying_type = TypeManager.GetElementType (type);
6078 dimensions = type.GetArrayRank ();
6083 public override Expression DoResolve (EmitContext ec)
6087 if (!LookupType (ec))
6091 // First step is to validate the initializers and fill
6092 // in any missing bits
6094 if (!ValidateInitializers (ec, type))
6097 if (arguments == null)
6100 arg_count = arguments.Count;
6101 foreach (Argument a in arguments){
6102 if (!a.Resolve (ec, loc))
6105 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
6106 if (real_arg == null)
6113 array_element_type = TypeManager.GetElementType (type);
6115 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
6116 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
6120 if (arg_count == 1) {
6121 is_one_dimensional = true;
6122 eclass = ExprClass.Value;
6126 is_builtin_type = TypeManager.IsBuiltinType (type);
6128 if (is_builtin_type) {
6131 ml = MemberLookup (ec, type, ".ctor", MemberTypes.Constructor,
6132 AllBindingFlags, loc);
6134 if (!(ml is MethodGroupExpr)) {
6135 ml.Error_UnexpectedKind (ec, "method group", loc);
6140 Error (-6, "New invocation: Can not find a constructor for " +
6141 "this argument list");
6145 new_method = Invocation.OverloadResolve (
6146 ec, (MethodGroupExpr) ml, arguments, false, loc);
6148 if (new_method == null) {
6149 Error (-6, "New invocation: Can not find a constructor for " +
6150 "this argument list");
6154 eclass = ExprClass.Value;
6157 ModuleBuilder mb = CodeGen.Module.Builder;
6158 ArrayList args = new ArrayList ();
6160 if (arguments != null) {
6161 for (int i = 0; i < arg_count; i++)
6162 args.Add (TypeManager.int32_type);
6165 Type [] arg_types = null;
6168 arg_types = new Type [args.Count];
6170 args.CopyTo (arg_types, 0);
6172 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6175 if (new_method == null) {
6176 Error (-6, "New invocation: Can not find a constructor for " +
6177 "this argument list");
6181 eclass = ExprClass.Value;
6186 public static byte [] MakeByteBlob (ArrayList array_data, Type underlying_type, Location loc)
6191 int count = array_data.Count;
6193 if (underlying_type.IsEnum)
6194 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
6196 factor = GetTypeSize (underlying_type);
6198 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
6200 data = new byte [(count * factor + 4) & ~3];
6203 for (int i = 0; i < count; ++i) {
6204 object v = array_data [i];
6206 if (v is EnumConstant)
6207 v = ((EnumConstant) v).Child;
6209 if (v is Constant && !(v is StringConstant))
6210 v = ((Constant) v).GetValue ();
6216 if (underlying_type == TypeManager.int64_type){
6217 if (!(v is Expression)){
6218 long val = (long) v;
6220 for (int j = 0; j < factor; ++j) {
6221 data [idx + j] = (byte) (val & 0xFF);
6225 } else if (underlying_type == TypeManager.uint64_type){
6226 if (!(v is Expression)){
6227 ulong val = (ulong) v;
6229 for (int j = 0; j < factor; ++j) {
6230 data [idx + j] = (byte) (val & 0xFF);
6234 } else if (underlying_type == TypeManager.float_type) {
6235 if (!(v is Expression)){
6236 element = BitConverter.GetBytes ((float) v);
6238 for (int j = 0; j < factor; ++j)
6239 data [idx + j] = element [j];
6241 } else if (underlying_type == TypeManager.double_type) {
6242 if (!(v is Expression)){
6243 element = BitConverter.GetBytes ((double) v);
6245 for (int j = 0; j < factor; ++j)
6246 data [idx + j] = element [j];
6248 } else if (underlying_type == TypeManager.char_type){
6249 if (!(v is Expression)){
6250 int val = (int) ((char) v);
6252 data [idx] = (byte) (val & 0xff);
6253 data [idx+1] = (byte) (val >> 8);
6255 } else if (underlying_type == TypeManager.short_type){
6256 if (!(v is Expression)){
6257 int val = (int) ((short) v);
6259 data [idx] = (byte) (val & 0xff);
6260 data [idx+1] = (byte) (val >> 8);
6262 } else if (underlying_type == TypeManager.ushort_type){
6263 if (!(v is Expression)){
6264 int val = (int) ((ushort) v);
6266 data [idx] = (byte) (val & 0xff);
6267 data [idx+1] = (byte) (val >> 8);
6269 } else if (underlying_type == TypeManager.int32_type) {
6270 if (!(v is Expression)){
6273 data [idx] = (byte) (val & 0xff);
6274 data [idx+1] = (byte) ((val >> 8) & 0xff);
6275 data [idx+2] = (byte) ((val >> 16) & 0xff);
6276 data [idx+3] = (byte) (val >> 24);
6278 } else if (underlying_type == TypeManager.uint32_type) {
6279 if (!(v is Expression)){
6280 uint val = (uint) v;
6282 data [idx] = (byte) (val & 0xff);
6283 data [idx+1] = (byte) ((val >> 8) & 0xff);
6284 data [idx+2] = (byte) ((val >> 16) & 0xff);
6285 data [idx+3] = (byte) (val >> 24);
6287 } else if (underlying_type == TypeManager.sbyte_type) {
6288 if (!(v is Expression)){
6289 sbyte val = (sbyte) v;
6290 data [idx] = (byte) val;
6292 } else if (underlying_type == TypeManager.byte_type) {
6293 if (!(v is Expression)){
6294 byte val = (byte) v;
6295 data [idx] = (byte) val;
6297 } else if (underlying_type == TypeManager.bool_type) {
6298 if (!(v is Expression)){
6299 bool val = (bool) v;
6300 data [idx] = (byte) (val ? 1 : 0);
6302 } else if (underlying_type == TypeManager.decimal_type){
6303 if (!(v is Expression)){
6304 int [] bits = Decimal.GetBits ((decimal) v);
6307 // FIXME: For some reason, this doesn't work on the MS runtime.
6308 int [] nbits = new int [4];
6309 nbits [0] = bits [3];
6310 nbits [1] = bits [2];
6311 nbits [2] = bits [0];
6312 nbits [3] = bits [1];
6314 for (int j = 0; j < 4; j++){
6315 data [p++] = (byte) (nbits [j] & 0xff);
6316 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6317 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6318 data [p++] = (byte) (nbits [j] >> 24);
6322 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6331 // Emits the initializers for the array
6333 void EmitStaticInitializers (EmitContext ec)
6336 // First, the static data
6339 ILGenerator ig = ec.ig;
6341 byte [] data = MakeByteBlob (array_data, underlying_type, loc);
6343 fb = RootContext.MakeStaticData (data);
6345 ig.Emit (OpCodes.Dup);
6346 ig.Emit (OpCodes.Ldtoken, fb);
6347 ig.Emit (OpCodes.Call,
6348 TypeManager.void_initializearray_array_fieldhandle);
6352 // Emits pieces of the array that can not be computed at compile
6353 // time (variables and string locations).
6355 // This always expect the top value on the stack to be the array
6357 void EmitDynamicInitializers (EmitContext ec)
6359 ILGenerator ig = ec.ig;
6360 int dims = bounds.Count;
6361 int [] current_pos = new int [dims];
6362 int top = array_data.Count;
6364 MethodInfo set = null;
6368 ModuleBuilder mb = null;
6369 mb = CodeGen.Module.Builder;
6370 args = new Type [dims + 1];
6373 for (j = 0; j < dims; j++)
6374 args [j] = TypeManager.int32_type;
6376 args [j] = array_element_type;
6378 set = mb.GetArrayMethod (
6380 CallingConventions.HasThis | CallingConventions.Standard,
6381 TypeManager.void_type, args);
6384 for (int i = 0; i < top; i++){
6386 Expression e = null;
6388 if (array_data [i] is Expression)
6389 e = (Expression) array_data [i];
6393 // Basically we do this for string literals and
6394 // other non-literal expressions
6396 if (e is EnumConstant){
6397 e = ((EnumConstant) e).Child;
6400 if (e is StringConstant || e is DecimalConstant || !(e is Constant) ||
6401 num_automatic_initializers <= max_automatic_initializers) {
6402 Type etype = e.Type;
6404 ig.Emit (OpCodes.Dup);
6406 for (int idx = 0; idx < dims; idx++)
6407 IntConstant.EmitInt (ig, current_pos [idx]);
6410 // If we are dealing with a struct, get the
6411 // address of it, so we can store it.
6414 TypeManager.IsValueType (etype) &&
6415 (!TypeManager.IsBuiltinOrEnum (etype) ||
6416 etype == TypeManager.decimal_type)) {
6421 // Let new know that we are providing
6422 // the address where to store the results
6424 n.DisableTemporaryValueType ();
6427 ig.Emit (OpCodes.Ldelema, etype);
6434 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj);
6436 ig.Emit (OpCodes.Stobj, etype);
6440 ig.Emit (OpCodes.Call, set);
6448 for (int j = dims - 1; j >= 0; j--){
6450 if (current_pos [j] < (int) bounds [j])
6452 current_pos [j] = 0;
6457 void EmitArrayArguments (EmitContext ec)
6459 ILGenerator ig = ec.ig;
6461 foreach (Argument a in arguments) {
6462 Type atype = a.Type;
6465 if (atype == TypeManager.uint64_type)
6466 ig.Emit (OpCodes.Conv_Ovf_U4);
6467 else if (atype == TypeManager.int64_type)
6468 ig.Emit (OpCodes.Conv_Ovf_I4);
6472 public override void Emit (EmitContext ec)
6474 ILGenerator ig = ec.ig;
6476 EmitArrayArguments (ec);
6477 if (is_one_dimensional)
6478 ig.Emit (OpCodes.Newarr, array_element_type);
6480 if (is_builtin_type)
6481 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6483 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6486 if (initializers != null){
6488 // FIXME: Set this variable correctly.
6490 bool dynamic_initializers = true;
6492 // This will never be true for array types that cannot be statically
6493 // initialized. num_automatic_initializers will always be zero. See
6495 if (num_automatic_initializers > max_automatic_initializers)
6496 EmitStaticInitializers (ec);
6498 if (dynamic_initializers)
6499 EmitDynamicInitializers (ec);
6503 public object EncodeAsAttribute ()
6505 if (!is_one_dimensional){
6506 Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6510 if (array_data == null){
6511 Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6515 object [] ret = new object [array_data.Count];
6517 foreach (Expression e in array_data){
6520 if (e is NullLiteral)
6523 if (!Attribute.GetAttributeArgumentExpression (e, Location, array_element_type, out v))
6533 /// Represents the `this' construct
6535 public class This : Expression, IAssignMethod, IMemoryLocation, IVariable {
6538 VariableInfo variable_info;
6540 public This (Block block, Location loc)
6546 public This (Location loc)
6551 public VariableInfo VariableInfo {
6552 get { return variable_info; }
6555 public bool VerifyFixed ()
6557 return !TypeManager.IsValueType (Type);
6560 public bool ResolveBase (EmitContext ec)
6562 eclass = ExprClass.Variable;
6563 type = ec.ContainerType;
6566 Error (26, "Keyword `this' is not valid in a static property, static method, or static field initializer");
6570 if (block != null && block.Toplevel.ThisVariable != null)
6571 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6573 if (ec.CurrentAnonymousMethod != null)
6579 public override Expression DoResolve (EmitContext ec)
6581 if (!ResolveBase (ec))
6584 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) && !variable_info.IsAssigned (ec)) {
6585 Error (188, "The `this' object cannot be used before all of its fields are assigned to");
6586 variable_info.SetAssigned (ec);
6590 if (ec.IsFieldInitializer) {
6591 Error (27, "Keyword `this' is not available in the current context");
6598 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6600 if (!ResolveBase (ec))
6603 if (variable_info != null)
6604 variable_info.SetAssigned (ec);
6606 if (ec.TypeContainer is Class){
6607 Error (1604, "Cannot assign to 'this' because it is read-only");
6614 public void Emit (EmitContext ec, bool leave_copy)
6618 ec.ig.Emit (OpCodes.Dup);
6621 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
6623 ILGenerator ig = ec.ig;
6625 if (ec.TypeContainer is Struct){
6629 ec.ig.Emit (OpCodes.Dup);
6630 ig.Emit (OpCodes.Stobj, type);
6632 throw new Exception ("how did you get here");
6636 public override void Emit (EmitContext ec)
6638 ILGenerator ig = ec.ig;
6641 if (ec.TypeContainer is Struct)
6642 ig.Emit (OpCodes.Ldobj, type);
6645 public override int GetHashCode()
6647 return block.GetHashCode ();
6650 public override bool Equals (object obj)
6652 This t = obj as This;
6656 return block == t.block;
6659 public void AddressOf (EmitContext ec, AddressOp mode)
6664 // FIGURE OUT WHY LDARG_S does not work
6666 // consider: struct X { int val; int P { set { val = value; }}}
6668 // Yes, this looks very bad. Look at `NOTAS' for
6670 // ec.ig.Emit (OpCodes.Ldarga_S, (byte) 0);
6675 /// Represents the `__arglist' construct
6677 public class ArglistAccess : Expression
6679 public ArglistAccess (Location loc)
6684 public bool ResolveBase (EmitContext ec)
6686 eclass = ExprClass.Variable;
6687 type = TypeManager.runtime_argument_handle_type;
6691 public override Expression DoResolve (EmitContext ec)
6693 if (!ResolveBase (ec))
6696 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs) {
6697 Error (190, "The __arglist construct is valid only within " +
6698 "a variable argument method.");
6705 public override void Emit (EmitContext ec)
6707 ec.ig.Emit (OpCodes.Arglist);
6712 /// Represents the `__arglist (....)' construct
6714 public class Arglist : Expression
6716 public readonly Argument[] Arguments;
6718 public Arglist (Argument[] args, Location l)
6724 public Type[] ArgumentTypes {
6726 Type[] retval = new Type [Arguments.Length];
6727 for (int i = 0; i < Arguments.Length; i++)
6728 retval [i] = Arguments [i].Type;
6733 public override Expression DoResolve (EmitContext ec)
6735 eclass = ExprClass.Variable;
6736 type = TypeManager.runtime_argument_handle_type;
6738 foreach (Argument arg in Arguments) {
6739 if (!arg.Resolve (ec, loc))
6746 public override void Emit (EmitContext ec)
6748 foreach (Argument arg in Arguments)
6754 // This produces the value that renders an instance, used by the iterators code
6756 public class ProxyInstance : Expression, IMemoryLocation {
6757 public override Expression DoResolve (EmitContext ec)
6759 eclass = ExprClass.Variable;
6760 type = ec.ContainerType;
6764 public override void Emit (EmitContext ec)
6766 ec.ig.Emit (OpCodes.Ldarg_0);
6770 public void AddressOf (EmitContext ec, AddressOp mode)
6772 ec.ig.Emit (OpCodes.Ldarg_0);
6777 /// Implements the typeof operator
6779 public class TypeOf : Expression {
6780 public Expression QueriedType;
6781 protected Type typearg;
6783 public TypeOf (Expression queried_type, Location l)
6785 QueriedType = queried_type;
6789 public override Expression DoResolve (EmitContext ec)
6791 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6795 typearg = texpr.ResolveType (ec);
6797 if (typearg == TypeManager.void_type) {
6798 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6802 if (typearg.IsPointer && !ec.InUnsafe){
6807 type = TypeManager.type_type;
6808 // Even though what is returned is a type object, it's treated as a value by the compiler.
6809 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6810 eclass = ExprClass.Value;
6814 public override void Emit (EmitContext ec)
6816 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6817 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6820 public Type TypeArg {
6821 get { return typearg; }
6826 /// Implements the `typeof (void)' operator
6828 public class TypeOfVoid : TypeOf {
6829 public TypeOfVoid (Location l) : base (null, l)
6834 public override Expression DoResolve (EmitContext ec)
6836 type = TypeManager.type_type;
6837 typearg = TypeManager.void_type;
6838 // See description in TypeOf.
6839 eclass = ExprClass.Value;
6845 /// Implements the sizeof expression
6847 public class SizeOf : Expression {
6848 public Expression QueriedType;
6851 public SizeOf (Expression queried_type, Location l)
6853 this.QueriedType = queried_type;
6857 public override Expression DoResolve (EmitContext ec)
6859 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6863 type_queried = texpr.ResolveType (ec);
6865 int size_of = GetTypeSize (type_queried);
6867 return new IntConstant (size_of, loc);
6871 Report.Error (233, loc, "`{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf)",
6872 TypeManager.CSharpName (type_queried));
6876 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
6880 type = TypeManager.int32_type;
6881 eclass = ExprClass.Value;
6885 public override void Emit (EmitContext ec)
6887 int size = GetTypeSize (type_queried);
6890 ec.ig.Emit (OpCodes.Sizeof, type_queried);
6892 IntConstant.EmitInt (ec.ig, size);
6897 /// Implements the qualified-alias-member (::) expression.
6899 public class QualifiedAliasMember : Expression
6901 string alias, identifier;
6903 public QualifiedAliasMember (string alias, string identifier, Location l)
6906 this.identifier = identifier;
6910 public override FullNamedExpression ResolveAsTypeStep (EmitContext ec, bool silent)
6912 if (alias == "global")
6913 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
6915 int errors = Report.Errors;
6916 FullNamedExpression fne = ec.DeclSpace.NamespaceEntry.LookupAlias (alias);
6918 if (errors == Report.Errors)
6919 Report.Error (432, loc, "Alias `{0}' not found", alias);
6922 if (fne.eclass != ExprClass.Namespace) {
6924 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6927 return new MemberAccess (fne, identifier, loc).ResolveAsTypeStep (ec, silent);
6930 public override Expression DoResolve (EmitContext ec)
6932 FullNamedExpression fne;
6933 if (alias == "global") {
6934 fne = RootNamespace.Global;
6936 int errors = Report.Errors;
6937 fne = ec.DeclSpace.NamespaceEntry.LookupAlias (alias);
6939 if (errors == Report.Errors)
6940 Report.Error (432, loc, "Alias `{0}' not found", alias);
6945 Expression retval = new MemberAccess (fne, identifier, loc).DoResolve (ec);
6949 if (!(retval is FullNamedExpression)) {
6950 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
6954 // We defer this check till the end to match the behaviour of CSC
6955 if (fne.eclass != ExprClass.Namespace) {
6956 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6962 public override void Emit (EmitContext ec)
6964 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
6968 public override string ToString ()
6970 return alias + "::" + identifier;
6973 public override string GetSignatureForError ()
6980 /// Implements the member access expression
6982 public class MemberAccess : Expression {
6983 public readonly string Identifier;
6986 // TODO: Location can be removed
6987 public MemberAccess (Expression expr, string id, Location l)
6991 loc = expr.Location;
6994 public Expression Expr {
6995 get { return expr; }
6998 // TODO: this method has very poor performace for Enum fields and
6999 // probably for other constants as well
7000 Expression DoResolve (EmitContext ec, Expression right_side)
7003 throw new Exception ();
7006 // Resolve the expression with flow analysis turned off, we'll do the definite
7007 // assignment checks later. This is because we don't know yet what the expression
7008 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7009 // definite assignment check on the actual field and not on the whole struct.
7012 SimpleName original = expr as SimpleName;
7013 Expression new_expr = expr.Resolve (ec,
7014 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7015 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7017 if (new_expr == null)
7020 if (new_expr is Namespace) {
7021 Namespace ns = (Namespace) new_expr;
7022 FullNamedExpression retval = ns.Lookup (ec.DeclSpace, Identifier, loc);
7024 Report.Error (234, loc, "The type or namespace name `{0}' does not exist in the namespace `{1}'. Are you missing an assembly reference?",
7025 Identifier, ns.FullName);
7029 Type expr_type = new_expr.Type;
7030 if (expr_type.IsPointer){
7031 Error (23, "The `.' operator can not be applied to pointer operands (" +
7032 TypeManager.CSharpName (expr_type) + ")");
7036 Expression member_lookup;
7037 member_lookup = MemberLookupFinal (ec, expr_type, expr_type, Identifier, loc);
7038 if (member_lookup == null)
7041 if (member_lookup is TypeExpr) {
7042 if (!(new_expr is TypeExpr) &&
7043 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
7044 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7045 Identifier, member_lookup.GetSignatureForError ());
7049 return member_lookup;
7052 MemberExpr me = (MemberExpr) member_lookup;
7053 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
7054 if (member_lookup == null)
7057 if (original != null && !TypeManager.IsValueType (expr_type)) {
7058 me = member_lookup as MemberExpr;
7059 if (me != null && me.IsInstance) {
7060 LocalVariableReference var = new_expr as LocalVariableReference;
7061 if (var != null && !var.VerifyAssigned (ec))
7066 // The following DoResolve/DoResolveLValue will do the definite assignment
7069 if (right_side != null)
7070 return member_lookup.DoResolveLValue (ec, right_side);
7072 return member_lookup.DoResolve (ec);
7075 public override Expression DoResolve (EmitContext ec)
7077 return DoResolve (ec, null);
7080 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7082 return DoResolve (ec, right_side);
7085 public override FullNamedExpression ResolveAsTypeStep (EmitContext ec, bool silent)
7087 return ResolveNamespaceOrType (ec, silent);
7090 public FullNamedExpression ResolveNamespaceOrType (EmitContext ec, bool silent)
7092 FullNamedExpression new_expr = expr.ResolveAsTypeStep (ec, silent);
7094 if (new_expr == null)
7097 if (new_expr is Namespace) {
7098 Namespace ns = (Namespace) new_expr;
7099 FullNamedExpression retval = ns.Lookup (ec.DeclSpace, Identifier, loc);
7100 if (!silent && retval == null)
7101 Report.Error (234, loc, "The type or namespace name `{0}' does not exist in the namespace `{1}'. Are you missing an assembly reference?",
7102 Identifier, ns.FullName);
7106 Type expr_type = new_expr.Type;
7108 if (expr_type.IsPointer){
7109 Error (23, "The `.' operator can not be applied to pointer operands (" +
7110 TypeManager.CSharpName (expr_type) + ")");
7114 Expression member_lookup = MemberLookup (ec, expr_type, expr_type, Identifier, loc);
7115 if (member_lookup == null) {
7116 int errors = Report.Errors;
7117 MemberLookupFailed (ec, expr_type, expr_type, Identifier, null, false, loc);
7119 if (!silent && errors == Report.Errors) {
7120 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7121 Identifier, new_expr.GetSignatureForError ());
7126 if (!(member_lookup is TypeExpr)) {
7127 new_expr.Error_UnexpectedKind (ec, "type", loc);
7131 member_lookup = member_lookup.Resolve (ec, ResolveFlags.Type);
7132 return (member_lookup as TypeExpr);
7135 public override void Emit (EmitContext ec)
7137 throw new Exception ("Should not happen");
7140 public override string ToString ()
7142 return expr + "." + Identifier;
7145 public override string GetSignatureForError ()
7147 return expr.GetSignatureForError () + "." + Identifier;
7152 /// Implements checked expressions
7154 public class CheckedExpr : Expression {
7156 public Expression Expr;
7158 public CheckedExpr (Expression e, Location l)
7164 public override Expression DoResolve (EmitContext ec)
7166 bool last_check = ec.CheckState;
7167 bool last_const_check = ec.ConstantCheckState;
7169 ec.CheckState = true;
7170 ec.ConstantCheckState = true;
7171 Expr = Expr.Resolve (ec);
7172 ec.CheckState = last_check;
7173 ec.ConstantCheckState = last_const_check;
7178 if (Expr is Constant)
7181 eclass = Expr.eclass;
7186 public override void Emit (EmitContext ec)
7188 bool last_check = ec.CheckState;
7189 bool last_const_check = ec.ConstantCheckState;
7191 ec.CheckState = true;
7192 ec.ConstantCheckState = true;
7194 ec.CheckState = last_check;
7195 ec.ConstantCheckState = last_const_check;
7201 /// Implements the unchecked expression
7203 public class UnCheckedExpr : Expression {
7205 public Expression Expr;
7207 public UnCheckedExpr (Expression e, Location l)
7213 public override Expression DoResolve (EmitContext ec)
7215 bool last_check = ec.CheckState;
7216 bool last_const_check = ec.ConstantCheckState;
7218 ec.CheckState = false;
7219 ec.ConstantCheckState = false;
7220 Expr = Expr.Resolve (ec);
7221 ec.CheckState = last_check;
7222 ec.ConstantCheckState = last_const_check;
7227 if (Expr is Constant)
7230 eclass = Expr.eclass;
7235 public override void Emit (EmitContext ec)
7237 bool last_check = ec.CheckState;
7238 bool last_const_check = ec.ConstantCheckState;
7240 ec.CheckState = false;
7241 ec.ConstantCheckState = false;
7243 ec.CheckState = last_check;
7244 ec.ConstantCheckState = last_const_check;
7250 /// An Element Access expression.
7252 /// During semantic analysis these are transformed into
7253 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7255 public class ElementAccess : Expression {
7256 public ArrayList Arguments;
7257 public Expression Expr;
7259 public ElementAccess (Expression e, ArrayList e_list)
7268 Arguments = new ArrayList ();
7269 foreach (Expression tmp in e_list)
7270 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7274 bool CommonResolve (EmitContext ec)
7276 Expr = Expr.Resolve (ec);
7281 if (Arguments == null)
7284 foreach (Argument a in Arguments){
7285 if (!a.Resolve (ec, loc))
7292 Expression MakePointerAccess (EmitContext ec, Type t)
7294 if (t == TypeManager.void_ptr_type){
7295 Error (242, "The array index operation is not valid on void pointers");
7298 if (Arguments.Count != 1){
7299 Error (196, "A pointer must be indexed by only one value");
7304 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7307 return new Indirection (p, loc).Resolve (ec);
7310 public override Expression DoResolve (EmitContext ec)
7312 if (!CommonResolve (ec))
7316 // We perform some simple tests, and then to "split" the emit and store
7317 // code we create an instance of a different class, and return that.
7319 // I am experimenting with this pattern.
7323 if (t == TypeManager.array_type){
7324 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7329 return (new ArrayAccess (this, loc)).Resolve (ec);
7331 return MakePointerAccess (ec, Expr.Type);
7333 FieldExpr fe = Expr as FieldExpr;
7335 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7337 return MakePointerAccess (ec, ff.ElementType);
7340 return (new IndexerAccess (this, loc)).Resolve (ec);
7343 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7345 if (!CommonResolve (ec))
7350 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7353 return MakePointerAccess (ec, Expr.Type);
7355 FieldExpr fe = Expr as FieldExpr;
7357 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7359 if (!(fe.InstanceExpression is LocalVariableReference) &&
7360 !(fe.InstanceExpression is This)) {
7361 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7364 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7365 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7368 return MakePointerAccess (ec, ff.ElementType);
7371 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7374 public override void Emit (EmitContext ec)
7376 throw new Exception ("Should never be reached");
7381 /// Implements array access
7383 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7385 // Points to our "data" repository
7389 LocalTemporary temp;
7392 public ArrayAccess (ElementAccess ea_data, Location l)
7395 eclass = ExprClass.Variable;
7399 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7401 return DoResolve (ec);
7404 public override Expression DoResolve (EmitContext ec)
7407 ExprClass eclass = ea.Expr.eclass;
7409 // As long as the type is valid
7410 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7411 eclass == ExprClass.Value)) {
7412 ea.Expr.Error_UnexpectedKind ("variable or value");
7417 Type t = ea.Expr.Type;
7418 if (t.GetArrayRank () != ea.Arguments.Count){
7419 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7420 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7424 type = TypeManager.GetElementType (t);
7425 if (type.IsPointer && !ec.InUnsafe){
7426 UnsafeError (ea.Location);
7430 foreach (Argument a in ea.Arguments){
7431 Type argtype = a.Type;
7433 if (argtype == TypeManager.int32_type ||
7434 argtype == TypeManager.uint32_type ||
7435 argtype == TypeManager.int64_type ||
7436 argtype == TypeManager.uint64_type) {
7437 Constant c = a.Expr as Constant;
7438 if (c != null && c.IsNegative) {
7439 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7445 // Mhm. This is strage, because the Argument.Type is not the same as
7446 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7448 // Wonder if I will run into trouble for this.
7450 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7455 eclass = ExprClass.Variable;
7461 /// Emits the right opcode to load an object of Type `t'
7462 /// from an array of T
7464 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7466 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7467 ig.Emit (OpCodes.Ldelem_U1);
7468 else if (type == TypeManager.sbyte_type)
7469 ig.Emit (OpCodes.Ldelem_I1);
7470 else if (type == TypeManager.short_type)
7471 ig.Emit (OpCodes.Ldelem_I2);
7472 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7473 ig.Emit (OpCodes.Ldelem_U2);
7474 else if (type == TypeManager.int32_type)
7475 ig.Emit (OpCodes.Ldelem_I4);
7476 else if (type == TypeManager.uint32_type)
7477 ig.Emit (OpCodes.Ldelem_U4);
7478 else if (type == TypeManager.uint64_type)
7479 ig.Emit (OpCodes.Ldelem_I8);
7480 else if (type == TypeManager.int64_type)
7481 ig.Emit (OpCodes.Ldelem_I8);
7482 else if (type == TypeManager.float_type)
7483 ig.Emit (OpCodes.Ldelem_R4);
7484 else if (type == TypeManager.double_type)
7485 ig.Emit (OpCodes.Ldelem_R8);
7486 else if (type == TypeManager.intptr_type)
7487 ig.Emit (OpCodes.Ldelem_I);
7488 else if (TypeManager.IsEnumType (type)){
7489 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7490 } else if (type.IsValueType){
7491 ig.Emit (OpCodes.Ldelema, type);
7492 ig.Emit (OpCodes.Ldobj, type);
7493 } else if (type.IsPointer)
7494 ig.Emit (OpCodes.Ldelem_I);
7496 ig.Emit (OpCodes.Ldelem_Ref);
7500 /// Returns the right opcode to store an object of Type `t'
7501 /// from an array of T.
7503 static public OpCode GetStoreOpcode (Type t, out bool is_stobj)
7505 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7507 t = TypeManager.TypeToCoreType (t);
7508 if (TypeManager.IsEnumType (t))
7509 t = TypeManager.EnumToUnderlying (t);
7510 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7511 t == TypeManager.bool_type)
7512 return OpCodes.Stelem_I1;
7513 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7514 t == TypeManager.char_type)
7515 return OpCodes.Stelem_I2;
7516 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7517 return OpCodes.Stelem_I4;
7518 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7519 return OpCodes.Stelem_I8;
7520 else if (t == TypeManager.float_type)
7521 return OpCodes.Stelem_R4;
7522 else if (t == TypeManager.double_type)
7523 return OpCodes.Stelem_R8;
7524 else if (t == TypeManager.intptr_type) {
7526 return OpCodes.Stobj;
7527 } else if (t.IsValueType) {
7529 return OpCodes.Stobj;
7530 } else if (t.IsPointer)
7531 return OpCodes.Stelem_I;
7533 return OpCodes.Stelem_Ref;
7536 MethodInfo FetchGetMethod ()
7538 ModuleBuilder mb = CodeGen.Module.Builder;
7539 int arg_count = ea.Arguments.Count;
7540 Type [] args = new Type [arg_count];
7543 for (int i = 0; i < arg_count; i++){
7544 //args [i++] = a.Type;
7545 args [i] = TypeManager.int32_type;
7548 get = mb.GetArrayMethod (
7549 ea.Expr.Type, "Get",
7550 CallingConventions.HasThis |
7551 CallingConventions.Standard,
7557 MethodInfo FetchAddressMethod ()
7559 ModuleBuilder mb = CodeGen.Module.Builder;
7560 int arg_count = ea.Arguments.Count;
7561 Type [] args = new Type [arg_count];
7565 ret_type = TypeManager.GetReferenceType (type);
7567 for (int i = 0; i < arg_count; i++){
7568 //args [i++] = a.Type;
7569 args [i] = TypeManager.int32_type;
7572 address = mb.GetArrayMethod (
7573 ea.Expr.Type, "Address",
7574 CallingConventions.HasThis |
7575 CallingConventions.Standard,
7582 // Load the array arguments into the stack.
7584 // If we have been requested to cache the values (cached_locations array
7585 // initialized), then load the arguments the first time and store them
7586 // in locals. otherwise load from local variables.
7588 void LoadArrayAndArguments (EmitContext ec)
7590 ILGenerator ig = ec.ig;
7593 foreach (Argument a in ea.Arguments){
7594 Type argtype = a.Expr.Type;
7598 if (argtype == TypeManager.int64_type)
7599 ig.Emit (OpCodes.Conv_Ovf_I);
7600 else if (argtype == TypeManager.uint64_type)
7601 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7605 public void Emit (EmitContext ec, bool leave_copy)
7607 int rank = ea.Expr.Type.GetArrayRank ();
7608 ILGenerator ig = ec.ig;
7611 LoadArrayAndArguments (ec);
7614 EmitLoadOpcode (ig, type);
7618 method = FetchGetMethod ();
7619 ig.Emit (OpCodes.Call, method);
7622 LoadFromPtr (ec.ig, this.type);
7625 ec.ig.Emit (OpCodes.Dup);
7626 temp = new LocalTemporary (ec, this.type);
7631 public override void Emit (EmitContext ec)
7636 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7638 int rank = ea.Expr.Type.GetArrayRank ();
7639 ILGenerator ig = ec.ig;
7640 Type t = source.Type;
7641 prepared = prepare_for_load;
7643 if (prepare_for_load) {
7644 AddressOf (ec, AddressOp.LoadStore);
7645 ec.ig.Emit (OpCodes.Dup);
7648 ec.ig.Emit (OpCodes.Dup);
7649 temp = new LocalTemporary (ec, this.type);
7652 StoreFromPtr (ec.ig, t);
7660 LoadArrayAndArguments (ec);
7664 OpCode op = GetStoreOpcode (t, out is_stobj);
7666 // The stobj opcode used by value types will need
7667 // an address on the stack, not really an array/array
7671 ig.Emit (OpCodes.Ldelema, t);
7675 ec.ig.Emit (OpCodes.Dup);
7676 temp = new LocalTemporary (ec, this.type);
7681 ig.Emit (OpCodes.Stobj, t);
7685 ModuleBuilder mb = CodeGen.Module.Builder;
7686 int arg_count = ea.Arguments.Count;
7687 Type [] args = new Type [arg_count + 1];
7692 ec.ig.Emit (OpCodes.Dup);
7693 temp = new LocalTemporary (ec, this.type);
7697 for (int i = 0; i < arg_count; i++){
7698 //args [i++] = a.Type;
7699 args [i] = TypeManager.int32_type;
7702 args [arg_count] = type;
7704 set = mb.GetArrayMethod (
7705 ea.Expr.Type, "Set",
7706 CallingConventions.HasThis |
7707 CallingConventions.Standard,
7708 TypeManager.void_type, args);
7710 ig.Emit (OpCodes.Call, set);
7717 public void AddressOf (EmitContext ec, AddressOp mode)
7719 int rank = ea.Expr.Type.GetArrayRank ();
7720 ILGenerator ig = ec.ig;
7722 LoadArrayAndArguments (ec);
7725 ig.Emit (OpCodes.Ldelema, type);
7727 MethodInfo address = FetchAddressMethod ();
7728 ig.Emit (OpCodes.Call, address);
7732 public void EmitGetLength (EmitContext ec, int dim)
7734 int rank = ea.Expr.Type.GetArrayRank ();
7735 ILGenerator ig = ec.ig;
7739 ig.Emit (OpCodes.Ldlen);
7740 ig.Emit (OpCodes.Conv_I4);
7742 IntLiteral.EmitInt (ig, dim);
7743 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7749 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7750 public readonly ArrayList Properties;
7751 static Indexers empty;
7753 public struct Indexer {
7754 public readonly PropertyInfo PropertyInfo;
7755 public readonly MethodInfo Getter, Setter;
7757 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7759 this.PropertyInfo = property_info;
7767 empty = new Indexers (null);
7770 Indexers (ArrayList array)
7775 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
7780 foreach (PropertyInfo property in mi){
7781 MethodInfo get, set;
7783 get = property.GetGetMethod (true);
7784 set = property.GetSetMethod (true);
7785 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
7787 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
7789 if (get != null || set != null) {
7791 ix = new Indexers (new ArrayList ());
7792 ix.Properties.Add (new Indexer (property, get, set));
7797 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
7799 string p_name = TypeManager.IndexerPropertyName (lookup_type);
7801 return TypeManager.MemberLookup (
7802 caller_type, caller_type, lookup_type, MemberTypes.Property,
7803 BindingFlags.Public | BindingFlags.Instance |
7804 BindingFlags.DeclaredOnly, p_name, null);
7807 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type, Location loc)
7809 Indexers ix = empty;
7811 Type copy = lookup_type;
7812 while (copy != TypeManager.object_type && copy != null){
7813 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
7814 copy = copy.BaseType;
7817 if (lookup_type.IsInterface) {
7818 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
7819 if (ifaces != null) {
7820 foreach (Type itype in ifaces)
7821 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
7830 /// Expressions that represent an indexer call.
7832 public class IndexerAccess : Expression, IAssignMethod {
7834 // Points to our "data" repository
7836 MethodInfo get, set;
7837 ArrayList set_arguments;
7838 bool is_base_indexer;
7840 protected Type indexer_type;
7841 protected Type current_type;
7842 protected Expression instance_expr;
7843 protected ArrayList arguments;
7845 public IndexerAccess (ElementAccess ea, Location loc)
7846 : this (ea.Expr, false, loc)
7848 this.arguments = ea.Arguments;
7851 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
7854 this.instance_expr = instance_expr;
7855 this.is_base_indexer = is_base_indexer;
7856 this.eclass = ExprClass.Value;
7860 protected virtual bool CommonResolve (EmitContext ec)
7862 indexer_type = instance_expr.Type;
7863 current_type = ec.ContainerType;
7868 public override Expression DoResolve (EmitContext ec)
7870 ArrayList AllGetters = new ArrayList();
7871 if (!CommonResolve (ec))
7875 // Step 1: Query for all `Item' *properties*. Notice
7876 // that the actual methods are pointed from here.
7878 // This is a group of properties, piles of them.
7880 bool found_any = false, found_any_getters = false;
7881 Type lookup_type = indexer_type;
7883 Indexers ilist = Indexers.GetIndexersForType (current_type, lookup_type, loc);
7884 if (ilist.Properties != null) {
7886 foreach (Indexers.Indexer ix in ilist.Properties) {
7887 if (ix.Getter != null)
7888 AllGetters.Add (ix.Getter);
7892 if (AllGetters.Count > 0) {
7893 found_any_getters = true;
7894 get = (MethodInfo) Invocation.OverloadResolve (
7895 ec, new MethodGroupExpr (AllGetters, loc),
7896 arguments, false, loc);
7900 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7901 TypeManager.CSharpName (indexer_type));
7905 if (!found_any_getters) {
7906 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
7912 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7917 // Only base will allow this invocation to happen.
7919 if (get.IsAbstract && this is BaseIndexerAccess){
7920 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
7924 type = get.ReturnType;
7925 if (type.IsPointer && !ec.InUnsafe){
7930 instance_expr.CheckMarshallByRefAccess (ec.ContainerType);
7932 eclass = ExprClass.IndexerAccess;
7936 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7938 ArrayList AllSetters = new ArrayList();
7939 if (!CommonResolve (ec))
7942 bool found_any = false, found_any_setters = false;
7944 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type, loc);
7945 if (ilist.Properties != null) {
7947 foreach (Indexers.Indexer ix in ilist.Properties) {
7948 if (ix.Setter != null)
7949 AllSetters.Add (ix.Setter);
7952 if (AllSetters.Count > 0) {
7953 found_any_setters = true;
7954 set_arguments = (ArrayList) arguments.Clone ();
7955 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
7956 set = (MethodInfo) Invocation.OverloadResolve (
7957 ec, new MethodGroupExpr (AllSetters, loc),
7958 set_arguments, false, loc);
7962 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7963 TypeManager.CSharpName (indexer_type));
7967 if (!found_any_setters) {
7968 Error (154, "indexer can not be used in this context, because " +
7969 "it lacks a `set' accessor");
7974 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7979 // Only base will allow this invocation to happen.
7981 if (set.IsAbstract && this is BaseIndexerAccess){
7982 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
7987 // Now look for the actual match in the list of indexers to set our "return" type
7989 type = TypeManager.void_type; // default value
7990 foreach (Indexers.Indexer ix in ilist.Properties){
7991 if (ix.Setter == set){
7992 type = ix.PropertyInfo.PropertyType;
7997 instance_expr.CheckMarshallByRefAccess (ec.ContainerType);
7999 eclass = ExprClass.IndexerAccess;
8003 bool prepared = false;
8004 LocalTemporary temp;
8006 public void Emit (EmitContext ec, bool leave_copy)
8008 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
8010 ec.ig.Emit (OpCodes.Dup);
8011 temp = new LocalTemporary (ec, Type);
8017 // source is ignored, because we already have a copy of it from the
8018 // LValue resolution and we have already constructed a pre-cached
8019 // version of the arguments (ea.set_arguments);
8021 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8023 prepared = prepare_for_load;
8024 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
8029 ec.ig.Emit (OpCodes.Dup);
8030 temp = new LocalTemporary (ec, Type);
8033 } else if (leave_copy) {
8034 temp = new LocalTemporary (ec, Type);
8040 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
8047 public override void Emit (EmitContext ec)
8054 /// The base operator for method names
8056 public class BaseAccess : Expression {
8059 public BaseAccess (string member, Location l)
8061 this.member = member;
8065 public override Expression DoResolve (EmitContext ec)
8067 Expression c = CommonResolve (ec);
8073 // MethodGroups use this opportunity to flag an error on lacking ()
8075 if (!(c is MethodGroupExpr))
8076 return c.Resolve (ec);
8080 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8082 Expression c = CommonResolve (ec);
8088 // MethodGroups use this opportunity to flag an error on lacking ()
8090 if (! (c is MethodGroupExpr))
8091 return c.DoResolveLValue (ec, right_side);
8096 Expression CommonResolve (EmitContext ec)
8098 Expression member_lookup;
8099 Type current_type = ec.ContainerType;
8100 Type base_type = current_type.BaseType;
8103 Error (1511, "Keyword `base' is not available in a static method");
8107 if (ec.IsFieldInitializer){
8108 Error (1512, "Keyword `base' is not available in the current context");
8112 member_lookup = MemberLookup (ec, ec.ContainerType, null, base_type, member,
8113 AllMemberTypes, AllBindingFlags, loc);
8114 if (member_lookup == null) {
8115 MemberLookupFailed (ec, base_type, base_type, member, null, true, loc);
8122 left = new TypeExpression (base_type, loc);
8124 left = ec.GetThis (loc);
8126 MemberExpr me = (MemberExpr) member_lookup;
8128 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8130 if (e is PropertyExpr) {
8131 PropertyExpr pe = (PropertyExpr) e;
8136 if (e is MethodGroupExpr)
8137 ((MethodGroupExpr) e).IsBase = true;
8142 public override void Emit (EmitContext ec)
8144 throw new Exception ("Should never be called");
8149 /// The base indexer operator
8151 public class BaseIndexerAccess : IndexerAccess {
8152 public BaseIndexerAccess (ArrayList args, Location loc)
8153 : base (null, true, loc)
8155 arguments = new ArrayList ();
8156 foreach (Expression tmp in args)
8157 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8160 protected override bool CommonResolve (EmitContext ec)
8162 instance_expr = ec.GetThis (loc);
8164 current_type = ec.ContainerType.BaseType;
8165 indexer_type = current_type;
8167 foreach (Argument a in arguments){
8168 if (!a.Resolve (ec, loc))
8177 /// This class exists solely to pass the Type around and to be a dummy
8178 /// that can be passed to the conversion functions (this is used by
8179 /// foreach implementation to typecast the object return value from
8180 /// get_Current into the proper type. All code has been generated and
8181 /// we only care about the side effect conversions to be performed
8183 /// This is also now used as a placeholder where a no-action expression
8184 /// is needed (the `New' class).
8186 public class EmptyExpression : Expression {
8187 public static readonly EmptyExpression Null = new EmptyExpression ();
8189 static EmptyExpression temp = new EmptyExpression ();
8190 public static EmptyExpression Grab ()
8193 throw new InternalErrorException ("Nested Grab");
8194 EmptyExpression retval = temp;
8199 public static void Release (EmptyExpression e)
8202 throw new InternalErrorException ("Already released");
8206 // TODO: should be protected
8207 public EmptyExpression ()
8209 type = TypeManager.object_type;
8210 eclass = ExprClass.Value;
8211 loc = Location.Null;
8214 public EmptyExpression (Type t)
8217 eclass = ExprClass.Value;
8218 loc = Location.Null;
8221 public override Expression DoResolve (EmitContext ec)
8226 public override void Emit (EmitContext ec)
8228 // nothing, as we only exist to not do anything.
8232 // This is just because we might want to reuse this bad boy
8233 // instead of creating gazillions of EmptyExpressions.
8234 // (CanImplicitConversion uses it)
8236 public void SetType (Type t)
8242 public class UserCast : Expression {
8246 public UserCast (MethodInfo method, Expression source, Location l)
8248 this.method = method;
8249 this.source = source;
8250 type = method.ReturnType;
8251 eclass = ExprClass.Value;
8255 public Expression Source {
8261 public override Expression DoResolve (EmitContext ec)
8264 // We are born fully resolved
8269 public override void Emit (EmitContext ec)
8271 ILGenerator ig = ec.ig;
8275 if (method is MethodInfo)
8276 ig.Emit (OpCodes.Call, (MethodInfo) method);
8278 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8284 // This class is used to "construct" the type during a typecast
8285 // operation. Since the Type.GetType class in .NET can parse
8286 // the type specification, we just use this to construct the type
8287 // one bit at a time.
8289 public class ComposedCast : TypeExpr {
8293 public ComposedCast (Expression left, string dim)
8294 : this (left, dim, left.Location)
8298 public ComposedCast (Expression left, string dim, Location l)
8305 public override TypeExpr DoResolveAsTypeStep (EmitContext ec)
8307 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8311 bool old = ec.TestObsoleteMethodUsage;
8312 ec.TestObsoleteMethodUsage = false;
8313 Type ltype = lexpr.ResolveType (ec);
8314 ec.TestObsoleteMethodUsage = old;
8316 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8317 Report.Error (1547, Location,
8318 "Keyword 'void' cannot be used in this context");
8322 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc)) {
8326 type = TypeManager.GetConstructedType (ltype, dim);
8328 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8331 if (!ec.InUnsafe && type.IsPointer){
8336 if (type.IsArray && (type.GetElementType () == TypeManager.arg_iterator_type ||
8337 type.GetElementType () == TypeManager.typed_reference_type)) {
8338 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (type.GetElementType ()));
8342 eclass = ExprClass.Type;
8346 public override string Name {
8352 public override string FullName {
8354 return type.FullName;
8359 public class FixedBufferPtr : Expression {
8362 public FixedBufferPtr (Expression array, Type array_type, Location l)
8367 type = TypeManager.GetPointerType (array_type);
8368 eclass = ExprClass.Value;
8371 public override void Emit(EmitContext ec)
8376 public override Expression DoResolve (EmitContext ec)
8379 // We are born fully resolved
8387 // This class is used to represent the address of an array, used
8388 // only by the Fixed statement, this generates "&a [0]" construct
8389 // for fixed (char *pa = a)
8391 public class ArrayPtr : FixedBufferPtr {
8394 public ArrayPtr (Expression array, Type array_type, Location l):
8395 base (array, array_type, l)
8397 this.array_type = array_type;
8400 public override void Emit (EmitContext ec)
8404 ILGenerator ig = ec.ig;
8405 IntLiteral.EmitInt (ig, 0);
8406 ig.Emit (OpCodes.Ldelema, array_type);
8411 // Used by the fixed statement
8413 public class StringPtr : Expression {
8416 public StringPtr (LocalBuilder b, Location l)
8419 eclass = ExprClass.Value;
8420 type = TypeManager.char_ptr_type;
8424 public override Expression DoResolve (EmitContext ec)
8426 // This should never be invoked, we are born in fully
8427 // initialized state.
8432 public override void Emit (EmitContext ec)
8434 ILGenerator ig = ec.ig;
8436 ig.Emit (OpCodes.Ldloc, b);
8437 ig.Emit (OpCodes.Conv_I);
8438 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8439 ig.Emit (OpCodes.Add);
8444 // Implements the `stackalloc' keyword
8446 public class StackAlloc : Expression {
8451 public StackAlloc (Expression type, Expression count, Location l)
8458 public override Expression DoResolve (EmitContext ec)
8460 count = count.Resolve (ec);
8464 if (count.Type != TypeManager.int32_type){
8465 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8470 Constant c = count as Constant;
8471 if (c != null && c.IsNegative) {
8472 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8476 if (ec.InCatch || ec.InFinally) {
8477 Error (255, "Cannot use stackalloc in finally or catch");
8481 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8485 otype = texpr.ResolveType (ec);
8487 if (!TypeManager.VerifyUnManaged (otype, loc))
8490 type = TypeManager.GetPointerType (otype);
8491 eclass = ExprClass.Value;
8496 public override void Emit (EmitContext ec)
8498 int size = GetTypeSize (otype);
8499 ILGenerator ig = ec.ig;
8502 ig.Emit (OpCodes.Sizeof, otype);
8504 IntConstant.EmitInt (ig, size);
8506 ig.Emit (OpCodes.Mul);
8507 ig.Emit (OpCodes.Localloc);