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.ContainerType, 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:
616 if (ec.CheckState && type != TypeManager.float_type && type != TypeManager.double_type) {
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 (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 (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.ContainerType, 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.DeclContainer, "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);
994 public override void Emit (EmitContext ec)
997 // We use recurse to allow ourselfs to be the source
998 // of an assignment. This little hack prevents us from
999 // having to allocate another expression
1002 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1004 LoadOneAndEmitOp (ec, expr.Type);
1006 ec.ig.Emit (OpCodes.Call, method.Method);
1011 EmitCode (ec, true);
1014 public override void EmitStatement (EmitContext ec)
1016 EmitCode (ec, false);
1021 /// Base class for the `Is' and `As' classes.
1025 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1028 public abstract class Probe : Expression {
1029 public Expression ProbeType;
1030 protected Expression expr;
1031 protected Type probe_type;
1033 public Probe (Expression expr, Expression probe_type, Location l)
1035 ProbeType = probe_type;
1040 public Expression Expr {
1046 public override Expression DoResolve (EmitContext ec)
1048 TypeExpr texpr = ProbeType.ResolveAsTypeTerminal (ec, false);
1052 probe_type = texpr.Type;
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);
1257 public override bool GetAttributableValue (Type valueType, out object value)
1259 return expr.GetAttributableValue (valueType, out value);
1264 /// This represents a typecast in the source language.
1266 /// FIXME: Cast expressions have an unusual set of parsing
1267 /// rules, we need to figure those out.
1269 public class Cast : Expression {
1270 Expression target_type;
1273 public Cast (Expression cast_type, Expression expr)
1274 : this (cast_type, expr, cast_type.Location)
1278 public Cast (Expression cast_type, Expression expr, Location loc)
1280 this.target_type = cast_type;
1284 if (target_type == TypeManager.system_void_expr)
1285 Report.Error (1547, loc, "Keyword `void' cannot be used in this context");
1288 public Expression TargetType {
1289 get { return target_type; }
1292 public Expression Expr {
1293 get { return expr; }
1294 set { expr = value; }
1297 public override Expression DoResolve (EmitContext ec)
1299 expr = expr.Resolve (ec);
1303 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1309 if (type.IsAbstract && type.IsSealed) {
1310 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1314 eclass = ExprClass.Value;
1316 Constant c = expr as Constant;
1319 c = c.TryReduce (ec, type, loc);
1323 catch (OverflowException) {
1328 if (type.IsPointer && !ec.InUnsafe) {
1332 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1336 public override void Emit (EmitContext ec)
1338 throw new Exception ("Should not happen");
1343 /// Binary operators
1345 public class Binary : Expression {
1346 public enum Operator : byte {
1347 Multiply, Division, Modulus,
1348 Addition, Subtraction,
1349 LeftShift, RightShift,
1350 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1351 Equality, Inequality,
1361 Expression left, right;
1363 // This must be kept in sync with Operator!!!
1364 public static readonly string [] oper_names;
1368 oper_names = new string [(int) Operator.TOP];
1370 oper_names [(int) Operator.Multiply] = "op_Multiply";
1371 oper_names [(int) Operator.Division] = "op_Division";
1372 oper_names [(int) Operator.Modulus] = "op_Modulus";
1373 oper_names [(int) Operator.Addition] = "op_Addition";
1374 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1375 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1376 oper_names [(int) Operator.RightShift] = "op_RightShift";
1377 oper_names [(int) Operator.LessThan] = "op_LessThan";
1378 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1379 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1380 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1381 oper_names [(int) Operator.Equality] = "op_Equality";
1382 oper_names [(int) Operator.Inequality] = "op_Inequality";
1383 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1384 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1385 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1386 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1387 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1390 public Binary (Operator oper, Expression left, Expression right)
1395 this.loc = left.Location;
1398 public Operator Oper {
1407 public Expression Left {
1416 public Expression Right {
1427 /// Returns a stringified representation of the Operator
1429 public static string OperName (Operator oper)
1432 case Operator.Multiply:
1434 case Operator.Division:
1436 case Operator.Modulus:
1438 case Operator.Addition:
1440 case Operator.Subtraction:
1442 case Operator.LeftShift:
1444 case Operator.RightShift:
1446 case Operator.LessThan:
1448 case Operator.GreaterThan:
1450 case Operator.LessThanOrEqual:
1452 case Operator.GreaterThanOrEqual:
1454 case Operator.Equality:
1456 case Operator.Inequality:
1458 case Operator.BitwiseAnd:
1460 case Operator.BitwiseOr:
1462 case Operator.ExclusiveOr:
1464 case Operator.LogicalOr:
1466 case Operator.LogicalAnd:
1470 return oper.ToString ();
1473 public override string ToString ()
1475 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1476 right.ToString () + ")";
1479 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1481 if (expr.Type == target_type)
1484 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1487 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1490 34, loc, "Operator `" + OperName (oper)
1491 + "' is ambiguous on operands of type `"
1492 + TypeManager.CSharpName (l) + "' "
1493 + "and `" + TypeManager.CSharpName (r)
1497 static bool IsOfType (EmitContext ec, Type l, Type r, Type t, bool check_user_conversions)
1499 if ((l == t) || (r == t))
1502 if (!check_user_conversions)
1505 if (Convert.ImplicitUserConversionExists (ec, l, t))
1507 else if (Convert.ImplicitUserConversionExists (ec, r, t))
1514 // Note that handling the case l == Decimal || r == Decimal
1515 // is taken care of by the Step 1 Operator Overload resolution.
1517 // If `check_user_conv' is true, we also check whether a user-defined conversion
1518 // exists. Note that we only need to do this if both arguments are of a user-defined
1519 // type, otherwise ConvertImplict() already finds the user-defined conversion for us,
1520 // so we don't explicitly check for performance reasons.
1522 bool DoNumericPromotions (EmitContext ec, Type l, Type r, Expression lexpr, Expression rexpr, bool check_user_conv)
1524 if (IsOfType (ec, l, r, TypeManager.double_type, check_user_conv)){
1526 // If either operand is of type double, the other operand is
1527 // conveted to type double.
1529 if (r != TypeManager.double_type)
1530 right = Convert.ImplicitConversion (ec, right, TypeManager.double_type, loc);
1531 if (l != TypeManager.double_type)
1532 left = Convert.ImplicitConversion (ec, left, TypeManager.double_type, loc);
1534 type = TypeManager.double_type;
1535 } else if (IsOfType (ec, l, r, TypeManager.float_type, check_user_conv)){
1537 // if either operand is of type float, the other operand is
1538 // converted to type float.
1540 if (r != TypeManager.double_type)
1541 right = Convert.ImplicitConversion (ec, right, TypeManager.float_type, loc);
1542 if (l != TypeManager.double_type)
1543 left = Convert.ImplicitConversion (ec, left, TypeManager.float_type, loc);
1544 type = TypeManager.float_type;
1545 } else if (IsOfType (ec, l, r, TypeManager.uint64_type, check_user_conv)){
1549 // If either operand is of type ulong, the other operand is
1550 // converted to type ulong. or an error ocurrs if the other
1551 // operand is of type sbyte, short, int or long
1553 if (l == TypeManager.uint64_type){
1554 if (r != TypeManager.uint64_type){
1555 if (right is IntConstant){
1556 IntConstant ic = (IntConstant) right;
1558 e = Convert.TryImplicitIntConversion (l, ic);
1561 } else if (right is LongConstant){
1562 long ll = ((LongConstant) right).Value;
1565 right = new ULongConstant ((ulong) ll, right.Location);
1567 e = Convert.ImplicitNumericConversion (right, l);
1574 if (left is IntConstant){
1575 e = Convert.TryImplicitIntConversion (r, (IntConstant) left);
1578 } else if (left is LongConstant){
1579 long ll = ((LongConstant) left).Value;
1582 left = new ULongConstant ((ulong) ll, right.Location);
1584 e = Convert.ImplicitNumericConversion (left, r);
1591 if ((other == TypeManager.sbyte_type) ||
1592 (other == TypeManager.short_type) ||
1593 (other == TypeManager.int32_type) ||
1594 (other == TypeManager.int64_type))
1595 Error_OperatorAmbiguous (loc, oper, l, r);
1597 left = ForceConversion (ec, left, TypeManager.uint64_type);
1598 right = ForceConversion (ec, right, TypeManager.uint64_type);
1600 type = TypeManager.uint64_type;
1601 } else if (IsOfType (ec, l, r, TypeManager.int64_type, check_user_conv)){
1603 // If either operand is of type long, the other operand is converted
1606 if (l != TypeManager.int64_type)
1607 left = Convert.ImplicitConversion (ec, left, TypeManager.int64_type, loc);
1608 if (r != TypeManager.int64_type)
1609 right = Convert.ImplicitConversion (ec, right, TypeManager.int64_type, loc);
1611 type = TypeManager.int64_type;
1612 } else if (IsOfType (ec, l, r, TypeManager.uint32_type, check_user_conv)){
1614 // If either operand is of type uint, and the other
1615 // operand is of type sbyte, short or int, othe operands are
1616 // converted to type long (unless we have an int constant).
1620 if (l == TypeManager.uint32_type){
1621 if (right is IntConstant){
1622 IntConstant ic = (IntConstant) right;
1626 right = new UIntConstant ((uint) val, ic.Location);
1633 } else if (r == TypeManager.uint32_type){
1634 if (left is IntConstant){
1635 IntConstant ic = (IntConstant) left;
1639 left = new UIntConstant ((uint) val, ic.Location);
1648 if ((other == TypeManager.sbyte_type) ||
1649 (other == TypeManager.short_type) ||
1650 (other == TypeManager.int32_type)){
1651 left = ForceConversion (ec, left, TypeManager.int64_type);
1652 right = ForceConversion (ec, right, TypeManager.int64_type);
1653 type = TypeManager.int64_type;
1656 // if either operand is of type uint, the other
1657 // operand is converd to type uint
1659 left = ForceConversion (ec, left, TypeManager.uint32_type);
1660 right = ForceConversion (ec, right, TypeManager.uint32_type);
1661 type = TypeManager.uint32_type;
1663 } else if (l == TypeManager.decimal_type || r == TypeManager.decimal_type){
1664 if (l != TypeManager.decimal_type)
1665 left = Convert.ImplicitConversion (ec, left, TypeManager.decimal_type, loc);
1667 if (r != TypeManager.decimal_type)
1668 right = Convert.ImplicitConversion (ec, right, TypeManager.decimal_type, loc);
1669 type = TypeManager.decimal_type;
1671 left = ForceConversion (ec, left, TypeManager.int32_type);
1672 right = ForceConversion (ec, right, TypeManager.int32_type);
1675 Convert.ImplicitConversionExists (ec, lexpr, TypeManager.string_type) &&
1676 Convert.ImplicitConversionExists (ec, rexpr, TypeManager.string_type);
1677 if (strConv && left != null && right != null)
1678 Error_OperatorAmbiguous (loc, oper, l, r);
1680 type = TypeManager.int32_type;
1683 return (left != null) && (right != null);
1686 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1688 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1691 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1693 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1697 void Error_OperatorCannotBeApplied ()
1699 Error_OperatorCannotBeApplied (Location, OperName (oper), left.GetSignatureForError (), right.GetSignatureForError ());
1702 static bool is_unsigned (Type t)
1704 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1705 t == TypeManager.short_type || t == TypeManager.byte_type);
1708 static bool is_user_defined (Type t)
1710 if (t.IsSubclassOf (TypeManager.value_type) &&
1711 (!TypeManager.IsBuiltinType (t) || t == TypeManager.decimal_type))
1717 Expression Make32or64 (EmitContext ec, Expression e)
1721 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1722 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1724 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1727 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1730 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1733 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1739 Expression CheckShiftArguments (EmitContext ec)
1743 e = ForceConversion (ec, right, TypeManager.int32_type);
1745 Error_OperatorCannotBeApplied ();
1750 if (((e = Convert.ImplicitConversion (ec, left, TypeManager.int32_type, loc)) != null) ||
1751 ((e = Convert.ImplicitConversion (ec, left, TypeManager.uint32_type, loc)) != null) ||
1752 ((e = Convert.ImplicitConversion (ec, left, TypeManager.int64_type, loc)) != null) ||
1753 ((e = Convert.ImplicitConversion (ec, left, TypeManager.uint64_type, loc)) != null)){
1757 if (type == TypeManager.int32_type || type == TypeManager.uint32_type){
1758 right = new Binary (Binary.Operator.BitwiseAnd, right, new IntConstant (31, loc));
1759 right = right.DoResolve (ec);
1761 right = new Binary (Binary.Operator.BitwiseAnd, right, new IntConstant (63, loc));
1762 right = right.DoResolve (ec);
1767 Error_OperatorCannotBeApplied ();
1772 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1773 // i.e., not invoke op_Equality.
1775 static bool EqualsNullIsReferenceEquals (Type t)
1777 return t == TypeManager.object_type || t == TypeManager.string_type ||
1778 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1781 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1783 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1784 "Possible unintended reference comparison; to get a value comparison, " +
1785 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1788 Expression ResolveOperator (EmitContext ec)
1791 Type r = right.Type;
1793 if (oper == Operator.Equality || oper == Operator.Inequality){
1795 // Optimize out call to op_Equality in a few cases.
1797 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1798 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1800 Type = TypeManager.bool_type;
1806 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1807 Type = TypeManager.bool_type;
1814 // Do not perform operator overload resolution when both sides are
1817 Expression left_operators = null, right_operators = null;
1818 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))){
1820 // Step 1: Perform Operator Overload location
1822 string op = oper_names [(int) oper];
1824 MethodGroupExpr union;
1825 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc);
1827 right_operators = MemberLookup (
1828 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc);
1829 union = Invocation.MakeUnionSet (left_operators, right_operators, loc);
1831 union = (MethodGroupExpr) left_operators;
1833 if (union != null) {
1834 ArrayList args = new ArrayList (2);
1835 args.Add (new Argument (left, Argument.AType.Expression));
1836 args.Add (new Argument (right, Argument.AType.Expression));
1838 MethodBase method = Invocation.OverloadResolve (
1839 ec, union, args, true, Location.Null);
1841 if (method != null) {
1842 MethodInfo mi = (MethodInfo) method;
1844 return new BinaryMethod (mi.ReturnType, method, args);
1850 // Step 0: String concatenation (because overloading will get this wrong)
1852 if (oper == Operator.Addition){
1854 // If any of the arguments is a string, cast to string
1857 // Simple constant folding
1858 if (left is StringConstant && right is StringConstant)
1859 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1861 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1863 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1864 Error_OperatorCannotBeApplied ();
1868 // try to fold it in on the left
1869 if (left is StringConcat) {
1872 // We have to test here for not-null, since we can be doubly-resolved
1873 // take care of not appending twice
1876 type = TypeManager.string_type;
1877 ((StringConcat) left).Append (ec, right);
1878 return left.Resolve (ec);
1884 // Otherwise, start a new concat expression
1885 return new StringConcat (ec, loc, left, right).Resolve (ec);
1889 // Transform a + ( - b) into a - b
1891 if (right is Unary){
1892 Unary right_unary = (Unary) right;
1894 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1895 oper = Operator.Subtraction;
1896 right = right_unary.Expr;
1902 if (oper == Operator.Equality || oper == Operator.Inequality){
1903 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1904 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1905 Error_OperatorCannotBeApplied ();
1909 type = TypeManager.bool_type;
1913 if (l.IsPointer || r.IsPointer) {
1914 if (l.IsPointer && r.IsPointer) {
1915 type = TypeManager.bool_type;
1919 if (l.IsPointer && r == TypeManager.null_type) {
1920 right = new EmptyCast (NullPointer.Null, l);
1921 type = TypeManager.bool_type;
1925 if (r.IsPointer && l == TypeManager.null_type) {
1926 left = new EmptyCast (NullPointer.Null, r);
1927 type = TypeManager.bool_type;
1933 // operator != (object a, object b)
1934 // operator == (object a, object b)
1936 // For this to be used, both arguments have to be reference-types.
1937 // Read the rationale on the spec (14.9.6)
1939 if (!(l.IsValueType || r.IsValueType)){
1940 type = TypeManager.bool_type;
1946 // Also, a standard conversion must exist from either one
1948 bool left_to_right =
1949 Convert.ImplicitStandardConversionExists (left, r);
1950 bool right_to_left = !left_to_right &&
1951 Convert.ImplicitStandardConversionExists (right, l);
1953 if (!left_to_right && !right_to_left) {
1954 Error_OperatorCannotBeApplied ();
1958 if (left_to_right && left_operators != null &&
1959 RootContext.WarningLevel >= 2) {
1960 ArrayList args = new ArrayList (2);
1961 args.Add (new Argument (left, Argument.AType.Expression));
1962 args.Add (new Argument (left, Argument.AType.Expression));
1963 MethodBase method = Invocation.OverloadResolve (
1964 ec, (MethodGroupExpr) left_operators, args, true, Location.Null);
1966 Warning_UnintendedReferenceComparison (loc, "right", l);
1969 if (right_to_left && right_operators != null &&
1970 RootContext.WarningLevel >= 2) {
1971 ArrayList args = new ArrayList (2);
1972 args.Add (new Argument (right, Argument.AType.Expression));
1973 args.Add (new Argument (right, Argument.AType.Expression));
1974 MethodBase method = Invocation.OverloadResolve (
1975 ec, (MethodGroupExpr) right_operators, args, true, Location.Null);
1977 Warning_UnintendedReferenceComparison (loc, "left", r);
1981 // We are going to have to convert to an object to compare
1983 if (l != TypeManager.object_type)
1984 left = new EmptyCast (left, TypeManager.object_type);
1985 if (r != TypeManager.object_type)
1986 right = new EmptyCast (right, TypeManager.object_type);
1989 // FIXME: CSC here catches errors cs254 and cs252
1995 // One of them is a valuetype, but the other one is not.
1997 if (!l.IsValueType || !r.IsValueType) {
1998 Error_OperatorCannotBeApplied ();
2003 // Only perform numeric promotions on:
2004 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
2006 if (oper == Operator.Addition || oper == Operator.Subtraction) {
2007 if (TypeManager.IsDelegateType (l)){
2008 if (((right.eclass == ExprClass.MethodGroup) ||
2009 (r == TypeManager.anonymous_method_type))){
2010 if ((RootContext.Version != LanguageVersion.ISO_1)){
2011 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
2019 if (TypeManager.IsDelegateType (r)){
2021 ArrayList args = new ArrayList (2);
2023 args = new ArrayList (2);
2024 args.Add (new Argument (left, Argument.AType.Expression));
2025 args.Add (new Argument (right, Argument.AType.Expression));
2027 if (oper == Operator.Addition)
2028 method = TypeManager.delegate_combine_delegate_delegate;
2030 method = TypeManager.delegate_remove_delegate_delegate;
2033 Error_OperatorCannotBeApplied ();
2037 return new BinaryDelegate (l, method, args);
2042 // Pointer arithmetic:
2044 // T* operator + (T* x, int y);
2045 // T* operator + (T* x, uint y);
2046 // T* operator + (T* x, long y);
2047 // T* operator + (T* x, ulong y);
2049 // T* operator + (int y, T* x);
2050 // T* operator + (uint y, T *x);
2051 // T* operator + (long y, T *x);
2052 // T* operator + (ulong y, T *x);
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 // long operator - (T* x, T *y)
2062 if (r.IsPointer && oper == Operator.Subtraction){
2064 return new PointerArithmetic (
2065 false, left, right, TypeManager.int64_type,
2068 Expression t = Make32or64 (ec, right);
2070 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
2072 } else if (r.IsPointer && oper == Operator.Addition){
2073 Expression t = Make32or64 (ec, left);
2075 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
2080 // Enumeration operators
2082 bool lie = TypeManager.IsEnumType (l);
2083 bool rie = TypeManager.IsEnumType (r);
2087 // U operator - (E e, E f)
2089 if (oper == Operator.Subtraction){
2091 type = TypeManager.EnumToUnderlying (l);
2094 Error_OperatorCannotBeApplied ();
2100 // operator + (E e, U x)
2101 // operator - (E e, U x)
2103 if (oper == Operator.Addition || oper == Operator.Subtraction){
2104 Type enum_type = lie ? l : r;
2105 Type other_type = lie ? r : l;
2106 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2108 if (underlying_type != other_type){
2109 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2119 Error_OperatorCannotBeApplied ();
2128 temp = Convert.ImplicitConversion (ec, right, l, loc);
2132 Error_OperatorCannotBeApplied ();
2136 temp = Convert.ImplicitConversion (ec, left, r, loc);
2141 Error_OperatorCannotBeApplied ();
2146 if (oper == Operator.Equality || oper == Operator.Inequality ||
2147 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2148 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2149 if (left.Type != right.Type){
2150 Error_OperatorCannotBeApplied ();
2153 type = TypeManager.bool_type;
2157 if (oper == Operator.BitwiseAnd ||
2158 oper == Operator.BitwiseOr ||
2159 oper == Operator.ExclusiveOr){
2160 if (left.Type != right.Type){
2161 Error_OperatorCannotBeApplied ();
2167 Error_OperatorCannotBeApplied ();
2171 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2172 return CheckShiftArguments (ec);
2174 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2175 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2176 type = TypeManager.bool_type;
2181 Error_OperatorCannotBeApplied ();
2185 Expression e = new ConditionalLogicalOperator (
2186 oper == Operator.LogicalAnd, left, right, l, loc);
2187 return e.Resolve (ec);
2191 // operator & (bool x, bool y)
2192 // operator | (bool x, bool y)
2193 // operator ^ (bool x, bool y)
2195 if (l == TypeManager.bool_type && r == TypeManager.bool_type){
2196 if (oper == Operator.BitwiseAnd ||
2197 oper == Operator.BitwiseOr ||
2198 oper == Operator.ExclusiveOr){
2205 // Pointer comparison
2207 if (l.IsPointer && r.IsPointer){
2208 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2209 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2210 type = TypeManager.bool_type;
2216 // This will leave left or right set to null if there is an error
2218 bool check_user_conv = is_user_defined (l) && is_user_defined (r);
2219 DoNumericPromotions (ec, l, r, left, right, check_user_conv);
2220 if (left == null || right == null){
2221 Error_OperatorCannotBeApplied (loc, OperName (oper), l, r);
2226 // reload our cached types if required
2231 if (oper == Operator.BitwiseAnd ||
2232 oper == Operator.BitwiseOr ||
2233 oper == Operator.ExclusiveOr){
2235 if (((l == TypeManager.int32_type) ||
2236 (l == TypeManager.uint32_type) ||
2237 (l == TypeManager.short_type) ||
2238 (l == TypeManager.ushort_type) ||
2239 (l == TypeManager.int64_type) ||
2240 (l == TypeManager.uint64_type))){
2243 Error_OperatorCannotBeApplied ();
2247 Error_OperatorCannotBeApplied ();
2252 if (oper == Operator.Equality ||
2253 oper == Operator.Inequality ||
2254 oper == Operator.LessThanOrEqual ||
2255 oper == Operator.LessThan ||
2256 oper == Operator.GreaterThanOrEqual ||
2257 oper == Operator.GreaterThan){
2258 type = TypeManager.bool_type;
2264 Constant EnumLiftUp (Constant left, Constant right)
2267 case Operator.BitwiseOr:
2268 case Operator.BitwiseAnd:
2269 case Operator.ExclusiveOr:
2270 case Operator.Equality:
2271 case Operator.Inequality:
2272 case Operator.LessThan:
2273 case Operator.LessThanOrEqual:
2274 case Operator.GreaterThan:
2275 case Operator.GreaterThanOrEqual:
2276 if (left is EnumConstant)
2279 if (left.IsZeroInteger)
2280 return new EnumConstant (left, right.Type);
2284 case Operator.Addition:
2285 case Operator.Subtraction:
2288 case Operator.Multiply:
2289 case Operator.Division:
2290 case Operator.Modulus:
2291 case Operator.LeftShift:
2292 case Operator.RightShift:
2293 if (right is EnumConstant || left is EnumConstant)
2297 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2301 public override Expression DoResolve (EmitContext ec)
2306 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2307 left = ((ParenthesizedExpression) left).Expr;
2308 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2312 if (left.eclass == ExprClass.Type) {
2313 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2317 left = left.Resolve (ec);
2322 Constant lc = left as Constant;
2323 if (lc != null && lc.Type == TypeManager.bool_type &&
2324 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2325 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2327 // TODO: make a sense to resolve unreachable expression as we do for statement
2328 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2332 right = right.Resolve (ec);
2336 eclass = ExprClass.Value;
2337 Constant rc = right as Constant;
2339 // The conversion rules are ignored in enum context but why
2340 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2341 left = lc = EnumLiftUp (lc, rc);
2345 right = rc = EnumLiftUp (rc, lc);
2350 if (oper == Operator.BitwiseAnd) {
2351 if (rc != null && rc.IsZeroInteger) {
2352 return lc is EnumConstant ?
2353 new EnumConstant (rc, lc.Type):
2357 if (lc != null && lc.IsZeroInteger) {
2358 return rc is EnumConstant ?
2359 new EnumConstant (lc, rc.Type):
2363 else if (oper == Operator.BitwiseOr) {
2364 if (lc is EnumConstant &&
2365 rc != null && rc.IsZeroInteger)
2367 if (rc is EnumConstant &&
2368 lc != null && lc.IsZeroInteger)
2370 } else if (oper == Operator.LogicalAnd) {
2371 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2373 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2377 if (rc != null && lc != null){
2378 int prev_e = Report.Errors;
2379 Expression e = ConstantFold.BinaryFold (
2380 ec, oper, lc, rc, loc);
2381 if (e != null || Report.Errors != prev_e)
2385 // Comparison warnings
2386 if (oper == Operator.Equality || oper == Operator.Inequality ||
2387 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2388 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2389 if (left.Equals (right)) {
2390 Report.Warning (1718, 3, loc, "Comparison made to same variable; did you mean to compare something else?");
2392 CheckUselessComparison (lc, right.Type);
2393 CheckUselessComparison (rc, left.Type);
2396 return ResolveOperator (ec);
2399 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2404 private void CheckUselessComparison (Constant c, Type type)
2406 if (c == null || !IsTypeIntegral (type)
2407 || c is StringConstant
2408 || c is BoolConstant
2409 || c is CharConstant
2410 || c is FloatConstant
2411 || c is DoubleConstant
2412 || c is DecimalConstant
2418 if (c is ULongConstant) {
2419 ulong uvalue = ((ULongConstant) c).Value;
2420 if (uvalue > long.MaxValue) {
2421 if (type == TypeManager.byte_type ||
2422 type == TypeManager.sbyte_type ||
2423 type == TypeManager.short_type ||
2424 type == TypeManager.ushort_type ||
2425 type == TypeManager.int32_type ||
2426 type == TypeManager.uint32_type ||
2427 type == TypeManager.int64_type)
2428 WarnUselessComparison (type);
2431 value = (long) uvalue;
2433 else if (c is ByteConstant)
2434 value = ((ByteConstant) c).Value;
2435 else if (c is SByteConstant)
2436 value = ((SByteConstant) c).Value;
2437 else if (c is ShortConstant)
2438 value = ((ShortConstant) c).Value;
2439 else if (c is UShortConstant)
2440 value = ((UShortConstant) c).Value;
2441 else if (c is IntConstant)
2442 value = ((IntConstant) c).Value;
2443 else if (c is UIntConstant)
2444 value = ((UIntConstant) c).Value;
2445 else if (c is LongConstant)
2446 value = ((LongConstant) c).Value;
2449 if (IsValueOutOfRange (value, type))
2450 WarnUselessComparison (type);
2455 private bool IsValueOutOfRange (long value, Type type)
2457 if (IsTypeUnsigned (type) && value < 0)
2459 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2460 type == TypeManager.byte_type && value >= 0x100 ||
2461 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2462 type == TypeManager.ushort_type && value >= 0x10000 ||
2463 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2464 type == TypeManager.uint32_type && value >= 0x100000000;
2467 private static bool IsTypeIntegral (Type type)
2469 return type == TypeManager.uint64_type ||
2470 type == TypeManager.int64_type ||
2471 type == TypeManager.uint32_type ||
2472 type == TypeManager.int32_type ||
2473 type == TypeManager.ushort_type ||
2474 type == TypeManager.short_type ||
2475 type == TypeManager.sbyte_type ||
2476 type == TypeManager.byte_type;
2479 private static bool IsTypeUnsigned (Type type)
2481 return type == TypeManager.uint64_type ||
2482 type == TypeManager.uint32_type ||
2483 type == TypeManager.ushort_type ||
2484 type == TypeManager.byte_type;
2487 private void WarnUselessComparison (Type type)
2489 Report.Warning (652, 2, loc, "Comparison to integral constant is useless; the constant is outside the range of type `{0}'",
2490 TypeManager.CSharpName (type));
2494 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2495 /// context of a conditional bool expression. This function will return
2496 /// false if it is was possible to use EmitBranchable, or true if it was.
2498 /// The expression's code is generated, and we will generate a branch to `target'
2499 /// if the resulting expression value is equal to isTrue
2501 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2503 ILGenerator ig = ec.ig;
2506 // This is more complicated than it looks, but its just to avoid
2507 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2508 // but on top of that we want for == and != to use a special path
2509 // if we are comparing against null
2511 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2512 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2515 // put the constant on the rhs, for simplicity
2517 if (left is Constant) {
2518 Expression swap = right;
2523 if (((Constant) right).IsZeroInteger) {
2526 ig.Emit (OpCodes.Brtrue, target);
2528 ig.Emit (OpCodes.Brfalse, target);
2531 } else if (right is BoolConstant) {
2533 if (my_on_true != ((BoolConstant) right).Value)
2534 ig.Emit (OpCodes.Brtrue, target);
2536 ig.Emit (OpCodes.Brfalse, target);
2541 } else if (oper == Operator.LogicalAnd) {
2544 Label tests_end = ig.DefineLabel ();
2546 left.EmitBranchable (ec, tests_end, false);
2547 right.EmitBranchable (ec, target, true);
2548 ig.MarkLabel (tests_end);
2550 left.EmitBranchable (ec, target, false);
2551 right.EmitBranchable (ec, target, false);
2556 } else if (oper == Operator.LogicalOr){
2558 left.EmitBranchable (ec, target, true);
2559 right.EmitBranchable (ec, target, true);
2562 Label tests_end = ig.DefineLabel ();
2563 left.EmitBranchable (ec, tests_end, true);
2564 right.EmitBranchable (ec, target, false);
2565 ig.MarkLabel (tests_end);
2570 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2571 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2572 oper == Operator.Equality || oper == Operator.Inequality)) {
2573 base.EmitBranchable (ec, target, onTrue);
2581 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2584 case Operator.Equality:
2586 ig.Emit (OpCodes.Beq, target);
2588 ig.Emit (OpCodes.Bne_Un, target);
2591 case Operator.Inequality:
2593 ig.Emit (OpCodes.Bne_Un, target);
2595 ig.Emit (OpCodes.Beq, target);
2598 case Operator.LessThan:
2601 ig.Emit (OpCodes.Blt_Un, target);
2603 ig.Emit (OpCodes.Blt, target);
2606 ig.Emit (OpCodes.Bge_Un, target);
2608 ig.Emit (OpCodes.Bge, target);
2611 case Operator.GreaterThan:
2614 ig.Emit (OpCodes.Bgt_Un, target);
2616 ig.Emit (OpCodes.Bgt, target);
2619 ig.Emit (OpCodes.Ble_Un, target);
2621 ig.Emit (OpCodes.Ble, target);
2624 case Operator.LessThanOrEqual:
2627 ig.Emit (OpCodes.Ble_Un, target);
2629 ig.Emit (OpCodes.Ble, target);
2632 ig.Emit (OpCodes.Bgt_Un, target);
2634 ig.Emit (OpCodes.Bgt, target);
2638 case Operator.GreaterThanOrEqual:
2641 ig.Emit (OpCodes.Bge_Un, target);
2643 ig.Emit (OpCodes.Bge, target);
2646 ig.Emit (OpCodes.Blt_Un, target);
2648 ig.Emit (OpCodes.Blt, target);
2651 Console.WriteLine (oper);
2652 throw new Exception ("what is THAT");
2656 public override void Emit (EmitContext ec)
2658 ILGenerator ig = ec.ig;
2663 // Handle short-circuit operators differently
2666 if (oper == Operator.LogicalAnd) {
2667 Label load_zero = ig.DefineLabel ();
2668 Label end = ig.DefineLabel ();
2670 left.EmitBranchable (ec, load_zero, false);
2672 ig.Emit (OpCodes.Br, end);
2674 ig.MarkLabel (load_zero);
2675 ig.Emit (OpCodes.Ldc_I4_0);
2678 } else if (oper == Operator.LogicalOr) {
2679 Label load_one = ig.DefineLabel ();
2680 Label end = ig.DefineLabel ();
2682 left.EmitBranchable (ec, load_one, true);
2684 ig.Emit (OpCodes.Br, end);
2686 ig.MarkLabel (load_one);
2687 ig.Emit (OpCodes.Ldc_I4_1);
2695 bool isUnsigned = is_unsigned (left.Type);
2698 case Operator.Multiply:
2700 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2701 opcode = OpCodes.Mul_Ovf;
2702 else if (isUnsigned)
2703 opcode = OpCodes.Mul_Ovf_Un;
2705 opcode = OpCodes.Mul;
2707 opcode = OpCodes.Mul;
2711 case Operator.Division:
2713 opcode = OpCodes.Div_Un;
2715 opcode = OpCodes.Div;
2718 case Operator.Modulus:
2720 opcode = OpCodes.Rem_Un;
2722 opcode = OpCodes.Rem;
2725 case Operator.Addition:
2727 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2728 opcode = OpCodes.Add_Ovf;
2729 else if (isUnsigned)
2730 opcode = OpCodes.Add_Ovf_Un;
2732 opcode = OpCodes.Add;
2734 opcode = OpCodes.Add;
2737 case Operator.Subtraction:
2739 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2740 opcode = OpCodes.Sub_Ovf;
2741 else if (isUnsigned)
2742 opcode = OpCodes.Sub_Ovf_Un;
2744 opcode = OpCodes.Sub;
2746 opcode = OpCodes.Sub;
2749 case Operator.RightShift:
2751 opcode = OpCodes.Shr_Un;
2753 opcode = OpCodes.Shr;
2756 case Operator.LeftShift:
2757 opcode = OpCodes.Shl;
2760 case Operator.Equality:
2761 opcode = OpCodes.Ceq;
2764 case Operator.Inequality:
2765 ig.Emit (OpCodes.Ceq);
2766 ig.Emit (OpCodes.Ldc_I4_0);
2768 opcode = OpCodes.Ceq;
2771 case Operator.LessThan:
2773 opcode = OpCodes.Clt_Un;
2775 opcode = OpCodes.Clt;
2778 case Operator.GreaterThan:
2780 opcode = OpCodes.Cgt_Un;
2782 opcode = OpCodes.Cgt;
2785 case Operator.LessThanOrEqual:
2786 Type lt = left.Type;
2788 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2789 ig.Emit (OpCodes.Cgt_Un);
2791 ig.Emit (OpCodes.Cgt);
2792 ig.Emit (OpCodes.Ldc_I4_0);
2794 opcode = OpCodes.Ceq;
2797 case Operator.GreaterThanOrEqual:
2798 Type le = left.Type;
2800 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2801 ig.Emit (OpCodes.Clt_Un);
2803 ig.Emit (OpCodes.Clt);
2805 ig.Emit (OpCodes.Ldc_I4_0);
2807 opcode = OpCodes.Ceq;
2810 case Operator.BitwiseOr:
2811 opcode = OpCodes.Or;
2814 case Operator.BitwiseAnd:
2815 opcode = OpCodes.And;
2818 case Operator.ExclusiveOr:
2819 opcode = OpCodes.Xor;
2823 throw new Exception ("This should not happen: Operator = "
2824 + oper.ToString ());
2832 // Object created by Binary when the binary operator uses an method instead of being
2833 // a binary operation that maps to a CIL binary operation.
2835 public class BinaryMethod : Expression {
2836 public MethodBase method;
2837 public ArrayList Arguments;
2839 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2844 eclass = ExprClass.Value;
2847 public override Expression DoResolve (EmitContext ec)
2852 public override void Emit (EmitContext ec)
2854 ILGenerator ig = ec.ig;
2856 if (Arguments != null)
2857 Invocation.EmitArguments (ec, method, Arguments, false, null);
2859 if (method is MethodInfo)
2860 ig.Emit (OpCodes.Call, (MethodInfo) method);
2862 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2867 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2868 // b, c, d... may be strings or objects.
2870 public class StringConcat : Expression {
2872 bool invalid = false;
2873 bool emit_conv_done = false;
2875 // Are we also concating objects?
2877 bool is_strings_only = true;
2879 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2882 type = TypeManager.string_type;
2883 eclass = ExprClass.Value;
2885 operands = new ArrayList (2);
2890 public override Expression DoResolve (EmitContext ec)
2898 public void Append (EmitContext ec, Expression operand)
2903 StringConstant sc = operand as StringConstant;
2905 // TODO: it will be better to do this silently as an optimalization
2907 // string s = "" + i;
2908 // because this code has poor performace
2909 // if (sc.Value.Length == 0)
2910 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2912 if (operands.Count != 0) {
2913 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2914 if (last_operand != null) {
2915 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2922 // Conversion to object
2924 if (operand.Type != TypeManager.string_type) {
2925 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2928 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2934 operands.Add (operand);
2937 public override void Emit (EmitContext ec)
2939 MethodInfo concat_method = null;
2942 // Do conversion to arguments; check for strings only
2945 // This can get called multiple times, so we have to deal with that.
2946 if (!emit_conv_done) {
2947 emit_conv_done = true;
2948 for (int i = 0; i < operands.Count; i ++) {
2949 Expression e = (Expression) operands [i];
2950 is_strings_only &= e.Type == TypeManager.string_type;
2953 for (int i = 0; i < operands.Count; i ++) {
2954 Expression e = (Expression) operands [i];
2956 if (! is_strings_only && e.Type == TypeManager.string_type) {
2957 // need to make sure this is an object, because the EmitParams
2958 // method might look at the type of this expression, see it is a
2959 // string and emit a string [] when we want an object [];
2961 e = new EmptyCast (e, TypeManager.object_type);
2963 operands [i] = new Argument (e, Argument.AType.Expression);
2968 // Find the right method
2970 switch (operands.Count) {
2973 // This should not be possible, because simple constant folding
2974 // is taken care of in the Binary code.
2976 throw new Exception ("how did you get here?");
2979 concat_method = is_strings_only ?
2980 TypeManager.string_concat_string_string :
2981 TypeManager.string_concat_object_object ;
2984 concat_method = is_strings_only ?
2985 TypeManager.string_concat_string_string_string :
2986 TypeManager.string_concat_object_object_object ;
2990 // There is not a 4 param overlaod for object (the one that there is
2991 // is actually a varargs methods, and is only in corlib because it was
2992 // introduced there before.).
2994 if (!is_strings_only)
2997 concat_method = TypeManager.string_concat_string_string_string_string;
3000 concat_method = is_strings_only ?
3001 TypeManager.string_concat_string_dot_dot_dot :
3002 TypeManager.string_concat_object_dot_dot_dot ;
3006 Invocation.EmitArguments (ec, concat_method, operands, false, null);
3007 ec.ig.Emit (OpCodes.Call, concat_method);
3012 // Object created with +/= on delegates
3014 public class BinaryDelegate : Expression {
3018 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
3023 eclass = ExprClass.Value;
3026 public override Expression DoResolve (EmitContext ec)
3031 public override void Emit (EmitContext ec)
3033 ILGenerator ig = ec.ig;
3035 Invocation.EmitArguments (ec, method, args, false, null);
3037 ig.Emit (OpCodes.Call, (MethodInfo) method);
3038 ig.Emit (OpCodes.Castclass, type);
3041 public Expression Right {
3043 Argument arg = (Argument) args [1];
3048 public bool IsAddition {
3050 return method == TypeManager.delegate_combine_delegate_delegate;
3056 // User-defined conditional logical operator
3057 public class ConditionalLogicalOperator : Expression {
3058 Expression left, right;
3061 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
3064 eclass = ExprClass.Value;
3068 this.is_and = is_and;
3071 protected void Error19 ()
3073 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3076 protected void Error218 ()
3078 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3079 "declarations of operator true and operator false");
3082 Expression op_true, op_false, op;
3083 LocalTemporary left_temp;
3085 public override Expression DoResolve (EmitContext ec)
3088 Expression operator_group;
3090 operator_group = MethodLookup (ec, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
3091 if (operator_group == null) {
3096 left_temp = new LocalTemporary (type);
3098 ArrayList arguments = new ArrayList ();
3099 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3100 arguments.Add (new Argument (right, Argument.AType.Expression));
3101 method = Invocation.OverloadResolve (
3102 ec, (MethodGroupExpr) operator_group, arguments, false, loc)
3104 if (method == null) {
3109 if (method.ReturnType != type) {
3110 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3111 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3115 op = new StaticCallExpr (method, arguments, loc);
3117 op_true = GetOperatorTrue (ec, left_temp, loc);
3118 op_false = GetOperatorFalse (ec, left_temp, loc);
3119 if ((op_true == null) || (op_false == null)) {
3127 public override void Emit (EmitContext ec)
3129 ILGenerator ig = ec.ig;
3130 Label false_target = ig.DefineLabel ();
3131 Label end_target = ig.DefineLabel ();
3134 left_temp.Store (ec);
3136 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3137 left_temp.Emit (ec);
3138 ig.Emit (OpCodes.Br, end_target);
3139 ig.MarkLabel (false_target);
3141 ig.MarkLabel (end_target);
3145 public class PointerArithmetic : Expression {
3146 Expression left, right;
3150 // We assume that `l' is always a pointer
3152 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3158 is_add = is_addition;
3161 public override Expression DoResolve (EmitContext ec)
3163 eclass = ExprClass.Variable;
3165 if (left.Type == TypeManager.void_ptr_type) {
3166 Error (242, "The operation in question is undefined on void pointers");
3173 public override void Emit (EmitContext ec)
3175 Type op_type = left.Type;
3176 ILGenerator ig = ec.ig;
3178 // It must be either array or fixed buffer
3179 Type element = TypeManager.HasElementType (op_type) ?
3180 element = TypeManager.GetElementType (op_type) :
3181 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3183 int size = GetTypeSize (element);
3184 Type rtype = right.Type;
3186 if (rtype.IsPointer){
3188 // handle (pointer - pointer)
3192 ig.Emit (OpCodes.Sub);
3196 ig.Emit (OpCodes.Sizeof, element);
3198 IntLiteral.EmitInt (ig, size);
3199 ig.Emit (OpCodes.Div);
3201 ig.Emit (OpCodes.Conv_I8);
3204 // handle + and - on (pointer op int)
3207 ig.Emit (OpCodes.Conv_I);
3209 Constant right_const = right as Constant;
3210 if (right_const != null && size != 0) {
3211 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3219 ig.Emit (OpCodes.Sizeof, element);
3221 IntLiteral.EmitInt (ig, size);
3222 if (rtype == TypeManager.int64_type)
3223 ig.Emit (OpCodes.Conv_I8);
3224 else if (rtype == TypeManager.uint64_type)
3225 ig.Emit (OpCodes.Conv_U8);
3226 ig.Emit (OpCodes.Mul);
3230 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3231 ig.Emit (OpCodes.Conv_I);
3234 ig.Emit (OpCodes.Add);
3236 ig.Emit (OpCodes.Sub);
3242 /// Implements the ternary conditional operator (?:)
3244 public class Conditional : Expression {
3245 Expression expr, trueExpr, falseExpr;
3247 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3250 this.trueExpr = trueExpr;
3251 this.falseExpr = falseExpr;
3252 this.loc = expr.Location;
3255 public Expression Expr {
3261 public Expression TrueExpr {
3267 public Expression FalseExpr {
3273 public override Expression DoResolve (EmitContext ec)
3275 expr = expr.Resolve (ec);
3280 if (expr.Type != TypeManager.bool_type){
3281 expr = Expression.ResolveBoolean (
3288 Assign ass = expr as Assign;
3289 if (ass != null && ass.Source is Constant) {
3290 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3293 trueExpr = trueExpr.Resolve (ec);
3294 falseExpr = falseExpr.Resolve (ec);
3296 if (trueExpr == null || falseExpr == null)
3299 eclass = ExprClass.Value;
3300 if (trueExpr.Type == falseExpr.Type)
3301 type = trueExpr.Type;
3304 Type true_type = trueExpr.Type;
3305 Type false_type = falseExpr.Type;
3308 // First, if an implicit conversion exists from trueExpr
3309 // to falseExpr, then the result type is of type falseExpr.Type
3311 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3314 // Check if both can convert implicitl to each other's type
3316 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3318 "Can not compute type of conditional expression " +
3319 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3320 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3321 "' convert implicitly to each other");
3326 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3330 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3331 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3336 // Dead code optimalization
3337 if (expr is BoolConstant){
3338 BoolConstant bc = (BoolConstant) expr;
3340 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3341 return bc.Value ? trueExpr : falseExpr;
3347 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3352 public override void Emit (EmitContext ec)
3354 ILGenerator ig = ec.ig;
3355 Label false_target = ig.DefineLabel ();
3356 Label end_target = ig.DefineLabel ();
3358 expr.EmitBranchable (ec, false_target, false);
3360 ig.Emit (OpCodes.Br, end_target);
3361 ig.MarkLabel (false_target);
3362 falseExpr.Emit (ec);
3363 ig.MarkLabel (end_target);
3371 public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3372 public readonly string Name;
3373 public readonly Block Block;
3374 public LocalInfo local_info;
3377 LocalTemporary temp;
3379 public LocalVariableReference (Block block, string name, Location l)
3384 eclass = ExprClass.Variable;
3388 // Setting `is_readonly' to false will allow you to create a writable
3389 // reference to a read-only variable. This is used by foreach and using.
3391 public LocalVariableReference (Block block, string name, Location l,
3392 LocalInfo local_info, bool is_readonly)
3393 : this (block, name, l)
3395 this.local_info = local_info;
3396 this.is_readonly = is_readonly;
3399 public VariableInfo VariableInfo {
3400 get { return local_info.VariableInfo; }
3403 public bool IsReadOnly {
3404 get { return is_readonly; }
3407 public bool VerifyAssigned (EmitContext ec)
3409 VariableInfo variable_info = local_info.VariableInfo;
3410 return variable_info == null || variable_info.IsAssigned (ec, loc);
3413 void ResolveLocalInfo ()
3415 if (local_info == null) {
3416 local_info = Block.GetLocalInfo (Name);
3417 is_readonly = local_info.ReadOnly;
3421 protected Expression DoResolveBase (EmitContext ec)
3423 type = local_info.VariableType;
3425 Expression e = Block.GetConstantExpression (Name);
3427 return e.Resolve (ec);
3429 if (!VerifyAssigned (ec))
3432 if (ec.CurrentAnonymousMethod != null){
3434 // If we are referencing a variable from the external block
3435 // flag it for capturing
3437 if ((local_info.Block.Toplevel != ec.CurrentBlock.Toplevel) ||
3438 ec.CurrentAnonymousMethod.IsIterator)
3440 if (local_info.AddressTaken){
3441 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3444 ec.CaptureVariable (local_info);
3451 public override Expression DoResolve (EmitContext ec)
3453 ResolveLocalInfo ();
3454 local_info.Used = true;
3455 return DoResolveBase (ec);
3458 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3460 ResolveLocalInfo ();
3465 if (right_side == EmptyExpression.OutAccess) {
3466 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3467 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3468 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3469 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3470 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3472 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
3474 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3479 if (right_side == EmptyExpression.OutAccess)
3480 local_info.Used = true;
3482 if (VariableInfo != null)
3483 VariableInfo.SetAssigned (ec);
3485 return DoResolveBase (ec);
3488 public bool VerifyFixed ()
3490 // A local Variable is always fixed.
3494 public override int GetHashCode ()
3496 return Name.GetHashCode ();
3499 public override bool Equals (object obj)
3501 LocalVariableReference lvr = obj as LocalVariableReference;
3505 return Name == lvr.Name && Block == lvr.Block;
3508 public override void Emit (EmitContext ec)
3510 ILGenerator ig = ec.ig;
3512 if (local_info.FieldBuilder == null){
3514 // A local variable on the local CLR stack
3516 ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
3519 // A local variable captured by anonymous methods.
3522 ec.EmitCapturedVariableInstance (local_info);
3524 ig.Emit (OpCodes.Ldfld, local_info.FieldBuilder);
3528 public void Emit (EmitContext ec, bool leave_copy)
3532 ec.ig.Emit (OpCodes.Dup);
3533 if (local_info.FieldBuilder != null){
3534 temp = new LocalTemporary (Type);
3540 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3542 ILGenerator ig = ec.ig;
3543 prepared = prepare_for_load;
3545 if (local_info.FieldBuilder == null){
3547 // A local variable on the local CLR stack
3549 if (local_info.LocalBuilder == null)
3550 throw new Exception ("This should not happen: both Field and Local are null");
3554 ec.ig.Emit (OpCodes.Dup);
3555 ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
3558 // A local variable captured by anonymous methods or itereators.
3560 ec.EmitCapturedVariableInstance (local_info);
3562 if (prepare_for_load)
3563 ig.Emit (OpCodes.Dup);
3566 ig.Emit (OpCodes.Dup);
3567 temp = new LocalTemporary (Type);
3570 ig.Emit (OpCodes.Stfld, local_info.FieldBuilder);
3576 public void AddressOf (EmitContext ec, AddressOp mode)
3578 ILGenerator ig = ec.ig;
3580 if (local_info.FieldBuilder == null){
3582 // A local variable on the local CLR stack
3584 ig.Emit (OpCodes.Ldloca, local_info.LocalBuilder);
3587 // A local variable captured by anonymous methods or iterators
3589 ec.EmitCapturedVariableInstance (local_info);
3590 ig.Emit (OpCodes.Ldflda, local_info.FieldBuilder);
3594 public override string ToString ()
3596 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3601 /// This represents a reference to a parameter in the intermediate
3604 public class ParameterReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3610 public bool is_ref, is_out, prepared;
3624 LocalTemporary temp;
3626 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3629 this.name = par.Name;
3633 eclass = ExprClass.Variable;
3636 public VariableInfo VariableInfo {
3640 public bool VerifyFixed ()
3642 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3643 return par.ModFlags == Parameter.Modifier.NONE;
3646 public bool IsAssigned (EmitContext ec, Location loc)
3648 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3651 Report.Error (269, loc,
3652 "Use of unassigned out parameter `{0}'", par.Name);
3656 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3658 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3661 Report.Error (170, loc,
3662 "Use of possibly unassigned field `" + field_name + "'");
3666 public void SetAssigned (EmitContext ec)
3668 if (is_out && ec.DoFlowAnalysis)
3669 ec.CurrentBranching.SetAssigned (vi);
3672 public void SetFieldAssigned (EmitContext ec, string field_name)
3674 if (is_out && ec.DoFlowAnalysis)
3675 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3678 protected bool DoResolveBase (EmitContext ec)
3680 if (!par.Resolve (ec)) {
3684 type = par.ParameterType;
3685 Parameter.Modifier mod = par.ModFlags;
3686 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3687 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3688 eclass = ExprClass.Variable;
3691 vi = block.ParameterMap [idx];
3693 if (ec.CurrentAnonymousMethod != null){
3694 if (is_ref && !block.Toplevel.IsLocalParameter (name)){
3695 Report.Error (1628, Location, "Cannot use ref or out parameter `{0}' inside an anonymous method block",
3701 // If we are referencing the parameter from the external block
3702 // flag it for capturing
3704 //Console.WriteLine ("Is parameter `{0}' local? {1}", name, block.IsLocalParameter (name));
3705 if (!block.Toplevel.IsLocalParameter (name)){
3706 ec.CaptureParameter (name, type, idx);
3713 public override int GetHashCode()
3715 return name.GetHashCode ();
3718 public override bool Equals (object obj)
3720 ParameterReference pr = obj as ParameterReference;
3724 return name == pr.name && block == pr.block;
3728 // Notice that for ref/out parameters, the type exposed is not the
3729 // same type exposed externally.
3732 // externally we expose "int&"
3733 // here we expose "int".
3735 // We record this in "is_ref". This means that the type system can treat
3736 // the type as it is expected, but when we generate the code, we generate
3737 // the alternate kind of code.
3739 public override Expression DoResolve (EmitContext ec)
3741 if (!DoResolveBase (ec))
3744 if (is_out && ec.DoFlowAnalysis && (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3750 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3752 if (!DoResolveBase (ec))
3760 static public void EmitLdArg (ILGenerator ig, int x)
3764 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3765 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3766 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3767 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3768 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3771 ig.Emit (OpCodes.Ldarg, x);
3775 // This method is used by parameters that are references, that are
3776 // being passed as references: we only want to pass the pointer (that
3777 // is already stored in the parameter, not the address of the pointer,
3778 // and not the value of the variable).
3780 public void EmitLoad (EmitContext ec)
3782 ILGenerator ig = ec.ig;
3785 if (!ec.MethodIsStatic)
3788 EmitLdArg (ig, arg_idx);
3791 // FIXME: Review for anonymous methods
3795 public override void Emit (EmitContext ec)
3800 public void Emit (EmitContext ec, bool leave_copy)
3802 ILGenerator ig = ec.ig;
3805 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3806 ec.EmitParameter (name, leave_copy, prepared, ref temp);
3810 if (!ec.MethodIsStatic)
3813 EmitLdArg (ig, arg_idx);
3817 ec.ig.Emit (OpCodes.Dup);
3820 // If we are a reference, we loaded on the stack a pointer
3821 // Now lets load the real value
3823 LoadFromPtr (ig, type);
3827 ec.ig.Emit (OpCodes.Dup);
3830 temp = new LocalTemporary (type);
3836 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3838 prepared = prepare_for_load;
3839 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3840 ec.EmitAssignParameter (name, source, leave_copy, prepare_for_load, ref temp);
3844 ILGenerator ig = ec.ig;
3849 if (!ec.MethodIsStatic)
3852 if (is_ref && !prepared)
3853 EmitLdArg (ig, arg_idx);
3858 ec.ig.Emit (OpCodes.Dup);
3862 temp = new LocalTemporary (type);
3866 StoreFromPtr (ig, type);
3872 ig.Emit (OpCodes.Starg_S, (byte) arg_idx);
3874 ig.Emit (OpCodes.Starg, arg_idx);
3878 public void AddressOf (EmitContext ec, AddressOp mode)
3880 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3881 ec.EmitAddressOfParameter (name);
3887 if (!ec.MethodIsStatic)
3892 ec.ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx);
3894 ec.ig.Emit (OpCodes.Ldarg, arg_idx);
3897 ec.ig.Emit (OpCodes.Ldarga_S, (byte) arg_idx);
3899 ec.ig.Emit (OpCodes.Ldarga, arg_idx);
3903 public override string ToString ()
3905 return "ParameterReference[" + name + "]";
3910 /// Used for arguments to New(), Invocation()
3912 public class Argument {
3913 public enum AType : byte {
3920 public readonly AType ArgType;
3921 public Expression Expr;
3923 public Argument (Expression expr, AType type)
3926 this.ArgType = type;
3929 public Argument (Expression expr)
3932 this.ArgType = AType.Expression;
3937 if (ArgType == AType.Ref || ArgType == AType.Out)
3938 return TypeManager.GetReferenceType (Expr.Type);
3944 public Parameter.Modifier Modifier
3949 return Parameter.Modifier.OUT;
3952 return Parameter.Modifier.REF;
3955 return Parameter.Modifier.NONE;
3960 public static string FullDesc (Argument a)
3962 if (a.ArgType == AType.ArgList)
3965 return (a.ArgType == AType.Ref ? "ref " :
3966 (a.ArgType == AType.Out ? "out " : "")) +
3967 TypeManager.CSharpName (a.Expr.Type);
3970 public bool ResolveMethodGroup (EmitContext ec)
3972 // FIXME: csc doesn't report any error if you try to use `ref' or
3973 // `out' in a delegate creation expression.
3974 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3981 public bool Resolve (EmitContext ec, Location loc)
3983 bool old_do_flow_analysis = ec.DoFlowAnalysis;
3984 ec.DoFlowAnalysis = true;
3986 // Verify that the argument is readable
3987 if (ArgType != AType.Out)
3988 Expr = Expr.Resolve (ec);
3990 // Verify that the argument is writeable
3991 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3992 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3994 ec.DoFlowAnalysis = old_do_flow_analysis;
3996 return Expr != null;
3999 public void Emit (EmitContext ec)
4001 if (ArgType != AType.Ref && ArgType != AType.Out) {
4006 AddressOp mode = AddressOp.Store;
4007 if (ArgType == AType.Ref)
4008 mode |= AddressOp.Load;
4010 IMemoryLocation ml = (IMemoryLocation) Expr;
4011 ParameterReference pr = ml as ParameterReference;
4014 // ParameterReferences might already be references, so we want
4015 // to pass just the value
4017 if (pr != null && pr.IsRef)
4020 ml.AddressOf (ec, mode);
4025 /// Invocation of methods or delegates.
4027 public class Invocation : ExpressionStatement {
4028 public readonly ArrayList Arguments;
4031 MethodBase method = null;
4034 // arguments is an ArrayList, but we do not want to typecast,
4035 // as it might be null.
4037 // FIXME: only allow expr to be a method invocation or a
4038 // delegate invocation (7.5.5)
4040 public Invocation (Expression expr, ArrayList arguments)
4043 Arguments = arguments;
4044 loc = expr.Location;
4047 public Expression Expr {
4054 /// Determines "better conversion" as specified in 14.4.2.3
4056 /// Returns : p if a->p is better,
4057 /// q if a->q is better,
4058 /// null if neither is better
4060 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q)
4062 Type argument_type = a.Type;
4063 Expression argument_expr = a.Expr;
4065 if (argument_type == null)
4066 throw new Exception ("Expression of type " + a.Expr +
4067 " does not resolve its type");
4069 if (p == null || q == null)
4070 throw new InternalErrorException ("BetterConversion Got a null conversion");
4075 if (argument_expr is NullLiteral) {
4077 // If the argument is null and one of the types to compare is 'object' and
4078 // the other is a reference type, we prefer the other.
4080 // This follows from the usual rules:
4081 // * There is an implicit conversion from 'null' to type 'object'
4082 // * There is an implicit conversion from 'null' to any reference type
4083 // * There is an implicit conversion from any reference type to type 'object'
4084 // * There is no implicit conversion from type 'object' to other reference types
4085 // => Conversion of 'null' to a reference type is better than conversion to 'object'
4087 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
4088 // null type. I think it used to be 'object' and thus needed a special
4089 // case to avoid the immediately following two checks.
4091 if (!p.IsValueType && q == TypeManager.object_type)
4093 if (!q.IsValueType && p == TypeManager.object_type)
4097 if (argument_type == p)
4100 if (argument_type == q)
4103 Expression p_tmp = new EmptyExpression (p);
4104 Expression q_tmp = new EmptyExpression (q);
4106 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
4107 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
4109 if (p_to_q && !q_to_p)
4112 if (q_to_p && !p_to_q)
4115 if (p == TypeManager.sbyte_type)
4116 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
4117 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4119 if (q == TypeManager.sbyte_type)
4120 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
4121 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4124 if (p == TypeManager.short_type)
4125 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
4126 q == TypeManager.uint64_type)
4128 if (q == TypeManager.short_type)
4129 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
4130 p == TypeManager.uint64_type)
4133 if (p == TypeManager.int32_type)
4134 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4136 if (q == TypeManager.int32_type)
4137 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4140 if (p == TypeManager.int64_type)
4141 if (q == TypeManager.uint64_type)
4143 if (q == TypeManager.int64_type)
4144 if (p == TypeManager.uint64_type)
4151 /// Determines "Better function" between candidate
4152 /// and the current best match
4155 /// Returns a boolean indicating :
4156 /// false if candidate ain't better
4157 /// true if candidate is better than the current best match
4159 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4160 MethodBase candidate, bool candidate_params,
4161 MethodBase best, bool best_params)
4163 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4164 ParameterData best_pd = TypeManager.GetParameterData (best);
4166 bool better_at_least_one = false;
4168 for (int j = 0; j < argument_count; ++j) {
4169 Argument a = (Argument) args [j];
4171 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
4172 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
4174 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4175 if (candidate_params)
4176 ct = TypeManager.GetElementType (ct);
4178 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4180 bt = TypeManager.GetElementType (bt);
4186 Type better = BetterConversion (ec, a, ct, bt);
4188 // for each argument, the conversion to 'ct' should be no worse than
4189 // the conversion to 'bt'.
4193 // for at least one argument, the conversion to 'ct' should be better than
4194 // the conversion to 'bt'.
4196 better_at_least_one = true;
4199 if (better_at_least_one)
4203 // This handles the case
4205 // Add (float f1, float f2, float f3);
4206 // Add (params decimal [] foo);
4208 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4209 // first candidate would've chosen as better.
4215 // This handles the following cases:
4217 // Trim () is better than Trim (params char[] chars)
4218 // Concat (string s1, string s2, string s3) is better than
4219 // Concat (string s1, params string [] srest)
4221 return !candidate_params && best_params;
4224 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4226 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4229 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4230 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4232 if (cand_pd.Count != base_pd.Count)
4235 for (int j = 0; j < cand_pd.Count; ++j) {
4236 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4237 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4238 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4239 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4241 if (cm != bm || ct != bt)
4248 public static string FullMethodDesc (MethodBase mb)
4254 if (mb is MethodInfo) {
4255 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4259 sb = new StringBuilder ();
4261 sb.Append (TypeManager.CSharpSignature (mb));
4262 return sb.ToString ();
4265 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4267 MemberInfo [] miset;
4268 MethodGroupExpr union;
4273 return (MethodGroupExpr) mg2;
4276 return (MethodGroupExpr) mg1;
4279 MethodGroupExpr left_set = null, right_set = null;
4280 int length1 = 0, length2 = 0;
4282 left_set = (MethodGroupExpr) mg1;
4283 length1 = left_set.Methods.Length;
4285 right_set = (MethodGroupExpr) mg2;
4286 length2 = right_set.Methods.Length;
4288 ArrayList common = new ArrayList ();
4290 foreach (MethodBase r in right_set.Methods){
4291 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4295 miset = new MemberInfo [length1 + length2 - common.Count];
4296 left_set.Methods.CopyTo (miset, 0);
4300 foreach (MethodBase r in right_set.Methods) {
4301 if (!common.Contains (r))
4305 union = new MethodGroupExpr (miset, loc);
4310 public static bool IsParamsMethodApplicable (EmitContext ec,
4311 ArrayList arguments, int arg_count,
4312 MethodBase candidate)
4314 return IsParamsMethodApplicable (
4315 ec, arguments, arg_count, candidate, false) ||
4316 IsParamsMethodApplicable (
4317 ec, arguments, arg_count, candidate, true);
4323 /// Determines if the candidate method, if a params method, is applicable
4324 /// in its expanded form to the given set of arguments
4326 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4327 int arg_count, MethodBase candidate,
4330 ParameterData pd = TypeManager.GetParameterData (candidate);
4332 int pd_count = pd.Count;
4336 int count = pd_count - 1;
4338 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4340 if (pd_count != arg_count)
4347 if (count > arg_count)
4350 if (pd_count == 1 && arg_count == 0)
4354 // If we have come this far, the case which
4355 // remains is when the number of parameters is
4356 // less than or equal to the argument count.
4358 for (int i = 0; i < count; ++i) {
4360 Argument a = (Argument) arguments [i];
4362 Parameter.Modifier a_mod = a.Modifier &
4363 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4364 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4365 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4367 if (a_mod == p_mod) {
4369 if (a_mod == Parameter.Modifier.NONE)
4370 if (!Convert.ImplicitConversionExists (ec,
4372 pd.ParameterType (i)))
4375 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4376 Type pt = pd.ParameterType (i);
4379 pt = TypeManager.GetReferenceType (pt);
4390 Argument a = (Argument) arguments [count];
4391 if (!(a.Expr is Arglist))
4397 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
4399 for (int i = pd_count - 1; i < arg_count; i++) {
4400 Argument a = (Argument) arguments [i];
4402 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4410 /// Determines if the candidate method is applicable (section 14.4.2.1)
4411 /// to the given set of arguments
4413 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4414 MethodBase candidate)
4416 ParameterData pd = TypeManager.GetParameterData (candidate);
4418 if (arg_count != pd.Count)
4421 for (int i = arg_count; i > 0; ) {
4424 Argument a = (Argument) arguments [i];
4426 Parameter.Modifier a_mod = a.Modifier &
4427 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4429 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4430 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4432 if (a_mod == p_mod) {
4433 Type pt = pd.ParameterType (i);
4435 if (a_mod == Parameter.Modifier.NONE) {
4436 if (!Convert.ImplicitConversionExists (ec, a.Expr, pt))
4450 static internal bool IsAncestralType (Type first_type, Type second_type)
4452 return first_type != second_type &&
4453 (TypeManager.IsSubclassOf (second_type, first_type) ||
4454 TypeManager.ImplementsInterface (second_type, first_type));
4458 /// Find the Applicable Function Members (7.4.2.1)
4460 /// me: Method Group expression with the members to select.
4461 /// it might contain constructors or methods (or anything
4462 /// that maps to a method).
4464 /// Arguments: ArrayList containing resolved Argument objects.
4466 /// loc: The location if we want an error to be reported, or a Null
4467 /// location for "probing" purposes.
4469 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4470 /// that is the best match of me on Arguments.
4473 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4474 ArrayList Arguments, bool may_fail,
4477 MethodBase method = null;
4478 bool method_params = false;
4479 Type applicable_type = null;
4481 ArrayList candidates = new ArrayList (2);
4482 ArrayList candidate_overrides = null;
4485 // Used to keep a map between the candidate
4486 // and whether it is being considered in its
4487 // normal or expanded form
4489 // false is normal form, true is expanded form
4491 Hashtable candidate_to_form = null;
4493 if (Arguments != null)
4494 arg_count = Arguments.Count;
4496 if ((me.Name == "Invoke") &&
4497 TypeManager.IsDelegateType (me.DeclaringType)) {
4498 Error_InvokeOnDelegate (loc);
4502 MethodBase[] methods = me.Methods;
4504 int nmethods = methods.Length;
4508 // Methods marked 'override' don't take part in 'applicable_type'
4509 // computation, nor in the actual overload resolution.
4510 // However, they still need to be emitted instead of a base virtual method.
4511 // So, we salt them away into the 'candidate_overrides' array.
4513 // In case of reflected methods, we replace each overriding method with
4514 // its corresponding base virtual method. This is to improve compatibility
4515 // with non-C# libraries which change the visibility of overrides (#75636)
4518 for (int i = 0; i < methods.Length; ++i) {
4519 MethodBase m = methods [i];
4520 if (TypeManager.IsOverride (m)) {
4521 if (candidate_overrides == null)
4522 candidate_overrides = new ArrayList ();
4523 candidate_overrides.Add (m);
4524 m = TypeManager.TryGetBaseDefinition (m);
4532 int applicable_errors = Report.Errors;
4535 // First we construct the set of applicable methods
4537 bool is_sorted = true;
4538 for (int i = 0; i < nmethods; i++){
4539 Type decl_type = methods [i].DeclaringType;
4542 // If we have already found an applicable method
4543 // we eliminate all base types (Section 14.5.5.1)
4545 if (applicable_type != null && IsAncestralType (decl_type, applicable_type))
4549 // Check if candidate is applicable (section 14.4.2.1)
4550 // Is candidate applicable in normal form?
4552 bool is_applicable = IsApplicable (ec, Arguments, arg_count, methods [i]);
4554 if (!is_applicable && IsParamsMethodApplicable (ec, Arguments, arg_count, methods [i])) {
4555 MethodBase candidate = methods [i];
4556 if (candidate_to_form == null)
4557 candidate_to_form = new PtrHashtable ();
4558 candidate_to_form [candidate] = candidate;
4559 // Candidate is applicable in expanded form
4560 is_applicable = true;
4566 candidates.Add (methods [i]);
4568 if (applicable_type == null)
4569 applicable_type = decl_type;
4570 else if (applicable_type != decl_type) {
4572 if (IsAncestralType (applicable_type, decl_type))
4573 applicable_type = decl_type;
4577 if (applicable_errors != Report.Errors)
4580 int candidate_top = candidates.Count;
4582 if (applicable_type == null) {
4584 // Okay so we have failed to find anything so we
4585 // return by providing info about the closest match
4587 int errors = Report.Errors;
4588 for (int i = 0; i < nmethods; ++i) {
4589 MethodBase c = (MethodBase) methods [i];
4590 ParameterData pd = TypeManager.GetParameterData (c);
4592 if (pd.Count != arg_count)
4595 VerifyArgumentsCompat (ec, Arguments, arg_count,
4596 c, false, null, may_fail, loc);
4598 if (!may_fail && errors == Report.Errors)
4599 throw new InternalErrorException (
4600 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4601 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4606 if (!may_fail && errors == Report.Errors) {
4607 string report_name = me.Name;
4608 if (report_name == ".ctor")
4609 report_name = me.DeclaringType.ToString ();
4610 Error_WrongNumArguments (loc, report_name, arg_count);
4618 // At this point, applicable_type is _one_ of the most derived types
4619 // in the set of types containing the methods in this MethodGroup.
4620 // Filter the candidates so that they only contain methods from the
4621 // most derived types.
4624 int finalized = 0; // Number of finalized candidates
4627 // Invariant: applicable_type is a most derived type
4629 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4630 // eliminating all it's base types. At the same time, we'll also move
4631 // every unrelated type to the end of the array, and pick the next
4632 // 'applicable_type'.
4634 Type next_applicable_type = null;
4635 int j = finalized; // where to put the next finalized candidate
4636 int k = finalized; // where to put the next undiscarded candidate
4637 for (int i = finalized; i < candidate_top; ++i) {
4638 MethodBase candidate = (MethodBase) candidates [i];
4639 Type decl_type = candidate.DeclaringType;
4641 if (decl_type == applicable_type) {
4642 candidates [k++] = candidates [j];
4643 candidates [j++] = candidates [i];
4647 if (IsAncestralType (decl_type, applicable_type))
4650 if (next_applicable_type != null &&
4651 IsAncestralType (decl_type, next_applicable_type))
4654 candidates [k++] = candidates [i];
4656 if (next_applicable_type == null ||
4657 IsAncestralType (next_applicable_type, decl_type))
4658 next_applicable_type = decl_type;
4661 applicable_type = next_applicable_type;
4664 } while (applicable_type != null);
4668 // Now we actually find the best method
4671 method = (MethodBase) candidates [0];
4672 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4673 for (int ix = 1; ix < candidate_top; ix++){
4674 MethodBase candidate = (MethodBase) candidates [ix];
4676 if (candidate == method)
4679 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4681 if (BetterFunction (ec, Arguments, arg_count,
4682 candidate, cand_params,
4683 method, method_params)) {
4685 method_params = cand_params;
4689 // Now check that there are no ambiguities i.e the selected method
4690 // should be better than all the others
4692 MethodBase ambiguous = null;
4693 for (int ix = 0; ix < candidate_top; ix++){
4694 MethodBase candidate = (MethodBase) candidates [ix];
4696 if (candidate == method)
4699 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4700 if (!BetterFunction (ec, Arguments, arg_count,
4701 method, method_params,
4702 candidate, cand_params)) {
4703 Report.SymbolRelatedToPreviousError (candidate);
4704 ambiguous = candidate;
4708 if (ambiguous != null) {
4709 Report.SymbolRelatedToPreviousError (method);
4710 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4711 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4716 // If the method is a virtual function, pick an override closer to the LHS type.
4718 if (!me.IsBase && method.IsVirtual) {
4719 if (TypeManager.IsOverride (method))
4720 throw new InternalErrorException (
4721 "Should not happen. An 'override' method took part in overload resolution: " + method);
4723 if (candidate_overrides != null)
4724 foreach (MethodBase candidate in candidate_overrides) {
4725 if (IsOverride (candidate, method))
4731 // And now check if the arguments are all
4732 // compatible, perform conversions if
4733 // necessary etc. and return if everything is
4736 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4737 method_params, null, may_fail, loc))
4743 IMethodData data = TypeManager.GetMethod (method);
4745 data.SetMemberIsUsed ();
4750 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4752 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4753 name, arg_count.ToString ());
4756 static void Error_InvokeOnDelegate (Location loc)
4758 Report.Error (1533, loc,
4759 "Invoke cannot be called directly on a delegate");
4762 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4763 Type delegate_type, Argument a, ParameterData expected_par)
4765 if (delegate_type == null)
4766 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4767 TypeManager.CSharpSignature (method));
4769 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4770 TypeManager.CSharpName (delegate_type));
4772 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4774 string index = (idx + 1).ToString ();
4775 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4776 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4777 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4778 index, Parameter.GetModifierSignature (a.Modifier));
4780 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4781 index, Parameter.GetModifierSignature (mod));
4783 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4784 index, Argument.FullDesc (a), expected_par.ParameterDesc (idx));
4788 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4789 int arg_count, MethodBase method,
4790 bool chose_params_expanded,
4791 Type delegate_type, bool may_fail,
4794 ParameterData pd = TypeManager.GetParameterData (method);
4796 for (j = 0; j < arg_count; j++) {
4797 Argument a = (Argument) Arguments [j];
4798 Expression a_expr = a.Expr;
4799 Type parameter_type = pd.ParameterType (j);
4800 Parameter.Modifier pm = pd.ParameterModifier (j);
4801 Parameter.Modifier am = a.Modifier;
4803 if (pm == Parameter.Modifier.ARGLIST) {
4804 if (!(a.Expr is Arglist))
4809 if (pm == Parameter.Modifier.PARAMS) {
4810 pm = Parameter.Modifier.NONE;
4811 if (chose_params_expanded)
4812 parameter_type = TypeManager.GetElementType (parameter_type);
4818 if (!a.Type.Equals (parameter_type)) {
4819 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4822 Expression conv = Convert.ImplicitConversion (ec, a_expr, parameter_type, loc);
4826 // Update the argument with the implicit conversion
4831 if (parameter_type.IsPointer && !ec.InUnsafe) {
4841 Error_InvalidArguments (loc, j, method, delegate_type, (Argument) Arguments [j], pd);
4845 private bool resolved = false;
4846 public override Expression DoResolve (EmitContext ec)
4849 return this.method == null ? null : this;
4853 // First, resolve the expression that is used to
4854 // trigger the invocation
4856 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4860 if (!(expr is MethodGroupExpr)) {
4861 Type expr_type = expr.Type;
4863 if (expr_type != null){
4864 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4866 return (new DelegateInvocation (
4867 this.expr, Arguments, loc)).Resolve (ec);
4871 if (!(expr is MethodGroupExpr)){
4872 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4877 // Next, evaluate all the expressions in the argument list
4879 if (Arguments != null){
4880 foreach (Argument a in Arguments){
4881 if (!a.Resolve (ec, loc))
4886 MethodGroupExpr mg = (MethodGroupExpr) expr;
4887 MethodBase method = OverloadResolve (ec, mg, Arguments, false, loc);
4892 MethodInfo mi = method as MethodInfo;
4894 type = TypeManager.TypeToCoreType (mi.ReturnType);
4895 Expression iexpr = mg.InstanceExpression;
4897 if (iexpr == null ||
4898 iexpr is This || iexpr is EmptyExpression ||
4899 mg.IdenticalTypeName) {
4900 mg.InstanceExpression = null;
4902 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
4906 if (iexpr == null || iexpr is EmptyExpression) {
4907 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
4913 if (type.IsPointer){
4921 // Only base will allow this invocation to happen.
4923 if (mg.IsBase && method.IsAbstract){
4924 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
4928 if (Arguments == null && method.Name == "Finalize") {
4930 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
4932 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
4936 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
4940 if (mg.InstanceExpression != null)
4941 mg.InstanceExpression.CheckMarshalByRefAccess ();
4943 eclass = ExprClass.Value;
4944 this.method = method;
4948 bool IsSpecialMethodInvocation (MethodBase method)
4950 IMethodData md = TypeManager.GetMethod (method);
4952 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
4955 if (!TypeManager.IsSpecialMethod (method))
4958 int args = TypeManager.GetParameterData (method).Count;
4959 if (method.Name.StartsWith ("get_") && args > 0)
4961 else if (method.Name.StartsWith ("set_") && args > 2)
4964 // TODO: check operators and events as well ?
4967 Report.SymbolRelatedToPreviousError (method);
4968 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
4969 TypeManager.CSharpSignature (method, true));
4975 // Emits the list of arguments as an array
4977 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
4979 ILGenerator ig = ec.ig;
4980 int count = arguments.Count - idx;
4981 Argument a = (Argument) arguments [idx];
4982 Type t = a.Expr.Type;
4984 IntConstant.EmitInt (ig, count);
4985 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
4987 int top = arguments.Count;
4988 for (int j = idx; j < top; j++){
4989 a = (Argument) arguments [j];
4991 ig.Emit (OpCodes.Dup);
4992 IntConstant.EmitInt (ig, j - idx);
4995 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj);
4997 ig.Emit (OpCodes.Ldelema, t);
5002 ig.Emit (OpCodes.Stobj, t);
5009 /// Emits a list of resolved Arguments that are in the arguments
5012 /// The MethodBase argument might be null if the
5013 /// emission of the arguments is known not to contain
5014 /// a `params' field (for example in constructors or other routines
5015 /// that keep their arguments in this structure)
5017 /// if `dup_args' is true, a copy of the arguments will be left
5018 /// on the stack. If `dup_args' is true, you can specify `this_arg'
5019 /// which will be duplicated before any other args. Only EmitCall
5020 /// should be using this interface.
5022 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
5024 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
5025 int top = arguments == null ? 0 : arguments.Count;
5026 LocalTemporary [] temps = null;
5028 if (dup_args && top != 0)
5029 temps = new LocalTemporary [top];
5031 for (int i = 0; i < top; i++){
5032 Argument a = (Argument) arguments [i];
5035 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
5037 // Special case if we are passing the same data as the
5038 // params argument, do not put it in an array.
5040 if (pd.ParameterType (i) == a.Type)
5043 EmitParams (ec, i, arguments);
5050 ec.ig.Emit (OpCodes.Dup);
5051 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
5056 if (this_arg != null)
5059 for (int i = 0; i < top; i ++)
5060 temps [i].Emit (ec);
5063 if (pd != null && pd.Count > top &&
5064 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
5065 ILGenerator ig = ec.ig;
5067 IntConstant.EmitInt (ig, 0);
5068 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
5072 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
5074 ParameterData pd = TypeManager.GetParameterData (mb);
5076 if (arguments == null)
5077 return new Type [0];
5079 Argument a = (Argument) arguments [pd.Count - 1];
5080 Arglist list = (Arglist) a.Expr;
5082 return list.ArgumentTypes;
5086 /// This checks the ConditionalAttribute on the method
5088 static bool IsMethodExcluded (MethodBase method)
5090 if (method.IsConstructor)
5093 IMethodData md = TypeManager.GetMethod (method);
5095 return md.IsExcluded ();
5097 // For some methods (generated by delegate class) GetMethod returns null
5098 // because they are not included in builder_to_method table
5099 if (method.DeclaringType is TypeBuilder)
5102 return AttributeTester.IsConditionalMethodExcluded (method);
5106 /// is_base tells whether we want to force the use of the `call'
5107 /// opcode instead of using callvirt. Call is required to call
5108 /// a specific method, while callvirt will always use the most
5109 /// recent method in the vtable.
5111 /// is_static tells whether this is an invocation on a static method
5113 /// instance_expr is an expression that represents the instance
5114 /// it must be non-null if is_static is false.
5116 /// method is the method to invoke.
5118 /// Arguments is the list of arguments to pass to the method or constructor.
5120 public static void EmitCall (EmitContext ec, bool is_base,
5121 bool is_static, Expression instance_expr,
5122 MethodBase method, ArrayList Arguments, Location loc)
5124 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
5127 // `dup_args' leaves an extra copy of the arguments on the stack
5128 // `omit_args' does not leave any arguments at all.
5129 // So, basically, you could make one call with `dup_args' set to true,
5130 // and then another with `omit_args' set to true, and the two calls
5131 // would have the same set of arguments. However, each argument would
5132 // only have been evaluated once.
5133 public static void EmitCall (EmitContext ec, bool is_base,
5134 bool is_static, Expression instance_expr,
5135 MethodBase method, ArrayList Arguments, Location loc,
5136 bool dup_args, bool omit_args)
5138 ILGenerator ig = ec.ig;
5139 bool struct_call = false;
5140 bool this_call = false;
5141 LocalTemporary this_arg = null;
5143 Type decl_type = method.DeclaringType;
5145 if (!RootContext.StdLib) {
5146 // Replace any calls to the system's System.Array type with calls to
5147 // the newly created one.
5148 if (method == TypeManager.system_int_array_get_length)
5149 method = TypeManager.int_array_get_length;
5150 else if (method == TypeManager.system_int_array_get_rank)
5151 method = TypeManager.int_array_get_rank;
5152 else if (method == TypeManager.system_object_array_clone)
5153 method = TypeManager.object_array_clone;
5154 else if (method == TypeManager.system_int_array_get_length_int)
5155 method = TypeManager.int_array_get_length_int;
5156 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5157 method = TypeManager.int_array_get_lower_bound_int;
5158 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5159 method = TypeManager.int_array_get_upper_bound_int;
5160 else if (method == TypeManager.system_void_array_copyto_array_int)
5161 method = TypeManager.void_array_copyto_array_int;
5164 if (!ec.IsInObsoleteScope) {
5166 // This checks ObsoleteAttribute on the method and on the declaring type
5168 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5170 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5172 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5174 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5178 if (IsMethodExcluded (method))
5182 if (instance_expr == EmptyExpression.Null) {
5183 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5187 this_call = instance_expr is This;
5188 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5192 // If this is ourselves, push "this"
5197 // Push the instance expression
5199 if (TypeManager.IsValueType (instance_expr.Type)) {
5201 // Special case: calls to a function declared in a
5202 // reference-type with a value-type argument need
5203 // to have their value boxed.
5204 if (decl_type.IsValueType) {
5206 // If the expression implements IMemoryLocation, then
5207 // we can optimize and use AddressOf on the
5210 // If not we have to use some temporary storage for
5212 if (instance_expr is IMemoryLocation) {
5213 ((IMemoryLocation)instance_expr).
5214 AddressOf (ec, AddressOp.LoadStore);
5216 LocalTemporary temp = new LocalTemporary (instance_expr.Type);
5217 instance_expr.Emit (ec);
5219 temp.AddressOf (ec, AddressOp.Load);
5222 // avoid the overhead of doing this all the time.
5224 t = TypeManager.GetReferenceType (instance_expr.Type);
5226 instance_expr.Emit (ec);
5227 ig.Emit (OpCodes.Box, instance_expr.Type);
5228 t = TypeManager.object_type;
5231 instance_expr.Emit (ec);
5232 t = instance_expr.Type;
5236 ig.Emit (OpCodes.Dup);
5237 if (Arguments != null && Arguments.Count != 0) {
5238 this_arg = new LocalTemporary (t);
5239 this_arg.Store (ec);
5246 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5249 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5250 call_op = OpCodes.Call;
5252 call_op = OpCodes.Callvirt;
5254 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5255 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5256 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5263 // and DoFoo is not virtual, you can omit the callvirt,
5264 // because you don't need the null checking behavior.
5266 if (method is MethodInfo)
5267 ig.Emit (call_op, (MethodInfo) method);
5269 ig.Emit (call_op, (ConstructorInfo) method);
5272 public override void Emit (EmitContext ec)
5274 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5276 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5279 public override void EmitStatement (EmitContext ec)
5284 // Pop the return value if there is one
5286 if (method is MethodInfo){
5287 Type ret = ((MethodInfo)method).ReturnType;
5288 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5289 ec.ig.Emit (OpCodes.Pop);
5294 public class InvocationOrCast : ExpressionStatement
5297 Expression argument;
5299 public InvocationOrCast (Expression expr, Expression argument)
5302 this.argument = argument;
5303 this.loc = expr.Location;
5306 public override Expression DoResolve (EmitContext ec)
5309 // First try to resolve it as a cast.
5311 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5313 Cast cast = new Cast (te, argument, loc);
5314 return cast.Resolve (ec);
5318 // This can either be a type or a delegate invocation.
5319 // Let's just resolve it and see what we'll get.
5321 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5326 // Ok, so it's a Cast.
5328 if (expr.eclass == ExprClass.Type) {
5329 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5330 return cast.Resolve (ec);
5334 // It's a delegate invocation.
5336 if (!TypeManager.IsDelegateType (expr.Type)) {
5337 Error (149, "Method name expected");
5341 ArrayList args = new ArrayList ();
5342 args.Add (new Argument (argument, Argument.AType.Expression));
5343 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5344 return invocation.Resolve (ec);
5349 Error (201, "Only assignment, call, increment, decrement and new object " +
5350 "expressions can be used as a statement");
5353 public override ExpressionStatement ResolveStatement (EmitContext ec)
5356 // First try to resolve it as a cast.
5358 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5365 // This can either be a type or a delegate invocation.
5366 // Let's just resolve it and see what we'll get.
5368 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5369 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5375 // It's a delegate invocation.
5377 if (!TypeManager.IsDelegateType (expr.Type)) {
5378 Error (149, "Method name expected");
5382 ArrayList args = new ArrayList ();
5383 args.Add (new Argument (argument, Argument.AType.Expression));
5384 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5385 return invocation.ResolveStatement (ec);
5388 public override void Emit (EmitContext ec)
5390 throw new Exception ("Cannot happen");
5393 public override void EmitStatement (EmitContext ec)
5395 throw new Exception ("Cannot happen");
5400 // This class is used to "disable" the code generation for the
5401 // temporary variable when initializing value types.
5403 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5404 public void AddressOf (EmitContext ec, AddressOp Mode)
5411 /// Implements the new expression
5413 public class New : ExpressionStatement, IMemoryLocation {
5414 public readonly ArrayList Arguments;
5417 // During bootstrap, it contains the RequestedType,
5418 // but if `type' is not null, it *might* contain a NewDelegate
5419 // (because of field multi-initialization)
5421 public Expression RequestedType;
5423 MethodBase method = null;
5426 // If set, the new expression is for a value_target, and
5427 // we will not leave anything on the stack.
5429 Expression value_target;
5430 bool value_target_set = false;
5432 public New (Expression requested_type, ArrayList arguments, Location l)
5434 RequestedType = requested_type;
5435 Arguments = arguments;
5439 public bool SetValueTypeVariable (Expression value)
5441 value_target = value;
5442 value_target_set = true;
5443 if (!(value_target is IMemoryLocation)){
5444 Error_UnexpectedKind (null, "variable", loc);
5451 // This function is used to disable the following code sequence for
5452 // value type initialization:
5454 // AddressOf (temporary)
5458 // Instead the provide will have provided us with the address on the
5459 // stack to store the results.
5461 static Expression MyEmptyExpression;
5463 public void DisableTemporaryValueType ()
5465 if (MyEmptyExpression == null)
5466 MyEmptyExpression = new EmptyAddressOf ();
5469 // To enable this, look into:
5470 // test-34 and test-89 and self bootstrapping.
5472 // For instance, we can avoid a copy by using `newobj'
5473 // instead of Call + Push-temp on value types.
5474 // value_target = MyEmptyExpression;
5479 /// Converts complex core type syntax like 'new int ()' to simple constant
5481 public static Constant Constantify (Type t)
5483 if (t == TypeManager.int32_type)
5484 return new IntConstant (0, Location.Null);
5485 if (t == TypeManager.uint32_type)
5486 return new UIntConstant (0, Location.Null);
5487 if (t == TypeManager.int64_type)
5488 return new LongConstant (0, Location.Null);
5489 if (t == TypeManager.uint64_type)
5490 return new ULongConstant (0, Location.Null);
5491 if (t == TypeManager.float_type)
5492 return new FloatConstant (0, Location.Null);
5493 if (t == TypeManager.double_type)
5494 return new DoubleConstant (0, Location.Null);
5495 if (t == TypeManager.short_type)
5496 return new ShortConstant (0, Location.Null);
5497 if (t == TypeManager.ushort_type)
5498 return new UShortConstant (0, Location.Null);
5499 if (t == TypeManager.sbyte_type)
5500 return new SByteConstant (0, Location.Null);
5501 if (t == TypeManager.byte_type)
5502 return new ByteConstant (0, Location.Null);
5503 if (t == TypeManager.char_type)
5504 return new CharConstant ('\0', Location.Null);
5505 if (t == TypeManager.bool_type)
5506 return new BoolConstant (false, Location.Null);
5507 if (t == TypeManager.decimal_type)
5508 return new DecimalConstant (0, Location.Null);
5514 // Checks whether the type is an interface that has the
5515 // [ComImport, CoClass] attributes and must be treated
5518 public Expression CheckComImport (EmitContext ec)
5520 if (!type.IsInterface)
5524 // Turn the call into:
5525 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5527 Type real_class = AttributeTester.GetCoClassAttribute (type);
5528 if (real_class == null)
5531 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5532 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5533 return cast.Resolve (ec);
5536 public override Expression DoResolve (EmitContext ec)
5539 // The New DoResolve might be called twice when initializing field
5540 // expressions (see EmitFieldInitializers, the call to
5541 // GetInitializerExpression will perform a resolve on the expression,
5542 // and later the assign will trigger another resolution
5544 // This leads to bugs (#37014)
5547 if (RequestedType is NewDelegate)
5548 return RequestedType;
5552 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5558 if (Arguments == null) {
5559 Expression c = Constantify (type);
5564 if (TypeManager.IsDelegateType (type)) {
5565 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5566 if (RequestedType != null)
5567 if (!(RequestedType is DelegateCreation))
5568 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5569 return RequestedType;
5572 if (type.IsAbstract && type.IsSealed) {
5573 Report.SymbolRelatedToPreviousError (type);
5574 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5578 if (type.IsInterface || type.IsAbstract){
5579 RequestedType = CheckComImport (ec);
5580 if (RequestedType != null)
5581 return RequestedType;
5583 Report.SymbolRelatedToPreviousError (type);
5584 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5588 bool is_struct = type.IsValueType;
5589 eclass = ExprClass.Value;
5592 // SRE returns a match for .ctor () on structs (the object constructor),
5593 // so we have to manually ignore it.
5595 if (is_struct && Arguments == null)
5598 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5599 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5600 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5605 MethodGroupExpr mg = ml as MethodGroupExpr;
5608 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5612 if (Arguments != null){
5613 foreach (Argument a in Arguments){
5614 if (!a.Resolve (ec, loc))
5619 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5620 if (method == null) {
5621 if (almostMatchedMembers.Count != 0)
5622 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5630 // This DoEmit can be invoked in two contexts:
5631 // * As a mechanism that will leave a value on the stack (new object)
5632 // * As one that wont (init struct)
5634 // You can control whether a value is required on the stack by passing
5635 // need_value_on_stack. The code *might* leave a value on the stack
5636 // so it must be popped manually
5638 // If we are dealing with a ValueType, we have a few
5639 // situations to deal with:
5641 // * The target is a ValueType, and we have been provided
5642 // the instance (this is easy, we are being assigned).
5644 // * The target of New is being passed as an argument,
5645 // to a boxing operation or a function that takes a
5648 // In this case, we need to create a temporary variable
5649 // that is the argument of New.
5651 // Returns whether a value is left on the stack
5653 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5655 bool is_value_type = TypeManager.IsValueType (type);
5656 ILGenerator ig = ec.ig;
5661 // Allow DoEmit() to be called multiple times.
5662 // We need to create a new LocalTemporary each time since
5663 // you can't share LocalBuilders among ILGeneators.
5664 if (!value_target_set)
5665 value_target = new LocalTemporary (type);
5667 ml = (IMemoryLocation) value_target;
5668 ml.AddressOf (ec, AddressOp.Store);
5672 Invocation.EmitArguments (ec, method, Arguments, false, null);
5676 ig.Emit (OpCodes.Initobj, type);
5678 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5679 if (need_value_on_stack){
5680 value_target.Emit (ec);
5685 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5690 public override void Emit (EmitContext ec)
5695 public override void EmitStatement (EmitContext ec)
5697 if (DoEmit (ec, false))
5698 ec.ig.Emit (OpCodes.Pop);
5701 public void AddressOf (EmitContext ec, AddressOp Mode)
5703 if (!type.IsValueType){
5705 // We throw an exception. So far, I believe we only need to support
5707 // foreach (int j in new StructType ())
5710 throw new Exception ("AddressOf should not be used for classes");
5713 if (!value_target_set)
5714 value_target = new LocalTemporary (type);
5716 IMemoryLocation ml = (IMemoryLocation) value_target;
5717 ml.AddressOf (ec, AddressOp.Store);
5719 Invocation.EmitArguments (ec, method, Arguments, false, null);
5722 ec.ig.Emit (OpCodes.Initobj, type);
5724 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5726 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5731 /// 14.5.10.2: Represents an array creation expression.
5735 /// There are two possible scenarios here: one is an array creation
5736 /// expression that specifies the dimensions and optionally the
5737 /// initialization data and the other which does not need dimensions
5738 /// specified but where initialization data is mandatory.
5740 public class ArrayCreation : Expression {
5741 Expression requested_base_type;
5742 ArrayList initializers;
5745 // The list of Argument types.
5746 // This is used to construct the `newarray' or constructor signature
5748 ArrayList arguments;
5751 // Method used to create the array object.
5753 MethodBase new_method = null;
5755 Type array_element_type;
5756 Type underlying_type;
5757 bool is_one_dimensional = false;
5758 bool is_builtin_type = false;
5759 bool expect_initializers = false;
5760 int num_arguments = 0;
5764 ArrayList array_data;
5768 // The number of constants in array initializers
5769 int const_initializers_count;
5771 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5773 this.requested_base_type = requested_base_type;
5774 this.initializers = initializers;
5778 arguments = new ArrayList ();
5780 foreach (Expression e in exprs) {
5781 arguments.Add (new Argument (e, Argument.AType.Expression));
5786 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5788 this.requested_base_type = requested_base_type;
5789 this.initializers = initializers;
5793 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5795 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5797 //dimensions = tmp.Length - 1;
5798 expect_initializers = true;
5801 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5803 StringBuilder sb = new StringBuilder (rank);
5806 for (int i = 1; i < idx_count; i++)
5811 return new ComposedCast (base_type, sb.ToString (), loc);
5814 void Error_IncorrectArrayInitializer ()
5816 Error (178, "Invalid rank specifier: expected `,' or `]'");
5819 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5821 if (specified_dims) {
5822 Argument a = (Argument) arguments [idx];
5824 if (!a.Resolve (ec, loc))
5827 Constant c = a.Expr as Constant;
5829 c = c.ToType (TypeManager.int32_type, a.Expr.Location);
5833 Report.Error (150, a.Expr.Location, "A constant value is expected");
5837 int value = (int) c.GetValue ();
5839 if (value != probe.Count) {
5840 Error_IncorrectArrayInitializer ();
5844 bounds [idx] = value;
5847 int child_bounds = -1;
5848 for (int i = 0; i < probe.Count; ++i) {
5849 object o = probe [i];
5850 if (o is ArrayList) {
5851 ArrayList sub_probe = o as ArrayList;
5852 int current_bounds = sub_probe.Count;
5854 if (child_bounds == -1)
5855 child_bounds = current_bounds;
5857 else if (child_bounds != current_bounds){
5858 Error_IncorrectArrayInitializer ();
5861 if (idx + 1 >= dimensions){
5862 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
5866 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
5870 if (child_bounds != -1){
5871 Error_IncorrectArrayInitializer ();
5875 Expression tmp = (Expression) o;
5876 tmp = tmp.Resolve (ec);
5880 Expression conv = Convert.ImplicitConversionRequired (
5881 ec, tmp, underlying_type, loc);
5886 // Initializers with the default values can be ignored
5887 Constant c = tmp as Constant;
5889 if (c.IsDefaultInitializer (array_element_type)) {
5893 ++const_initializers_count;
5896 // Used to invalidate static initializer
5897 const_initializers_count = int.MinValue;
5900 array_data.Add (conv);
5907 public void UpdateIndices ()
5910 for (ArrayList probe = initializers; probe != null;) {
5911 if (probe.Count > 0 && probe [0] is ArrayList) {
5912 Expression e = new IntConstant (probe.Count, Location.Null);
5913 arguments.Add (new Argument (e, Argument.AType.Expression));
5915 bounds [i++] = probe.Count;
5917 probe = (ArrayList) probe [0];
5920 Expression e = new IntConstant (probe.Count, Location.Null);
5921 arguments.Add (new Argument (e, Argument.AType.Expression));
5923 bounds [i++] = probe.Count;
5930 bool ResolveInitializers (EmitContext ec)
5932 if (initializers == null) {
5933 return !expect_initializers;
5936 if (underlying_type == null)
5940 // We use this to store all the date values in the order in which we
5941 // will need to store them in the byte blob later
5943 array_data = new ArrayList ();
5944 bounds = new System.Collections.Specialized.HybridDictionary ();
5946 if (arguments != null)
5947 return CheckIndices (ec, initializers, 0, true);
5949 arguments = new ArrayList ();
5951 if (!CheckIndices (ec, initializers, 0, false))
5956 if (arguments.Count != dimensions) {
5957 Error_IncorrectArrayInitializer ();
5965 // Creates the type of the array
5967 bool LookupType (EmitContext ec)
5969 StringBuilder array_qualifier = new StringBuilder (rank);
5972 // `In the first form allocates an array instace of the type that results
5973 // from deleting each of the individual expression from the expression list'
5975 if (num_arguments > 0) {
5976 array_qualifier.Append ("[");
5977 for (int i = num_arguments-1; i > 0; i--)
5978 array_qualifier.Append (",");
5979 array_qualifier.Append ("]");
5985 TypeExpr array_type_expr;
5986 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
5987 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
5988 if (array_type_expr == null)
5991 type = array_type_expr.Type;
5992 underlying_type = TypeManager.GetElementType (type);
5993 dimensions = type.GetArrayRank ();
5998 public override Expression DoResolve (EmitContext ec)
6003 if (!LookupType (ec))
6006 array_element_type = TypeManager.GetElementType (type);
6007 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
6008 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
6013 // First step is to validate the initializers and fill
6014 // in any missing bits
6016 if (!ResolveInitializers (ec))
6020 if (arguments == null)
6023 arg_count = arguments.Count;
6024 foreach (Argument a in arguments){
6025 if (!a.Resolve (ec, loc))
6028 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
6029 if (real_arg == null)
6036 if (arg_count == 1) {
6037 is_one_dimensional = true;
6038 eclass = ExprClass.Value;
6042 is_builtin_type = TypeManager.IsBuiltinType (type);
6044 if (is_builtin_type) {
6047 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
6048 AllBindingFlags, loc);
6050 if (!(ml is MethodGroupExpr)) {
6051 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
6056 Error (-6, "New invocation: Can not find a constructor for " +
6057 "this argument list");
6061 new_method = Invocation.OverloadResolve (
6062 ec, (MethodGroupExpr) ml, arguments, false, loc);
6064 if (new_method == null) {
6065 Error (-6, "New invocation: Can not find a constructor for " +
6066 "this argument list");
6070 eclass = ExprClass.Value;
6073 ModuleBuilder mb = CodeGen.Module.Builder;
6074 ArrayList args = new ArrayList ();
6076 if (arguments != null) {
6077 for (int i = 0; i < arg_count; i++)
6078 args.Add (TypeManager.int32_type);
6081 Type [] arg_types = null;
6084 arg_types = new Type [args.Count];
6086 args.CopyTo (arg_types, 0);
6088 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6091 if (new_method == null) {
6092 Error (-6, "New invocation: Can not find a constructor for " +
6093 "this argument list");
6097 eclass = ExprClass.Value;
6102 byte [] MakeByteBlob ()
6107 int count = array_data.Count;
6109 if (underlying_type.IsEnum)
6110 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
6112 factor = GetTypeSize (underlying_type);
6114 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
6116 data = new byte [(count * factor + 4) & ~3];
6119 for (int i = 0; i < count; ++i) {
6120 object v = array_data [i];
6122 if (v is EnumConstant)
6123 v = ((EnumConstant) v).Child;
6125 if (v is Constant && !(v is StringConstant))
6126 v = ((Constant) v).GetValue ();
6132 if (underlying_type == TypeManager.int64_type){
6133 if (!(v is Expression)){
6134 long val = (long) v;
6136 for (int j = 0; j < factor; ++j) {
6137 data [idx + j] = (byte) (val & 0xFF);
6141 } else if (underlying_type == TypeManager.uint64_type){
6142 if (!(v is Expression)){
6143 ulong val = (ulong) v;
6145 for (int j = 0; j < factor; ++j) {
6146 data [idx + j] = (byte) (val & 0xFF);
6150 } else if (underlying_type == TypeManager.float_type) {
6151 if (!(v is Expression)){
6152 element = BitConverter.GetBytes ((float) v);
6154 for (int j = 0; j < factor; ++j)
6155 data [idx + j] = element [j];
6157 } else if (underlying_type == TypeManager.double_type) {
6158 if (!(v is Expression)){
6159 element = BitConverter.GetBytes ((double) v);
6161 for (int j = 0; j < factor; ++j)
6162 data [idx + j] = element [j];
6164 } else if (underlying_type == TypeManager.char_type){
6165 if (!(v is Expression)){
6166 int val = (int) ((char) v);
6168 data [idx] = (byte) (val & 0xff);
6169 data [idx+1] = (byte) (val >> 8);
6171 } else if (underlying_type == TypeManager.short_type){
6172 if (!(v is Expression)){
6173 int val = (int) ((short) v);
6175 data [idx] = (byte) (val & 0xff);
6176 data [idx+1] = (byte) (val >> 8);
6178 } else if (underlying_type == TypeManager.ushort_type){
6179 if (!(v is Expression)){
6180 int val = (int) ((ushort) v);
6182 data [idx] = (byte) (val & 0xff);
6183 data [idx+1] = (byte) (val >> 8);
6185 } else if (underlying_type == TypeManager.int32_type) {
6186 if (!(v is Expression)){
6189 data [idx] = (byte) (val & 0xff);
6190 data [idx+1] = (byte) ((val >> 8) & 0xff);
6191 data [idx+2] = (byte) ((val >> 16) & 0xff);
6192 data [idx+3] = (byte) (val >> 24);
6194 } else if (underlying_type == TypeManager.uint32_type) {
6195 if (!(v is Expression)){
6196 uint val = (uint) v;
6198 data [idx] = (byte) (val & 0xff);
6199 data [idx+1] = (byte) ((val >> 8) & 0xff);
6200 data [idx+2] = (byte) ((val >> 16) & 0xff);
6201 data [idx+3] = (byte) (val >> 24);
6203 } else if (underlying_type == TypeManager.sbyte_type) {
6204 if (!(v is Expression)){
6205 sbyte val = (sbyte) v;
6206 data [idx] = (byte) val;
6208 } else if (underlying_type == TypeManager.byte_type) {
6209 if (!(v is Expression)){
6210 byte val = (byte) v;
6211 data [idx] = (byte) val;
6213 } else if (underlying_type == TypeManager.bool_type) {
6214 if (!(v is Expression)){
6215 bool val = (bool) v;
6216 data [idx] = (byte) (val ? 1 : 0);
6218 } else if (underlying_type == TypeManager.decimal_type){
6219 if (!(v is Expression)){
6220 int [] bits = Decimal.GetBits ((decimal) v);
6223 // FIXME: For some reason, this doesn't work on the MS runtime.
6224 int [] nbits = new int [4];
6225 nbits [0] = bits [3];
6226 nbits [1] = bits [2];
6227 nbits [2] = bits [0];
6228 nbits [3] = bits [1];
6230 for (int j = 0; j < 4; j++){
6231 data [p++] = (byte) (nbits [j] & 0xff);
6232 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6233 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6234 data [p++] = (byte) (nbits [j] >> 24);
6238 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6247 // Emits the initializers for the array
6249 void EmitStaticInitializers (EmitContext ec)
6252 // First, the static data
6255 ILGenerator ig = ec.ig;
6257 byte [] data = MakeByteBlob ();
6259 fb = RootContext.MakeStaticData (data);
6261 ig.Emit (OpCodes.Dup);
6262 ig.Emit (OpCodes.Ldtoken, fb);
6263 ig.Emit (OpCodes.Call,
6264 TypeManager.void_initializearray_array_fieldhandle);
6268 // Emits pieces of the array that can not be computed at compile
6269 // time (variables and string locations).
6271 // This always expect the top value on the stack to be the array
6273 void EmitDynamicInitializers (EmitContext ec)
6275 ILGenerator ig = ec.ig;
6276 int dims = bounds.Count;
6277 int [] current_pos = new int [dims];
6279 MethodInfo set = null;
6282 Type [] args = new Type [dims + 1];
6284 for (int j = 0; j < dims; j++)
6285 args [j] = TypeManager.int32_type;
6286 args [dims] = array_element_type;
6288 set = CodeGen.Module.Builder.GetArrayMethod (
6290 CallingConventions.HasThis | CallingConventions.Standard,
6291 TypeManager.void_type, args);
6294 for (int i = 0; i < array_data.Count; i++){
6296 Expression e = (Expression)array_data [i];
6299 Type etype = e.Type;
6301 ig.Emit (OpCodes.Dup);
6303 for (int idx = 0; idx < dims; idx++)
6304 IntConstant.EmitInt (ig, current_pos [idx]);
6307 // If we are dealing with a struct, get the
6308 // address of it, so we can store it.
6311 TypeManager.IsValueType (etype) &&
6312 (!TypeManager.IsBuiltinOrEnum (etype) ||
6313 etype == TypeManager.decimal_type)) {
6318 // Let new know that we are providing
6319 // the address where to store the results
6321 n.DisableTemporaryValueType ();
6324 ig.Emit (OpCodes.Ldelema, etype);
6331 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj);
6333 ig.Emit (OpCodes.Stobj, etype);
6337 ig.Emit (OpCodes.Call, set);
6344 for (int j = dims - 1; j >= 0; j--){
6346 if (current_pos [j] < (int) bounds [j])
6348 current_pos [j] = 0;
6353 void EmitArrayArguments (EmitContext ec)
6355 ILGenerator ig = ec.ig;
6357 foreach (Argument a in arguments) {
6358 Type atype = a.Type;
6361 if (atype == TypeManager.uint64_type)
6362 ig.Emit (OpCodes.Conv_Ovf_U4);
6363 else if (atype == TypeManager.int64_type)
6364 ig.Emit (OpCodes.Conv_Ovf_I4);
6368 public override void Emit (EmitContext ec)
6370 ILGenerator ig = ec.ig;
6372 EmitArrayArguments (ec);
6373 if (is_one_dimensional)
6374 ig.Emit (OpCodes.Newarr, array_element_type);
6376 if (is_builtin_type)
6377 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6379 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6382 if (initializers == null)
6385 // This is a treshold for static initializers
6386 // I tried to make more accurate but it seems to me that Array.Initialize is
6387 // always slower (managed -> unmanaged switch?)
6388 const int max_automatic_initializers = 200;
6390 if (const_initializers_count > max_automatic_initializers && TypeManager.IsPrimitiveType (array_element_type)) {
6391 EmitStaticInitializers (ec);
6395 EmitDynamicInitializers (ec);
6398 public override bool GetAttributableValue (Type valueType, out object value)
6400 if (!is_one_dimensional){
6401 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6402 return base.GetAttributableValue (null, out value);
6405 if (array_data == null) {
6406 Constant c = (Constant)((Argument)arguments [0]).Expr;
6407 if (c.IsDefaultValue) {
6408 value = Array.CreateInstance (array_element_type, 0);
6411 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6412 return base.GetAttributableValue (null, out value);
6415 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6416 object element_value;
6417 for (int i = 0; i < ret.Length; ++i)
6419 Expression e = (Expression)array_data [i];
6420 if (e == null) // Is null when initializer is optimized away
6421 e = (Expression)initializers [i];
6423 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6427 ret.SetValue (element_value, i);
6434 public sealed class CompilerGeneratedThis : This
6436 public static This Instance = new CompilerGeneratedThis ();
6438 private CompilerGeneratedThis ()
6439 : base (Location.Null)
6443 public override Expression DoResolve (EmitContext ec)
6445 eclass = ExprClass.Variable;
6446 type = ec.ContainerType;
6452 /// Represents the `this' construct
6454 public class This : Expression, IAssignMethod, IMemoryLocation, IVariable {
6457 VariableInfo variable_info;
6459 public This (Block block, Location loc)
6465 public This (Location loc)
6470 public VariableInfo VariableInfo {
6471 get { return variable_info; }
6474 public bool VerifyFixed ()
6476 return !TypeManager.IsValueType (Type);
6479 public bool ResolveBase (EmitContext ec)
6481 eclass = ExprClass.Variable;
6482 type = ec.ContainerType;
6485 Error (26, "Keyword `this' is not valid in a static property, static method, or static field initializer");
6489 if (block != null && block.Toplevel.ThisVariable != null)
6490 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6492 if (ec.CurrentAnonymousMethod != null)
6498 public override Expression DoResolve (EmitContext ec)
6500 if (!ResolveBase (ec))
6503 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) && !variable_info.IsAssigned (ec)) {
6504 Error (188, "The `this' object cannot be used before all of its fields are assigned to");
6505 variable_info.SetAssigned (ec);
6509 if (ec.IsFieldInitializer) {
6510 Error (27, "Keyword `this' is not available in the current context");
6517 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6519 if (!ResolveBase (ec))
6522 if (variable_info != null)
6523 variable_info.SetAssigned (ec);
6525 if (ec.TypeContainer is Class){
6526 Error (1604, "Cannot assign to 'this' because it is read-only");
6533 public void Emit (EmitContext ec, bool leave_copy)
6537 ec.ig.Emit (OpCodes.Dup);
6540 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
6542 ILGenerator ig = ec.ig;
6544 if (ec.TypeContainer is Struct){
6545 ec.EmitThis (false);
6548 LocalTemporary t = null;
6550 t = new LocalTemporary (type);
6551 ec.ig.Emit (OpCodes.Dup);
6555 ig.Emit (OpCodes.Stobj, type);
6560 throw new Exception ("how did you get here");
6564 public override void Emit (EmitContext ec)
6566 ILGenerator ig = ec.ig;
6568 ec.EmitThis (false);
6569 if (ec.TypeContainer is Struct)
6570 ig.Emit (OpCodes.Ldobj, type);
6573 public override int GetHashCode()
6575 return block.GetHashCode ();
6578 public override bool Equals (object obj)
6580 This t = obj as This;
6584 return block == t.block;
6587 public void AddressOf (EmitContext ec, AddressOp mode)
6592 // FIGURE OUT WHY LDARG_S does not work
6594 // consider: struct X { int val; int P { set { val = value; }}}
6596 // Yes, this looks very bad. Look at `NOTAS' for
6598 // ec.ig.Emit (OpCodes.Ldarga_S, (byte) 0);
6603 /// Represents the `__arglist' construct
6605 public class ArglistAccess : Expression
6607 public ArglistAccess (Location loc)
6612 public override Expression DoResolve (EmitContext ec)
6614 eclass = ExprClass.Variable;
6615 type = TypeManager.runtime_argument_handle_type;
6617 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6619 Error (190, "The __arglist construct is valid only within " +
6620 "a variable argument method");
6627 public override void Emit (EmitContext ec)
6629 ec.ig.Emit (OpCodes.Arglist);
6634 /// Represents the `__arglist (....)' construct
6636 public class Arglist : Expression
6638 public readonly Argument[] Arguments;
6640 public Arglist (Argument[] args, Location l)
6646 public Type[] ArgumentTypes {
6648 Type[] retval = new Type [Arguments.Length];
6649 for (int i = 0; i < Arguments.Length; i++)
6650 retval [i] = Arguments [i].Type;
6655 public override Expression DoResolve (EmitContext ec)
6657 eclass = ExprClass.Variable;
6658 type = TypeManager.runtime_argument_handle_type;
6660 foreach (Argument arg in Arguments) {
6661 if (!arg.Resolve (ec, loc))
6668 public override void Emit (EmitContext ec)
6670 foreach (Argument arg in Arguments)
6676 // This produces the value that renders an instance, used by the iterators code
6678 public class ProxyInstance : Expression, IMemoryLocation {
6679 public override Expression DoResolve (EmitContext ec)
6681 eclass = ExprClass.Variable;
6682 type = ec.ContainerType;
6686 public override void Emit (EmitContext ec)
6688 ec.ig.Emit (OpCodes.Ldarg_0);
6692 public void AddressOf (EmitContext ec, AddressOp mode)
6694 ec.ig.Emit (OpCodes.Ldarg_0);
6699 /// Implements the typeof operator
6701 public class TypeOf : Expression {
6702 readonly Expression QueriedType;
6703 protected Type typearg;
6705 public TypeOf (Expression queried_type, Location l)
6707 QueriedType = queried_type;
6711 public override Expression DoResolve (EmitContext ec)
6713 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6717 typearg = texpr.Type;
6719 if (typearg == TypeManager.void_type) {
6720 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6724 if (typearg.IsPointer && !ec.InUnsafe){
6729 type = TypeManager.type_type;
6730 // Even though what is returned is a type object, it's treated as a value by the compiler.
6731 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6732 eclass = ExprClass.Value;
6736 public override void Emit (EmitContext ec)
6738 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6739 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6742 public override bool GetAttributableValue (Type valueType, out object value)
6744 if (valueType == TypeManager.object_type) {
6745 value = (object)typearg;
6754 /// Implements the `typeof (void)' operator
6756 public class TypeOfVoid : TypeOf {
6757 public TypeOfVoid (Location l) : base (null, l)
6762 public override Expression DoResolve (EmitContext ec)
6764 type = TypeManager.type_type;
6765 typearg = TypeManager.void_type;
6766 // See description in TypeOf.
6767 eclass = ExprClass.Value;
6773 /// Implements the sizeof expression
6775 public class SizeOf : Expression {
6776 public Expression QueriedType;
6779 public SizeOf (Expression queried_type, Location l)
6781 this.QueriedType = queried_type;
6785 public override Expression DoResolve (EmitContext ec)
6787 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6791 type_queried = texpr.Type;
6793 int size_of = GetTypeSize (type_queried);
6795 return new IntConstant (size_of, loc);
6799 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)",
6800 TypeManager.CSharpName (type_queried));
6804 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
6808 type = TypeManager.int32_type;
6809 eclass = ExprClass.Value;
6813 public override void Emit (EmitContext ec)
6815 int size = GetTypeSize (type_queried);
6818 ec.ig.Emit (OpCodes.Sizeof, type_queried);
6820 IntConstant.EmitInt (ec.ig, size);
6825 /// Implements the qualified-alias-member (::) expression.
6827 public class QualifiedAliasMember : Expression
6829 string alias, identifier;
6831 public QualifiedAliasMember (string alias, string identifier, Location l)
6834 this.identifier = identifier;
6838 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
6840 if (alias == "global")
6841 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
6843 int errors = Report.Errors;
6844 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6846 if (errors == Report.Errors)
6847 Report.Error (432, loc, "Alias `{0}' not found", alias);
6850 if (fne.eclass != ExprClass.Namespace) {
6852 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6855 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
6858 public override Expression DoResolve (EmitContext ec)
6860 FullNamedExpression fne;
6861 if (alias == "global") {
6862 fne = RootNamespace.Global;
6864 int errors = Report.Errors;
6865 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6867 if (errors == Report.Errors)
6868 Report.Error (432, loc, "Alias `{0}' not found", alias);
6873 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
6877 if (!(retval is FullNamedExpression)) {
6878 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
6882 // We defer this check till the end to match the behaviour of CSC
6883 if (fne.eclass != ExprClass.Namespace) {
6884 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6890 public override void Emit (EmitContext ec)
6892 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
6896 public override string ToString ()
6898 return alias + "::" + identifier;
6901 public override string GetSignatureForError ()
6908 /// Implements the member access expression
6910 public class MemberAccess : Expression {
6911 public readonly string Identifier;
6914 public MemberAccess (Expression expr, string id)
6915 : this (expr, id, expr.Location)
6919 public MemberAccess (Expression expr, string identifier, Location loc)
6922 Identifier = identifier;
6926 public Expression Expr {
6927 get { return expr; }
6930 // TODO: this method has very poor performace for Enum fields and
6931 // probably for other constants as well
6932 Expression DoResolve (EmitContext ec, Expression right_side)
6935 throw new Exception ();
6938 // Resolve the expression with flow analysis turned off, we'll do the definite
6939 // assignment checks later. This is because we don't know yet what the expression
6940 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
6941 // definite assignment check on the actual field and not on the whole struct.
6944 SimpleName original = expr as SimpleName;
6945 Expression new_expr = expr.Resolve (ec,
6946 ResolveFlags.VariableOrValue | ResolveFlags.Type |
6947 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
6949 if (new_expr == null)
6952 if (new_expr is Namespace) {
6953 Namespace ns = (Namespace) new_expr;
6954 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, Identifier, loc);
6956 ns.Error_NamespaceDoesNotExist (loc, Identifier);
6960 Type expr_type = new_expr.Type;
6961 if (expr_type.IsPointer){
6962 Error (23, "The `.' operator can not be applied to pointer operands (" +
6963 TypeManager.CSharpName (expr_type) + ")");
6965 } else if (expr_type == TypeManager.void_type) {
6966 Error (23, "The `.' operator can not be applied to operands of type 'void'");
6968 } else if (expr_type == TypeManager.anonymous_method_type){
6969 Error (23, "The `.' operator can not be applied to anonymous methods");
6973 Expression member_lookup;
6974 member_lookup = MemberLookupFinal (ec, expr_type, expr_type, Identifier, loc);
6975 if (member_lookup == null)
6978 if (member_lookup is TypeExpr) {
6979 if (!(new_expr is TypeExpr) &&
6980 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
6981 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
6982 Identifier, member_lookup.GetSignatureForError ());
6986 return member_lookup;
6989 MemberExpr me = (MemberExpr) member_lookup;
6990 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
6991 if (member_lookup == null)
6994 if (original != null && !TypeManager.IsValueType (expr_type)) {
6995 me = member_lookup as MemberExpr;
6996 if (me != null && me.IsInstance) {
6997 LocalVariableReference var = new_expr as LocalVariableReference;
6998 if (var != null && !var.VerifyAssigned (ec))
7003 // The following DoResolve/DoResolveLValue will do the definite assignment
7006 if (right_side != null)
7007 return member_lookup.DoResolveLValue (ec, right_side);
7009 return member_lookup.DoResolve (ec);
7012 public override Expression DoResolve (EmitContext ec)
7014 return DoResolve (ec, null);
7017 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7019 return DoResolve (ec, right_side);
7022 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7024 return ResolveNamespaceOrType (ec, silent);
7027 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7029 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
7031 if (new_expr == null)
7034 if (new_expr is Namespace) {
7035 Namespace ns = (Namespace) new_expr;
7036 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, Identifier, loc);
7037 if (!silent && retval == null)
7038 ns.Error_NamespaceDoesNotExist (loc, Identifier);
7042 Type expr_type = new_expr.Type;
7044 if (expr_type.IsPointer){
7045 Error (23, "The `.' operator can not be applied to pointer operands (" +
7046 TypeManager.CSharpName (expr_type) + ")");
7050 Expression member_lookup = MemberLookup (rc.DeclContainer.TypeBuilder, expr_type, expr_type, Identifier, loc);
7051 if (member_lookup == null) {
7052 int errors = Report.Errors;
7053 MemberLookupFailed (rc.DeclContainer.TypeBuilder, expr_type, expr_type, Identifier, null, false, loc);
7055 if (!silent && errors == Report.Errors) {
7056 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7057 Identifier, new_expr.GetSignatureForError ());
7062 if (!(member_lookup is TypeExpr)) {
7063 new_expr.Error_UnexpectedKind (rc.DeclContainer, "type", loc);
7067 return member_lookup.ResolveAsTypeTerminal (rc, silent);
7070 public override void Emit (EmitContext ec)
7072 throw new Exception ("Should not happen");
7075 public override string ToString ()
7077 return expr + "." + Identifier;
7080 public override string GetSignatureForError ()
7082 return expr.GetSignatureForError () + "." + Identifier;
7087 /// Implements checked expressions
7089 public class CheckedExpr : Expression {
7091 public Expression Expr;
7093 public CheckedExpr (Expression e, Location l)
7099 public override Expression DoResolve (EmitContext ec)
7101 bool last_check = ec.CheckState;
7102 bool last_const_check = ec.ConstantCheckState;
7104 ec.CheckState = true;
7105 ec.ConstantCheckState = true;
7106 Expr = Expr.Resolve (ec);
7107 ec.CheckState = last_check;
7108 ec.ConstantCheckState = last_const_check;
7113 if (Expr is Constant)
7116 eclass = Expr.eclass;
7121 public override void Emit (EmitContext ec)
7123 bool last_check = ec.CheckState;
7124 bool last_const_check = ec.ConstantCheckState;
7126 ec.CheckState = true;
7127 ec.ConstantCheckState = true;
7129 ec.CheckState = last_check;
7130 ec.ConstantCheckState = last_const_check;
7136 /// Implements the unchecked expression
7138 public class UnCheckedExpr : Expression {
7140 public Expression Expr;
7142 public UnCheckedExpr (Expression e, Location l)
7148 public override Expression DoResolve (EmitContext ec)
7150 bool last_check = ec.CheckState;
7151 bool last_const_check = ec.ConstantCheckState;
7153 ec.CheckState = false;
7154 ec.ConstantCheckState = false;
7155 Expr = Expr.Resolve (ec);
7156 ec.CheckState = last_check;
7157 ec.ConstantCheckState = last_const_check;
7162 if (Expr is Constant)
7165 eclass = Expr.eclass;
7170 public override void Emit (EmitContext ec)
7172 bool last_check = ec.CheckState;
7173 bool last_const_check = ec.ConstantCheckState;
7175 ec.CheckState = false;
7176 ec.ConstantCheckState = false;
7178 ec.CheckState = last_check;
7179 ec.ConstantCheckState = last_const_check;
7185 /// An Element Access expression.
7187 /// During semantic analysis these are transformed into
7188 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7190 public class ElementAccess : Expression {
7191 public ArrayList Arguments;
7192 public Expression Expr;
7194 public ElementAccess (Expression e, ArrayList e_list)
7203 Arguments = new ArrayList ();
7204 foreach (Expression tmp in e_list)
7205 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7209 bool CommonResolve (EmitContext ec)
7211 Expr = Expr.Resolve (ec);
7216 if (Arguments == null)
7219 foreach (Argument a in Arguments){
7220 if (!a.Resolve (ec, loc))
7227 Expression MakePointerAccess (EmitContext ec, Type t)
7229 if (t == TypeManager.void_ptr_type){
7230 Error (242, "The array index operation is not valid on void pointers");
7233 if (Arguments.Count != 1){
7234 Error (196, "A pointer must be indexed by only one value");
7239 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7242 return new Indirection (p, loc).Resolve (ec);
7245 public override Expression DoResolve (EmitContext ec)
7247 if (!CommonResolve (ec))
7251 // We perform some simple tests, and then to "split" the emit and store
7252 // code we create an instance of a different class, and return that.
7254 // I am experimenting with this pattern.
7258 if (t == TypeManager.array_type){
7259 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7264 return (new ArrayAccess (this, loc)).Resolve (ec);
7266 return MakePointerAccess (ec, Expr.Type);
7268 FieldExpr fe = Expr as FieldExpr;
7270 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7272 return MakePointerAccess (ec, ff.ElementType);
7275 return (new IndexerAccess (this, loc)).Resolve (ec);
7278 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7280 if (!CommonResolve (ec))
7285 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7288 return MakePointerAccess (ec, Expr.Type);
7290 FieldExpr fe = Expr as FieldExpr;
7292 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7294 if (!(fe.InstanceExpression is LocalVariableReference) &&
7295 !(fe.InstanceExpression is This)) {
7296 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7299 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7300 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7303 return MakePointerAccess (ec, ff.ElementType);
7306 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7309 public override void Emit (EmitContext ec)
7311 throw new Exception ("Should never be reached");
7316 /// Implements array access
7318 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7320 // Points to our "data" repository
7324 LocalTemporary temp;
7327 public ArrayAccess (ElementAccess ea_data, Location l)
7330 eclass = ExprClass.Variable;
7334 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7336 return DoResolve (ec);
7339 public override Expression DoResolve (EmitContext ec)
7342 ExprClass eclass = ea.Expr.eclass;
7344 // As long as the type is valid
7345 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7346 eclass == ExprClass.Value)) {
7347 ea.Expr.Error_UnexpectedKind ("variable or value");
7352 Type t = ea.Expr.Type;
7353 if (t.GetArrayRank () != ea.Arguments.Count){
7354 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7355 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7359 type = TypeManager.GetElementType (t);
7360 if (type.IsPointer && !ec.InUnsafe){
7361 UnsafeError (ea.Location);
7365 foreach (Argument a in ea.Arguments){
7366 Type argtype = a.Type;
7368 if (argtype == TypeManager.int32_type ||
7369 argtype == TypeManager.uint32_type ||
7370 argtype == TypeManager.int64_type ||
7371 argtype == TypeManager.uint64_type) {
7372 Constant c = a.Expr as Constant;
7373 if (c != null && c.IsNegative) {
7374 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7380 // Mhm. This is strage, because the Argument.Type is not the same as
7381 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7383 // Wonder if I will run into trouble for this.
7385 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7390 eclass = ExprClass.Variable;
7396 /// Emits the right opcode to load an object of Type `t'
7397 /// from an array of T
7399 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7401 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7402 ig.Emit (OpCodes.Ldelem_U1);
7403 else if (type == TypeManager.sbyte_type)
7404 ig.Emit (OpCodes.Ldelem_I1);
7405 else if (type == TypeManager.short_type)
7406 ig.Emit (OpCodes.Ldelem_I2);
7407 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7408 ig.Emit (OpCodes.Ldelem_U2);
7409 else if (type == TypeManager.int32_type)
7410 ig.Emit (OpCodes.Ldelem_I4);
7411 else if (type == TypeManager.uint32_type)
7412 ig.Emit (OpCodes.Ldelem_U4);
7413 else if (type == TypeManager.uint64_type)
7414 ig.Emit (OpCodes.Ldelem_I8);
7415 else if (type == TypeManager.int64_type)
7416 ig.Emit (OpCodes.Ldelem_I8);
7417 else if (type == TypeManager.float_type)
7418 ig.Emit (OpCodes.Ldelem_R4);
7419 else if (type == TypeManager.double_type)
7420 ig.Emit (OpCodes.Ldelem_R8);
7421 else if (type == TypeManager.intptr_type)
7422 ig.Emit (OpCodes.Ldelem_I);
7423 else if (TypeManager.IsEnumType (type)){
7424 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7425 } else if (type.IsValueType){
7426 ig.Emit (OpCodes.Ldelema, type);
7427 ig.Emit (OpCodes.Ldobj, type);
7428 } else if (type.IsPointer)
7429 ig.Emit (OpCodes.Ldelem_I);
7431 ig.Emit (OpCodes.Ldelem_Ref);
7435 /// Returns the right opcode to store an object of Type `t'
7436 /// from an array of T.
7438 static public OpCode GetStoreOpcode (Type t, out bool is_stobj)
7440 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7442 t = TypeManager.TypeToCoreType (t);
7443 if (TypeManager.IsEnumType (t))
7444 t = TypeManager.EnumToUnderlying (t);
7445 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7446 t == TypeManager.bool_type)
7447 return OpCodes.Stelem_I1;
7448 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7449 t == TypeManager.char_type)
7450 return OpCodes.Stelem_I2;
7451 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7452 return OpCodes.Stelem_I4;
7453 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7454 return OpCodes.Stelem_I8;
7455 else if (t == TypeManager.float_type)
7456 return OpCodes.Stelem_R4;
7457 else if (t == TypeManager.double_type)
7458 return OpCodes.Stelem_R8;
7459 else if (t == TypeManager.intptr_type) {
7461 return OpCodes.Stobj;
7462 } else if (t.IsValueType) {
7464 return OpCodes.Stobj;
7465 } else if (t.IsPointer)
7466 return OpCodes.Stelem_I;
7468 return OpCodes.Stelem_Ref;
7471 MethodInfo FetchGetMethod ()
7473 ModuleBuilder mb = CodeGen.Module.Builder;
7474 int arg_count = ea.Arguments.Count;
7475 Type [] args = new Type [arg_count];
7478 for (int i = 0; i < arg_count; i++){
7479 //args [i++] = a.Type;
7480 args [i] = TypeManager.int32_type;
7483 get = mb.GetArrayMethod (
7484 ea.Expr.Type, "Get",
7485 CallingConventions.HasThis |
7486 CallingConventions.Standard,
7492 MethodInfo FetchAddressMethod ()
7494 ModuleBuilder mb = CodeGen.Module.Builder;
7495 int arg_count = ea.Arguments.Count;
7496 Type [] args = new Type [arg_count];
7500 ret_type = TypeManager.GetReferenceType (type);
7502 for (int i = 0; i < arg_count; i++){
7503 //args [i++] = a.Type;
7504 args [i] = TypeManager.int32_type;
7507 address = mb.GetArrayMethod (
7508 ea.Expr.Type, "Address",
7509 CallingConventions.HasThis |
7510 CallingConventions.Standard,
7517 // Load the array arguments into the stack.
7519 // If we have been requested to cache the values (cached_locations array
7520 // initialized), then load the arguments the first time and store them
7521 // in locals. otherwise load from local variables.
7523 void LoadArrayAndArguments (EmitContext ec)
7525 ILGenerator ig = ec.ig;
7528 foreach (Argument a in ea.Arguments){
7529 Type argtype = a.Expr.Type;
7533 if (argtype == TypeManager.int64_type)
7534 ig.Emit (OpCodes.Conv_Ovf_I);
7535 else if (argtype == TypeManager.uint64_type)
7536 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7540 public void Emit (EmitContext ec, bool leave_copy)
7542 int rank = ea.Expr.Type.GetArrayRank ();
7543 ILGenerator ig = ec.ig;
7546 LoadArrayAndArguments (ec);
7549 EmitLoadOpcode (ig, type);
7553 method = FetchGetMethod ();
7554 ig.Emit (OpCodes.Call, method);
7557 LoadFromPtr (ec.ig, this.type);
7560 ec.ig.Emit (OpCodes.Dup);
7561 temp = new LocalTemporary (this.type);
7566 public override void Emit (EmitContext ec)
7571 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7573 int rank = ea.Expr.Type.GetArrayRank ();
7574 ILGenerator ig = ec.ig;
7575 Type t = source.Type;
7576 prepared = prepare_for_load;
7578 if (prepare_for_load) {
7579 AddressOf (ec, AddressOp.LoadStore);
7580 ec.ig.Emit (OpCodes.Dup);
7583 ec.ig.Emit (OpCodes.Dup);
7584 temp = new LocalTemporary (this.type);
7587 StoreFromPtr (ec.ig, t);
7595 LoadArrayAndArguments (ec);
7599 OpCode op = GetStoreOpcode (t, out is_stobj);
7601 // The stobj opcode used by value types will need
7602 // an address on the stack, not really an array/array
7606 ig.Emit (OpCodes.Ldelema, t);
7610 ec.ig.Emit (OpCodes.Dup);
7611 temp = new LocalTemporary (this.type);
7616 ig.Emit (OpCodes.Stobj, t);
7620 ModuleBuilder mb = CodeGen.Module.Builder;
7621 int arg_count = ea.Arguments.Count;
7622 Type [] args = new Type [arg_count + 1];
7627 ec.ig.Emit (OpCodes.Dup);
7628 temp = new LocalTemporary (this.type);
7632 for (int i = 0; i < arg_count; i++){
7633 //args [i++] = a.Type;
7634 args [i] = TypeManager.int32_type;
7637 args [arg_count] = type;
7639 set = mb.GetArrayMethod (
7640 ea.Expr.Type, "Set",
7641 CallingConventions.HasThis |
7642 CallingConventions.Standard,
7643 TypeManager.void_type, args);
7645 ig.Emit (OpCodes.Call, set);
7652 public void AddressOf (EmitContext ec, AddressOp mode)
7654 int rank = ea.Expr.Type.GetArrayRank ();
7655 ILGenerator ig = ec.ig;
7657 LoadArrayAndArguments (ec);
7660 ig.Emit (OpCodes.Ldelema, type);
7662 MethodInfo address = FetchAddressMethod ();
7663 ig.Emit (OpCodes.Call, address);
7667 public void EmitGetLength (EmitContext ec, int dim)
7669 int rank = ea.Expr.Type.GetArrayRank ();
7670 ILGenerator ig = ec.ig;
7674 ig.Emit (OpCodes.Ldlen);
7675 ig.Emit (OpCodes.Conv_I4);
7677 IntLiteral.EmitInt (ig, dim);
7678 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7684 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7685 public readonly ArrayList Properties;
7686 static Indexers empty;
7688 public struct Indexer {
7689 public readonly PropertyInfo PropertyInfo;
7690 public readonly MethodInfo Getter, Setter;
7692 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7694 this.PropertyInfo = property_info;
7702 empty = new Indexers (null);
7705 Indexers (ArrayList array)
7710 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
7715 foreach (PropertyInfo property in mi){
7716 MethodInfo get, set;
7718 get = property.GetGetMethod (true);
7719 set = property.GetSetMethod (true);
7720 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
7722 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
7724 if (get != null || set != null) {
7726 ix = new Indexers (new ArrayList ());
7727 ix.Properties.Add (new Indexer (property, get, set));
7732 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
7734 string p_name = TypeManager.IndexerPropertyName (lookup_type);
7736 return TypeManager.MemberLookup (
7737 caller_type, caller_type, lookup_type, MemberTypes.Property,
7738 BindingFlags.Public | BindingFlags.Instance |
7739 BindingFlags.DeclaredOnly, p_name, null);
7742 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
7744 Indexers ix = empty;
7746 Type copy = lookup_type;
7747 while (copy != TypeManager.object_type && copy != null){
7748 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
7749 copy = copy.BaseType;
7752 if (lookup_type.IsInterface) {
7753 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
7754 if (ifaces != null) {
7755 foreach (Type itype in ifaces)
7756 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
7765 /// Expressions that represent an indexer call.
7767 public class IndexerAccess : Expression, IAssignMethod {
7769 // Points to our "data" repository
7771 MethodInfo get, set;
7772 ArrayList set_arguments;
7773 bool is_base_indexer;
7775 protected Type indexer_type;
7776 protected Type current_type;
7777 protected Expression instance_expr;
7778 protected ArrayList arguments;
7780 public IndexerAccess (ElementAccess ea, Location loc)
7781 : this (ea.Expr, false, loc)
7783 this.arguments = ea.Arguments;
7786 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
7789 this.instance_expr = instance_expr;
7790 this.is_base_indexer = is_base_indexer;
7791 this.eclass = ExprClass.Value;
7795 protected virtual bool CommonResolve (EmitContext ec)
7797 indexer_type = instance_expr.Type;
7798 current_type = ec.ContainerType;
7803 public override Expression DoResolve (EmitContext ec)
7805 ArrayList AllGetters = new ArrayList();
7806 if (!CommonResolve (ec))
7810 // Step 1: Query for all `Item' *properties*. Notice
7811 // that the actual methods are pointed from here.
7813 // This is a group of properties, piles of them.
7815 bool found_any = false, found_any_getters = false;
7816 Type lookup_type = indexer_type;
7818 Indexers ilist = Indexers.GetIndexersForType (current_type, lookup_type);
7819 if (ilist.Properties != null) {
7821 foreach (Indexers.Indexer ix in ilist.Properties) {
7822 if (ix.Getter != null)
7823 AllGetters.Add (ix.Getter);
7827 if (AllGetters.Count > 0) {
7828 found_any_getters = true;
7829 get = (MethodInfo) Invocation.OverloadResolve (
7830 ec, new MethodGroupExpr (AllGetters, loc),
7831 arguments, false, loc);
7835 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7836 TypeManager.CSharpName (indexer_type));
7840 if (!found_any_getters) {
7841 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
7847 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7852 // Only base will allow this invocation to happen.
7854 if (get.IsAbstract && this is BaseIndexerAccess){
7855 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
7859 type = get.ReturnType;
7860 if (type.IsPointer && !ec.InUnsafe){
7865 instance_expr.CheckMarshalByRefAccess ();
7867 eclass = ExprClass.IndexerAccess;
7871 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7873 if (right_side == EmptyExpression.OutAccess) {
7874 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
7875 GetSignatureForError ());
7879 // if the indexer returns a value type, and we try to set a field in it
7880 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
7881 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
7882 GetSignatureForError ());
7886 ArrayList AllSetters = new ArrayList();
7887 if (!CommonResolve (ec))
7890 bool found_any = false, found_any_setters = false;
7892 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
7893 if (ilist.Properties != null) {
7895 foreach (Indexers.Indexer ix in ilist.Properties) {
7896 if (ix.Setter != null)
7897 AllSetters.Add (ix.Setter);
7900 if (AllSetters.Count > 0) {
7901 found_any_setters = true;
7902 set_arguments = (ArrayList) arguments.Clone ();
7903 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
7904 set = (MethodInfo) Invocation.OverloadResolve (
7905 ec, new MethodGroupExpr (AllSetters, loc),
7906 set_arguments, false, loc);
7910 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7911 TypeManager.CSharpName (indexer_type));
7915 if (!found_any_setters) {
7916 Error (154, "indexer can not be used in this context, because " +
7917 "it lacks a `set' accessor");
7922 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7927 // Only base will allow this invocation to happen.
7929 if (set.IsAbstract && this is BaseIndexerAccess){
7930 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
7935 // Now look for the actual match in the list of indexers to set our "return" type
7937 type = TypeManager.void_type; // default value
7938 foreach (Indexers.Indexer ix in ilist.Properties){
7939 if (ix.Setter == set){
7940 type = ix.PropertyInfo.PropertyType;
7945 instance_expr.CheckMarshalByRefAccess ();
7947 eclass = ExprClass.IndexerAccess;
7951 bool prepared = false;
7952 LocalTemporary temp;
7954 public void Emit (EmitContext ec, bool leave_copy)
7956 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
7958 ec.ig.Emit (OpCodes.Dup);
7959 temp = new LocalTemporary (Type);
7965 // source is ignored, because we already have a copy of it from the
7966 // LValue resolution and we have already constructed a pre-cached
7967 // version of the arguments (ea.set_arguments);
7969 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7971 prepared = prepare_for_load;
7972 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
7977 ec.ig.Emit (OpCodes.Dup);
7978 temp = new LocalTemporary (Type);
7981 } else if (leave_copy) {
7982 temp = new LocalTemporary (Type);
7988 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
7995 public override void Emit (EmitContext ec)
8000 public override string GetSignatureForError ()
8002 // FIXME: print the argument list of the indexer
8003 return instance_expr.GetSignatureForError () + ".this[...]";
8008 /// The base operator for method names
8010 public class BaseAccess : Expression {
8013 public BaseAccess (string member, Location l)
8015 this.member = member;
8019 public override Expression DoResolve (EmitContext ec)
8021 Expression c = CommonResolve (ec);
8027 // MethodGroups use this opportunity to flag an error on lacking ()
8029 if (!(c is MethodGroupExpr))
8030 return c.Resolve (ec);
8034 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8036 Expression c = CommonResolve (ec);
8042 // MethodGroups use this opportunity to flag an error on lacking ()
8044 if (! (c is MethodGroupExpr))
8045 return c.DoResolveLValue (ec, right_side);
8050 Expression CommonResolve (EmitContext ec)
8052 Expression member_lookup;
8053 Type current_type = ec.ContainerType;
8054 Type base_type = current_type.BaseType;
8057 Error (1511, "Keyword `base' is not available in a static method");
8061 if (ec.IsFieldInitializer){
8062 Error (1512, "Keyword `base' is not available in the current context");
8066 member_lookup = MemberLookup (ec.ContainerType, null, base_type, member,
8067 AllMemberTypes, AllBindingFlags, loc);
8068 if (member_lookup == null) {
8069 MemberLookupFailed (ec.ContainerType, base_type, base_type, member, null, true, loc);
8076 left = new TypeExpression (base_type, loc);
8078 left = ec.GetThis (loc);
8080 MemberExpr me = (MemberExpr) member_lookup;
8082 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8084 if (e is PropertyExpr) {
8085 PropertyExpr pe = (PropertyExpr) e;
8090 if (e is MethodGroupExpr)
8091 ((MethodGroupExpr) e).IsBase = true;
8096 public override void Emit (EmitContext ec)
8098 throw new Exception ("Should never be called");
8103 /// The base indexer operator
8105 public class BaseIndexerAccess : IndexerAccess {
8106 public BaseIndexerAccess (ArrayList args, Location loc)
8107 : base (null, true, loc)
8109 arguments = new ArrayList ();
8110 foreach (Expression tmp in args)
8111 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8114 protected override bool CommonResolve (EmitContext ec)
8116 instance_expr = ec.GetThis (loc);
8118 current_type = ec.ContainerType.BaseType;
8119 indexer_type = current_type;
8121 foreach (Argument a in arguments){
8122 if (!a.Resolve (ec, loc))
8131 /// This class exists solely to pass the Type around and to be a dummy
8132 /// that can be passed to the conversion functions (this is used by
8133 /// foreach implementation to typecast the object return value from
8134 /// get_Current into the proper type. All code has been generated and
8135 /// we only care about the side effect conversions to be performed
8137 /// This is also now used as a placeholder where a no-action expression
8138 /// is needed (the `New' class).
8140 public class EmptyExpression : Expression {
8141 public static readonly EmptyExpression Null = new EmptyExpression ();
8143 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8144 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8145 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8147 static EmptyExpression temp = new EmptyExpression ();
8148 public static EmptyExpression Grab ()
8151 throw new InternalErrorException ("Nested Grab");
8152 EmptyExpression retval = temp;
8157 public static void Release (EmptyExpression e)
8160 throw new InternalErrorException ("Already released");
8164 // TODO: should be protected
8165 public EmptyExpression ()
8167 type = TypeManager.object_type;
8168 eclass = ExprClass.Value;
8169 loc = Location.Null;
8172 public EmptyExpression (Type t)
8175 eclass = ExprClass.Value;
8176 loc = Location.Null;
8179 public override Expression DoResolve (EmitContext ec)
8184 public override void Emit (EmitContext ec)
8186 // nothing, as we only exist to not do anything.
8190 // This is just because we might want to reuse this bad boy
8191 // instead of creating gazillions of EmptyExpressions.
8192 // (CanImplicitConversion uses it)
8194 public void SetType (Type t)
8200 public class UserCast : Expression {
8204 public UserCast (MethodInfo method, Expression source, Location l)
8206 this.method = method;
8207 this.source = source;
8208 type = method.ReturnType;
8209 eclass = ExprClass.Value;
8213 public Expression Source {
8219 public override Expression DoResolve (EmitContext ec)
8222 // We are born fully resolved
8227 public override void Emit (EmitContext ec)
8229 ILGenerator ig = ec.ig;
8233 if (method is MethodInfo)
8234 ig.Emit (OpCodes.Call, (MethodInfo) method);
8236 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8242 // This class is used to "construct" the type during a typecast
8243 // operation. Since the Type.GetType class in .NET can parse
8244 // the type specification, we just use this to construct the type
8245 // one bit at a time.
8247 public class ComposedCast : TypeExpr {
8251 public ComposedCast (Expression left, string dim)
8252 : this (left, dim, left.Location)
8256 public ComposedCast (Expression left, string dim, Location l)
8263 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8265 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8269 Type ltype = lexpr.Type;
8270 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8271 Report.Error (1547, Location,
8272 "Keyword 'void' cannot be used in this context");
8276 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc)) {
8280 type = TypeManager.GetConstructedType (ltype, dim);
8282 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8285 if (type.IsPointer && !ec.IsInUnsafeScope){
8290 if (type.IsArray && (type.GetElementType () == TypeManager.arg_iterator_type ||
8291 type.GetElementType () == TypeManager.typed_reference_type)) {
8292 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (type.GetElementType ()));
8296 eclass = ExprClass.Type;
8300 public override string Name {
8301 get { return left + dim; }
8304 public override string FullName {
8305 get { return type.FullName; }
8308 public override string GetSignatureForError ()
8310 return left.GetSignatureForError () + dim;
8314 public class FixedBufferPtr : Expression {
8317 public FixedBufferPtr (Expression array, Type array_type, Location l)
8322 type = TypeManager.GetPointerType (array_type);
8323 eclass = ExprClass.Value;
8326 public override void Emit(EmitContext ec)
8331 public override Expression DoResolve (EmitContext ec)
8334 // We are born fully resolved
8342 // This class is used to represent the address of an array, used
8343 // only by the Fixed statement, this generates "&a [0]" construct
8344 // for fixed (char *pa = a)
8346 public class ArrayPtr : FixedBufferPtr {
8349 public ArrayPtr (Expression array, Type array_type, Location l):
8350 base (array, array_type, l)
8352 this.array_type = array_type;
8355 public override void Emit (EmitContext ec)
8359 ILGenerator ig = ec.ig;
8360 IntLiteral.EmitInt (ig, 0);
8361 ig.Emit (OpCodes.Ldelema, array_type);
8366 // Used by the fixed statement
8368 public class StringPtr : Expression {
8371 public StringPtr (LocalBuilder b, Location l)
8374 eclass = ExprClass.Value;
8375 type = TypeManager.char_ptr_type;
8379 public override Expression DoResolve (EmitContext ec)
8381 // This should never be invoked, we are born in fully
8382 // initialized state.
8387 public override void Emit (EmitContext ec)
8389 ILGenerator ig = ec.ig;
8391 ig.Emit (OpCodes.Ldloc, b);
8392 ig.Emit (OpCodes.Conv_I);
8393 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8394 ig.Emit (OpCodes.Add);
8399 // Implements the `stackalloc' keyword
8401 public class StackAlloc : Expression {
8406 public StackAlloc (Expression type, Expression count, Location l)
8413 public override Expression DoResolve (EmitContext ec)
8415 count = count.Resolve (ec);
8419 if (count.Type != TypeManager.int32_type){
8420 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8425 Constant c = count as Constant;
8426 if (c != null && c.IsNegative) {
8427 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8431 if (ec.InCatch || ec.InFinally) {
8432 Error (255, "Cannot use stackalloc in finally or catch");
8436 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8442 if (!TypeManager.VerifyUnManaged (otype, loc))
8445 type = TypeManager.GetPointerType (otype);
8446 eclass = ExprClass.Value;
8451 public override void Emit (EmitContext ec)
8453 int size = GetTypeSize (otype);
8454 ILGenerator ig = ec.ig;
8457 ig.Emit (OpCodes.Sizeof, otype);
8459 IntConstant.EmitInt (ig, size);
8461 ig.Emit (OpCodes.Mul);
8462 ig.Emit (OpCodes.Localloc);