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 = mg.OverloadResolve (
71 ec, 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;
118 protected override void CloneTo (CloneContext clonectx, Expression t)
120 ParenthesizedExpression target = (ParenthesizedExpression) t;
122 target.Expr = Expr.Clone (clonectx);
127 /// Unary expressions.
131 /// Unary implements unary expressions. It derives from
132 /// ExpressionStatement becuase the pre/post increment/decrement
133 /// operators can be used in a statement context.
135 public class Unary : Expression {
136 public enum Operator : byte {
137 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
138 Indirection, AddressOf, TOP
141 public Operator Oper;
142 public Expression Expr;
144 public Unary (Operator op, Expression expr, Location loc)
152 /// Returns a stringified representation of the Operator
154 static public string OperName (Operator oper)
157 case Operator.UnaryPlus:
159 case Operator.UnaryNegation:
161 case Operator.LogicalNot:
163 case Operator.OnesComplement:
165 case Operator.AddressOf:
167 case Operator.Indirection:
171 return oper.ToString ();
174 public static readonly string [] oper_names;
178 oper_names = new string [(int)Operator.TOP];
180 oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus";
181 oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation";
182 oper_names [(int) Operator.LogicalNot] = "op_LogicalNot";
183 oper_names [(int) Operator.OnesComplement] = "op_OnesComplement";
184 oper_names [(int) Operator.Indirection] = "op_Indirection";
185 oper_names [(int) Operator.AddressOf] = "op_AddressOf";
188 public static void Error_OperatorCannotBeApplied (Location loc, string oper, Type t)
190 Error_OperatorCannotBeApplied (loc, oper, TypeManager.CSharpName (t));
193 public static void Error_OperatorCannotBeApplied (Location loc, string oper, string type)
195 Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'",
199 void Error23 (Type t)
201 Error_OperatorCannotBeApplied (loc, OperName (Oper), t);
205 // This routine will attempt to simplify the unary expression when the
206 // argument is a constant.
208 Constant TryReduceConstant (EmitContext ec, Constant e)
210 Type expr_type = e.Type;
213 case Operator.UnaryPlus:
214 // Unary numeric promotions
215 if (expr_type == TypeManager.byte_type)
216 return new IntConstant (((ByteConstant)e).Value, e.Location);
217 if (expr_type == TypeManager.sbyte_type)
218 return new IntConstant (((SByteConstant)e).Value, e.Location);
219 if (expr_type == TypeManager.short_type)
220 return new IntConstant (((ShortConstant)e).Value, e.Location);
221 if (expr_type == TypeManager.ushort_type)
222 return new IntConstant (((UShortConstant)e).Value, e.Location);
223 if (expr_type == TypeManager.char_type)
224 return new IntConstant (((CharConstant)e).Value, e.Location);
226 // Predefined operators
227 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
228 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
229 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
230 expr_type == TypeManager.decimal_type)
237 case Operator.UnaryNegation:
238 // Unary numeric promotions
239 if (expr_type == TypeManager.byte_type)
240 return new IntConstant (-((ByteConstant)e).Value, e.Location);
241 if (expr_type == TypeManager.sbyte_type)
242 return new IntConstant (-((SByteConstant)e).Value, e.Location);
243 if (expr_type == TypeManager.short_type)
244 return new IntConstant (-((ShortConstant)e).Value, e.Location);
245 if (expr_type == TypeManager.ushort_type)
246 return new IntConstant (-((UShortConstant)e).Value, e.Location);
247 if (expr_type == TypeManager.char_type)
248 return new IntConstant (-((CharConstant)e).Value, e.Location);
250 // Predefined operators
251 if (expr_type == TypeManager.int32_type) {
252 int value = ((IntConstant)e).Value;
253 if (value == int.MinValue) {
254 if (ec.ConstantCheckState) {
255 ConstantFold.Error_CompileTimeOverflow (loc);
260 return new IntConstant (-value, e.Location);
262 if (expr_type == TypeManager.int64_type) {
263 long value = ((LongConstant)e).Value;
264 if (value == long.MinValue) {
265 if (ec.ConstantCheckState) {
266 ConstantFold.Error_CompileTimeOverflow (loc);
271 return new LongConstant (-value, e.Location);
274 if (expr_type == TypeManager.uint32_type) {
275 UIntLiteral uil = e as UIntLiteral;
277 if (uil.Value == 2147483648)
278 return new IntLiteral (int.MinValue, e.Location);
279 return new LongLiteral (-uil.Value, e.Location);
281 return new LongConstant (-((UIntConstant)e).Value, e.Location);
284 if (expr_type == TypeManager.uint64_type) {
285 ULongLiteral ull = e as ULongLiteral;
286 if (ull != null && ull.Value == 9223372036854775808)
287 return new LongLiteral (long.MinValue, e.Location);
291 if (expr_type == TypeManager.float_type) {
292 FloatLiteral fl = e as FloatLiteral;
293 // For better error reporting
295 fl.Value = -fl.Value;
298 return new FloatConstant (-((FloatConstant)e).Value, e.Location);
300 if (expr_type == TypeManager.double_type) {
301 DoubleLiteral dl = e as DoubleLiteral;
302 // For better error reporting
304 dl.Value = -dl.Value;
308 return new DoubleConstant (-((DoubleConstant)e).Value, e.Location);
310 if (expr_type == TypeManager.decimal_type)
311 return new DecimalConstant (-((DecimalConstant)e).Value, e.Location);
315 case Operator.LogicalNot:
316 if (expr_type != TypeManager.bool_type)
319 BoolConstant b = (BoolConstant) e;
320 return new BoolConstant (!(b.Value), b.Location);
322 case Operator.OnesComplement:
323 // Unary numeric promotions
324 if (expr_type == TypeManager.byte_type)
325 return new IntConstant (~((ByteConstant)e).Value, e.Location);
326 if (expr_type == TypeManager.sbyte_type)
327 return new IntConstant (~((SByteConstant)e).Value, e.Location);
328 if (expr_type == TypeManager.short_type)
329 return new IntConstant (~((ShortConstant)e).Value, e.Location);
330 if (expr_type == TypeManager.ushort_type)
331 return new IntConstant (~((UShortConstant)e).Value, e.Location);
332 if (expr_type == TypeManager.char_type)
333 return new IntConstant (~((CharConstant)e).Value, e.Location);
335 // Predefined operators
336 if (expr_type == TypeManager.int32_type)
337 return new IntConstant (~((IntConstant)e).Value, e.Location);
338 if (expr_type == TypeManager.uint32_type)
339 return new UIntConstant (~((UIntConstant)e).Value, e.Location);
340 if (expr_type == TypeManager.int64_type)
341 return new LongConstant (~((LongConstant)e).Value, e.Location);
342 if (expr_type == TypeManager.uint64_type){
343 return new ULongConstant (~((ULongConstant)e).Value, e.Location);
345 if (e is EnumConstant) {
346 e = TryReduceConstant (ec, ((EnumConstant)e).Child);
348 e = new EnumConstant (e, expr_type);
353 case Operator.AddressOf:
356 case Operator.Indirection:
359 throw new Exception ("Can not constant fold: " + Oper.ToString());
362 Expression ResolveOperator (EmitContext ec)
365 // Step 1: Default operations on CLI native types.
368 // Attempt to use a constant folding operation.
369 Constant cexpr = Expr as Constant;
371 cexpr = TryReduceConstant (ec, cexpr);
378 // Step 2: Perform Operator Overload location
380 Type expr_type = Expr.Type;
381 string op_name = oper_names [(int) Oper];
383 Expression mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
385 Expression e = StaticCallExpr.MakeSimpleCall (
386 ec, (MethodGroupExpr) mg, Expr, loc);
397 case Operator.LogicalNot:
398 if (expr_type != TypeManager.bool_type) {
399 Expr = ResolveBoolean (ec, Expr, loc);
406 type = TypeManager.bool_type;
409 case Operator.OnesComplement:
410 // Unary numeric promotions
411 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
412 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
413 expr_type == TypeManager.char_type)
415 type = TypeManager.int32_type;
416 return new EmptyCast (this, type);
419 // Predefined operators
420 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
421 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
422 TypeManager.IsEnumType (expr_type))
428 type = TypeManager.int32_type;
429 Expr = Convert.ImplicitUserConversion(ec, Expr, type, loc);
436 case Operator.AddressOf:
442 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){
446 IVariable variable = Expr as IVariable;
447 bool is_fixed = variable != null && variable.VerifyFixed ();
449 if (!ec.InFixedInitializer && !is_fixed) {
450 Error (212, "You can only take the address of unfixed expression inside " +
451 "of a fixed statement initializer");
455 if (ec.InFixedInitializer && is_fixed) {
456 Error (213, "You cannot use the fixed statement to take the address of an already fixed expression");
460 LocalVariableReference lr = Expr as LocalVariableReference;
462 if (lr.local_info.IsCaptured){
463 AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc);
466 lr.local_info.AddressTaken = true;
467 lr.local_info.Used = true;
470 ParameterReference pr = Expr as ParameterReference;
471 if ((pr != null) && pr.Parameter.IsCaptured) {
472 AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc);
476 // According to the specs, a variable is considered definitely assigned if you take
478 if ((variable != null) && (variable.VariableInfo != null)){
479 variable.VariableInfo.SetAssigned (ec);
482 type = TypeManager.GetPointerType (Expr.Type);
485 case Operator.Indirection:
491 if (!expr_type.IsPointer){
492 Error (193, "The * or -> operator must be applied to a pointer");
497 // We create an Indirection expression, because
498 // it can implement the IMemoryLocation.
500 return new Indirection (Expr, loc);
502 case Operator.UnaryPlus:
503 // Unary numeric promotions
504 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
505 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
506 expr_type == TypeManager.char_type)
508 return new EmptyCast (Expr, TypeManager.int32_type);
511 // Predefined operators
512 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
513 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
514 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
515 expr_type == TypeManager.decimal_type)
520 Expr = Convert.ImplicitUserConversion(ec, Expr, TypeManager.int32_type, loc);
522 // Because we can completely ignore unary +
529 case Operator.UnaryNegation:
531 // transform - - expr into expr
533 Unary u = Expr as Unary;
534 if (u != null && u.Oper == Operator.UnaryNegation) {
538 // Unary numeric promotions
539 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
540 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
541 expr_type == TypeManager.char_type)
543 type = TypeManager.int32_type;
544 return new EmptyCast (this, type);
548 // Predefined operators
550 if (expr_type == TypeManager.uint32_type) {
551 type = TypeManager.int64_type;
552 Expr = Convert.ImplicitNumericConversion (Expr, type);
556 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.int64_type ||
557 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
558 expr_type == TypeManager.decimal_type)
567 type = TypeManager.int32_type;
568 Expr = Convert.ImplicitUserConversion(ec, Expr, type, loc);
576 Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" +
577 TypeManager.CSharpName (expr_type) + "'");
581 public override Expression DoResolve (EmitContext ec)
583 if (Oper == Operator.AddressOf) {
584 Expr = Expr.DoResolveLValue (ec, new EmptyExpression ());
586 if (Expr == null || Expr.eclass != ExprClass.Variable){
587 Error (211, "Cannot take the address of the given expression");
592 Expr = Expr.Resolve (ec);
598 if (TypeManager.IsNullableValueType (Expr.Type))
599 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
602 eclass = ExprClass.Value;
603 return ResolveOperator (ec);
606 public override Expression DoResolveLValue (EmitContext ec, Expression right)
608 if (Oper == Operator.Indirection)
609 return DoResolve (ec);
614 public override void Emit (EmitContext ec)
616 ILGenerator ig = ec.ig;
619 case Operator.UnaryPlus:
620 throw new Exception ("This should be caught by Resolve");
622 case Operator.UnaryNegation:
623 if (ec.CheckState && type != TypeManager.float_type && type != TypeManager.double_type) {
624 ig.Emit (OpCodes.Ldc_I4_0);
625 if (type == TypeManager.int64_type)
626 ig.Emit (OpCodes.Conv_U8);
628 ig.Emit (OpCodes.Sub_Ovf);
631 ig.Emit (OpCodes.Neg);
636 case Operator.LogicalNot:
638 ig.Emit (OpCodes.Ldc_I4_0);
639 ig.Emit (OpCodes.Ceq);
642 case Operator.OnesComplement:
644 ig.Emit (OpCodes.Not);
647 case Operator.AddressOf:
648 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
652 throw new Exception ("This should not happen: Operator = "
657 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
659 if (Oper == Operator.LogicalNot)
660 Expr.EmitBranchable (ec, target, !onTrue);
662 base.EmitBranchable (ec, target, onTrue);
665 public override string ToString ()
667 return "Unary (" + Oper + ", " + Expr + ")";
670 protected override void CloneTo (CloneContext clonectx, Expression t)
672 Unary target = (Unary) t;
674 target.Expr = Expr.Clone (clonectx);
679 // Unary operators are turned into Indirection expressions
680 // after semantic analysis (this is so we can take the address
681 // of an indirection).
683 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
685 LocalTemporary temporary;
688 public Indirection (Expression expr, Location l)
691 type = TypeManager.HasElementType (expr.Type) ? TypeManager.GetElementType (expr.Type) : expr.Type;
692 eclass = ExprClass.Variable;
696 public override void Emit (EmitContext ec)
701 LoadFromPtr (ec.ig, Type);
704 public void Emit (EmitContext ec, bool leave_copy)
708 ec.ig.Emit (OpCodes.Dup);
709 temporary = new LocalTemporary (expr.Type);
710 temporary.Store (ec);
714 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
716 prepared = prepare_for_load;
720 if (prepare_for_load)
721 ec.ig.Emit (OpCodes.Dup);
725 ec.ig.Emit (OpCodes.Dup);
726 temporary = new LocalTemporary (expr.Type);
727 temporary.Store (ec);
730 StoreFromPtr (ec.ig, type);
732 if (temporary != null) {
734 temporary.Release (ec);
738 public void AddressOf (EmitContext ec, AddressOp Mode)
743 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
745 return DoResolve (ec);
748 public override Expression DoResolve (EmitContext ec)
751 // Born fully resolved
756 public override string ToString ()
758 return "*(" + expr + ")";
761 #region IVariable Members
763 public VariableInfo VariableInfo {
767 public bool VerifyFixed ()
769 // A pointer-indirection is always fixed.
777 /// Unary Mutator expressions (pre and post ++ and --)
781 /// UnaryMutator implements ++ and -- expressions. It derives from
782 /// ExpressionStatement becuase the pre/post increment/decrement
783 /// operators can be used in a statement context.
785 /// FIXME: Idea, we could split this up in two classes, one simpler
786 /// for the common case, and one with the extra fields for more complex
787 /// classes (indexers require temporary access; overloaded require method)
790 public class UnaryMutator : ExpressionStatement {
792 public enum Mode : byte {
799 PreDecrement = IsDecrement,
800 PostIncrement = IsPost,
801 PostDecrement = IsPost | IsDecrement
805 bool is_expr = false;
806 bool recurse = false;
811 // This is expensive for the simplest case.
813 StaticCallExpr method;
815 public UnaryMutator (Mode m, Expression e, Location l)
822 static string OperName (Mode mode)
824 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
829 /// Returns whether an object of type `t' can be incremented
830 /// or decremented with add/sub (ie, basically whether we can
831 /// use pre-post incr-decr operations on it, but it is not a
832 /// System.Decimal, which we require operator overloading to catch)
834 static bool IsIncrementableNumber (Type t)
836 return (t == TypeManager.sbyte_type) ||
837 (t == TypeManager.byte_type) ||
838 (t == TypeManager.short_type) ||
839 (t == TypeManager.ushort_type) ||
840 (t == TypeManager.int32_type) ||
841 (t == TypeManager.uint32_type) ||
842 (t == TypeManager.int64_type) ||
843 (t == TypeManager.uint64_type) ||
844 (t == TypeManager.char_type) ||
845 (t.IsSubclassOf (TypeManager.enum_type)) ||
846 (t == TypeManager.float_type) ||
847 (t == TypeManager.double_type) ||
848 (t.IsPointer && t != TypeManager.void_ptr_type);
851 Expression ResolveOperator (EmitContext ec)
853 Type expr_type = expr.Type;
856 // Step 1: Perform Operator Overload location
861 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
862 op_name = "op_Increment";
864 op_name = "op_Decrement";
866 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
869 method = StaticCallExpr.MakeSimpleCall (
870 ec, (MethodGroupExpr) mg, expr, loc);
873 } else if (!IsIncrementableNumber (expr_type)) {
874 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
875 TypeManager.CSharpName (expr_type) + "'");
880 // The operand of the prefix/postfix increment decrement operators
881 // should be an expression that is classified as a variable,
882 // a property access or an indexer access
885 if (expr.eclass == ExprClass.Variable){
886 LocalVariableReference var = expr as LocalVariableReference;
887 if ((var != null) && var.IsReadOnly) {
888 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
891 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
892 expr = expr.ResolveLValue (ec, this, Location);
896 if (expr.eclass == ExprClass.Value) {
897 Error_ValueAssignment (loc);
899 expr.Error_UnexpectedKind (ec.DeclContainer, "variable, indexer or property access", loc);
907 public override Expression DoResolve (EmitContext ec)
909 expr = expr.Resolve (ec);
914 eclass = ExprClass.Value;
917 if (TypeManager.IsNullableValueType (expr.Type))
918 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
921 return ResolveOperator (ec);
924 static int PtrTypeSize (Type t)
926 return GetTypeSize (TypeManager.GetElementType (t));
930 // Loads the proper "1" into the stack based on the type, then it emits the
931 // opcode for the operation requested
933 void LoadOneAndEmitOp (EmitContext ec, Type t)
936 // Measure if getting the typecode and using that is more/less efficient
937 // that comparing types. t.GetTypeCode() is an internal call.
939 ILGenerator ig = ec.ig;
941 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
942 LongConstant.EmitLong (ig, 1);
943 else if (t == TypeManager.double_type)
944 ig.Emit (OpCodes.Ldc_R8, 1.0);
945 else if (t == TypeManager.float_type)
946 ig.Emit (OpCodes.Ldc_R4, 1.0F);
947 else if (t.IsPointer){
948 int n = PtrTypeSize (t);
951 ig.Emit (OpCodes.Sizeof, t);
953 IntConstant.EmitInt (ig, n);
955 ig.Emit (OpCodes.Ldc_I4_1);
958 // Now emit the operation
961 if (t == TypeManager.int32_type ||
962 t == TypeManager.int64_type){
963 if ((mode & Mode.IsDecrement) != 0)
964 ig.Emit (OpCodes.Sub_Ovf);
966 ig.Emit (OpCodes.Add_Ovf);
967 } else if (t == TypeManager.uint32_type ||
968 t == TypeManager.uint64_type){
969 if ((mode & Mode.IsDecrement) != 0)
970 ig.Emit (OpCodes.Sub_Ovf_Un);
972 ig.Emit (OpCodes.Add_Ovf_Un);
974 if ((mode & Mode.IsDecrement) != 0)
975 ig.Emit (OpCodes.Sub_Ovf);
977 ig.Emit (OpCodes.Add_Ovf);
980 if ((mode & Mode.IsDecrement) != 0)
981 ig.Emit (OpCodes.Sub);
983 ig.Emit (OpCodes.Add);
986 if (t == TypeManager.sbyte_type){
988 ig.Emit (OpCodes.Conv_Ovf_I1);
990 ig.Emit (OpCodes.Conv_I1);
991 } else if (t == TypeManager.byte_type){
993 ig.Emit (OpCodes.Conv_Ovf_U1);
995 ig.Emit (OpCodes.Conv_U1);
996 } else if (t == TypeManager.short_type){
998 ig.Emit (OpCodes.Conv_Ovf_I2);
1000 ig.Emit (OpCodes.Conv_I2);
1001 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
1003 ig.Emit (OpCodes.Conv_Ovf_U2);
1005 ig.Emit (OpCodes.Conv_U2);
1010 void EmitCode (EmitContext ec, bool is_expr)
1013 this.is_expr = is_expr;
1014 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1017 public override void Emit (EmitContext ec)
1020 // We use recurse to allow ourselfs to be the source
1021 // of an assignment. This little hack prevents us from
1022 // having to allocate another expression
1025 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1027 LoadOneAndEmitOp (ec, expr.Type);
1029 ec.ig.Emit (OpCodes.Call, method.Method);
1034 EmitCode (ec, true);
1037 public override void EmitStatement (EmitContext ec)
1039 EmitCode (ec, false);
1042 protected override void CloneTo (CloneContext clonectx, Expression t)
1044 UnaryMutator target = (UnaryMutator) t;
1046 target.expr = expr.Clone (clonectx);
1051 /// Base class for the `Is' and `As' classes.
1055 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1058 public abstract class Probe : Expression {
1059 public Expression ProbeType;
1060 protected Expression expr;
1061 protected TypeExpr probe_type_expr;
1063 public Probe (Expression expr, Expression probe_type, Location l)
1065 ProbeType = probe_type;
1070 public Expression Expr {
1076 public override Expression DoResolve (EmitContext ec)
1078 probe_type_expr = ProbeType.ResolveAsTypeTerminal (ec, false);
1079 if (probe_type_expr == null)
1082 expr = expr.Resolve (ec);
1086 if (expr.Type.IsPointer) {
1087 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1093 protected override void CloneTo (CloneContext clonectx, Expression t)
1095 Probe target = (Probe) t;
1097 target.expr = expr.Clone (clonectx);
1098 target.ProbeType = ProbeType.Clone (clonectx);
1104 /// Implementation of the `is' operator.
1106 public class Is : Probe {
1107 public Is (Expression expr, Expression probe_type, Location l)
1108 : base (expr, probe_type, l)
1113 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1118 public override void Emit (EmitContext ec)
1120 ILGenerator ig = ec.ig;
1125 case Action.AlwaysFalse:
1126 ig.Emit (OpCodes.Pop);
1127 IntConstant.EmitInt (ig, 0);
1129 case Action.AlwaysTrue:
1130 ig.Emit (OpCodes.Pop);
1131 IntConstant.EmitInt (ig, 1);
1133 case Action.LeaveOnStack:
1134 // the `e != null' rule.
1135 ig.Emit (OpCodes.Ldnull);
1136 ig.Emit (OpCodes.Ceq);
1137 ig.Emit (OpCodes.Ldc_I4_0);
1138 ig.Emit (OpCodes.Ceq);
1141 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1142 ig.Emit (OpCodes.Ldnull);
1143 ig.Emit (OpCodes.Cgt_Un);
1146 throw new Exception ("never reached");
1149 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1151 ILGenerator ig = ec.ig;
1154 case Action.AlwaysFalse:
1156 ig.Emit (OpCodes.Br, target);
1159 case Action.AlwaysTrue:
1161 ig.Emit (OpCodes.Br, target);
1164 case Action.LeaveOnStack:
1165 // the `e != null' rule.
1167 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1171 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1172 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1175 throw new Exception ("never reached");
1178 public override Expression DoResolve (EmitContext ec)
1180 Expression e = base.DoResolve (ec);
1182 if ((e == null) || (expr == null))
1185 Type etype = expr.Type;
1186 type = TypeManager.bool_type;
1187 eclass = ExprClass.Value;
1190 // First case, if at compile time, there is an implicit conversion
1191 // then e != null (objects) or true (value types)
1193 Type probe_type = probe_type_expr.Type;
1194 e = Convert.ImplicitConversionStandard (ec, expr, probe_type, loc);
1197 if (etype.IsValueType)
1198 action = Action.AlwaysTrue;
1200 action = Action.LeaveOnStack;
1202 Constant c = e as Constant;
1203 if (c != null && c.GetValue () == null) {
1204 action = Action.AlwaysFalse;
1205 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
1206 TypeManager.CSharpName (probe_type));
1208 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
1209 TypeManager.CSharpName (probe_type));
1214 if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1215 if (TypeManager.IsGenericParameter (etype))
1216 expr = new BoxedCast (expr, etype);
1219 // Second case: explicit reference convresion
1221 if (expr is NullLiteral)
1222 action = Action.AlwaysFalse;
1224 action = Action.Probe;
1225 } else if (TypeManager.ContainsGenericParameters (etype) ||
1226 TypeManager.ContainsGenericParameters (probe_type)) {
1227 expr = new BoxedCast (expr, etype);
1228 action = Action.Probe;
1230 action = Action.AlwaysFalse;
1231 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1232 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1240 /// Implementation of the `as' operator.
1242 public class As : Probe {
1243 public As (Expression expr, Expression probe_type, Location l)
1244 : base (expr, probe_type, l)
1248 bool do_isinst = false;
1249 Expression resolved_type;
1251 public override void Emit (EmitContext ec)
1253 ILGenerator ig = ec.ig;
1258 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1261 static void Error_CannotConvertType (Type source, Type target, Location loc)
1263 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1264 TypeManager.CSharpName (source),
1265 TypeManager.CSharpName (target));
1268 public override Expression DoResolve (EmitContext ec)
1270 if (resolved_type == null) {
1271 resolved_type = base.DoResolve (ec);
1273 if (resolved_type == null)
1277 type = probe_type_expr.Type;
1278 eclass = ExprClass.Value;
1279 Type etype = expr.Type;
1281 if (type.IsValueType) {
1282 Report.Error (77, loc, "The as operator must be used with a reference type (`" +
1283 TypeManager.CSharpName (type) + "' is a value type)");
1290 // If the type is a type parameter, ensure
1291 // that it is constrained by a class
1293 TypeParameterExpr tpe = probe_type_expr as TypeParameterExpr;
1295 GenericConstraints constraints = tpe.TypeParameter.GenericConstraints;
1298 if (constraints == null)
1301 if (!constraints.HasClassConstraint)
1302 if ((constraints.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == 0)
1306 Report.Error (413, loc,
1307 "The as operator requires that the `{0}' type parameter be constrained by a class",
1308 probe_type_expr.GetSignatureForError ());
1314 Expression e = Convert.ImplicitConversion (ec, expr, type, loc);
1321 if (Convert.ExplicitReferenceConversionExists (etype, type)){
1322 if (TypeManager.IsGenericParameter (etype))
1323 expr = new BoxedCast (expr, etype);
1329 if (TypeManager.ContainsGenericParameters (etype) ||
1330 TypeManager.ContainsGenericParameters (type)) {
1331 expr = new BoxedCast (expr, etype);
1336 Error_CannotConvertType (etype, type, loc);
1340 public override bool GetAttributableValue (Type valueType, out object value)
1342 return expr.GetAttributableValue (valueType, out value);
1347 /// This represents a typecast in the source language.
1349 /// FIXME: Cast expressions have an unusual set of parsing
1350 /// rules, we need to figure those out.
1352 public class Cast : Expression {
1353 Expression target_type;
1356 public Cast (Expression cast_type, Expression expr)
1357 : this (cast_type, expr, cast_type.Location)
1361 public Cast (Expression cast_type, Expression expr, Location loc)
1363 this.target_type = cast_type;
1367 if (target_type == TypeManager.system_void_expr)
1368 Error_VoidInvalidInTheContext (loc);
1371 public Expression TargetType {
1372 get { return target_type; }
1375 public Expression Expr {
1376 get { return expr; }
1377 set { expr = value; }
1380 public override Expression DoResolve (EmitContext ec)
1382 expr = expr.Resolve (ec);
1386 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1392 if (type.IsAbstract && type.IsSealed) {
1393 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1397 eclass = ExprClass.Value;
1399 Constant c = expr as Constant;
1401 c = c.TryReduce (ec, type, loc);
1406 if (type.IsPointer && !ec.InUnsafe) {
1410 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1414 public override void Emit (EmitContext ec)
1416 throw new Exception ("Should not happen");
1419 protected override void CloneTo (CloneContext clonectx, Expression t)
1421 Cast target = (Cast) t;
1423 target.target_type = target_type.Clone (clonectx);
1424 target.expr = expr.Clone (clonectx);
1429 /// Binary operators
1431 public class Binary : Expression {
1432 public enum Operator : byte {
1433 Multiply, Division, Modulus,
1434 Addition, Subtraction,
1435 LeftShift, RightShift,
1436 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1437 Equality, Inequality,
1447 Expression left, right;
1449 // This must be kept in sync with Operator!!!
1450 public static readonly string [] oper_names;
1454 oper_names = new string [(int) Operator.TOP];
1456 oper_names [(int) Operator.Multiply] = "op_Multiply";
1457 oper_names [(int) Operator.Division] = "op_Division";
1458 oper_names [(int) Operator.Modulus] = "op_Modulus";
1459 oper_names [(int) Operator.Addition] = "op_Addition";
1460 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1461 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1462 oper_names [(int) Operator.RightShift] = "op_RightShift";
1463 oper_names [(int) Operator.LessThan] = "op_LessThan";
1464 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1465 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1466 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1467 oper_names [(int) Operator.Equality] = "op_Equality";
1468 oper_names [(int) Operator.Inequality] = "op_Inequality";
1469 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1470 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1471 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1472 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1473 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1476 public Binary (Operator oper, Expression left, Expression right)
1481 this.loc = left.Location;
1484 public Operator Oper {
1493 public Expression Left {
1502 public Expression Right {
1513 /// Returns a stringified representation of the Operator
1515 public static string OperName (Operator oper)
1518 case Operator.Multiply:
1520 case Operator.Division:
1522 case Operator.Modulus:
1524 case Operator.Addition:
1526 case Operator.Subtraction:
1528 case Operator.LeftShift:
1530 case Operator.RightShift:
1532 case Operator.LessThan:
1534 case Operator.GreaterThan:
1536 case Operator.LessThanOrEqual:
1538 case Operator.GreaterThanOrEqual:
1540 case Operator.Equality:
1542 case Operator.Inequality:
1544 case Operator.BitwiseAnd:
1546 case Operator.BitwiseOr:
1548 case Operator.ExclusiveOr:
1550 case Operator.LogicalOr:
1552 case Operator.LogicalAnd:
1556 return oper.ToString ();
1559 public override string ToString ()
1561 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1562 right.ToString () + ")";
1565 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1567 if (expr.Type == target_type)
1570 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1573 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1576 34, loc, "Operator `" + OperName (oper)
1577 + "' is ambiguous on operands of type `"
1578 + TypeManager.CSharpName (l) + "' "
1579 + "and `" + TypeManager.CSharpName (r)
1583 bool IsConvertible (EmitContext ec, Expression le, Expression re, Type t)
1585 return Convert.ImplicitConversionExists (ec, le, t) && Convert.ImplicitConversionExists (ec, re, t);
1588 bool VerifyApplicable_Predefined (EmitContext ec, Type t)
1590 if (!IsConvertible (ec, left, right, t))
1592 left = ForceConversion (ec, left, t);
1593 right = ForceConversion (ec, right, t);
1598 bool IsApplicable_String (EmitContext ec, Expression le, Expression re, Operator oper)
1600 bool l = Convert.ImplicitConversionExists (ec, le, TypeManager.string_type);
1601 bool r = Convert.ImplicitConversionExists (ec, re, TypeManager.string_type);
1603 if (oper == Operator.Equality || oper == Operator.Inequality)
1605 if (oper == Operator.Addition)
1610 bool OverloadResolve_PredefinedString (EmitContext ec, Operator oper)
1612 if (!IsApplicable_String (ec, left, right, oper))
1614 Type t = TypeManager.string_type;
1615 if (Convert.ImplicitConversionExists (ec, left, t))
1616 left = ForceConversion (ec, left, t);
1617 if (Convert.ImplicitConversionExists (ec, right, t))
1618 right = ForceConversion (ec, right, t);
1623 bool OverloadResolve_PredefinedIntegral (EmitContext ec)
1625 return VerifyApplicable_Predefined (ec, TypeManager.int32_type) ||
1626 VerifyApplicable_Predefined (ec, TypeManager.uint32_type) ||
1627 VerifyApplicable_Predefined (ec, TypeManager.int64_type) ||
1628 VerifyApplicable_Predefined (ec, TypeManager.uint64_type) ||
1632 bool OverloadResolve_PredefinedFloating (EmitContext ec)
1634 return VerifyApplicable_Predefined (ec, TypeManager.float_type) ||
1635 VerifyApplicable_Predefined (ec, TypeManager.double_type) ||
1639 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1641 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1644 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1646 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1650 void Error_OperatorCannotBeApplied ()
1652 Error_OperatorCannotBeApplied (Location, OperName (oper), TypeManager.CSharpName (left.Type),
1653 TypeManager.CSharpName(right.Type));
1656 static bool is_unsigned (Type t)
1658 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1659 t == TypeManager.short_type || t == TypeManager.byte_type);
1662 Expression Make32or64 (EmitContext ec, Expression e)
1666 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1667 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1669 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1672 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1675 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1678 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1684 Expression CheckShiftArguments (EmitContext ec)
1686 Expression new_left = Make32or64 (ec, left);
1687 Expression new_right = ForceConversion (ec, right, TypeManager.int32_type);
1688 if (new_left == null || new_right == null) {
1689 Error_OperatorCannotBeApplied ();
1692 type = new_left.Type;
1693 int shiftmask = (type == TypeManager.int32_type || type == TypeManager.uint32_type) ? 31 : 63;
1695 right = new Binary (Binary.Operator.BitwiseAnd, new_right, new IntConstant (shiftmask, loc)).DoResolve (ec);
1700 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1701 // i.e., not invoke op_Equality.
1703 static bool EqualsNullIsReferenceEquals (Type t)
1705 return t == TypeManager.object_type || t == TypeManager.string_type ||
1706 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1709 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1711 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1712 "Possible unintended reference comparison; to get a value comparison, " +
1713 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1716 Expression ResolveOperator (EmitContext ec)
1719 Type r = right.Type;
1721 if (oper == Operator.Equality || oper == Operator.Inequality){
1722 if (TypeManager.IsGenericParameter (l) && (right is NullLiteral)) {
1723 if (l.BaseType == TypeManager.value_type) {
1724 Error_OperatorCannotBeApplied ();
1728 left = new BoxedCast (left, TypeManager.object_type);
1729 Type = TypeManager.bool_type;
1733 if (TypeManager.IsGenericParameter (r) && (left is NullLiteral)) {
1734 if (r.BaseType == TypeManager.value_type) {
1735 Error_OperatorCannotBeApplied ();
1739 right = new BoxedCast (right, TypeManager.object_type);
1740 Type = TypeManager.bool_type;
1745 // Optimize out call to op_Equality in a few cases.
1747 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1748 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1749 Type = TypeManager.bool_type;
1754 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1755 Type = TypeManager.bool_type;
1761 // Do not perform operator overload resolution when both sides are
1764 Expression left_operators = null, right_operators = null;
1765 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))) {
1767 // Step 1: Perform Operator Overload location
1769 string op = oper_names [(int) oper];
1771 MethodGroupExpr union;
1772 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc);
1774 right_operators = MemberLookup (
1775 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc);
1776 union = Invocation.MakeUnionSet (left_operators, right_operators, loc);
1778 union = (MethodGroupExpr) left_operators;
1780 if (union != null) {
1781 ArrayList args = new ArrayList (2);
1782 args.Add (new Argument (left, Argument.AType.Expression));
1783 args.Add (new Argument (right, Argument.AType.Expression));
1785 MethodBase method = union.OverloadResolve (ec, args, true, Location.Null);
1787 if (method != null) {
1788 MethodInfo mi = (MethodInfo) method;
1789 return new BinaryMethod (mi.ReturnType, method, args);
1795 // Step 0: String concatenation (because overloading will get this wrong)
1797 if (oper == Operator.Addition){
1799 // If any of the arguments is a string, cast to string
1802 // Simple constant folding
1803 if (left is StringConstant && right is StringConstant)
1804 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1806 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1808 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1809 Error_OperatorCannotBeApplied ();
1813 // try to fold it in on the left
1814 if (left is StringConcat) {
1817 // We have to test here for not-null, since we can be doubly-resolved
1818 // take care of not appending twice
1821 type = TypeManager.string_type;
1822 ((StringConcat) left).Append (ec, right);
1823 return left.Resolve (ec);
1829 // Otherwise, start a new concat expression
1830 return new StringConcat (ec, loc, left, right).Resolve (ec);
1834 // Transform a + ( - b) into a - b
1836 if (right is Unary){
1837 Unary right_unary = (Unary) right;
1839 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1840 oper = Operator.Subtraction;
1841 right = right_unary.Expr;
1847 if (oper == Operator.Equality || oper == Operator.Inequality){
1848 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1849 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1850 Error_OperatorCannotBeApplied ();
1854 type = TypeManager.bool_type;
1858 if (l.IsPointer || r.IsPointer) {
1859 if (l.IsPointer && r.IsPointer) {
1860 type = TypeManager.bool_type;
1864 if (l.IsPointer && r == TypeManager.null_type) {
1865 right = new EmptyCast (NullPointer.Null, l);
1866 type = TypeManager.bool_type;
1870 if (r.IsPointer && l == TypeManager.null_type) {
1871 left = new EmptyCast (NullPointer.Null, r);
1872 type = TypeManager.bool_type;
1878 if (l.IsGenericParameter && r.IsGenericParameter) {
1879 GenericConstraints l_gc, r_gc;
1881 l_gc = TypeManager.GetTypeParameterConstraints (l);
1882 r_gc = TypeManager.GetTypeParameterConstraints (r);
1884 if ((l_gc == null) || (r_gc == null) ||
1885 !(l_gc.HasReferenceTypeConstraint || l_gc.HasClassConstraint) ||
1886 !(r_gc.HasReferenceTypeConstraint || r_gc.HasClassConstraint)) {
1887 Error_OperatorCannotBeApplied ();
1895 // operator != (object a, object b)
1896 // operator == (object a, object b)
1898 // For this to be used, both arguments have to be reference-types.
1899 // Read the rationale on the spec (14.9.6)
1901 if (!(l.IsValueType || r.IsValueType)){
1902 type = TypeManager.bool_type;
1908 // Also, a standard conversion must exist from either one
1910 bool left_to_right =
1911 Convert.ImplicitStandardConversionExists (left, r);
1912 bool right_to_left = !left_to_right &&
1913 Convert.ImplicitStandardConversionExists (right, l);
1915 if (!left_to_right && !right_to_left) {
1916 Error_OperatorCannotBeApplied ();
1920 if (left_to_right && left_operators != null &&
1921 RootContext.WarningLevel >= 2) {
1922 ArrayList args = new ArrayList (2);
1923 args.Add (new Argument (left, Argument.AType.Expression));
1924 args.Add (new Argument (left, Argument.AType.Expression));
1925 MethodBase method = ((MethodGroupExpr)left_operators).OverloadResolve (
1926 ec, args, true, Location.Null);
1928 Warning_UnintendedReferenceComparison (loc, "right", l);
1931 if (right_to_left && right_operators != null &&
1932 RootContext.WarningLevel >= 2) {
1933 ArrayList args = new ArrayList (2);
1934 args.Add (new Argument (right, Argument.AType.Expression));
1935 args.Add (new Argument (right, Argument.AType.Expression));
1936 MethodBase method = ((MethodGroupExpr)right_operators).OverloadResolve (
1937 ec, args, true, Location.Null);
1939 Warning_UnintendedReferenceComparison (loc, "left", r);
1943 // We are going to have to convert to an object to compare
1945 if (l != TypeManager.object_type)
1946 left = new EmptyCast (left, TypeManager.object_type);
1947 if (r != TypeManager.object_type)
1948 right = new EmptyCast (right, TypeManager.object_type);
1954 // Only perform numeric promotions on:
1955 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
1957 if (oper == Operator.Addition || oper == Operator.Subtraction) {
1958 if (TypeManager.IsDelegateType (l)){
1959 if (((right.eclass == ExprClass.MethodGroup) ||
1960 (r == TypeManager.anonymous_method_type))){
1961 if ((RootContext.Version != LanguageVersion.ISO_1)){
1962 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
1970 if (TypeManager.IsDelegateType (r) || right is NullLiteral){
1972 ArrayList args = new ArrayList (2);
1974 args = new ArrayList (2);
1975 args.Add (new Argument (left, Argument.AType.Expression));
1976 args.Add (new Argument (right, Argument.AType.Expression));
1978 if (oper == Operator.Addition)
1979 method = TypeManager.delegate_combine_delegate_delegate;
1981 method = TypeManager.delegate_remove_delegate_delegate;
1983 if (!TypeManager.IsEqual (l, r) && !(right is NullLiteral)) {
1984 Error_OperatorCannotBeApplied ();
1988 return new BinaryDelegate (l, method, args);
1993 // Pointer arithmetic:
1995 // T* operator + (T* x, int y);
1996 // T* operator + (T* x, uint y);
1997 // T* operator + (T* x, long y);
1998 // T* operator + (T* x, ulong y);
2000 // T* operator + (int y, T* x);
2001 // T* operator + (uint y, T *x);
2002 // T* operator + (long y, T *x);
2003 // T* operator + (ulong y, T *x);
2005 // T* operator - (T* x, int y);
2006 // T* operator - (T* x, uint y);
2007 // T* operator - (T* x, long y);
2008 // T* operator - (T* x, ulong y);
2010 // long operator - (T* x, T *y)
2013 if (r.IsPointer && oper == Operator.Subtraction){
2015 return new PointerArithmetic (
2016 false, left, right, TypeManager.int64_type,
2019 Expression t = Make32or64 (ec, right);
2021 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
2023 } else if (r.IsPointer && oper == Operator.Addition){
2024 Expression t = Make32or64 (ec, left);
2026 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
2031 // Enumeration operators
2033 bool lie = TypeManager.IsEnumType (l);
2034 bool rie = TypeManager.IsEnumType (r);
2038 // U operator - (E e, E f)
2040 if (oper == Operator.Subtraction){
2042 type = TypeManager.EnumToUnderlying (l);
2045 Error_OperatorCannotBeApplied ();
2051 // operator + (E e, U x)
2052 // operator - (E e, U x)
2054 if (oper == Operator.Addition || oper == Operator.Subtraction){
2055 Type enum_type = lie ? l : r;
2056 Type other_type = lie ? r : l;
2057 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2059 if (underlying_type != other_type){
2060 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2070 Error_OperatorCannotBeApplied ();
2079 temp = Convert.ImplicitConversion (ec, right, l, loc);
2083 Error_OperatorCannotBeApplied ();
2087 temp = Convert.ImplicitConversion (ec, left, r, loc);
2092 Error_OperatorCannotBeApplied ();
2097 if (oper == Operator.Equality || oper == Operator.Inequality ||
2098 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2099 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2100 if (left.Type != right.Type){
2101 Error_OperatorCannotBeApplied ();
2104 type = TypeManager.bool_type;
2108 if (oper == Operator.BitwiseAnd ||
2109 oper == Operator.BitwiseOr ||
2110 oper == Operator.ExclusiveOr){
2111 if (left.Type != right.Type){
2112 Error_OperatorCannotBeApplied ();
2118 Error_OperatorCannotBeApplied ();
2122 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2123 return CheckShiftArguments (ec);
2125 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2126 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2127 type = TypeManager.bool_type;
2131 left_operators = l == TypeManager.bool_type ?
2132 left : Convert.ImplicitUserConversion (ec, left, TypeManager.bool_type, loc);
2133 right_operators = r == TypeManager.bool_type ?
2134 right : Convert.ImplicitUserConversion (ec, right, TypeManager.bool_type, loc);
2136 if (left_operators != null && right_operators != null) {
2137 left = left_operators;
2138 right = right_operators;
2139 type = TypeManager.bool_type;
2143 Expression e = new ConditionalLogicalOperator (
2144 oper == Operator.LogicalAnd, left, right, l, loc);
2145 return e.Resolve (ec);
2148 Expression orig_left = left;
2149 Expression orig_right = right;
2152 // operator & (bool x, bool y)
2153 // operator | (bool x, bool y)
2154 // operator ^ (bool x, bool y)
2156 if (oper == Operator.BitwiseAnd ||
2157 oper == Operator.BitwiseOr ||
2158 oper == Operator.ExclusiveOr) {
2159 if (OverloadResolve_PredefinedIntegral (ec)) {
2160 if (IsConvertible (ec, orig_left, orig_right, TypeManager.bool_type)) {
2161 Error_OperatorAmbiguous (loc, oper, l, r);
2165 if (oper == Operator.BitwiseOr && l != r && !(orig_right is Constant) && right is OpcodeCast &&
2166 (r == TypeManager.sbyte_type || r == TypeManager.short_type ||
2167 r == TypeManager.int32_type || r == TypeManager.int64_type)) {
2168 Report.Warning (675, 3, loc, "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first",
2169 TypeManager.CSharpName (r));
2172 } else if (!VerifyApplicable_Predefined (ec, TypeManager.bool_type)) {
2173 Error_OperatorCannotBeApplied ();
2180 // Pointer comparison
2182 if (l.IsPointer && r.IsPointer){
2183 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2184 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2185 type = TypeManager.bool_type;
2190 if (OverloadResolve_PredefinedIntegral (ec)) {
2191 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2192 Error_OperatorAmbiguous (loc, oper, l, r);
2195 } else if (OverloadResolve_PredefinedFloating (ec)) {
2196 if (IsConvertible (ec, orig_left, orig_right, TypeManager.decimal_type) ||
2197 IsApplicable_String (ec, orig_left, orig_right, oper)) {
2198 Error_OperatorAmbiguous (loc, oper, l, r);
2201 } else if (VerifyApplicable_Predefined (ec, TypeManager.decimal_type)) {
2202 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2203 Error_OperatorAmbiguous (loc, oper, l, r);
2206 } else if (!OverloadResolve_PredefinedString (ec, oper)) {
2207 Error_OperatorCannotBeApplied ();
2211 if (oper == Operator.Equality ||
2212 oper == Operator.Inequality ||
2213 oper == Operator.LessThanOrEqual ||
2214 oper == Operator.LessThan ||
2215 oper == Operator.GreaterThanOrEqual ||
2216 oper == Operator.GreaterThan)
2217 type = TypeManager.bool_type;
2222 if (l == TypeManager.decimal_type || l == TypeManager.string_type || r == TypeManager.string_type) {
2224 if (r == TypeManager.string_type)
2226 MethodGroupExpr ops = (MethodGroupExpr) MemberLookup (
2227 ec.ContainerType, lookup, oper_names [(int) oper],
2228 MemberTypes.Method, AllBindingFlags, loc);
2229 ArrayList args = new ArrayList (2);
2230 args.Add (new Argument (left, Argument.AType.Expression));
2231 args.Add (new Argument (right, Argument.AType.Expression));
2232 MethodBase method = ops.OverloadResolve (ec, args, true, Location.Null);
2233 return new BinaryMethod (type, method, args);
2239 Constant EnumLiftUp (Constant left, Constant right)
2242 case Operator.BitwiseOr:
2243 case Operator.BitwiseAnd:
2244 case Operator.ExclusiveOr:
2245 case Operator.Equality:
2246 case Operator.Inequality:
2247 case Operator.LessThan:
2248 case Operator.LessThanOrEqual:
2249 case Operator.GreaterThan:
2250 case Operator.GreaterThanOrEqual:
2251 if (left is EnumConstant)
2254 if (left.IsZeroInteger)
2255 return new EnumConstant (left, right.Type);
2259 case Operator.Addition:
2260 case Operator.Subtraction:
2263 case Operator.Multiply:
2264 case Operator.Division:
2265 case Operator.Modulus:
2266 case Operator.LeftShift:
2267 case Operator.RightShift:
2268 if (right is EnumConstant || left is EnumConstant)
2272 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2276 public override Expression DoResolve (EmitContext ec)
2281 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2282 left = ((ParenthesizedExpression) left).Expr;
2283 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2287 if (left.eclass == ExprClass.Type) {
2288 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2292 left = left.Resolve (ec);
2297 Constant lc = left as Constant;
2298 if (lc != null && lc.Type == TypeManager.bool_type &&
2299 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2300 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2302 // TODO: make a sense to resolve unreachable expression as we do for statement
2303 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2307 right = right.Resolve (ec);
2311 eclass = ExprClass.Value;
2312 Constant rc = right as Constant;
2314 // The conversion rules are ignored in enum context but why
2315 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2316 left = lc = EnumLiftUp (lc, rc);
2320 right = rc = EnumLiftUp (rc, lc);
2325 if (oper == Operator.BitwiseAnd) {
2326 if (rc != null && rc.IsZeroInteger) {
2327 return lc is EnumConstant ?
2328 new EnumConstant (rc, lc.Type):
2332 if (lc != null && lc.IsZeroInteger) {
2333 return rc is EnumConstant ?
2334 new EnumConstant (lc, rc.Type):
2338 else if (oper == Operator.BitwiseOr) {
2339 if (lc is EnumConstant &&
2340 rc != null && rc.IsZeroInteger)
2342 if (rc is EnumConstant &&
2343 lc != null && lc.IsZeroInteger)
2345 } else if (oper == Operator.LogicalAnd) {
2346 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2348 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2352 if (rc != null && lc != null){
2353 int prev_e = Report.Errors;
2354 Expression e = ConstantFold.BinaryFold (
2355 ec, oper, lc, rc, loc);
2356 if (e != null || Report.Errors != prev_e)
2361 if ((left is NullLiteral || left.Type.IsValueType) &&
2362 (right is NullLiteral || right.Type.IsValueType) &&
2363 !(left is NullLiteral && right is NullLiteral) &&
2364 (TypeManager.IsNullableType (left.Type) || TypeManager.IsNullableType (right.Type)))
2365 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2368 // Comparison warnings
2369 if (oper == Operator.Equality || oper == Operator.Inequality ||
2370 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2371 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2372 if (left.Equals (right)) {
2373 Report.Warning (1718, 3, loc, "A comparison made to same variable. Did you mean to compare something else?");
2375 CheckUselessComparison (lc, right.Type);
2376 CheckUselessComparison (rc, left.Type);
2379 return ResolveOperator (ec);
2382 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2387 private void CheckUselessComparison (Constant c, Type type)
2389 if (c == null || !IsTypeIntegral (type)
2390 || c is StringConstant
2391 || c is BoolConstant
2392 || c is FloatConstant
2393 || c is DoubleConstant
2394 || c is DecimalConstant
2400 if (c is ULongConstant) {
2401 ulong uvalue = ((ULongConstant) c).Value;
2402 if (uvalue > long.MaxValue) {
2403 if (type == TypeManager.byte_type ||
2404 type == TypeManager.sbyte_type ||
2405 type == TypeManager.short_type ||
2406 type == TypeManager.ushort_type ||
2407 type == TypeManager.int32_type ||
2408 type == TypeManager.uint32_type ||
2409 type == TypeManager.int64_type ||
2410 type == TypeManager.char_type)
2411 WarnUselessComparison (type);
2414 value = (long) uvalue;
2416 else if (c is ByteConstant)
2417 value = ((ByteConstant) c).Value;
2418 else if (c is SByteConstant)
2419 value = ((SByteConstant) c).Value;
2420 else if (c is ShortConstant)
2421 value = ((ShortConstant) c).Value;
2422 else if (c is UShortConstant)
2423 value = ((UShortConstant) c).Value;
2424 else if (c is IntConstant)
2425 value = ((IntConstant) c).Value;
2426 else if (c is UIntConstant)
2427 value = ((UIntConstant) c).Value;
2428 else if (c is LongConstant)
2429 value = ((LongConstant) c).Value;
2430 else if (c is CharConstant)
2431 value = ((CharConstant)c).Value;
2436 if (IsValueOutOfRange (value, type))
2437 WarnUselessComparison (type);
2440 private bool IsValueOutOfRange (long value, Type type)
2442 if (IsTypeUnsigned (type) && value < 0)
2444 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2445 type == TypeManager.byte_type && value >= 0x100 ||
2446 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2447 type == TypeManager.ushort_type && value >= 0x10000 ||
2448 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2449 type == TypeManager.uint32_type && value >= 0x100000000;
2452 private static bool IsTypeIntegral (Type type)
2454 return type == TypeManager.uint64_type ||
2455 type == TypeManager.int64_type ||
2456 type == TypeManager.uint32_type ||
2457 type == TypeManager.int32_type ||
2458 type == TypeManager.ushort_type ||
2459 type == TypeManager.short_type ||
2460 type == TypeManager.sbyte_type ||
2461 type == TypeManager.byte_type ||
2462 type == TypeManager.char_type;
2465 private static bool IsTypeUnsigned (Type type)
2467 return type == TypeManager.uint64_type ||
2468 type == TypeManager.uint32_type ||
2469 type == TypeManager.ushort_type ||
2470 type == TypeManager.byte_type ||
2471 type == TypeManager.char_type;
2474 private void WarnUselessComparison (Type type)
2476 Report.Warning (652, 2, loc, "A comparison between a constant and a variable is useless. The constant is out of the range of the variable type `{0}'",
2477 TypeManager.CSharpName (type));
2481 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2482 /// context of a conditional bool expression. This function will return
2483 /// false if it is was possible to use EmitBranchable, or true if it was.
2485 /// The expression's code is generated, and we will generate a branch to `target'
2486 /// if the resulting expression value is equal to isTrue
2488 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2490 ILGenerator ig = ec.ig;
2493 // This is more complicated than it looks, but its just to avoid
2494 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2495 // but on top of that we want for == and != to use a special path
2496 // if we are comparing against null
2498 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2499 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2502 // put the constant on the rhs, for simplicity
2504 if (left is Constant) {
2505 Expression swap = right;
2510 if (((Constant) right).IsZeroInteger) {
2513 ig.Emit (OpCodes.Brtrue, target);
2515 ig.Emit (OpCodes.Brfalse, target);
2518 } else if (right is BoolConstant) {
2520 if (my_on_true != ((BoolConstant) right).Value)
2521 ig.Emit (OpCodes.Brtrue, target);
2523 ig.Emit (OpCodes.Brfalse, target);
2528 } else if (oper == Operator.LogicalAnd) {
2531 Label tests_end = ig.DefineLabel ();
2533 left.EmitBranchable (ec, tests_end, false);
2534 right.EmitBranchable (ec, target, true);
2535 ig.MarkLabel (tests_end);
2538 // This optimizes code like this
2539 // if (true && i > 4)
2541 if (!(left is Constant))
2542 left.EmitBranchable (ec, target, false);
2544 if (!(right is Constant))
2545 right.EmitBranchable (ec, target, false);
2550 } else if (oper == Operator.LogicalOr){
2552 left.EmitBranchable (ec, target, true);
2553 right.EmitBranchable (ec, target, true);
2556 Label tests_end = ig.DefineLabel ();
2557 left.EmitBranchable (ec, tests_end, true);
2558 right.EmitBranchable (ec, target, false);
2559 ig.MarkLabel (tests_end);
2564 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2565 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2566 oper == Operator.Equality || oper == Operator.Inequality)) {
2567 base.EmitBranchable (ec, target, onTrue);
2575 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2578 case Operator.Equality:
2580 ig.Emit (OpCodes.Beq, target);
2582 ig.Emit (OpCodes.Bne_Un, target);
2585 case Operator.Inequality:
2587 ig.Emit (OpCodes.Bne_Un, target);
2589 ig.Emit (OpCodes.Beq, target);
2592 case Operator.LessThan:
2595 ig.Emit (OpCodes.Blt_Un, target);
2597 ig.Emit (OpCodes.Blt, target);
2600 ig.Emit (OpCodes.Bge_Un, target);
2602 ig.Emit (OpCodes.Bge, target);
2605 case Operator.GreaterThan:
2608 ig.Emit (OpCodes.Bgt_Un, target);
2610 ig.Emit (OpCodes.Bgt, target);
2613 ig.Emit (OpCodes.Ble_Un, target);
2615 ig.Emit (OpCodes.Ble, target);
2618 case Operator.LessThanOrEqual:
2621 ig.Emit (OpCodes.Ble_Un, target);
2623 ig.Emit (OpCodes.Ble, target);
2626 ig.Emit (OpCodes.Bgt_Un, target);
2628 ig.Emit (OpCodes.Bgt, target);
2632 case Operator.GreaterThanOrEqual:
2635 ig.Emit (OpCodes.Bge_Un, target);
2637 ig.Emit (OpCodes.Bge, target);
2640 ig.Emit (OpCodes.Blt_Un, target);
2642 ig.Emit (OpCodes.Blt, target);
2645 Console.WriteLine (oper);
2646 throw new Exception ("what is THAT");
2650 public override void Emit (EmitContext ec)
2652 ILGenerator ig = ec.ig;
2657 // Handle short-circuit operators differently
2660 if (oper == Operator.LogicalAnd) {
2661 Label load_zero = ig.DefineLabel ();
2662 Label end = ig.DefineLabel ();
2664 left.EmitBranchable (ec, load_zero, false);
2666 ig.Emit (OpCodes.Br, end);
2668 ig.MarkLabel (load_zero);
2669 ig.Emit (OpCodes.Ldc_I4_0);
2672 } else if (oper == Operator.LogicalOr) {
2673 Label load_one = ig.DefineLabel ();
2674 Label end = ig.DefineLabel ();
2676 left.EmitBranchable (ec, load_one, true);
2678 ig.Emit (OpCodes.Br, end);
2680 ig.MarkLabel (load_one);
2681 ig.Emit (OpCodes.Ldc_I4_1);
2689 bool isUnsigned = is_unsigned (left.Type);
2692 case Operator.Multiply:
2694 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2695 opcode = OpCodes.Mul_Ovf;
2696 else if (isUnsigned)
2697 opcode = OpCodes.Mul_Ovf_Un;
2699 opcode = OpCodes.Mul;
2701 opcode = OpCodes.Mul;
2705 case Operator.Division:
2707 opcode = OpCodes.Div_Un;
2709 opcode = OpCodes.Div;
2712 case Operator.Modulus:
2714 opcode = OpCodes.Rem_Un;
2716 opcode = OpCodes.Rem;
2719 case Operator.Addition:
2721 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2722 opcode = OpCodes.Add_Ovf;
2723 else if (isUnsigned)
2724 opcode = OpCodes.Add_Ovf_Un;
2726 opcode = OpCodes.Add;
2728 opcode = OpCodes.Add;
2731 case Operator.Subtraction:
2733 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2734 opcode = OpCodes.Sub_Ovf;
2735 else if (isUnsigned)
2736 opcode = OpCodes.Sub_Ovf_Un;
2738 opcode = OpCodes.Sub;
2740 opcode = OpCodes.Sub;
2743 case Operator.RightShift:
2745 opcode = OpCodes.Shr_Un;
2747 opcode = OpCodes.Shr;
2750 case Operator.LeftShift:
2751 opcode = OpCodes.Shl;
2754 case Operator.Equality:
2755 opcode = OpCodes.Ceq;
2758 case Operator.Inequality:
2759 ig.Emit (OpCodes.Ceq);
2760 ig.Emit (OpCodes.Ldc_I4_0);
2762 opcode = OpCodes.Ceq;
2765 case Operator.LessThan:
2767 opcode = OpCodes.Clt_Un;
2769 opcode = OpCodes.Clt;
2772 case Operator.GreaterThan:
2774 opcode = OpCodes.Cgt_Un;
2776 opcode = OpCodes.Cgt;
2779 case Operator.LessThanOrEqual:
2780 Type lt = left.Type;
2782 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2783 ig.Emit (OpCodes.Cgt_Un);
2785 ig.Emit (OpCodes.Cgt);
2786 ig.Emit (OpCodes.Ldc_I4_0);
2788 opcode = OpCodes.Ceq;
2791 case Operator.GreaterThanOrEqual:
2792 Type le = left.Type;
2794 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2795 ig.Emit (OpCodes.Clt_Un);
2797 ig.Emit (OpCodes.Clt);
2799 ig.Emit (OpCodes.Ldc_I4_0);
2801 opcode = OpCodes.Ceq;
2804 case Operator.BitwiseOr:
2805 opcode = OpCodes.Or;
2808 case Operator.BitwiseAnd:
2809 opcode = OpCodes.And;
2812 case Operator.ExclusiveOr:
2813 opcode = OpCodes.Xor;
2817 throw new Exception ("This should not happen: Operator = "
2818 + oper.ToString ());
2824 protected override void CloneTo (CloneContext clonectx, Expression t)
2826 Binary target = (Binary) t;
2828 target.left = left.Clone (clonectx);
2829 target.right = right.Clone (clonectx);
2834 // Object created by Binary when the binary operator uses an method instead of being
2835 // a binary operation that maps to a CIL binary operation.
2837 public class BinaryMethod : Expression {
2838 public MethodBase method;
2839 public ArrayList Arguments;
2841 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2846 eclass = ExprClass.Value;
2849 public override Expression DoResolve (EmitContext ec)
2854 public override void Emit (EmitContext ec)
2856 ILGenerator ig = ec.ig;
2858 if (Arguments != null)
2859 Invocation.EmitArguments (ec, method, Arguments, false, null);
2861 if (method is MethodInfo)
2862 ig.Emit (OpCodes.Call, (MethodInfo) method);
2864 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2869 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2870 // b, c, d... may be strings or objects.
2872 public class StringConcat : Expression {
2874 bool invalid = false;
2875 bool emit_conv_done = false;
2877 // Are we also concating objects?
2879 bool is_strings_only = true;
2881 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2884 type = TypeManager.string_type;
2885 eclass = ExprClass.Value;
2887 operands = new ArrayList (2);
2892 public override Expression DoResolve (EmitContext ec)
2900 public void Append (EmitContext ec, Expression operand)
2905 StringConstant sc = operand as StringConstant;
2907 // TODO: it will be better to do this silently as an optimalization
2909 // string s = "" + i;
2910 // because this code has poor performace
2911 // if (sc.Value.Length == 0)
2912 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2914 if (operands.Count != 0) {
2915 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2916 if (last_operand != null) {
2917 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2924 // Conversion to object
2926 if (operand.Type != TypeManager.string_type) {
2927 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2930 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2936 operands.Add (operand);
2939 public override void Emit (EmitContext ec)
2941 MethodInfo concat_method = null;
2944 // Do conversion to arguments; check for strings only
2947 // This can get called multiple times, so we have to deal with that.
2948 if (!emit_conv_done) {
2949 emit_conv_done = true;
2950 for (int i = 0; i < operands.Count; i ++) {
2951 Expression e = (Expression) operands [i];
2952 is_strings_only &= e.Type == TypeManager.string_type;
2955 for (int i = 0; i < operands.Count; i ++) {
2956 Expression e = (Expression) operands [i];
2958 if (! is_strings_only && e.Type == TypeManager.string_type) {
2959 // need to make sure this is an object, because the EmitParams
2960 // method might look at the type of this expression, see it is a
2961 // string and emit a string [] when we want an object [];
2963 e = new EmptyCast (e, TypeManager.object_type);
2965 operands [i] = new Argument (e, Argument.AType.Expression);
2970 // Find the right method
2972 switch (operands.Count) {
2975 // This should not be possible, because simple constant folding
2976 // is taken care of in the Binary code.
2978 throw new Exception ("how did you get here?");
2981 concat_method = is_strings_only ?
2982 TypeManager.string_concat_string_string :
2983 TypeManager.string_concat_object_object ;
2986 concat_method = is_strings_only ?
2987 TypeManager.string_concat_string_string_string :
2988 TypeManager.string_concat_object_object_object ;
2992 // There is not a 4 param overlaod for object (the one that there is
2993 // is actually a varargs methods, and is only in corlib because it was
2994 // introduced there before.).
2996 if (!is_strings_only)
2999 concat_method = TypeManager.string_concat_string_string_string_string;
3002 concat_method = is_strings_only ?
3003 TypeManager.string_concat_string_dot_dot_dot :
3004 TypeManager.string_concat_object_dot_dot_dot ;
3008 Invocation.EmitArguments (ec, concat_method, operands, false, null);
3009 ec.ig.Emit (OpCodes.Call, concat_method);
3014 // Object created with +/= on delegates
3016 public class BinaryDelegate : Expression {
3020 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
3025 eclass = ExprClass.Value;
3028 public override Expression DoResolve (EmitContext ec)
3033 public override void Emit (EmitContext ec)
3035 ILGenerator ig = ec.ig;
3037 Invocation.EmitArguments (ec, method, args, false, null);
3039 ig.Emit (OpCodes.Call, (MethodInfo) method);
3040 ig.Emit (OpCodes.Castclass, type);
3043 public Expression Right {
3045 Argument arg = (Argument) args [1];
3050 public bool IsAddition {
3052 return method == TypeManager.delegate_combine_delegate_delegate;
3058 // User-defined conditional logical operator
3059 public class ConditionalLogicalOperator : Expression {
3060 Expression left, right;
3063 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
3066 eclass = ExprClass.Value;
3070 this.is_and = is_and;
3073 protected void Error19 ()
3075 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3078 protected void Error218 ()
3080 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3081 "declarations of operator true and operator false");
3084 Expression op_true, op_false, op;
3085 LocalTemporary left_temp;
3087 public override Expression DoResolve (EmitContext ec)
3090 Expression operator_group;
3092 operator_group = MethodLookup (ec.ContainerType, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
3093 if (operator_group == null) {
3098 left_temp = new LocalTemporary (type);
3100 ArrayList arguments = new ArrayList (2);
3101 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3102 arguments.Add (new Argument (right, Argument.AType.Expression));
3103 method = ((MethodGroupExpr) operator_group).OverloadResolve (
3104 ec, arguments, false, loc)
3106 if (method == null) {
3111 if (method.ReturnType != type) {
3112 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3113 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3117 op = new StaticCallExpr (method, arguments, loc);
3119 op_true = GetOperatorTrue (ec, left_temp, loc);
3120 op_false = GetOperatorFalse (ec, left_temp, loc);
3121 if ((op_true == null) || (op_false == null)) {
3129 public override void Emit (EmitContext ec)
3131 ILGenerator ig = ec.ig;
3132 Label false_target = ig.DefineLabel ();
3133 Label end_target = ig.DefineLabel ();
3136 left_temp.Store (ec);
3138 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3139 left_temp.Emit (ec);
3140 ig.Emit (OpCodes.Br, end_target);
3141 ig.MarkLabel (false_target);
3143 ig.MarkLabel (end_target);
3145 // We release 'left_temp' here since 'op' may refer to it too
3146 left_temp.Release (ec);
3150 public class PointerArithmetic : Expression {
3151 Expression left, right;
3155 // We assume that `l' is always a pointer
3157 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3163 is_add = is_addition;
3166 public override Expression DoResolve (EmitContext ec)
3168 eclass = ExprClass.Variable;
3170 if (left.Type == TypeManager.void_ptr_type) {
3171 Error (242, "The operation in question is undefined on void pointers");
3178 public override void Emit (EmitContext ec)
3180 Type op_type = left.Type;
3181 ILGenerator ig = ec.ig;
3183 // It must be either array or fixed buffer
3184 Type element = TypeManager.HasElementType (op_type) ?
3185 element = TypeManager.GetElementType (op_type) :
3186 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3188 int size = GetTypeSize (element);
3189 Type rtype = right.Type;
3191 if (rtype.IsPointer){
3193 // handle (pointer - pointer)
3197 ig.Emit (OpCodes.Sub);
3201 ig.Emit (OpCodes.Sizeof, element);
3203 IntLiteral.EmitInt (ig, size);
3204 ig.Emit (OpCodes.Div);
3206 ig.Emit (OpCodes.Conv_I8);
3209 // handle + and - on (pointer op int)
3212 ig.Emit (OpCodes.Conv_I);
3214 Constant right_const = right as Constant;
3215 if (right_const != null && size != 0) {
3216 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3224 ig.Emit (OpCodes.Sizeof, element);
3226 IntLiteral.EmitInt (ig, size);
3227 if (rtype == TypeManager.int64_type)
3228 ig.Emit (OpCodes.Conv_I8);
3229 else if (rtype == TypeManager.uint64_type)
3230 ig.Emit (OpCodes.Conv_U8);
3231 ig.Emit (OpCodes.Mul);
3235 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3236 ig.Emit (OpCodes.Conv_I);
3239 ig.Emit (OpCodes.Add);
3241 ig.Emit (OpCodes.Sub);
3247 /// Implements the ternary conditional operator (?:)
3249 public class Conditional : Expression {
3250 Expression expr, trueExpr, falseExpr;
3252 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3255 this.trueExpr = trueExpr;
3256 this.falseExpr = falseExpr;
3257 this.loc = expr.Location;
3260 public Expression Expr {
3266 public Expression TrueExpr {
3272 public Expression FalseExpr {
3278 public override Expression DoResolve (EmitContext ec)
3280 expr = expr.Resolve (ec);
3286 if (TypeManager.IsNullableValueType (expr.Type))
3287 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
3290 if (expr.Type != TypeManager.bool_type){
3291 expr = Expression.ResolveBoolean (
3298 Assign ass = expr as Assign;
3299 if (ass != null && ass.Source is Constant) {
3300 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3303 trueExpr = trueExpr.Resolve (ec);
3304 falseExpr = falseExpr.Resolve (ec);
3306 if (trueExpr == null || falseExpr == null)
3309 eclass = ExprClass.Value;
3310 if (trueExpr.Type == falseExpr.Type) {
3311 type = trueExpr.Type;
3312 if (type == TypeManager.null_type) {
3313 // TODO: probably will have to implement ConditionalConstant
3314 // to call method without return constant as well
3315 Report.Warning (-101, 1, loc, "Conditional expression will always return same value");
3320 Type true_type = trueExpr.Type;
3321 Type false_type = falseExpr.Type;
3324 // First, if an implicit conversion exists from trueExpr
3325 // to falseExpr, then the result type is of type falseExpr.Type
3327 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3330 // Check if both can convert implicitl to each other's type
3332 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3334 "Can not compute type of conditional expression " +
3335 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3336 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3337 "' convert implicitly to each other");
3342 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3346 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3347 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3352 // Dead code optimalization
3353 if (expr is BoolConstant){
3354 BoolConstant bc = (BoolConstant) expr;
3356 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3357 return bc.Value ? trueExpr : falseExpr;
3363 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3368 public override void Emit (EmitContext ec)
3370 ILGenerator ig = ec.ig;
3371 Label false_target = ig.DefineLabel ();
3372 Label end_target = ig.DefineLabel ();
3374 expr.EmitBranchable (ec, false_target, false);
3376 ig.Emit (OpCodes.Br, end_target);
3377 ig.MarkLabel (false_target);
3378 falseExpr.Emit (ec);
3379 ig.MarkLabel (end_target);
3382 protected override void CloneTo (CloneContext clonectx, Expression t)
3384 Conditional target = (Conditional) t;
3386 target.expr = expr.Clone (clonectx);
3387 target.trueExpr = trueExpr.Clone (clonectx);
3388 target.falseExpr = falseExpr.Clone (clonectx);
3392 public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation {
3394 LocalTemporary temp;
3396 public abstract Variable Variable {
3400 public abstract bool IsRef {
3404 public override void Emit (EmitContext ec)
3410 // This method is used by parameters that are references, that are
3411 // being passed as references: we only want to pass the pointer (that
3412 // is already stored in the parameter, not the address of the pointer,
3413 // and not the value of the variable).
3415 public void EmitLoad (EmitContext ec)
3417 Report.Debug (64, "VARIABLE EMIT LOAD", this, Variable, type, loc);
3419 Variable.EmitInstance (ec);
3423 public void Emit (EmitContext ec, bool leave_copy)
3425 Report.Debug (64, "VARIABLE EMIT", this, Variable, type, IsRef, loc);
3431 ec.ig.Emit (OpCodes.Dup);
3434 // If we are a reference, we loaded on the stack a pointer
3435 // Now lets load the real value
3437 LoadFromPtr (ec.ig, type);
3441 ec.ig.Emit (OpCodes.Dup);
3443 if (IsRef || Variable.NeedsTemporary) {
3444 temp = new LocalTemporary (Type);
3450 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
3451 bool prepare_for_load)
3453 Report.Debug (64, "VARIABLE EMIT ASSIGN", this, Variable, type, IsRef,
3456 ILGenerator ig = ec.ig;
3457 prepared = prepare_for_load;
3459 Variable.EmitInstance (ec);
3460 if (prepare_for_load && Variable.HasInstance)
3461 ig.Emit (OpCodes.Dup);
3462 else if (IsRef && !prepared)
3468 ig.Emit (OpCodes.Dup);
3469 if (IsRef || Variable.NeedsTemporary) {
3470 temp = new LocalTemporary (Type);
3476 StoreFromPtr (ig, type);
3478 Variable.EmitAssign (ec);
3486 public void AddressOf (EmitContext ec, AddressOp mode)
3488 Variable.EmitInstance (ec);
3489 Variable.EmitAddressOf (ec);
3496 public class LocalVariableReference : VariableReference, IVariable {
3497 public readonly string Name;
3499 public LocalInfo local_info;
3503 public LocalVariableReference (Block block, string name, Location l)
3508 eclass = ExprClass.Variable;
3512 // Setting `is_readonly' to false will allow you to create a writable
3513 // reference to a read-only variable. This is used by foreach and using.
3515 public LocalVariableReference (Block block, string name, Location l,
3516 LocalInfo local_info, bool is_readonly)
3517 : this (block, name, l)
3519 this.local_info = local_info;
3520 this.is_readonly = is_readonly;
3523 public VariableInfo VariableInfo {
3524 get { return local_info.VariableInfo; }
3527 public override bool IsRef {
3528 get { return false; }
3531 public bool IsReadOnly {
3532 get { return is_readonly; }
3535 public bool VerifyAssigned (EmitContext ec)
3537 VariableInfo variable_info = local_info.VariableInfo;
3538 return variable_info == null || variable_info.IsAssigned (ec, loc);
3541 void ResolveLocalInfo ()
3543 if (local_info == null) {
3544 local_info = Block.GetLocalInfo (Name);
3545 is_readonly = local_info.ReadOnly;
3549 protected Expression DoResolveBase (EmitContext ec)
3551 type = local_info.VariableType;
3553 Expression e = Block.GetConstantExpression (Name);
3555 return e.Resolve (ec);
3557 if (!VerifyAssigned (ec))
3561 // If we are referencing a variable from the external block
3562 // flag it for capturing
3564 if (ec.MustCaptureVariable (local_info)) {
3565 if (local_info.AddressTaken){
3566 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3570 ScopeInfo scope = local_info.Block.CreateScopeInfo ();
3571 variable = scope.AddLocal (local_info);
3572 type = variable.Type;
3578 public override Expression DoResolve (EmitContext ec)
3580 ResolveLocalInfo ();
3581 local_info.Used = true;
3582 return DoResolveBase (ec);
3585 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3587 ResolveLocalInfo ();
3592 if (right_side == EmptyExpression.OutAccess) {
3593 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3594 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3595 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3596 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3597 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3599 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
3601 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3606 if (right_side == EmptyExpression.OutAccess)
3607 local_info.Used = true;
3609 if (VariableInfo != null)
3610 VariableInfo.SetAssigned (ec);
3612 return DoResolveBase (ec);
3615 public bool VerifyFixed ()
3617 // A local Variable is always fixed.
3621 public override int GetHashCode ()
3623 return Name.GetHashCode ();
3626 public override bool Equals (object obj)
3628 LocalVariableReference lvr = obj as LocalVariableReference;
3632 return Name == lvr.Name && Block == lvr.Block;
3635 public override Variable Variable {
3636 get { return variable != null ? variable : local_info.Variable; }
3639 public override string ToString ()
3641 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3644 protected override void CloneTo (CloneContext clonectx, Expression t)
3646 LocalVariableReference target = (LocalVariableReference) t;
3648 target.Block = clonectx.LookupBlock (Block);
3653 /// This represents a reference to a parameter in the intermediate
3656 public class ParameterReference : VariableReference, IVariable {
3662 public bool is_ref, is_out;
3670 public override bool IsRef {
3676 public string Name {
3682 public Parameter Parameter {
3690 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3693 this.name = par.Name;
3697 eclass = ExprClass.Variable;
3700 public VariableInfo VariableInfo {
3704 public override Variable Variable {
3705 get { return variable != null ? variable : par.Variable; }
3708 public bool VerifyFixed ()
3710 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3711 return par.ModFlags == Parameter.Modifier.NONE;
3714 public bool IsAssigned (EmitContext ec, Location loc)
3716 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3719 Report.Error (269, loc,
3720 "Use of unassigned out parameter `{0}'", par.Name);
3724 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3726 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3729 Report.Error (170, loc,
3730 "Use of possibly unassigned field `" + field_name + "'");
3734 public void SetAssigned (EmitContext ec)
3736 if (is_out && ec.DoFlowAnalysis)
3737 ec.CurrentBranching.SetAssigned (vi);
3740 public void SetFieldAssigned (EmitContext ec, string field_name)
3742 if (is_out && ec.DoFlowAnalysis)
3743 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3746 protected bool DoResolveBase (EmitContext ec)
3748 if (!par.Resolve (ec)) {
3752 type = par.ParameterType;
3753 Parameter.Modifier mod = par.ModFlags;
3754 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3755 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3756 eclass = ExprClass.Variable;
3759 vi = block.ParameterMap [idx];
3761 AnonymousContainer am = ec.CurrentAnonymousMethod;
3765 if (is_ref && !block.Toplevel.IsLocalParameter (name)){
3766 Report.Error (1628, Location,
3767 "Cannot use ref or out parameter `{0}' inside an " +
3768 "anonymous method block", par.Name);
3772 if (!am.IsIterator && block.Toplevel.IsLocalParameter (name))
3775 ToplevelBlock toplevel = block.Toplevel;
3776 while (toplevel != null) {
3777 if (toplevel.IsLocalParameter (name))
3780 toplevel = toplevel.Container;
3783 ScopeInfo scope = toplevel.CreateScopeInfo ();
3784 variable = scope.AddParameter (par, idx);
3785 type = variable.Type;
3789 public override int GetHashCode()
3791 return name.GetHashCode ();
3794 public override bool Equals (object obj)
3796 ParameterReference pr = obj as ParameterReference;
3800 return name == pr.name && block == pr.block;
3804 // Notice that for ref/out parameters, the type exposed is not the
3805 // same type exposed externally.
3808 // externally we expose "int&"
3809 // here we expose "int".
3811 // We record this in "is_ref". This means that the type system can treat
3812 // the type as it is expected, but when we generate the code, we generate
3813 // the alternate kind of code.
3815 public override Expression DoResolve (EmitContext ec)
3817 if (!DoResolveBase (ec))
3820 if (is_out && ec.DoFlowAnalysis &&
3821 (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3827 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3829 if (!DoResolveBase (ec))
3837 static public void EmitLdArg (ILGenerator ig, int x)
3841 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3842 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3843 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3844 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3845 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3848 ig.Emit (OpCodes.Ldarg, x);
3851 public override string ToString ()
3853 return "ParameterReference[" + name + "]";
3858 /// Used for arguments to New(), Invocation()
3860 public class Argument {
3861 public enum AType : byte {
3868 public static readonly Argument[] Empty = new Argument [0];
3870 public readonly AType ArgType;
3871 public Expression Expr;
3873 public Argument (Expression expr, AType type)
3876 this.ArgType = type;
3879 public Argument (Expression expr)
3882 this.ArgType = AType.Expression;
3887 if (ArgType == AType.Ref || ArgType == AType.Out)
3888 return TypeManager.GetReferenceType (Expr.Type);
3894 public Parameter.Modifier Modifier
3899 return Parameter.Modifier.OUT;
3902 return Parameter.Modifier.REF;
3905 return Parameter.Modifier.NONE;
3910 public static string FullDesc (Argument a)
3912 if (a.ArgType == AType.ArgList)
3915 return (a.ArgType == AType.Ref ? "ref " :
3916 (a.ArgType == AType.Out ? "out " : "")) +
3917 TypeManager.CSharpName (a.Expr.Type);
3920 public bool ResolveMethodGroup (EmitContext ec)
3922 SimpleName sn = Expr as SimpleName;
3924 Expr = sn.GetMethodGroup ();
3926 // FIXME: csc doesn't report any error if you try to use `ref' or
3927 // `out' in a delegate creation expression.
3928 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3935 public bool Resolve (EmitContext ec, Location loc)
3937 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3938 // Verify that the argument is readable
3939 if (ArgType != AType.Out)
3940 Expr = Expr.Resolve (ec);
3942 // Verify that the argument is writeable
3943 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3944 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3946 return Expr != null;
3950 public void Emit (EmitContext ec)
3952 if (ArgType != AType.Ref && ArgType != AType.Out) {
3957 AddressOp mode = AddressOp.Store;
3958 if (ArgType == AType.Ref)
3959 mode |= AddressOp.Load;
3961 IMemoryLocation ml = (IMemoryLocation) Expr;
3962 ParameterReference pr = ml as ParameterReference;
3965 // ParameterReferences might already be references, so we want
3966 // to pass just the value
3968 if (pr != null && pr.IsRef)
3971 ml.AddressOf (ec, mode);
3974 public Argument Clone (CloneContext clonectx)
3976 return new Argument (Expr.Clone (clonectx), ArgType);
3981 /// Invocation of methods or delegates.
3983 public class Invocation : ExpressionStatement {
3984 public ArrayList Arguments;
3987 MethodBase method = null;
3990 // arguments is an ArrayList, but we do not want to typecast,
3991 // as it might be null.
3993 // FIXME: only allow expr to be a method invocation or a
3994 // delegate invocation (7.5.5)
3996 public Invocation (Expression expr, ArrayList arguments)
3999 Arguments = arguments;
4000 loc = expr.Location;
4003 public Expression Expr {
4009 public static string FullMethodDesc (MethodBase mb)
4015 if (mb is MethodInfo) {
4016 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4020 sb = new StringBuilder ();
4022 sb.Append (TypeManager.CSharpSignature (mb));
4023 return sb.ToString ();
4026 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4028 MemberInfo [] miset;
4029 MethodGroupExpr union;
4034 return (MethodGroupExpr) mg2;
4037 return (MethodGroupExpr) mg1;
4040 MethodGroupExpr left_set = null, right_set = null;
4041 int length1 = 0, length2 = 0;
4043 left_set = (MethodGroupExpr) mg1;
4044 length1 = left_set.Methods.Length;
4046 right_set = (MethodGroupExpr) mg2;
4047 length2 = right_set.Methods.Length;
4049 ArrayList common = new ArrayList ();
4051 foreach (MethodBase r in right_set.Methods){
4052 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4056 miset = new MemberInfo [length1 + length2 - common.Count];
4057 left_set.Methods.CopyTo (miset, 0);
4061 foreach (MethodBase r in right_set.Methods) {
4062 if (!common.Contains (r))
4066 union = new MethodGroupExpr (miset, loc);
4071 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4072 ArrayList arguments, int arg_count,
4073 ref MethodBase candidate)
4075 return IsParamsMethodApplicable (
4076 ec, me, arguments, arg_count, false, ref candidate) ||
4077 IsParamsMethodApplicable (
4078 ec, me, arguments, arg_count, true, ref candidate);
4083 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4084 ArrayList arguments, int arg_count,
4085 bool do_varargs, ref MethodBase candidate)
4088 if (!me.HasTypeArguments &&
4089 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4092 if (TypeManager.IsGenericMethodDefinition (candidate))
4093 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4096 return IsParamsMethodApplicable (
4097 ec, arguments, arg_count, candidate, do_varargs);
4101 /// Determines if the candidate method, if a params method, is applicable
4102 /// in its expanded form to the given set of arguments
4104 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4105 int arg_count, MethodBase candidate,
4108 ParameterData pd = TypeManager.GetParameterData (candidate);
4110 int pd_count = pd.Count;
4114 int count = pd_count - 1;
4116 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4118 if (pd_count != arg_count)
4121 if (!(((Argument) arguments [count]).Expr is Arglist))
4129 if (count > arg_count)
4132 if (pd_count == 1 && arg_count == 0)
4136 // If we have come this far, the case which
4137 // remains is when the number of parameters is
4138 // less than or equal to the argument count.
4140 int argument_index = 0;
4142 for (int i = 0; i < pd_count; ++i) {
4144 if ((pd.ParameterModifier (i) & Parameter.Modifier.PARAMS) != 0) {
4145 Type element_type = TypeManager.GetElementType (pd.ParameterType (i));
4146 int params_args_count = arg_count - pd_count;
4147 if (params_args_count < 0)
4151 a = (Argument) arguments [argument_index++];
4153 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4155 } while (params_args_count-- > 0);
4159 a = (Argument) arguments [argument_index++];
4161 Parameter.Modifier a_mod = a.Modifier &
4162 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4163 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4164 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4166 if (a_mod == p_mod) {
4168 if (a_mod == Parameter.Modifier.NONE)
4169 if (!Convert.ImplicitConversionExists (ec,
4171 pd.ParameterType (i)))
4174 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4175 Type pt = pd.ParameterType (i);
4178 pt = TypeManager.GetReferenceType (pt);
4191 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4192 ArrayList arguments, int arg_count,
4193 ref MethodBase candidate)
4196 if (!me.HasTypeArguments &&
4197 !TypeManager.InferTypeArguments (ec, arguments, ref candidate))
4200 if (TypeManager.IsGenericMethodDefinition (candidate))
4201 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4204 return IsApplicable (ec, arguments, arg_count, candidate);
4208 /// Determines if the candidate method is applicable (section 14.4.2.1)
4209 /// to the given set of arguments
4211 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4212 MethodBase candidate)
4214 ParameterData pd = TypeManager.GetParameterData (candidate);
4216 if (arg_count != pd.Count)
4219 for (int i = arg_count; i > 0; ) {
4222 Argument a = (Argument) arguments [i];
4224 Parameter.Modifier a_mod = a.Modifier &
4225 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4227 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4228 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4230 if (a_mod == p_mod) {
4231 Type pt = pd.ParameterType (i);
4232 EmitContext prevec = EmitContext.TempEc;
4233 EmitContext.TempEc = ec;
4236 if (a_mod == Parameter.Modifier.NONE) {
4237 if (!TypeManager.IsEqual (a.Type, pt) &&
4238 !Convert.ImplicitConversionExists (ec, a.Expr, pt))
4243 EmitContext.TempEc = prevec;
4255 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4257 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4258 name, arg_count.ToString ());
4261 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4262 Type delegate_type, Argument a, ParameterData expected_par)
4264 if (delegate_type == null)
4265 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4266 TypeManager.CSharpSignature (method));
4268 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4269 TypeManager.CSharpName (delegate_type));
4271 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4273 string index = (idx + 1).ToString ();
4274 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4275 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4276 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4277 index, Parameter.GetModifierSignature (a.Modifier));
4279 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4280 index, Parameter.GetModifierSignature (mod));
4282 string p1 = Argument.FullDesc (a);
4283 string p2 = expected_par.ParameterDesc (idx);
4286 Report.ExtraInformation (loc, "(equally named types possibly from different assemblies in previous ");
4287 Report.SymbolRelatedToPreviousError (a.Expr.Type);
4288 Report.SymbolRelatedToPreviousError (expected_par.ParameterType (idx));
4290 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'", index, p1, p2);
4294 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4295 int arg_count, MethodBase method,
4296 bool chose_params_expanded,
4297 Type delegate_type, bool may_fail,
4300 ParameterData pd = TypeManager.GetParameterData (method);
4304 for (j = 0; j < pd.Count; j++) {
4305 Type parameter_type = pd.ParameterType (j);
4306 Parameter.Modifier pm = pd.ParameterModifier (j);
4308 if (pm == Parameter.Modifier.ARGLIST) {
4309 a = (Argument) Arguments [a_idx];
4310 if (!(a.Expr is Arglist))
4316 int params_arg_count = 1;
4317 if (pm == Parameter.Modifier.PARAMS) {
4318 pm = Parameter.Modifier.NONE;
4319 params_arg_count = arg_count - pd.Count + 1;
4320 if (chose_params_expanded)
4321 parameter_type = TypeManager.GetElementType (parameter_type);
4324 while (params_arg_count > 0) {
4325 a = (Argument) Arguments [a_idx];
4326 if (pm != a.Modifier)
4329 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4330 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4333 Expression conv = Convert.ImplicitConversion (ec, a.Expr, parameter_type, loc);
4337 // Update the argument with the implicit conversion
4345 if (params_arg_count > 0)
4348 if (parameter_type.IsPointer && !ec.InUnsafe) {
4355 if (a_idx == arg_count)
4359 Error_InvalidArguments (loc, a_idx, method, delegate_type, a, pd);
4363 private bool resolved = false;
4364 public override Expression DoResolve (EmitContext ec)
4367 return this.method == null ? null : this;
4371 // First, resolve the expression that is used to
4372 // trigger the invocation
4374 SimpleName sn = expr as SimpleName;
4376 expr = sn.GetMethodGroup ();
4378 Expression expr_resolved = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4379 if (expr_resolved == null)
4382 MethodGroupExpr mg = expr_resolved as MethodGroupExpr;
4384 Type expr_type = expr_resolved.Type;
4386 if (expr_type != null && TypeManager.IsDelegateType (expr_type)){
4387 return (new DelegateInvocation (
4388 expr_resolved, Arguments, loc)).Resolve (ec);
4390 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4395 // Next, evaluate all the expressions in the argument list
4397 if (Arguments != null){
4398 foreach (Argument a in Arguments){
4399 if (!a.Resolve (ec, loc))
4404 MethodBase method = mg.OverloadExtensionResolve (ec, ref Arguments, ref mg, expr, loc);
4410 MethodInfo mi = method as MethodInfo;
4412 type = TypeManager.TypeToCoreType (mi.ReturnType);
4413 Expression iexpr = mg.InstanceExpression;
4415 if (iexpr == null ||
4416 iexpr is This || iexpr is EmptyExpression ||
4417 mg.IdenticalTypeName) {
4418 mg.InstanceExpression = null;
4420 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
4424 if (iexpr == null || iexpr is EmptyExpression) {
4425 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
4431 if (type.IsPointer){
4439 // Only base will allow this invocation to happen.
4441 if (mg.IsBase && method.IsAbstract){
4442 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
4446 if (Arguments == null && method.Name == "Finalize") {
4448 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
4450 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
4454 if (IsSpecialMethodInvocation (method)) {
4458 if (mg.InstanceExpression != null){
4459 mg.InstanceExpression.CheckMarshalByRefAccess ();
4462 // This is used to check that no methods are called in struct
4463 // constructors before all the fields on the struct have been
4466 if (!method.IsStatic){
4467 This mgthis = mg.InstanceExpression as This;
4468 if (mgthis != null){
4469 if (!mgthis.CheckThisUsage (ec))
4475 eclass = ExprClass.Value;
4476 this.method = method;
4480 bool IsSpecialMethodInvocation (MethodBase method)
4482 if (!TypeManager.IsSpecialMethod (method))
4485 Report.SymbolRelatedToPreviousError (method);
4486 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
4487 TypeManager.CSharpSignature (method, true));
4493 // Emits the list of arguments as an array
4495 static void EmitParams (EmitContext ec, ArrayList arguments, int idx, int count)
4497 ILGenerator ig = ec.ig;
4499 for (int j = 0; j < count; j++){
4500 Argument a = (Argument) arguments [j + idx];
4503 IntConstant.EmitInt (ig, count);
4504 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
4507 ig.Emit (OpCodes.Dup);
4508 IntConstant.EmitInt (ig, j);
4510 bool is_stobj, has_type_arg;
4511 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
4513 ig.Emit (OpCodes.Ldelema, t);
4525 /// Emits a list of resolved Arguments that are in the arguments
4528 /// The MethodBase argument might be null if the
4529 /// emission of the arguments is known not to contain
4530 /// a `params' field (for example in constructors or other routines
4531 /// that keep their arguments in this structure)
4533 /// if `dup_args' is true, a copy of the arguments will be left
4534 /// on the stack. If `dup_args' is true, you can specify `this_arg'
4535 /// which will be duplicated before any other args. Only EmitCall
4536 /// should be using this interface.
4538 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
4540 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
4542 LocalTemporary [] temps = null;
4544 if (dup_args && top != 0)
4545 temps = new LocalTemporary [top];
4547 int argument_index = 0;
4549 for (int i = 0; i < top; i++){
4551 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
4552 //Type element_type = TypeManager.GetElementType (pd.ParameterType (i));
4553 int params_args_count = arguments == null ?
4554 0 : arguments.Count - top + 1;
4556 // Fill not provided argument
4557 if (params_args_count <= 0) {
4558 ILGenerator ig = ec.ig;
4559 IntConstant.EmitInt (ig, 0);
4560 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (i)));
4565 // Special case if we are passing the same data as the
4566 // params argument, we do not need to recreate an array.
4568 a = (Argument) arguments [argument_index];
4569 if (params_args_count == 1 && pd.ParameterType (i) == a.Type) {
4575 EmitParams (ec, arguments, i, params_args_count);
4576 argument_index += params_args_count;
4581 a = (Argument) arguments [argument_index++];
4584 ec.ig.Emit (OpCodes.Dup);
4585 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
4590 if (this_arg != null)
4593 for (int i = 0; i < top; i ++) {
4594 temps [i].Emit (ec);
4595 temps [i].Release (ec);
4600 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
4602 ParameterData pd = TypeManager.GetParameterData (mb);
4604 if (arguments == null)
4605 return new Type [0];
4607 Argument a = (Argument) arguments [pd.Count - 1];
4608 Arglist list = (Arglist) a.Expr;
4610 return list.ArgumentTypes;
4614 /// This checks the ConditionalAttribute on the method
4616 static bool IsMethodExcluded (MethodBase method)
4618 if (method.IsConstructor)
4621 IMethodData md = TypeManager.GetMethod (method);
4623 return md.IsExcluded ();
4625 // For some methods (generated by delegate class) GetMethod returns null
4626 // because they are not included in builder_to_method table
4627 if (method.DeclaringType is TypeBuilder)
4630 return AttributeTester.IsConditionalMethodExcluded (method);
4634 /// is_base tells whether we want to force the use of the `call'
4635 /// opcode instead of using callvirt. Call is required to call
4636 /// a specific method, while callvirt will always use the most
4637 /// recent method in the vtable.
4639 /// is_static tells whether this is an invocation on a static method
4641 /// instance_expr is an expression that represents the instance
4642 /// it must be non-null if is_static is false.
4644 /// method is the method to invoke.
4646 /// Arguments is the list of arguments to pass to the method or constructor.
4648 public static void EmitCall (EmitContext ec, bool is_base,
4649 bool is_static, Expression instance_expr,
4650 MethodBase method, ArrayList Arguments, Location loc)
4652 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
4655 // `dup_args' leaves an extra copy of the arguments on the stack
4656 // `omit_args' does not leave any arguments at all.
4657 // So, basically, you could make one call with `dup_args' set to true,
4658 // and then another with `omit_args' set to true, and the two calls
4659 // would have the same set of arguments. However, each argument would
4660 // only have been evaluated once.
4661 public static void EmitCall (EmitContext ec, bool is_base,
4662 bool is_static, Expression instance_expr,
4663 MethodBase method, ArrayList Arguments, Location loc,
4664 bool dup_args, bool omit_args)
4666 ILGenerator ig = ec.ig;
4667 bool struct_call = false;
4668 bool this_call = false;
4669 LocalTemporary this_arg = null;
4671 Type decl_type = method.DeclaringType;
4673 if (!RootContext.StdLib) {
4674 // Replace any calls to the system's System.Array type with calls to
4675 // the newly created one.
4676 if (method == TypeManager.system_int_array_get_length)
4677 method = TypeManager.int_array_get_length;
4678 else if (method == TypeManager.system_int_array_get_rank)
4679 method = TypeManager.int_array_get_rank;
4680 else if (method == TypeManager.system_object_array_clone)
4681 method = TypeManager.object_array_clone;
4682 else if (method == TypeManager.system_int_array_get_length_int)
4683 method = TypeManager.int_array_get_length_int;
4684 else if (method == TypeManager.system_int_array_get_lower_bound_int)
4685 method = TypeManager.int_array_get_lower_bound_int;
4686 else if (method == TypeManager.system_int_array_get_upper_bound_int)
4687 method = TypeManager.int_array_get_upper_bound_int;
4688 else if (method == TypeManager.system_void_array_copyto_array_int)
4689 method = TypeManager.void_array_copyto_array_int;
4692 if (!ec.IsInObsoleteScope) {
4694 // This checks ObsoleteAttribute on the method and on the declaring type
4696 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
4698 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
4700 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
4702 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
4706 if (IsMethodExcluded (method))
4710 if (instance_expr == EmptyExpression.Null) {
4711 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
4715 this_call = instance_expr is This;
4716 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
4720 // If this is ourselves, push "this"
4724 Type iexpr_type = instance_expr.Type;
4727 // Push the instance expression
4729 if (TypeManager.IsValueType (iexpr_type)) {
4731 // Special case: calls to a function declared in a
4732 // reference-type with a value-type argument need
4733 // to have their value boxed.
4734 if (decl_type.IsValueType ||
4735 TypeManager.IsGenericParameter (iexpr_type)) {
4737 // If the expression implements IMemoryLocation, then
4738 // we can optimize and use AddressOf on the
4741 // If not we have to use some temporary storage for
4743 if (instance_expr is IMemoryLocation) {
4744 ((IMemoryLocation)instance_expr).
4745 AddressOf (ec, AddressOp.LoadStore);
4747 LocalTemporary temp = new LocalTemporary (iexpr_type);
4748 instance_expr.Emit (ec);
4750 temp.AddressOf (ec, AddressOp.Load);
4753 // avoid the overhead of doing this all the time.
4755 t = TypeManager.GetReferenceType (iexpr_type);
4757 instance_expr.Emit (ec);
4758 ig.Emit (OpCodes.Box, instance_expr.Type);
4759 t = TypeManager.object_type;
4762 instance_expr.Emit (ec);
4763 t = instance_expr.Type;
4767 ig.Emit (OpCodes.Dup);
4768 if (Arguments != null && Arguments.Count != 0) {
4769 this_arg = new LocalTemporary (t);
4770 this_arg.Store (ec);
4777 EmitArguments (ec, method, Arguments, dup_args, this_arg);
4780 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
4781 ig.Emit (OpCodes.Constrained, instance_expr.Type);
4785 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
4786 call_op = OpCodes.Call;
4788 call_op = OpCodes.Callvirt;
4790 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
4791 Type[] varargs_types = GetVarargsTypes (method, Arguments);
4792 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
4799 // and DoFoo is not virtual, you can omit the callvirt,
4800 // because you don't need the null checking behavior.
4802 if (method is MethodInfo)
4803 ig.Emit (call_op, (MethodInfo) method);
4805 ig.Emit (call_op, (ConstructorInfo) method);
4808 public override void Emit (EmitContext ec)
4810 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
4812 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
4815 public override void EmitStatement (EmitContext ec)
4820 // Pop the return value if there is one
4822 if (method is MethodInfo){
4823 Type ret = ((MethodInfo)method).ReturnType;
4824 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
4825 ec.ig.Emit (OpCodes.Pop);
4829 protected override void CloneTo (CloneContext clonectx, Expression t)
4831 Invocation target = (Invocation) t;
4833 if (Arguments != null){
4834 target.Arguments = new ArrayList ();
4835 foreach (Argument a in Arguments)
4836 target.Arguments.Add (a.Clone (clonectx));
4839 expr = expr.Clone (clonectx);
4843 public class InvocationOrCast : ExpressionStatement
4846 Expression argument;
4848 public InvocationOrCast (Expression expr, Expression argument)
4851 this.argument = argument;
4852 this.loc = expr.Location;
4855 public override Expression DoResolve (EmitContext ec)
4858 // First try to resolve it as a cast.
4860 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
4861 if ((te != null) && (te.eclass == ExprClass.Type)) {
4862 Cast cast = new Cast (te, argument, loc);
4863 return cast.Resolve (ec);
4867 // This can either be a type or a delegate invocation.
4868 // Let's just resolve it and see what we'll get.
4870 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
4875 // Ok, so it's a Cast.
4877 if (expr.eclass == ExprClass.Type) {
4878 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
4879 return cast.Resolve (ec);
4883 // It's a delegate invocation.
4885 if (!TypeManager.IsDelegateType (expr.Type)) {
4886 Error (149, "Method name expected");
4890 ArrayList args = new ArrayList ();
4891 args.Add (new Argument (argument, Argument.AType.Expression));
4892 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
4893 return invocation.Resolve (ec);
4896 public override ExpressionStatement ResolveStatement (EmitContext ec)
4899 // First try to resolve it as a cast.
4901 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
4902 if ((te != null) && (te.eclass == ExprClass.Type)) {
4903 Error_InvalidExpressionStatement ();
4908 // This can either be a type or a delegate invocation.
4909 // Let's just resolve it and see what we'll get.
4911 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
4912 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
4913 Error_InvalidExpressionStatement ();
4918 // It's a delegate invocation.
4920 if (!TypeManager.IsDelegateType (expr.Type)) {
4921 Error (149, "Method name expected");
4925 ArrayList args = new ArrayList ();
4926 args.Add (new Argument (argument, Argument.AType.Expression));
4927 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
4928 return invocation.ResolveStatement (ec);
4931 public override void Emit (EmitContext ec)
4933 throw new Exception ("Cannot happen");
4936 public override void EmitStatement (EmitContext ec)
4938 throw new Exception ("Cannot happen");
4941 protected override void CloneTo (CloneContext clonectx, Expression t)
4943 InvocationOrCast target = (InvocationOrCast) t;
4945 target.expr = expr.Clone (clonectx);
4946 target.argument = argument.Clone (clonectx);
4951 // This class is used to "disable" the code generation for the
4952 // temporary variable when initializing value types.
4954 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
4955 public void AddressOf (EmitContext ec, AddressOp Mode)
4962 /// Implements the new expression
4964 public class New : ExpressionStatement, IMemoryLocation {
4965 public ArrayList Arguments;
4968 // During bootstrap, it contains the RequestedType,
4969 // but if `type' is not null, it *might* contain a NewDelegate
4970 // (because of field multi-initialization)
4972 public Expression RequestedType;
4974 MethodBase method = null;
4977 // If set, the new expression is for a value_target, and
4978 // we will not leave anything on the stack.
4980 Expression value_target;
4981 bool value_target_set = false;
4982 bool is_type_parameter = false;
4984 public New (Expression requested_type, ArrayList arguments, Location l)
4986 RequestedType = requested_type;
4987 Arguments = arguments;
4991 public bool SetValueTypeVariable (Expression value)
4993 value_target = value;
4994 value_target_set = true;
4995 if (!(value_target is IMemoryLocation)){
4996 Error_UnexpectedKind (null, "variable", loc);
5003 // This function is used to disable the following code sequence for
5004 // value type initialization:
5006 // AddressOf (temporary)
5010 // Instead the provide will have provided us with the address on the
5011 // stack to store the results.
5013 static Expression MyEmptyExpression;
5015 public void DisableTemporaryValueType ()
5017 if (MyEmptyExpression == null)
5018 MyEmptyExpression = new EmptyAddressOf ();
5021 // To enable this, look into:
5022 // test-34 and test-89 and self bootstrapping.
5024 // For instance, we can avoid a copy by using `newobj'
5025 // instead of Call + Push-temp on value types.
5026 // value_target = MyEmptyExpression;
5031 /// Converts complex core type syntax like 'new int ()' to simple constant
5033 public static Constant Constantify (Type t)
5035 if (t == TypeManager.int32_type)
5036 return new IntConstant (0, Location.Null);
5037 if (t == TypeManager.uint32_type)
5038 return new UIntConstant (0, Location.Null);
5039 if (t == TypeManager.int64_type)
5040 return new LongConstant (0, Location.Null);
5041 if (t == TypeManager.uint64_type)
5042 return new ULongConstant (0, Location.Null);
5043 if (t == TypeManager.float_type)
5044 return new FloatConstant (0, Location.Null);
5045 if (t == TypeManager.double_type)
5046 return new DoubleConstant (0, Location.Null);
5047 if (t == TypeManager.short_type)
5048 return new ShortConstant (0, Location.Null);
5049 if (t == TypeManager.ushort_type)
5050 return new UShortConstant (0, Location.Null);
5051 if (t == TypeManager.sbyte_type)
5052 return new SByteConstant (0, Location.Null);
5053 if (t == TypeManager.byte_type)
5054 return new ByteConstant (0, Location.Null);
5055 if (t == TypeManager.char_type)
5056 return new CharConstant ('\0', Location.Null);
5057 if (t == TypeManager.bool_type)
5058 return new BoolConstant (false, Location.Null);
5059 if (t == TypeManager.decimal_type)
5060 return new DecimalConstant (0, Location.Null);
5061 if (TypeManager.IsEnumType (t))
5062 return new EnumConstant (Constantify (TypeManager.EnumToUnderlying (t)), t);
5068 // Checks whether the type is an interface that has the
5069 // [ComImport, CoClass] attributes and must be treated
5072 public Expression CheckComImport (EmitContext ec)
5074 if (!type.IsInterface)
5078 // Turn the call into:
5079 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5081 Type real_class = AttributeTester.GetCoClassAttribute (type);
5082 if (real_class == null)
5085 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5086 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5087 return cast.Resolve (ec);
5090 public override Expression DoResolve (EmitContext ec)
5093 // The New DoResolve might be called twice when initializing field
5094 // expressions (see EmitFieldInitializers, the call to
5095 // GetInitializerExpression will perform a resolve on the expression,
5096 // and later the assign will trigger another resolution
5098 // This leads to bugs (#37014)
5101 if (RequestedType is NewDelegate)
5102 return RequestedType;
5106 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5112 if (type == TypeManager.void_type) {
5113 Error_VoidInvalidInTheContext (loc);
5117 if (Arguments == null) {
5118 Expression c = Constantify (type);
5123 if (TypeManager.IsDelegateType (type)) {
5124 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5125 if (RequestedType != null)
5126 if (!(RequestedType is DelegateCreation))
5127 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5128 return RequestedType;
5132 if (type.IsGenericParameter) {
5133 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5135 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5136 Error (304, String.Format (
5137 "Cannot create an instance of the " +
5138 "variable type '{0}' because it " +
5139 "doesn't have the new() constraint",
5144 if ((Arguments != null) && (Arguments.Count != 0)) {
5145 Error (417, String.Format (
5146 "`{0}': cannot provide arguments " +
5147 "when creating an instance of a " +
5148 "variable type.", type));
5152 is_type_parameter = true;
5153 eclass = ExprClass.Value;
5158 if (type.IsAbstract && type.IsSealed) {
5159 Report.SymbolRelatedToPreviousError (type);
5160 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5164 if (type.IsInterface || type.IsAbstract){
5165 RequestedType = CheckComImport (ec);
5166 if (RequestedType != null)
5167 return RequestedType;
5169 Report.SymbolRelatedToPreviousError (type);
5170 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5174 bool is_struct = type.IsValueType;
5175 eclass = ExprClass.Value;
5178 // SRE returns a match for .ctor () on structs (the object constructor),
5179 // so we have to manually ignore it.
5181 if (is_struct && Arguments == null)
5184 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5185 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5186 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5191 MethodGroupExpr mg = ml as MethodGroupExpr;
5194 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5198 if (Arguments != null){
5199 foreach (Argument a in Arguments){
5200 if (!a.Resolve (ec, loc))
5205 method = mg.OverloadResolve (ec, Arguments, false, loc);
5206 if (method == null) {
5207 if (almostMatchedMembers.Count != 0)
5208 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5215 bool DoEmitTypeParameter (EmitContext ec)
5218 ILGenerator ig = ec.ig;
5220 ig.Emit (OpCodes.Ldtoken, type);
5221 ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
5222 ig.Emit (OpCodes.Call, TypeManager.activator_create_instance);
5223 ig.Emit (OpCodes.Unbox_Any, type);
5226 throw new InternalErrorException ();
5231 // This DoEmit can be invoked in two contexts:
5232 // * As a mechanism that will leave a value on the stack (new object)
5233 // * As one that wont (init struct)
5235 // You can control whether a value is required on the stack by passing
5236 // need_value_on_stack. The code *might* leave a value on the stack
5237 // so it must be popped manually
5239 // If we are dealing with a ValueType, we have a few
5240 // situations to deal with:
5242 // * The target is a ValueType, and we have been provided
5243 // the instance (this is easy, we are being assigned).
5245 // * The target of New is being passed as an argument,
5246 // to a boxing operation or a function that takes a
5249 // In this case, we need to create a temporary variable
5250 // that is the argument of New.
5252 // Returns whether a value is left on the stack
5254 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5256 bool is_value_type = TypeManager.IsValueType (type);
5257 ILGenerator ig = ec.ig;
5262 // Allow DoEmit() to be called multiple times.
5263 // We need to create a new LocalTemporary each time since
5264 // you can't share LocalBuilders among ILGeneators.
5265 if (!value_target_set)
5266 value_target = new LocalTemporary (type);
5268 ml = (IMemoryLocation) value_target;
5269 ml.AddressOf (ec, AddressOp.Store);
5273 Invocation.EmitArguments (ec, method, Arguments, false, null);
5277 ig.Emit (OpCodes.Initobj, type);
5279 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5280 if (need_value_on_stack){
5281 value_target.Emit (ec);
5286 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5291 public override void Emit (EmitContext ec)
5293 if (is_type_parameter)
5294 DoEmitTypeParameter (ec);
5299 public override void EmitStatement (EmitContext ec)
5301 bool value_on_stack;
5303 if (is_type_parameter)
5304 value_on_stack = DoEmitTypeParameter (ec);
5306 value_on_stack = DoEmit (ec, false);
5309 ec.ig.Emit (OpCodes.Pop);
5313 public void AddressOf (EmitContext ec, AddressOp Mode)
5315 if (is_type_parameter)
5316 throw new InvalidOperationException ();
5318 if (!type.IsValueType){
5320 // We throw an exception. So far, I believe we only need to support
5322 // foreach (int j in new StructType ())
5325 throw new Exception ("AddressOf should not be used for classes");
5328 if (!value_target_set)
5329 value_target = new LocalTemporary (type);
5331 IMemoryLocation ml = (IMemoryLocation) value_target;
5332 ml.AddressOf (ec, AddressOp.Store);
5334 Invocation.EmitArguments (ec, method, Arguments, false, null);
5337 ec.ig.Emit (OpCodes.Initobj, type);
5339 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5341 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5344 protected override void CloneTo (CloneContext clonectx, Expression t)
5346 New target = (New) t;
5348 target.RequestedType = RequestedType.Clone (clonectx);
5349 if (Arguments != null){
5350 target.Arguments = new ArrayList ();
5351 foreach (Argument a in Arguments){
5352 target.Arguments.Add (a.Clone (clonectx));
5359 /// 14.5.10.2: Represents an array creation expression.
5363 /// There are two possible scenarios here: one is an array creation
5364 /// expression that specifies the dimensions and optionally the
5365 /// initialization data and the other which does not need dimensions
5366 /// specified but where initialization data is mandatory.
5368 public class ArrayCreation : Expression {
5369 Expression requested_base_type;
5370 ArrayList initializers;
5373 // The list of Argument types.
5374 // This is used to construct the `newarray' or constructor signature
5376 ArrayList arguments;
5379 // Method used to create the array object.
5381 MethodBase new_method = null;
5383 Type array_element_type;
5384 Type underlying_type;
5385 bool is_one_dimensional = false;
5386 bool is_builtin_type = false;
5387 bool expect_initializers = false;
5388 int num_arguments = 0;
5392 ArrayList array_data;
5396 // The number of constants in array initializers
5397 int const_initializers_count;
5398 bool only_constant_initializers;
5400 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5402 this.requested_base_type = requested_base_type;
5403 this.initializers = initializers;
5407 arguments = new ArrayList ();
5409 foreach (Expression e in exprs) {
5410 arguments.Add (new Argument (e, Argument.AType.Expression));
5415 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5417 this.requested_base_type = requested_base_type;
5418 this.initializers = initializers;
5422 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5424 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5426 //dimensions = tmp.Length - 1;
5427 expect_initializers = true;
5430 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5432 StringBuilder sb = new StringBuilder (rank);
5435 for (int i = 1; i < idx_count; i++)
5440 return new ComposedCast (base_type, sb.ToString (), loc);
5443 void Error_IncorrectArrayInitializer ()
5445 Error (178, "Invalid rank specifier: expected `,' or `]'");
5448 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5450 if (specified_dims) {
5451 Argument a = (Argument) arguments [idx];
5453 if (!a.Resolve (ec, loc))
5456 Constant c = a.Expr as Constant;
5458 c = c.ImplicitConversionRequired (TypeManager.int32_type, a.Expr.Location);
5462 Report.Error (150, a.Expr.Location, "A constant value is expected");
5466 int value = (int) c.GetValue ();
5468 if (value != probe.Count) {
5469 Error_IncorrectArrayInitializer ();
5473 bounds [idx] = value;
5476 int child_bounds = -1;
5477 only_constant_initializers = true;
5478 for (int i = 0; i < probe.Count; ++i) {
5479 object o = probe [i];
5480 if (o is ArrayList) {
5481 ArrayList sub_probe = o as ArrayList;
5482 int current_bounds = sub_probe.Count;
5484 if (child_bounds == -1)
5485 child_bounds = current_bounds;
5487 else if (child_bounds != current_bounds){
5488 Error_IncorrectArrayInitializer ();
5491 if (idx + 1 >= dimensions){
5492 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
5496 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
5500 if (child_bounds != -1){
5501 Error_IncorrectArrayInitializer ();
5505 Expression tmp = (Expression) o;
5506 tmp = tmp.Resolve (ec);
5510 Expression conv = Convert.ImplicitConversionRequired (
5511 ec, tmp, underlying_type, loc);
5516 // Initializers with the default values can be ignored
5517 Constant c = conv as Constant;
5519 if (c.IsDefaultInitializer (array_element_type)) {
5523 ++const_initializers_count;
5526 only_constant_initializers = false;
5529 array_data.Add (conv);
5536 public void UpdateIndices ()
5539 for (ArrayList probe = initializers; probe != null;) {
5540 if (probe.Count > 0 && probe [0] is ArrayList) {
5541 Expression e = new IntConstant (probe.Count, Location.Null);
5542 arguments.Add (new Argument (e, Argument.AType.Expression));
5544 bounds [i++] = probe.Count;
5546 probe = (ArrayList) probe [0];
5549 Expression e = new IntConstant (probe.Count, Location.Null);
5550 arguments.Add (new Argument (e, Argument.AType.Expression));
5552 bounds [i++] = probe.Count;
5559 bool ResolveInitializers (EmitContext ec)
5561 if (initializers == null) {
5562 return !expect_initializers;
5565 if (underlying_type == null)
5569 // We use this to store all the date values in the order in which we
5570 // will need to store them in the byte blob later
5572 array_data = new ArrayList ();
5573 bounds = new System.Collections.Specialized.HybridDictionary ();
5575 if (arguments != null)
5576 return CheckIndices (ec, initializers, 0, true);
5578 arguments = new ArrayList ();
5580 if (!CheckIndices (ec, initializers, 0, false))
5585 if (arguments.Count != dimensions) {
5586 Error_IncorrectArrayInitializer ();
5594 // Creates the type of the array
5596 bool LookupType (EmitContext ec)
5598 StringBuilder array_qualifier = new StringBuilder (rank);
5601 // `In the first form allocates an array instace of the type that results
5602 // from deleting each of the individual expression from the expression list'
5604 if (num_arguments > 0) {
5605 array_qualifier.Append ("[");
5606 for (int i = num_arguments-1; i > 0; i--)
5607 array_qualifier.Append (",");
5608 array_qualifier.Append ("]");
5614 TypeExpr array_type_expr;
5615 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
5616 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
5617 if (array_type_expr == null)
5620 type = array_type_expr.Type;
5621 underlying_type = TypeManager.GetElementType (type);
5622 dimensions = type.GetArrayRank ();
5627 public override Expression DoResolve (EmitContext ec)
5632 if (!LookupType (ec))
5635 array_element_type = TypeManager.GetElementType (type);
5636 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
5637 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
5642 // First step is to validate the initializers and fill
5643 // in any missing bits
5645 if (!ResolveInitializers (ec))
5649 if (arguments == null)
5652 arg_count = arguments.Count;
5653 foreach (Argument a in arguments){
5654 if (!a.Resolve (ec, loc))
5657 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
5658 if (real_arg == null)
5665 if (arg_count == 1) {
5666 is_one_dimensional = true;
5667 eclass = ExprClass.Value;
5671 is_builtin_type = TypeManager.IsBuiltinType (type);
5673 if (is_builtin_type) {
5676 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
5677 AllBindingFlags, loc);
5679 if (!(ml is MethodGroupExpr)) {
5680 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5685 Error (-6, "New invocation: Can not find a constructor for " +
5686 "this argument list");
5690 new_method = ((MethodGroupExpr) ml).OverloadResolve (
5691 ec, arguments, false, loc);
5693 if (new_method == null) {
5694 Error (-6, "New invocation: Can not find a constructor for " +
5695 "this argument list");
5699 eclass = ExprClass.Value;
5702 ModuleBuilder mb = CodeGen.Module.Builder;
5703 ArrayList args = new ArrayList ();
5705 if (arguments != null) {
5706 for (int i = 0; i < arg_count; i++)
5707 args.Add (TypeManager.int32_type);
5710 Type [] arg_types = null;
5713 arg_types = new Type [args.Count];
5715 args.CopyTo (arg_types, 0);
5717 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
5720 if (new_method == null) {
5721 Error (-6, "New invocation: Can not find a constructor for " +
5722 "this argument list");
5726 eclass = ExprClass.Value;
5731 byte [] MakeByteBlob ()
5736 int count = array_data.Count;
5738 if (underlying_type.IsEnum)
5739 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
5741 factor = GetTypeSize (underlying_type);
5743 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
5745 data = new byte [(count * factor + 4) & ~3];
5748 for (int i = 0; i < count; ++i) {
5749 object v = array_data [i];
5751 if (v is EnumConstant)
5752 v = ((EnumConstant) v).Child;
5754 if (v is Constant && !(v is StringConstant))
5755 v = ((Constant) v).GetValue ();
5761 if (underlying_type == TypeManager.int64_type){
5762 if (!(v is Expression)){
5763 long val = (long) v;
5765 for (int j = 0; j < factor; ++j) {
5766 data [idx + j] = (byte) (val & 0xFF);
5770 } else if (underlying_type == TypeManager.uint64_type){
5771 if (!(v is Expression)){
5772 ulong val = (ulong) v;
5774 for (int j = 0; j < factor; ++j) {
5775 data [idx + j] = (byte) (val & 0xFF);
5779 } else if (underlying_type == TypeManager.float_type) {
5780 if (!(v is Expression)){
5781 element = BitConverter.GetBytes ((float) v);
5783 for (int j = 0; j < factor; ++j)
5784 data [idx + j] = element [j];
5785 if (!BitConverter.IsLittleEndian)
5786 System.Array.Reverse (data, idx, 4);
5788 } else if (underlying_type == TypeManager.double_type) {
5789 if (!(v is Expression)){
5790 element = BitConverter.GetBytes ((double) v);
5792 for (int j = 0; j < factor; ++j)
5793 data [idx + j] = element [j];
5795 // FIXME: Handle the ARM float format.
5796 if (!BitConverter.IsLittleEndian)
5797 System.Array.Reverse (data, idx, 8);
5799 } else if (underlying_type == TypeManager.char_type){
5800 if (!(v is Expression)){
5801 int val = (int) ((char) v);
5803 data [idx] = (byte) (val & 0xff);
5804 data [idx+1] = (byte) (val >> 8);
5806 } else if (underlying_type == TypeManager.short_type){
5807 if (!(v is Expression)){
5808 int val = (int) ((short) v);
5810 data [idx] = (byte) (val & 0xff);
5811 data [idx+1] = (byte) (val >> 8);
5813 } else if (underlying_type == TypeManager.ushort_type){
5814 if (!(v is Expression)){
5815 int val = (int) ((ushort) v);
5817 data [idx] = (byte) (val & 0xff);
5818 data [idx+1] = (byte) (val >> 8);
5820 } else if (underlying_type == TypeManager.int32_type) {
5821 if (!(v is Expression)){
5824 data [idx] = (byte) (val & 0xff);
5825 data [idx+1] = (byte) ((val >> 8) & 0xff);
5826 data [idx+2] = (byte) ((val >> 16) & 0xff);
5827 data [idx+3] = (byte) (val >> 24);
5829 } else if (underlying_type == TypeManager.uint32_type) {
5830 if (!(v is Expression)){
5831 uint val = (uint) v;
5833 data [idx] = (byte) (val & 0xff);
5834 data [idx+1] = (byte) ((val >> 8) & 0xff);
5835 data [idx+2] = (byte) ((val >> 16) & 0xff);
5836 data [idx+3] = (byte) (val >> 24);
5838 } else if (underlying_type == TypeManager.sbyte_type) {
5839 if (!(v is Expression)){
5840 sbyte val = (sbyte) v;
5841 data [idx] = (byte) val;
5843 } else if (underlying_type == TypeManager.byte_type) {
5844 if (!(v is Expression)){
5845 byte val = (byte) v;
5846 data [idx] = (byte) val;
5848 } else if (underlying_type == TypeManager.bool_type) {
5849 if (!(v is Expression)){
5850 bool val = (bool) v;
5851 data [idx] = (byte) (val ? 1 : 0);
5853 } else if (underlying_type == TypeManager.decimal_type){
5854 if (!(v is Expression)){
5855 int [] bits = Decimal.GetBits ((decimal) v);
5858 // FIXME: For some reason, this doesn't work on the MS runtime.
5859 int [] nbits = new int [4];
5860 nbits [0] = bits [3];
5861 nbits [1] = bits [2];
5862 nbits [2] = bits [0];
5863 nbits [3] = bits [1];
5865 for (int j = 0; j < 4; j++){
5866 data [p++] = (byte) (nbits [j] & 0xff);
5867 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
5868 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
5869 data [p++] = (byte) (nbits [j] >> 24);
5873 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
5882 // Emits the initializers for the array
5884 void EmitStaticInitializers (EmitContext ec)
5887 // First, the static data
5890 ILGenerator ig = ec.ig;
5892 byte [] data = MakeByteBlob ();
5894 fb = RootContext.MakeStaticData (data);
5896 ig.Emit (OpCodes.Dup);
5897 ig.Emit (OpCodes.Ldtoken, fb);
5898 ig.Emit (OpCodes.Call,
5899 TypeManager.void_initializearray_array_fieldhandle);
5903 // Emits pieces of the array that can not be computed at compile
5904 // time (variables and string locations).
5906 // This always expect the top value on the stack to be the array
5908 void EmitDynamicInitializers (EmitContext ec, bool emitConstants)
5910 ILGenerator ig = ec.ig;
5911 int dims = bounds.Count;
5912 int [] current_pos = new int [dims];
5914 MethodInfo set = null;
5917 Type [] args = new Type [dims + 1];
5919 for (int j = 0; j < dims; j++)
5920 args [j] = TypeManager.int32_type;
5921 args [dims] = array_element_type;
5923 set = CodeGen.Module.Builder.GetArrayMethod (
5925 CallingConventions.HasThis | CallingConventions.Standard,
5926 TypeManager.void_type, args);
5929 for (int i = 0; i < array_data.Count; i++){
5931 Expression e = (Expression)array_data [i];
5933 // Constant can be initialized via StaticInitializer
5934 if (e != null && !(!emitConstants && e is Constant)) {
5935 Type etype = e.Type;
5937 ig.Emit (OpCodes.Dup);
5939 for (int idx = 0; idx < dims; idx++)
5940 IntConstant.EmitInt (ig, current_pos [idx]);
5943 // If we are dealing with a struct, get the
5944 // address of it, so we can store it.
5946 if ((dims == 1) && etype.IsValueType &&
5947 (!TypeManager.IsBuiltinOrEnum (etype) ||
5948 etype == TypeManager.decimal_type)) {
5953 // Let new know that we are providing
5954 // the address where to store the results
5956 n.DisableTemporaryValueType ();
5959 ig.Emit (OpCodes.Ldelema, etype);
5965 bool is_stobj, has_type_arg;
5966 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
5968 ig.Emit (OpCodes.Stobj, etype);
5969 else if (has_type_arg)
5970 ig.Emit (op, etype);
5974 ig.Emit (OpCodes.Call, set);
5981 for (int j = dims - 1; j >= 0; j--){
5983 if (current_pos [j] < (int) bounds [j])
5985 current_pos [j] = 0;
5990 void EmitArrayArguments (EmitContext ec)
5992 ILGenerator ig = ec.ig;
5994 foreach (Argument a in arguments) {
5995 Type atype = a.Type;
5998 if (atype == TypeManager.uint64_type)
5999 ig.Emit (OpCodes.Conv_Ovf_U4);
6000 else if (atype == TypeManager.int64_type)
6001 ig.Emit (OpCodes.Conv_Ovf_I4);
6005 public override void Emit (EmitContext ec)
6007 ILGenerator ig = ec.ig;
6009 EmitArrayArguments (ec);
6010 if (is_one_dimensional)
6011 ig.Emit (OpCodes.Newarr, array_element_type);
6013 if (is_builtin_type)
6014 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6016 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6019 if (initializers == null)
6022 // Emit static initializer for arrays which have contain more than 4 items and
6023 // the static initializer will initialize at least 25% of array values.
6024 // NOTE: const_initializers_count does not contain default constant values.
6025 if (const_initializers_count >= 4 && const_initializers_count * 4 > (array_data.Count) &&
6026 TypeManager.IsPrimitiveType (array_element_type)) {
6027 EmitStaticInitializers (ec);
6029 if (!only_constant_initializers)
6030 EmitDynamicInitializers (ec, false);
6032 EmitDynamicInitializers (ec, true);
6036 public override bool GetAttributableValue (Type valueType, out object value)
6038 if (!is_one_dimensional){
6039 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6040 return base.GetAttributableValue (null, out value);
6043 if (array_data == null) {
6044 Constant c = (Constant)((Argument)arguments [0]).Expr;
6045 if (c.IsDefaultValue) {
6046 value = Array.CreateInstance (array_element_type, 0);
6049 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6050 return base.GetAttributableValue (null, out value);
6053 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6054 object element_value;
6055 for (int i = 0; i < ret.Length; ++i)
6057 Expression e = (Expression)array_data [i];
6059 // Is null when an initializer is optimized (value == predefined value)
6063 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6067 ret.SetValue (element_value, i);
6073 protected override void CloneTo (CloneContext clonectx, Expression t)
6075 ArrayCreation target = (ArrayCreation) t;
6077 target.requested_base_type = requested_base_type.Clone (clonectx);
6078 target.arguments = new ArrayList ();
6079 foreach (Argument a in arguments)
6080 target.arguments.Add (a.Clone (clonectx));
6082 if (initializers != null){
6083 target.initializers = new ArrayList ();
6084 foreach (Expression initializer in initializers)
6085 target.initializers.Add (initializer.Clone (clonectx));
6090 public sealed class CompilerGeneratedThis : This
6092 public static This Instance = new CompilerGeneratedThis ();
6094 private CompilerGeneratedThis ()
6095 : base (Location.Null)
6099 public override Expression DoResolve (EmitContext ec)
6101 eclass = ExprClass.Variable;
6102 type = ec.ContainerType;
6103 variable = new SimpleThis (type);
6109 /// Represents the `this' construct
6112 public class This : VariableReference, IVariable
6115 VariableInfo variable_info;
6116 protected Variable variable;
6119 public This (Block block, Location loc)
6125 public This (Location loc)
6130 public VariableInfo VariableInfo {
6131 get { return variable_info; }
6134 public bool VerifyFixed ()
6136 return !TypeManager.IsValueType (Type);
6139 public override bool IsRef {
6140 get { return is_struct; }
6143 public override Variable Variable {
6144 get { return variable; }
6147 public bool ResolveBase (EmitContext ec)
6149 eclass = ExprClass.Variable;
6151 if (ec.TypeContainer.CurrentType != null)
6152 type = ec.TypeContainer.CurrentType;
6154 type = ec.ContainerType;
6156 is_struct = ec.TypeContainer is Struct;
6159 Error (26, "Keyword `this' is not valid in a static property, " +
6160 "static method, or static field initializer");
6164 if (block != null) {
6165 if (block.Toplevel.ThisVariable != null)
6166 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6168 AnonymousContainer am = ec.CurrentAnonymousMethod;
6169 if (is_struct && (am != null) && !am.IsIterator) {
6170 Report.Error (1673, loc, "Anonymous methods inside structs " +
6171 "cannot access instance members of `this'. " +
6172 "Consider copying `this' to a local variable " +
6173 "outside the anonymous method and using the " +
6178 RootScopeInfo host = block.Toplevel.RootScope;
6179 if ((host != null) && !ec.IsConstructor &&
6180 (!is_struct || host.IsIterator)) {
6181 variable = host.CaptureThis ();
6182 type = variable.Type;
6187 if (variable == null)
6188 variable = new SimpleThis (type);
6194 // Called from Invocation to check if the invocation is correct
6196 public bool CheckThisUsage (EmitContext ec)
6198 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) &&
6199 !variable_info.IsAssigned (ec)) {
6200 Error (188, "The `this' object cannot be used before all of its " +
6201 "fields are assigned to");
6202 variable_info.SetAssigned (ec);
6209 public override Expression DoResolve (EmitContext ec)
6211 if (!ResolveBase (ec))
6215 if (ec.IsFieldInitializer) {
6216 Error (27, "Keyword `this' is not available in the current context");
6223 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6225 if (!ResolveBase (ec))
6228 if (variable_info != null)
6229 variable_info.SetAssigned (ec);
6231 if (ec.TypeContainer is Class){
6232 Error (1604, "Cannot assign to 'this' because it is read-only");
6238 public override int GetHashCode()
6240 return block.GetHashCode ();
6243 public override bool Equals (object obj)
6245 This t = obj as This;
6249 return block == t.block;
6252 protected class SimpleThis : Variable
6256 public SimpleThis (Type type)
6261 public override Type Type {
6262 get { return type; }
6265 public override bool HasInstance {
6266 get { return false; }
6269 public override bool NeedsTemporary {
6270 get { return false; }
6273 public override void EmitInstance (EmitContext ec)
6278 public override void Emit (EmitContext ec)
6280 ec.ig.Emit (OpCodes.Ldarg_0);
6283 public override void EmitAssign (EmitContext ec)
6285 throw new InvalidOperationException ();
6288 public override void EmitAddressOf (EmitContext ec)
6290 ec.ig.Emit (OpCodes.Ldarg_0);
6294 protected override void CloneTo (CloneContext clonectx, Expression t)
6296 This target = (This) t;
6298 target.block = clonectx.LookupBlock (block);
6303 /// Represents the `__arglist' construct
6305 public class ArglistAccess : Expression
6307 public ArglistAccess (Location loc)
6312 public override Expression DoResolve (EmitContext ec)
6314 eclass = ExprClass.Variable;
6315 type = TypeManager.runtime_argument_handle_type;
6317 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6319 Error (190, "The __arglist construct is valid only within " +
6320 "a variable argument method");
6327 public override void Emit (EmitContext ec)
6329 ec.ig.Emit (OpCodes.Arglist);
6332 protected override void CloneTo (CloneContext clonectx, Expression target)
6339 /// Represents the `__arglist (....)' construct
6341 public class Arglist : Expression
6343 public Argument[] Arguments;
6345 public Arglist (Location loc)
6346 : this (Argument.Empty, loc)
6350 public Arglist (Argument[] args, Location l)
6356 public Type[] ArgumentTypes {
6358 Type[] retval = new Type [Arguments.Length];
6359 for (int i = 0; i < Arguments.Length; i++)
6360 retval [i] = Arguments [i].Type;
6365 public override Expression DoResolve (EmitContext ec)
6367 eclass = ExprClass.Variable;
6368 type = TypeManager.runtime_argument_handle_type;
6370 foreach (Argument arg in Arguments) {
6371 if (!arg.Resolve (ec, loc))
6378 public override void Emit (EmitContext ec)
6380 foreach (Argument arg in Arguments)
6384 protected override void CloneTo (CloneContext clonectx, Expression t)
6386 Arglist target = (Arglist) t;
6388 target.Arguments = new Argument [Arguments.Length];
6389 for (int i = 0; i < Arguments.Length; i++)
6390 target.Arguments [i] = Arguments [i].Clone (clonectx);
6395 // This produces the value that renders an instance, used by the iterators code
6397 public class ProxyInstance : Expression, IMemoryLocation {
6398 public override Expression DoResolve (EmitContext ec)
6400 eclass = ExprClass.Variable;
6401 type = ec.ContainerType;
6405 public override void Emit (EmitContext ec)
6407 ec.ig.Emit (OpCodes.Ldarg_0);
6411 public void AddressOf (EmitContext ec, AddressOp mode)
6413 ec.ig.Emit (OpCodes.Ldarg_0);
6418 /// Implements the typeof operator
6420 public class TypeOf : Expression {
6421 Expression QueriedType;
6422 protected Type typearg;
6424 public TypeOf (Expression queried_type, Location l)
6426 QueriedType = queried_type;
6430 public override Expression DoResolve (EmitContext ec)
6432 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6436 typearg = texpr.Type;
6438 if (typearg == TypeManager.void_type) {
6439 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6443 if (typearg.IsPointer && !ec.InUnsafe){
6448 type = TypeManager.type_type;
6449 // Even though what is returned is a type object, it's treated as a value by the compiler.
6450 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6451 eclass = ExprClass.Value;
6455 public override void Emit (EmitContext ec)
6457 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6458 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6461 public override bool GetAttributableValue (Type valueType, out object value)
6463 if (TypeManager.ContainsGenericParameters (typearg)) {
6464 Report.SymbolRelatedToPreviousError(typearg);
6465 Report.Error(416, loc, "`{0}': an attribute argument cannot use type parameters",
6466 TypeManager.CSharpName(typearg));
6471 if (valueType == TypeManager.object_type) {
6472 value = (object)typearg;
6479 public Type TypeArgument
6487 protected override void CloneTo (CloneContext clonectx, Expression t)
6489 TypeOf target = (TypeOf) t;
6491 target.QueriedType = QueriedType.Clone (clonectx);
6496 /// Implements the `typeof (void)' operator
6498 public class TypeOfVoid : TypeOf {
6499 public TypeOfVoid (Location l) : base (null, l)
6504 public override Expression DoResolve (EmitContext ec)
6506 type = TypeManager.type_type;
6507 typearg = TypeManager.void_type;
6508 // See description in TypeOf.
6509 eclass = ExprClass.Value;
6515 /// Implements the sizeof expression
6517 public class SizeOf : Expression {
6518 public Expression QueriedType;
6521 public SizeOf (Expression queried_type, Location l)
6523 this.QueriedType = queried_type;
6527 public override Expression DoResolve (EmitContext ec)
6529 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6534 if (texpr is TypeParameterExpr){
6535 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
6540 type_queried = texpr.Type;
6541 if (type_queried.IsEnum)
6542 type_queried = TypeManager.EnumToUnderlying (type_queried);
6544 if (type_queried == TypeManager.void_type) {
6545 Expression.Error_VoidInvalidInTheContext (loc);
6549 int size_of = GetTypeSize (type_queried);
6551 return new IntConstant (size_of, loc);
6555 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)",
6556 TypeManager.CSharpName (type_queried));
6560 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
6564 type = TypeManager.int32_type;
6565 eclass = ExprClass.Value;
6569 public override void Emit (EmitContext ec)
6571 int size = GetTypeSize (type_queried);
6574 ec.ig.Emit (OpCodes.Sizeof, type_queried);
6576 IntConstant.EmitInt (ec.ig, size);
6579 protected override void CloneTo (CloneContext clonectx, Expression t)
6581 SizeOf target = (SizeOf) t;
6583 target.QueriedType = QueriedType.Clone (clonectx);
6588 /// Implements the qualified-alias-member (::) expression.
6590 public class QualifiedAliasMember : Expression
6592 string alias, identifier;
6594 public QualifiedAliasMember (string alias, string identifier, Location l)
6597 this.identifier = identifier;
6601 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
6603 if (alias == "global")
6604 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
6606 int errors = Report.Errors;
6607 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6609 if (errors == Report.Errors)
6610 Report.Error (432, loc, "Alias `{0}' not found", alias);
6613 if (fne.eclass != ExprClass.Namespace) {
6615 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6618 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
6621 public override Expression DoResolve (EmitContext ec)
6623 FullNamedExpression fne;
6624 if (alias == "global") {
6625 fne = RootNamespace.Global;
6627 int errors = Report.Errors;
6628 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6630 if (errors == Report.Errors)
6631 Report.Error (432, loc, "Alias `{0}' not found", alias);
6636 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
6640 if (!(retval is FullNamedExpression)) {
6641 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
6645 // We defer this check till the end to match the behaviour of CSC
6646 if (fne.eclass != ExprClass.Namespace) {
6647 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6653 public override void Emit (EmitContext ec)
6655 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
6659 public override string ToString ()
6661 return alias + "::" + identifier;
6664 public override string GetSignatureForError ()
6669 protected override void CloneTo (CloneContext clonectx, Expression t)
6676 /// Implements the member access expression
6678 public class MemberAccess : Expression {
6679 public readonly string Identifier;
6682 public MemberAccess (Expression expr, string id)
6683 : this (expr, id, expr.Location)
6687 public MemberAccess (Expression expr, string identifier, Location loc)
6690 Identifier = identifier;
6694 public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
6695 : this (expr, identifier, loc)
6702 public Expression Expr {
6703 get { return expr; }
6706 protected string LookupIdentifier {
6707 get { return MemberName.MakeName (Identifier, args); }
6710 // TODO: this method has very poor performace for Enum fields and
6711 // probably for other constants as well
6712 Expression DoResolve (EmitContext ec, Expression right_side)
6715 throw new Exception ();
6718 // Resolve the expression with flow analysis turned off, we'll do the definite
6719 // assignment checks later. This is because we don't know yet what the expression
6720 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
6721 // definite assignment check on the actual field and not on the whole struct.
6724 SimpleName original = expr as SimpleName;
6725 expr = expr.Resolve (ec,
6726 ResolveFlags.VariableOrValue | ResolveFlags.Type |
6727 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
6732 if (expr is Namespace) {
6733 Namespace ns = (Namespace) expr;
6734 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, LookupIdentifier, loc);
6736 if ((retval != null) && (args != null))
6737 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
6741 ns.Error_NamespaceDoesNotExist (ec.DeclContainer, loc, Identifier);
6745 Type expr_type = expr.Type;
6746 if (expr_type.IsPointer || expr_type == TypeManager.void_type || expr is NullLiteral){
6747 Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type);
6750 if (expr_type == TypeManager.anonymous_method_type){
6751 Unary.Error_OperatorCannotBeApplied (loc, ".", "anonymous method");
6755 Constant c = expr as Constant;
6756 if (c != null && c.GetValue () == null) {
6757 Report.Warning (1720, 1, loc, "Expression will always cause a `{0}'",
6758 "System.NullReferenceException");
6761 Expression member_lookup;
6762 member_lookup = MemberLookup (
6763 ec.ContainerType, expr_type, expr_type, Identifier, loc);
6765 if ((member_lookup == null) && (args != null)) {
6766 member_lookup = MemberLookup (
6767 ec.ContainerType, expr_type, expr_type, LookupIdentifier, loc);
6770 if (member_lookup == null) {
6771 member_lookup = ec.DeclContainer.LookupExtensionMethod (expr_type, Identifier);
6772 if (member_lookup != null)
6773 return member_lookup.DoResolve (ec);
6775 MemberLookupFailed (
6776 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
6780 TypeExpr texpr = member_lookup as TypeExpr;
6781 if (texpr != null) {
6782 if (!(expr is TypeExpr) &&
6783 (original == null || !original.IdenticalNameAndTypeName (ec, expr, loc))) {
6784 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
6785 Identifier, member_lookup.GetSignatureForError ());
6789 if (!texpr.CheckAccessLevel (ec.DeclContainer)) {
6790 Report.SymbolRelatedToPreviousError (member_lookup.Type);
6791 ErrorIsInaccesible (loc, TypeManager.CSharpName (member_lookup.Type));
6796 ConstructedType ct = expr as ConstructedType;
6799 // When looking up a nested type in a generic instance
6800 // via reflection, we always get a generic type definition
6801 // and not a generic instance - so we have to do this here.
6803 // See gtest-172-lib.cs and gtest-172.cs for an example.
6805 ct = new ConstructedType (
6806 member_lookup.Type, ct.TypeArguments, loc);
6808 return ct.ResolveAsTypeStep (ec, false);
6811 return member_lookup;
6814 MemberExpr me = (MemberExpr) member_lookup;
6815 member_lookup = me.ResolveMemberAccess (ec, expr, loc, original);
6816 if (member_lookup == null)
6820 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
6822 throw new InternalErrorException ();
6824 return mg.ResolveGeneric (ec, args);
6827 if (original != null && !TypeManager.IsValueType (expr_type)) {
6828 me = member_lookup as MemberExpr;
6829 if (me != null && me.IsInstance) {
6830 LocalVariableReference var = expr as LocalVariableReference;
6831 if (var != null && !var.VerifyAssigned (ec))
6836 // The following DoResolve/DoResolveLValue will do the definite assignment
6839 if (right_side != null)
6840 return member_lookup.DoResolveLValue (ec, right_side);
6842 return member_lookup.DoResolve (ec);
6845 public override Expression DoResolve (EmitContext ec)
6847 return DoResolve (ec, null);
6850 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
6852 return DoResolve (ec, right_side);
6855 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
6857 return ResolveNamespaceOrType (ec, silent);
6860 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
6862 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
6864 if (new_expr == null)
6867 if (new_expr is Namespace) {
6868 Namespace ns = (Namespace) new_expr;
6869 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, LookupIdentifier, loc);
6871 if ((retval != null) && (args != null))
6872 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
6874 if (!silent && retval == null)
6875 ns.Error_NamespaceDoesNotExist (rc.DeclContainer, loc, LookupIdentifier);
6879 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
6880 if (tnew_expr == null)
6883 Type expr_type = tnew_expr.Type;
6885 if (expr_type.IsPointer){
6886 Error (23, "The `.' operator can not be applied to pointer operands (" +
6887 TypeManager.CSharpName (expr_type) + ")");
6891 Expression member_lookup = MemberLookup (
6892 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
6893 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
6894 if (member_lookup == null) {
6898 member_lookup = MemberLookup(
6899 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
6900 MemberTypes.All, BindingFlags.Public | BindingFlags.NonPublic, loc);
6902 if (member_lookup == null) {
6903 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
6904 Identifier, new_expr.GetSignatureForError ());
6906 // TODO: Report.SymbolRelatedToPreviousError
6907 member_lookup.Error_UnexpectedKind (null, "type", loc);
6912 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
6917 TypeArguments the_args = args;
6918 if (TypeManager.HasGenericArguments (expr_type)) {
6919 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
6921 TypeArguments new_args = new TypeArguments (loc);
6922 foreach (Type decl in decl_args)
6923 new_args.Add (new TypeExpression (decl, loc));
6926 new_args.Add (args);
6928 the_args = new_args;
6931 if (the_args != null) {
6932 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
6933 return ctype.ResolveAsTypeStep (rc, false);
6940 public override void Emit (EmitContext ec)
6942 throw new Exception ("Should not happen");
6945 public override string ToString ()
6947 return expr + "." + MemberName.MakeName (Identifier, args);
6950 public override string GetSignatureForError ()
6952 return expr.GetSignatureForError () + "." + Identifier;
6955 protected override void CloneTo (CloneContext clonectx, Expression t)
6957 MemberAccess target = (MemberAccess) t;
6959 target.expr = expr.Clone (clonectx);
6964 /// Implements checked expressions
6966 public class CheckedExpr : Expression {
6968 public Expression Expr;
6970 public CheckedExpr (Expression e, Location l)
6976 public override Expression DoResolve (EmitContext ec)
6978 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
6979 Expr = Expr.Resolve (ec);
6984 if (Expr is Constant)
6987 eclass = Expr.eclass;
6992 public override void Emit (EmitContext ec)
6994 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
6998 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7000 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7001 Expr.EmitBranchable (ec, target, onTrue);
7004 protected override void CloneTo (CloneContext clonectx, Expression t)
7006 CheckedExpr target = (CheckedExpr) t;
7008 target.Expr = Expr.Clone (clonectx);
7013 /// Implements the unchecked expression
7015 public class UnCheckedExpr : Expression {
7017 public Expression Expr;
7019 public UnCheckedExpr (Expression e, Location l)
7025 public override Expression DoResolve (EmitContext ec)
7027 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7028 Expr = Expr.Resolve (ec);
7033 if (Expr is Constant)
7036 eclass = Expr.eclass;
7041 public override void Emit (EmitContext ec)
7043 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7047 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7049 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7050 Expr.EmitBranchable (ec, target, onTrue);
7053 protected override void CloneTo (CloneContext clonectx, Expression t)
7055 UnCheckedExpr target = (UnCheckedExpr) t;
7057 target.Expr = Expr.Clone (clonectx);
7062 /// An Element Access expression.
7064 /// During semantic analysis these are transformed into
7065 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7067 public class ElementAccess : Expression {
7068 public ArrayList Arguments;
7069 public Expression Expr;
7071 public ElementAccess (Expression e, ArrayList e_list)
7080 Arguments = new ArrayList ();
7081 foreach (Expression tmp in e_list)
7082 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7086 bool CommonResolve (EmitContext ec)
7088 Expr = Expr.Resolve (ec);
7093 if (Arguments == null)
7096 foreach (Argument a in Arguments){
7097 if (!a.Resolve (ec, loc))
7104 Expression MakePointerAccess (EmitContext ec, Type t)
7106 if (t == TypeManager.void_ptr_type){
7107 Error (242, "The array index operation is not valid on void pointers");
7110 if (Arguments.Count != 1){
7111 Error (196, "A pointer must be indexed by only one value");
7116 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7119 return new Indirection (p, loc).Resolve (ec);
7122 public override Expression DoResolve (EmitContext ec)
7124 if (!CommonResolve (ec))
7128 // We perform some simple tests, and then to "split" the emit and store
7129 // code we create an instance of a different class, and return that.
7131 // I am experimenting with this pattern.
7135 if (t == TypeManager.array_type){
7136 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7141 return (new ArrayAccess (this, loc)).Resolve (ec);
7143 return MakePointerAccess (ec, Expr.Type);
7145 FieldExpr fe = Expr as FieldExpr;
7147 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7149 return MakePointerAccess (ec, ff.ElementType);
7152 return (new IndexerAccess (this, loc)).Resolve (ec);
7155 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7157 if (!CommonResolve (ec))
7162 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7165 return MakePointerAccess (ec, Expr.Type);
7167 FieldExpr fe = Expr as FieldExpr;
7169 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7171 if (!(fe.InstanceExpression is LocalVariableReference) &&
7172 !(fe.InstanceExpression is This)) {
7173 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7176 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7177 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7180 return MakePointerAccess (ec, ff.ElementType);
7183 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7186 public override void Emit (EmitContext ec)
7188 throw new Exception ("Should never be reached");
7191 protected override void CloneTo (CloneContext clonectx, Expression t)
7193 ElementAccess target = (ElementAccess) t;
7195 target.Expr = Expr.Clone (clonectx);
7196 target.Arguments = new ArrayList ();
7197 foreach (Argument a in Arguments)
7198 target.Arguments.Add (a.Clone (clonectx));
7203 /// Implements array access
7205 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7207 // Points to our "data" repository
7211 LocalTemporary temp;
7214 public ArrayAccess (ElementAccess ea_data, Location l)
7217 eclass = ExprClass.Variable;
7221 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7223 return DoResolve (ec);
7226 public override Expression DoResolve (EmitContext ec)
7229 ExprClass eclass = ea.Expr.eclass;
7231 // As long as the type is valid
7232 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7233 eclass == ExprClass.Value)) {
7234 ea.Expr.Error_UnexpectedKind ("variable or value");
7239 Type t = ea.Expr.Type;
7240 if (t.GetArrayRank () != ea.Arguments.Count){
7241 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7242 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7246 type = TypeManager.GetElementType (t);
7247 if (type.IsPointer && !ec.InUnsafe){
7248 UnsafeError (ea.Location);
7252 foreach (Argument a in ea.Arguments){
7253 Type argtype = a.Type;
7255 if (argtype == TypeManager.int32_type ||
7256 argtype == TypeManager.uint32_type ||
7257 argtype == TypeManager.int64_type ||
7258 argtype == TypeManager.uint64_type) {
7259 Constant c = a.Expr as Constant;
7260 if (c != null && c.IsNegative) {
7261 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7267 // Mhm. This is strage, because the Argument.Type is not the same as
7268 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7270 // Wonder if I will run into trouble for this.
7272 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7277 eclass = ExprClass.Variable;
7283 /// Emits the right opcode to load an object of Type `t'
7284 /// from an array of T
7286 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7288 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7289 ig.Emit (OpCodes.Ldelem_U1);
7290 else if (type == TypeManager.sbyte_type)
7291 ig.Emit (OpCodes.Ldelem_I1);
7292 else if (type == TypeManager.short_type)
7293 ig.Emit (OpCodes.Ldelem_I2);
7294 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7295 ig.Emit (OpCodes.Ldelem_U2);
7296 else if (type == TypeManager.int32_type)
7297 ig.Emit (OpCodes.Ldelem_I4);
7298 else if (type == TypeManager.uint32_type)
7299 ig.Emit (OpCodes.Ldelem_U4);
7300 else if (type == TypeManager.uint64_type)
7301 ig.Emit (OpCodes.Ldelem_I8);
7302 else if (type == TypeManager.int64_type)
7303 ig.Emit (OpCodes.Ldelem_I8);
7304 else if (type == TypeManager.float_type)
7305 ig.Emit (OpCodes.Ldelem_R4);
7306 else if (type == TypeManager.double_type)
7307 ig.Emit (OpCodes.Ldelem_R8);
7308 else if (type == TypeManager.intptr_type)
7309 ig.Emit (OpCodes.Ldelem_I);
7310 else if (TypeManager.IsEnumType (type)){
7311 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7312 } else if (type.IsValueType){
7313 ig.Emit (OpCodes.Ldelema, type);
7314 ig.Emit (OpCodes.Ldobj, type);
7316 } else if (type.IsGenericParameter) {
7317 ig.Emit (OpCodes.Ldelem, type);
7319 } else if (type.IsPointer)
7320 ig.Emit (OpCodes.Ldelem_I);
7322 ig.Emit (OpCodes.Ldelem_Ref);
7326 /// Returns the right opcode to store an object of Type `t'
7327 /// from an array of T.
7329 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7331 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7332 has_type_arg = false; is_stobj = false;
7333 t = TypeManager.TypeToCoreType (t);
7334 if (TypeManager.IsEnumType (t))
7335 t = TypeManager.EnumToUnderlying (t);
7336 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7337 t == TypeManager.bool_type)
7338 return OpCodes.Stelem_I1;
7339 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7340 t == TypeManager.char_type)
7341 return OpCodes.Stelem_I2;
7342 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7343 return OpCodes.Stelem_I4;
7344 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7345 return OpCodes.Stelem_I8;
7346 else if (t == TypeManager.float_type)
7347 return OpCodes.Stelem_R4;
7348 else if (t == TypeManager.double_type)
7349 return OpCodes.Stelem_R8;
7350 else if (t == TypeManager.intptr_type) {
7351 has_type_arg = true;
7353 return OpCodes.Stobj;
7354 } else if (t.IsValueType) {
7355 has_type_arg = true;
7357 return OpCodes.Stobj;
7359 } else if (t.IsGenericParameter) {
7360 has_type_arg = true;
7361 return OpCodes.Stelem;
7364 } else if (t.IsPointer)
7365 return OpCodes.Stelem_I;
7367 return OpCodes.Stelem_Ref;
7370 MethodInfo FetchGetMethod ()
7372 ModuleBuilder mb = CodeGen.Module.Builder;
7373 int arg_count = ea.Arguments.Count;
7374 Type [] args = new Type [arg_count];
7377 for (int i = 0; i < arg_count; i++){
7378 //args [i++] = a.Type;
7379 args [i] = TypeManager.int32_type;
7382 get = mb.GetArrayMethod (
7383 ea.Expr.Type, "Get",
7384 CallingConventions.HasThis |
7385 CallingConventions.Standard,
7391 MethodInfo FetchAddressMethod ()
7393 ModuleBuilder mb = CodeGen.Module.Builder;
7394 int arg_count = ea.Arguments.Count;
7395 Type [] args = new Type [arg_count];
7399 ret_type = TypeManager.GetReferenceType (type);
7401 for (int i = 0; i < arg_count; i++){
7402 //args [i++] = a.Type;
7403 args [i] = TypeManager.int32_type;
7406 address = mb.GetArrayMethod (
7407 ea.Expr.Type, "Address",
7408 CallingConventions.HasThis |
7409 CallingConventions.Standard,
7416 // Load the array arguments into the stack.
7418 // If we have been requested to cache the values (cached_locations array
7419 // initialized), then load the arguments the first time and store them
7420 // in locals. otherwise load from local variables.
7422 void LoadArrayAndArguments (EmitContext ec)
7424 ILGenerator ig = ec.ig;
7427 foreach (Argument a in ea.Arguments){
7428 Type argtype = a.Expr.Type;
7432 if (argtype == TypeManager.int64_type)
7433 ig.Emit (OpCodes.Conv_Ovf_I);
7434 else if (argtype == TypeManager.uint64_type)
7435 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7439 public void Emit (EmitContext ec, bool leave_copy)
7441 int rank = ea.Expr.Type.GetArrayRank ();
7442 ILGenerator ig = ec.ig;
7445 LoadArrayAndArguments (ec);
7448 EmitLoadOpcode (ig, type);
7452 method = FetchGetMethod ();
7453 ig.Emit (OpCodes.Call, method);
7456 LoadFromPtr (ec.ig, this.type);
7459 ec.ig.Emit (OpCodes.Dup);
7460 temp = new LocalTemporary (this.type);
7465 public override void Emit (EmitContext ec)
7470 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7472 int rank = ea.Expr.Type.GetArrayRank ();
7473 ILGenerator ig = ec.ig;
7474 Type t = source.Type;
7475 prepared = prepare_for_load;
7477 if (prepare_for_load) {
7478 AddressOf (ec, AddressOp.LoadStore);
7479 ec.ig.Emit (OpCodes.Dup);
7482 ec.ig.Emit (OpCodes.Dup);
7483 temp = new LocalTemporary (this.type);
7486 StoreFromPtr (ec.ig, t);
7496 LoadArrayAndArguments (ec);
7499 bool is_stobj, has_type_arg;
7500 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7502 // The stobj opcode used by value types will need
7503 // an address on the stack, not really an array/array
7507 ig.Emit (OpCodes.Ldelema, t);
7511 ec.ig.Emit (OpCodes.Dup);
7512 temp = new LocalTemporary (this.type);
7517 ig.Emit (OpCodes.Stobj, t);
7518 else if (has_type_arg)
7523 ModuleBuilder mb = CodeGen.Module.Builder;
7524 int arg_count = ea.Arguments.Count;
7525 Type [] args = new Type [arg_count + 1];
7530 ec.ig.Emit (OpCodes.Dup);
7531 temp = new LocalTemporary (this.type);
7535 for (int i = 0; i < arg_count; i++){
7536 //args [i++] = a.Type;
7537 args [i] = TypeManager.int32_type;
7540 args [arg_count] = type;
7542 set = mb.GetArrayMethod (
7543 ea.Expr.Type, "Set",
7544 CallingConventions.HasThis |
7545 CallingConventions.Standard,
7546 TypeManager.void_type, args);
7548 ig.Emit (OpCodes.Call, set);
7557 public void AddressOf (EmitContext ec, AddressOp mode)
7559 int rank = ea.Expr.Type.GetArrayRank ();
7560 ILGenerator ig = ec.ig;
7562 LoadArrayAndArguments (ec);
7565 ig.Emit (OpCodes.Ldelema, type);
7567 MethodInfo address = FetchAddressMethod ();
7568 ig.Emit (OpCodes.Call, address);
7572 public void EmitGetLength (EmitContext ec, int dim)
7574 int rank = ea.Expr.Type.GetArrayRank ();
7575 ILGenerator ig = ec.ig;
7579 ig.Emit (OpCodes.Ldlen);
7580 ig.Emit (OpCodes.Conv_I4);
7582 IntLiteral.EmitInt (ig, dim);
7583 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7589 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7590 public readonly ArrayList Properties;
7591 static Indexers empty;
7593 public struct Indexer {
7594 public readonly PropertyInfo PropertyInfo;
7595 public readonly MethodInfo Getter, Setter;
7597 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7599 this.PropertyInfo = property_info;
7607 empty = new Indexers (null);
7610 Indexers (ArrayList array)
7615 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
7620 foreach (PropertyInfo property in mi){
7621 MethodInfo get, set;
7623 get = property.GetGetMethod (true);
7624 set = property.GetSetMethod (true);
7625 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
7627 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
7629 if (get != null || set != null) {
7631 ix = new Indexers (new ArrayList ());
7632 ix.Properties.Add (new Indexer (property, get, set));
7637 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
7639 string p_name = TypeManager.IndexerPropertyName (lookup_type);
7641 return TypeManager.MemberLookup (
7642 caller_type, caller_type, lookup_type, MemberTypes.Property,
7643 BindingFlags.Public | BindingFlags.Instance |
7644 BindingFlags.DeclaredOnly, p_name, null);
7647 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
7649 Indexers ix = empty;
7652 if (lookup_type.IsGenericParameter) {
7653 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
7657 if (gc.HasClassConstraint)
7658 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
7660 Type[] ifaces = gc.InterfaceConstraints;
7661 foreach (Type itype in ifaces)
7662 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
7668 Type copy = lookup_type;
7669 while (copy != TypeManager.object_type && copy != null){
7670 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
7671 copy = copy.BaseType;
7674 if (lookup_type.IsInterface) {
7675 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
7676 if (ifaces != null) {
7677 foreach (Type itype in ifaces)
7678 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
7687 /// Expressions that represent an indexer call.
7689 public class IndexerAccess : Expression, IAssignMethod {
7691 // Points to our "data" repository
7693 MethodInfo get, set;
7694 ArrayList set_arguments;
7695 bool is_base_indexer;
7697 protected Type indexer_type;
7698 protected Type current_type;
7699 protected Expression instance_expr;
7700 protected ArrayList arguments;
7702 public IndexerAccess (ElementAccess ea, Location loc)
7703 : this (ea.Expr, false, loc)
7705 this.arguments = ea.Arguments;
7708 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
7711 this.instance_expr = instance_expr;
7712 this.is_base_indexer = is_base_indexer;
7713 this.eclass = ExprClass.Value;
7717 protected virtual bool CommonResolve (EmitContext ec)
7719 indexer_type = instance_expr.Type;
7720 current_type = ec.ContainerType;
7725 public override Expression DoResolve (EmitContext ec)
7727 if (!CommonResolve (ec))
7731 // Step 1: Query for all `Item' *properties*. Notice
7732 // that the actual methods are pointed from here.
7734 // This is a group of properties, piles of them.
7736 ArrayList AllGetters = null;
7738 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
7739 if (ilist.Properties != null) {
7740 AllGetters = new ArrayList(ilist.Properties.Count);
7741 foreach (Indexers.Indexer ix in ilist.Properties) {
7742 if (ix.Getter != null)
7743 AllGetters.Add (ix.Getter);
7747 if (AllGetters == null) {
7748 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7749 TypeManager.CSharpName (indexer_type));
7753 if (AllGetters.Count == 0) {
7754 // FIXME: we cannot simply select first one as the error message is missleading when
7755 // multiple indexers exist
7756 Indexers.Indexer first_indexer = (Indexers.Indexer)ilist.Properties[ilist.Properties.Count - 1];
7757 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
7758 TypeManager.GetFullNameSignature (first_indexer.PropertyInfo));
7762 get = (MethodInfo)new MethodGroupExpr (AllGetters, loc).OverloadResolve (ec,
7763 arguments, false, loc);
7766 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7771 // Only base will allow this invocation to happen.
7773 if (get.IsAbstract && this is BaseIndexerAccess){
7774 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
7778 type = get.ReturnType;
7779 if (type.IsPointer && !ec.InUnsafe){
7784 instance_expr.CheckMarshalByRefAccess ();
7786 eclass = ExprClass.IndexerAccess;
7790 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7792 if (right_side == EmptyExpression.OutAccess) {
7793 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
7794 GetSignatureForError ());
7798 // if the indexer returns a value type, and we try to set a field in it
7799 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
7800 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
7801 GetSignatureForError ());
7805 ArrayList AllSetters = new ArrayList();
7806 if (!CommonResolve (ec))
7809 bool found_any = false, found_any_setters = false;
7811 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
7812 if (ilist.Properties != null) {
7814 foreach (Indexers.Indexer ix in ilist.Properties) {
7815 if (ix.Setter != null)
7816 AllSetters.Add (ix.Setter);
7819 if (AllSetters.Count > 0) {
7820 found_any_setters = true;
7821 set_arguments = (ArrayList) arguments.Clone ();
7822 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
7823 set = (MethodInfo)(new MethodGroupExpr (AllSetters, loc)).OverloadResolve (
7825 set_arguments, false, loc);
7829 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7830 TypeManager.CSharpName (indexer_type));
7834 if (!found_any_setters) {
7835 Error (154, "indexer can not be used in this context, because " +
7836 "it lacks a `set' accessor");
7841 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7846 // Only base will allow this invocation to happen.
7848 if (set.IsAbstract && this is BaseIndexerAccess){
7849 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
7854 // Now look for the actual match in the list of indexers to set our "return" type
7856 type = TypeManager.void_type; // default value
7857 foreach (Indexers.Indexer ix in ilist.Properties){
7858 if (ix.Setter == set){
7859 type = ix.PropertyInfo.PropertyType;
7864 instance_expr.CheckMarshalByRefAccess ();
7866 eclass = ExprClass.IndexerAccess;
7870 bool prepared = false;
7871 LocalTemporary temp;
7873 public void Emit (EmitContext ec, bool leave_copy)
7875 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
7877 ec.ig.Emit (OpCodes.Dup);
7878 temp = new LocalTemporary (Type);
7884 // source is ignored, because we already have a copy of it from the
7885 // LValue resolution and we have already constructed a pre-cached
7886 // version of the arguments (ea.set_arguments);
7888 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7890 prepared = prepare_for_load;
7891 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
7896 ec.ig.Emit (OpCodes.Dup);
7897 temp = new LocalTemporary (Type);
7900 } else if (leave_copy) {
7901 temp = new LocalTemporary (Type);
7907 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
7916 public override void Emit (EmitContext ec)
7921 public override string GetSignatureForError ()
7923 // FIXME: print the argument list of the indexer
7924 return instance_expr.GetSignatureForError () + ".this[...]";
7927 protected override void CloneTo (CloneContext clonectx, Expression t)
7929 IndexerAccess target = (IndexerAccess) t;
7931 if (arguments != null){
7932 target.arguments = new ArrayList ();
7933 foreach (Argument a in arguments)
7934 target.arguments.Add (a.Clone (clonectx));
7936 if (instance_expr != null)
7937 target.instance_expr = instance_expr.Clone (clonectx);
7942 /// The base operator for method names
7944 public class BaseAccess : Expression {
7945 public readonly string Identifier;
7948 public BaseAccess (string member, Location l)
7950 this.Identifier = member;
7954 public BaseAccess (string member, TypeArguments args, Location l)
7960 public override Expression DoResolve (EmitContext ec)
7962 Expression c = CommonResolve (ec);
7968 // MethodGroups use this opportunity to flag an error on lacking ()
7970 if (!(c is MethodGroupExpr))
7971 return c.Resolve (ec);
7975 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7977 Expression c = CommonResolve (ec);
7983 // MethodGroups use this opportunity to flag an error on lacking ()
7985 if (! (c is MethodGroupExpr))
7986 return c.DoResolveLValue (ec, right_side);
7991 Expression CommonResolve (EmitContext ec)
7993 Expression member_lookup;
7994 Type current_type = ec.ContainerType;
7995 Type base_type = current_type.BaseType;
7998 Error (1511, "Keyword `base' is not available in a static method");
8002 if (ec.IsFieldInitializer){
8003 Error (1512, "Keyword `base' is not available in the current context");
8007 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8008 AllMemberTypes, AllBindingFlags, loc);
8009 if (member_lookup == null) {
8010 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8017 left = new TypeExpression (base_type, loc);
8019 left = ec.GetThis (loc);
8021 MemberExpr me = (MemberExpr) member_lookup;
8023 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8025 if (e is PropertyExpr) {
8026 PropertyExpr pe = (PropertyExpr) e;
8031 MethodGroupExpr mg = e as MethodGroupExpr;
8037 return mg.ResolveGeneric (ec, args);
8039 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8047 public override void Emit (EmitContext ec)
8049 throw new Exception ("Should never be called");
8052 protected override void CloneTo (CloneContext clonectx, Expression t)
8054 BaseAccess target = (BaseAccess) t;
8056 target.args = args.Clone ();
8061 /// The base indexer operator
8063 public class BaseIndexerAccess : IndexerAccess {
8064 public BaseIndexerAccess (ArrayList args, Location loc)
8065 : base (null, true, loc)
8067 arguments = new ArrayList ();
8068 foreach (Expression tmp in args)
8069 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8072 protected override bool CommonResolve (EmitContext ec)
8074 instance_expr = ec.GetThis (loc);
8076 current_type = ec.ContainerType.BaseType;
8077 indexer_type = current_type;
8079 foreach (Argument a in arguments){
8080 if (!a.Resolve (ec, loc))
8089 /// This class exists solely to pass the Type around and to be a dummy
8090 /// that can be passed to the conversion functions (this is used by
8091 /// foreach implementation to typecast the object return value from
8092 /// get_Current into the proper type. All code has been generated and
8093 /// we only care about the side effect conversions to be performed
8095 /// This is also now used as a placeholder where a no-action expression
8096 /// is needed (the `New' class).
8098 public class EmptyExpression : Expression {
8099 public static readonly EmptyExpression Null = new EmptyExpression ();
8101 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8102 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8103 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8105 static EmptyExpression temp = new EmptyExpression ();
8106 public static EmptyExpression Grab ()
8108 EmptyExpression retval = temp == null ? new EmptyExpression () : temp;
8113 public static void Release (EmptyExpression e)
8118 // TODO: should be protected
8119 public EmptyExpression ()
8121 type = TypeManager.object_type;
8122 eclass = ExprClass.Value;
8123 loc = Location.Null;
8126 public EmptyExpression (Type t)
8129 eclass = ExprClass.Value;
8130 loc = Location.Null;
8133 public override Expression DoResolve (EmitContext ec)
8138 public override void Emit (EmitContext ec)
8140 // nothing, as we only exist to not do anything.
8144 // This is just because we might want to reuse this bad boy
8145 // instead of creating gazillions of EmptyExpressions.
8146 // (CanImplicitConversion uses it)
8148 public void SetType (Type t)
8154 public class UserCast : Expression {
8158 public UserCast (MethodInfo method, Expression source, Location l)
8160 this.method = method;
8161 this.source = source;
8162 type = method.ReturnType;
8163 eclass = ExprClass.Value;
8167 public Expression Source {
8173 public override Expression DoResolve (EmitContext ec)
8176 // We are born fully resolved
8181 public override void Emit (EmitContext ec)
8183 ILGenerator ig = ec.ig;
8187 if (method is MethodInfo)
8188 ig.Emit (OpCodes.Call, (MethodInfo) method);
8190 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8196 // This class is used to "construct" the type during a typecast
8197 // operation. Since the Type.GetType class in .NET can parse
8198 // the type specification, we just use this to construct the type
8199 // one bit at a time.
8201 public class ComposedCast : TypeExpr {
8205 public ComposedCast (Expression left, string dim)
8206 : this (left, dim, left.Location)
8210 public ComposedCast (Expression left, string dim, Location l)
8218 public Expression RemoveNullable ()
8220 if (dim.EndsWith ("?")) {
8221 dim = dim.Substring (0, dim.Length - 1);
8230 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8232 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8236 Type ltype = lexpr.Type;
8237 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8238 Error_VoidInvalidInTheContext (loc);
8243 if ((dim.Length > 0) && (dim [0] == '?')) {
8244 TypeExpr nullable = new NullableType (left, loc);
8246 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8247 return nullable.ResolveAsTypeTerminal (ec, false);
8251 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc))
8254 if (dim != "" && dim [0] == '[' &&
8255 (ltype == TypeManager.arg_iterator_type || ltype == TypeManager.typed_reference_type)) {
8256 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (ltype));
8261 type = TypeManager.GetConstructedType (ltype, dim);
8266 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8268 if (type.IsPointer && !ec.IsInUnsafeScope){
8273 eclass = ExprClass.Type;
8277 public override string Name {
8278 get { return left + dim; }
8281 public override string FullName {
8282 get { return type.FullName; }
8285 public override string GetSignatureForError ()
8287 return left.GetSignatureForError () + dim;
8290 protected override void CloneTo (CloneContext clonectx, Expression t)
8292 ComposedCast target = (ComposedCast) t;
8294 target.left = left.Clone (clonectx);
8298 public class FixedBufferPtr : Expression {
8301 public FixedBufferPtr (Expression array, Type array_type, Location l)
8306 type = TypeManager.GetPointerType (array_type);
8307 eclass = ExprClass.Value;
8310 public override void Emit(EmitContext ec)
8315 public override Expression DoResolve (EmitContext ec)
8318 // We are born fully resolved
8326 // This class is used to represent the address of an array, used
8327 // only by the Fixed statement, this generates "&a [0]" construct
8328 // for fixed (char *pa = a)
8330 public class ArrayPtr : FixedBufferPtr {
8333 public ArrayPtr (Expression array, Type array_type, Location l):
8334 base (array, array_type, l)
8336 this.array_type = array_type;
8339 public override void Emit (EmitContext ec)
8343 ILGenerator ig = ec.ig;
8344 IntLiteral.EmitInt (ig, 0);
8345 ig.Emit (OpCodes.Ldelema, array_type);
8350 // Used by the fixed statement
8352 public class StringPtr : Expression {
8355 public StringPtr (LocalBuilder b, Location l)
8358 eclass = ExprClass.Value;
8359 type = TypeManager.char_ptr_type;
8363 public override Expression DoResolve (EmitContext ec)
8365 // This should never be invoked, we are born in fully
8366 // initialized state.
8371 public override void Emit (EmitContext ec)
8373 ILGenerator ig = ec.ig;
8375 ig.Emit (OpCodes.Ldloc, b);
8376 ig.Emit (OpCodes.Conv_I);
8377 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8378 ig.Emit (OpCodes.Add);
8383 // Implements the `stackalloc' keyword
8385 public class StackAlloc : Expression {
8390 public StackAlloc (Expression type, Expression count, Location l)
8397 public override Expression DoResolve (EmitContext ec)
8399 count = count.Resolve (ec);
8403 if (count.Type != TypeManager.int32_type){
8404 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8409 Constant c = count as Constant;
8410 if (c != null && c.IsNegative) {
8411 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8415 if (ec.InCatch || ec.InFinally) {
8416 Error (255, "Cannot use stackalloc in finally or catch");
8420 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8426 if (!TypeManager.VerifyUnManaged (otype, loc))
8429 type = TypeManager.GetPointerType (otype);
8430 eclass = ExprClass.Value;
8435 public override void Emit (EmitContext ec)
8437 int size = GetTypeSize (otype);
8438 ILGenerator ig = ec.ig;
8441 ig.Emit (OpCodes.Sizeof, otype);
8443 IntConstant.EmitInt (ig, size);
8445 ig.Emit (OpCodes.Mul);
8446 ig.Emit (OpCodes.Localloc);
8449 protected override void CloneTo (CloneContext clonectx, Expression t)
8451 StackAlloc target = (StackAlloc) t;
8452 target.count = count.Clone (clonectx);
8453 target.t = t.Clone (clonectx);