2009-02-20 Zoltan Varga <vargaz@gmail.com>
[mcs.git] / mcs / expression.cs
blob65e978195c92bd6a9177f48459d86727a46fa342
1 //
2 // expression.cs: Expression representation for the IL tree.
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
7 //
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2003-2008 Novell, Inc.
11 #define USE_OLD
13 namespace Mono.CSharp {
14 using System;
15 using System.Collections;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Text;
21 // This is an user operator expression, automatically created during
22 // resolve phase
24 public class UserOperatorCall : Expression {
25 public delegate Expression ExpressionTreeExpression (EmitContext ec, MethodGroupExpr mg);
27 protected readonly ArrayList arguments;
28 protected readonly MethodGroupExpr mg;
29 readonly ExpressionTreeExpression expr_tree;
31 public UserOperatorCall (MethodGroupExpr mg, ArrayList args, ExpressionTreeExpression expr_tree, Location loc)
33 this.mg = mg;
34 this.arguments = args;
35 this.expr_tree = expr_tree;
37 type = TypeManager.TypeToCoreType (((MethodInfo) mg).ReturnType);
38 eclass = ExprClass.Value;
39 this.loc = loc;
42 public override Expression CreateExpressionTree (EmitContext ec)
44 if (expr_tree != null)
45 return expr_tree (ec, mg);
47 ArrayList args = new ArrayList (arguments.Count + 1);
48 args.Add (new Argument (new NullLiteral (loc)));
49 args.Add (new Argument (mg.CreateExpressionTree (ec)));
50 foreach (Argument a in arguments) {
51 args.Add (new Argument (a.Expr.CreateExpressionTree (ec)));
54 return CreateExpressionFactoryCall ("Call", args);
57 protected override void CloneTo (CloneContext context, Expression target)
59 // Nothing to clone
62 public override Expression DoResolve (EmitContext ec)
65 // We are born fully resolved
67 return this;
70 public override void Emit (EmitContext ec)
72 mg.EmitCall (ec, arguments);
75 public MethodGroupExpr Method {
76 get { return mg; }
79 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
81 foreach (Argument a in arguments)
82 a.Expr.MutateHoistedGenericType (storey);
84 mg.MutateHoistedGenericType (storey);
88 public class ParenthesizedExpression : Expression
90 public Expression Expr;
92 public ParenthesizedExpression (Expression expr)
94 Expr = expr;
95 loc = expr.Location;
98 public override Expression CreateExpressionTree (EmitContext ec)
100 throw new NotSupportedException ("ET");
103 public override Expression DoResolve (EmitContext ec)
105 Expr = Expr.Resolve (ec);
106 return Expr;
109 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
111 return Expr.DoResolveLValue (ec, right_side);
114 public override void Emit (EmitContext ec)
116 throw new Exception ("Should not happen");
119 protected override void CloneTo (CloneContext clonectx, Expression t)
121 ParenthesizedExpression target = (ParenthesizedExpression) t;
123 target.Expr = Expr.Clone (clonectx);
128 // Unary implements unary expressions.
130 public class Unary : Expression {
131 public enum Operator : byte {
132 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
133 AddressOf, TOP
136 static Type [] [] predefined_operators;
138 public readonly Operator Oper;
139 public Expression Expr;
140 Expression enum_conversion;
142 public Unary (Operator op, Expression expr)
144 Oper = op;
145 Expr = expr;
146 loc = expr.Location;
149 // <summary>
150 // This routine will attempt to simplify the unary expression when the
151 // argument is a constant.
152 // </summary>
153 Constant TryReduceConstant (EmitContext ec, Constant e)
155 if (e is EmptyConstantCast)
156 return TryReduceConstant (ec, ((EmptyConstantCast) e).child);
158 if (e is SideEffectConstant) {
159 Constant r = TryReduceConstant (ec, ((SideEffectConstant) e).value);
160 return r == null ? null : new SideEffectConstant (r, e, r.Location);
163 Type expr_type = e.Type;
165 switch (Oper){
166 case Operator.UnaryPlus:
167 // Unary numeric promotions
168 if (expr_type == TypeManager.byte_type)
169 return new IntConstant (((ByteConstant)e).Value, e.Location);
170 if (expr_type == TypeManager.sbyte_type)
171 return new IntConstant (((SByteConstant)e).Value, e.Location);
172 if (expr_type == TypeManager.short_type)
173 return new IntConstant (((ShortConstant)e).Value, e.Location);
174 if (expr_type == TypeManager.ushort_type)
175 return new IntConstant (((UShortConstant)e).Value, e.Location);
176 if (expr_type == TypeManager.char_type)
177 return new IntConstant (((CharConstant)e).Value, e.Location);
179 // Predefined operators
180 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
181 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
182 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
183 expr_type == TypeManager.decimal_type) {
184 return e;
187 return null;
189 case Operator.UnaryNegation:
190 // Unary numeric promotions
191 if (expr_type == TypeManager.byte_type)
192 return new IntConstant (-((ByteConstant)e).Value, e.Location);
193 if (expr_type == TypeManager.sbyte_type)
194 return new IntConstant (-((SByteConstant)e).Value, e.Location);
195 if (expr_type == TypeManager.short_type)
196 return new IntConstant (-((ShortConstant)e).Value, e.Location);
197 if (expr_type == TypeManager.ushort_type)
198 return new IntConstant (-((UShortConstant)e).Value, e.Location);
199 if (expr_type == TypeManager.char_type)
200 return new IntConstant (-((CharConstant)e).Value, e.Location);
202 // Predefined operators
203 if (expr_type == TypeManager.int32_type) {
204 int value = ((IntConstant)e).Value;
205 if (value == int.MinValue) {
206 if (ec.ConstantCheckState) {
207 ConstantFold.Error_CompileTimeOverflow (loc);
208 return null;
210 return e;
212 return new IntConstant (-value, e.Location);
214 if (expr_type == TypeManager.int64_type) {
215 long value = ((LongConstant)e).Value;
216 if (value == long.MinValue) {
217 if (ec.ConstantCheckState) {
218 ConstantFold.Error_CompileTimeOverflow (loc);
219 return null;
221 return e;
223 return new LongConstant (-value, e.Location);
226 if (expr_type == TypeManager.uint32_type) {
227 UIntLiteral uil = e as UIntLiteral;
228 if (uil != null) {
229 if (uil.Value == 2147483648)
230 return new IntLiteral (int.MinValue, e.Location);
231 return new LongLiteral (-uil.Value, e.Location);
233 return new LongConstant (-((UIntConstant)e).Value, e.Location);
236 if (expr_type == TypeManager.uint64_type) {
237 ULongLiteral ull = e as ULongLiteral;
238 if (ull != null && ull.Value == 9223372036854775808)
239 return new LongLiteral (long.MinValue, e.Location);
240 return null;
243 if (expr_type == TypeManager.float_type) {
244 FloatLiteral fl = e as FloatLiteral;
245 // For better error reporting
246 if (fl != null)
247 return new FloatLiteral (-fl.Value, e.Location);
249 return new FloatConstant (-((FloatConstant)e).Value, e.Location);
251 if (expr_type == TypeManager.double_type) {
252 DoubleLiteral dl = e as DoubleLiteral;
253 // For better error reporting
254 if (dl != null)
255 return new DoubleLiteral (-dl.Value, e.Location);
257 return new DoubleConstant (-((DoubleConstant)e).Value, e.Location);
259 if (expr_type == TypeManager.decimal_type)
260 return new DecimalConstant (-((DecimalConstant)e).Value, e.Location);
262 return null;
264 case Operator.LogicalNot:
265 if (expr_type != TypeManager.bool_type)
266 return null;
268 bool b = (bool)e.GetValue ();
269 return new BoolConstant (!b, e.Location);
271 case Operator.OnesComplement:
272 // Unary numeric promotions
273 if (expr_type == TypeManager.byte_type)
274 return new IntConstant (~((ByteConstant)e).Value, e.Location);
275 if (expr_type == TypeManager.sbyte_type)
276 return new IntConstant (~((SByteConstant)e).Value, e.Location);
277 if (expr_type == TypeManager.short_type)
278 return new IntConstant (~((ShortConstant)e).Value, e.Location);
279 if (expr_type == TypeManager.ushort_type)
280 return new IntConstant (~((UShortConstant)e).Value, e.Location);
281 if (expr_type == TypeManager.char_type)
282 return new IntConstant (~((CharConstant)e).Value, e.Location);
284 // Predefined operators
285 if (expr_type == TypeManager.int32_type)
286 return new IntConstant (~((IntConstant)e).Value, e.Location);
287 if (expr_type == TypeManager.uint32_type)
288 return new UIntConstant (~((UIntConstant)e).Value, e.Location);
289 if (expr_type == TypeManager.int64_type)
290 return new LongConstant (~((LongConstant)e).Value, e.Location);
291 if (expr_type == TypeManager.uint64_type){
292 return new ULongConstant (~((ULongConstant)e).Value, e.Location);
294 if (e is EnumConstant) {
295 e = TryReduceConstant (ec, ((EnumConstant)e).Child);
296 if (e != null)
297 e = new EnumConstant (e, expr_type);
298 return e;
300 return null;
302 throw new Exception ("Can not constant fold: " + Oper.ToString());
305 protected Expression ResolveOperator (EmitContext ec, Expression expr)
307 eclass = ExprClass.Value;
309 if (predefined_operators == null)
310 CreatePredefinedOperatorsTable ();
312 Type expr_type = expr.Type;
313 Expression best_expr;
316 // Primitive types first
318 if (TypeManager.IsPrimitiveType (expr_type)) {
319 best_expr = ResolvePrimitivePredefinedType (expr);
320 if (best_expr == null)
321 return null;
323 type = best_expr.Type;
324 Expr = best_expr;
325 return this;
329 // E operator ~(E x);
331 if (Oper == Operator.OnesComplement && TypeManager.IsEnumType (expr_type))
332 return ResolveEnumOperator (ec, expr);
334 return ResolveUserType (ec, expr);
337 protected virtual Expression ResolveEnumOperator (EmitContext ec, Expression expr)
339 Type underlying_type = TypeManager.GetEnumUnderlyingType (expr.Type);
340 Expression best_expr = ResolvePrimitivePredefinedType (EmptyCast.Create (expr, underlying_type));
341 if (best_expr == null)
342 return null;
344 Expr = best_expr;
345 enum_conversion = Convert.ExplicitNumericConversion (new EmptyExpression (best_expr.Type), underlying_type);
346 type = expr.Type;
347 return EmptyCast.Create (this, type);
350 public override Expression CreateExpressionTree (EmitContext ec)
352 return CreateExpressionTree (ec, null);
355 Expression CreateExpressionTree (EmitContext ec, MethodGroupExpr user_op)
357 string method_name;
358 switch (Oper) {
359 case Operator.AddressOf:
360 Error_PointerInsideExpressionTree ();
361 return null;
362 case Operator.UnaryNegation:
363 if (ec.CheckState && user_op == null && !IsFloat (type))
364 method_name = "NegateChecked";
365 else
366 method_name = "Negate";
367 break;
368 case Operator.OnesComplement:
369 case Operator.LogicalNot:
370 method_name = "Not";
371 break;
372 case Operator.UnaryPlus:
373 method_name = "UnaryPlus";
374 break;
375 default:
376 throw new InternalErrorException ("Unknown unary operator " + Oper.ToString ());
379 ArrayList args = new ArrayList (2);
380 args.Add (new Argument (Expr.CreateExpressionTree (ec)));
381 if (user_op != null)
382 args.Add (new Argument (user_op.CreateExpressionTree (ec)));
383 return CreateExpressionFactoryCall (method_name, args);
386 static void CreatePredefinedOperatorsTable ()
388 predefined_operators = new Type [(int) Operator.TOP] [];
391 // 7.6.1 Unary plus operator
393 predefined_operators [(int) Operator.UnaryPlus] = new Type [] {
394 TypeManager.int32_type, TypeManager.uint32_type,
395 TypeManager.int64_type, TypeManager.uint64_type,
396 TypeManager.float_type, TypeManager.double_type,
397 TypeManager.decimal_type
401 // 7.6.2 Unary minus operator
403 predefined_operators [(int) Operator.UnaryNegation] = new Type [] {
404 TypeManager.int32_type,
405 TypeManager.int64_type,
406 TypeManager.float_type, TypeManager.double_type,
407 TypeManager.decimal_type
411 // 7.6.3 Logical negation operator
413 predefined_operators [(int) Operator.LogicalNot] = new Type [] {
414 TypeManager.bool_type
418 // 7.6.4 Bitwise complement operator
420 predefined_operators [(int) Operator.OnesComplement] = new Type [] {
421 TypeManager.int32_type, TypeManager.uint32_type,
422 TypeManager.int64_type, TypeManager.uint64_type
427 // Unary numeric promotions
429 static Expression DoNumericPromotion (Operator op, Expression expr)
431 Type expr_type = expr.Type;
432 if ((op == Operator.UnaryPlus || op == Operator.UnaryNegation || op == Operator.OnesComplement) &&
433 expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
434 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
435 expr_type == TypeManager.char_type)
436 return Convert.ImplicitNumericConversion (expr, TypeManager.int32_type);
438 if (op == Operator.UnaryNegation && expr_type == TypeManager.uint32_type)
439 return Convert.ImplicitNumericConversion (expr, TypeManager.int64_type);
441 return expr;
444 public override Expression DoResolve (EmitContext ec)
446 if (Oper == Operator.AddressOf) {
447 return ResolveAddressOf (ec);
450 Expr = Expr.Resolve (ec);
451 if (Expr == null)
452 return null;
454 if (TypeManager.IsNullableValueType (Expr.Type))
455 return new Nullable.LiftedUnaryOperator (Oper, Expr).Resolve (ec);
458 // Attempt to use a constant folding operation.
460 Constant cexpr = Expr as Constant;
461 if (cexpr != null) {
462 cexpr = TryReduceConstant (ec, cexpr);
463 if (cexpr != null)
464 return cexpr;
467 Expression expr = ResolveOperator (ec, Expr);
468 if (expr == null)
469 Error_OperatorCannotBeApplied (loc, OperName (Oper), Expr.Type);
472 // Reduce unary operator on predefined types
474 if (expr == this && Oper == Operator.UnaryPlus)
475 return Expr;
477 return expr;
480 public override Expression DoResolveLValue (EmitContext ec, Expression right)
482 return null;
485 public override void Emit (EmitContext ec)
487 EmitOperator (ec, type);
490 protected void EmitOperator (EmitContext ec, Type type)
492 ILGenerator ig = ec.ig;
494 switch (Oper) {
495 case Operator.UnaryPlus:
496 Expr.Emit (ec);
497 break;
499 case Operator.UnaryNegation:
500 if (ec.CheckState && !IsFloat (type)) {
501 ig.Emit (OpCodes.Ldc_I4_0);
502 if (type == TypeManager.int64_type)
503 ig.Emit (OpCodes.Conv_U8);
504 Expr.Emit (ec);
505 ig.Emit (OpCodes.Sub_Ovf);
506 } else {
507 Expr.Emit (ec);
508 ig.Emit (OpCodes.Neg);
511 break;
513 case Operator.LogicalNot:
514 Expr.Emit (ec);
515 ig.Emit (OpCodes.Ldc_I4_0);
516 ig.Emit (OpCodes.Ceq);
517 break;
519 case Operator.OnesComplement:
520 Expr.Emit (ec);
521 ig.Emit (OpCodes.Not);
522 break;
524 case Operator.AddressOf:
525 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
526 break;
528 default:
529 throw new Exception ("This should not happen: Operator = "
530 + Oper.ToString ());
534 // Same trick as in Binary expression
536 if (enum_conversion != null)
537 enum_conversion.Emit (ec);
540 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
542 if (Oper == Operator.LogicalNot)
543 Expr.EmitBranchable (ec, target, !on_true);
544 else
545 base.EmitBranchable (ec, target, on_true);
548 public override void EmitSideEffect (EmitContext ec)
550 Expr.EmitSideEffect (ec);
553 public static void Error_OperatorCannotBeApplied (Location loc, string oper, Type t)
555 Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'",
556 oper, TypeManager.CSharpName (t));
559 static bool IsFloat (Type t)
561 return t == TypeManager.float_type || t == TypeManager.double_type;
565 // Returns a stringified representation of the Operator
567 public static string OperName (Operator oper)
569 switch (oper) {
570 case Operator.UnaryPlus:
571 return "+";
572 case Operator.UnaryNegation:
573 return "-";
574 case Operator.LogicalNot:
575 return "!";
576 case Operator.OnesComplement:
577 return "~";
578 case Operator.AddressOf:
579 return "&";
582 throw new NotImplementedException (oper.ToString ());
585 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
587 type = storey.MutateType (type);
588 Expr.MutateHoistedGenericType (storey);
591 Expression ResolveAddressOf (EmitContext ec)
593 if (!ec.InUnsafe)
594 UnsafeError (loc);
596 Expr = Expr.DoResolveLValue (ec, EmptyExpression.UnaryAddress);
597 if (Expr == null || Expr.eclass != ExprClass.Variable) {
598 Error (211, "Cannot take the address of the given expression");
599 return null;
602 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)) {
603 return null;
606 IVariableReference vr = Expr as IVariableReference;
607 bool is_fixed;
608 if (vr != null) {
609 VariableInfo vi = vr.VariableInfo;
610 if (vi != null) {
611 if (vi.LocalInfo != null)
612 vi.LocalInfo.Used = true;
615 // A variable is considered definitely assigned if you take its address.
617 vi.SetAssigned (ec);
620 is_fixed = vr.IsFixed;
621 vr.SetHasAddressTaken ();
623 if (vr.IsHoisted) {
624 AnonymousMethodExpression.Error_AddressOfCapturedVar (vr, loc);
626 } else {
627 IFixedExpression fe = Expr as IFixedExpression;
628 is_fixed = fe != null && fe.IsFixed;
631 if (!is_fixed && !ec.InFixedInitializer) {
632 Error (212, "You can only take the address of unfixed expression inside of a fixed statement initializer");
635 type = TypeManager.GetPointerType (Expr.Type);
636 eclass = ExprClass.Value;
637 return this;
640 Expression ResolvePrimitivePredefinedType (Expression expr)
642 expr = DoNumericPromotion (Oper, expr);
643 Type expr_type = expr.Type;
644 Type[] predefined = predefined_operators [(int) Oper];
645 foreach (Type t in predefined) {
646 if (t == expr_type)
647 return expr;
649 return null;
653 // Perform user-operator overload resolution
655 protected virtual Expression ResolveUserOperator (EmitContext ec, Expression expr)
657 CSharp.Operator.OpType op_type;
658 switch (Oper) {
659 case Operator.LogicalNot:
660 op_type = CSharp.Operator.OpType.LogicalNot; break;
661 case Operator.OnesComplement:
662 op_type = CSharp.Operator.OpType.OnesComplement; break;
663 case Operator.UnaryNegation:
664 op_type = CSharp.Operator.OpType.UnaryNegation; break;
665 case Operator.UnaryPlus:
666 op_type = CSharp.Operator.OpType.UnaryPlus; break;
667 default:
668 throw new InternalErrorException (Oper.ToString ());
671 string op_name = CSharp.Operator.GetMetadataName (op_type);
672 MethodGroupExpr user_op = MemberLookup (ec.ContainerType, expr.Type, op_name, MemberTypes.Method, AllBindingFlags, expr.Location) as MethodGroupExpr;
673 if (user_op == null)
674 return null;
676 ArrayList args = new ArrayList (1);
677 args.Add (new Argument (expr));
678 user_op = user_op.OverloadResolve (ec, ref args, false, expr.Location);
680 if (user_op == null)
681 return null;
683 Expr = ((Argument) args [0]).Expr;
684 return new UserOperatorCall (user_op, args, CreateExpressionTree, expr.Location);
688 // Unary user type overload resolution
690 Expression ResolveUserType (EmitContext ec, Expression expr)
692 Expression best_expr = ResolveUserOperator (ec, expr);
693 if (best_expr != null)
694 return best_expr;
696 Type[] predefined = predefined_operators [(int) Oper];
697 foreach (Type t in predefined) {
698 Expression oper_expr = Convert.UserDefinedConversion (ec, expr, t, expr.Location, false);
699 if (oper_expr == null)
700 continue;
703 // decimal type is predefined but has user-operators
705 if (oper_expr.Type == TypeManager.decimal_type)
706 oper_expr = ResolveUserType (ec, oper_expr);
707 else
708 oper_expr = ResolvePrimitivePredefinedType (oper_expr);
710 if (oper_expr == null)
711 continue;
713 if (best_expr == null) {
714 best_expr = oper_expr;
715 continue;
718 int result = MethodGroupExpr.BetterTypeConversion (ec, best_expr.Type, t);
719 if (result == 0) {
720 Report.Error (35, loc, "Operator `{0}' is ambiguous on an operand of type `{1}'",
721 OperName (Oper), TypeManager.CSharpName (expr.Type));
722 break;
725 if (result == 2)
726 best_expr = oper_expr;
729 if (best_expr == null)
730 return null;
733 // HACK: Decimal user-operator is included in standard operators
735 if (best_expr.Type == TypeManager.decimal_type)
736 return best_expr;
738 Expr = best_expr;
739 type = best_expr.Type;
740 return this;
743 protected override void CloneTo (CloneContext clonectx, Expression t)
745 Unary target = (Unary) t;
747 target.Expr = Expr.Clone (clonectx);
752 // Unary operators are turned into Indirection expressions
753 // after semantic analysis (this is so we can take the address
754 // of an indirection).
756 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IFixedExpression {
757 Expression expr;
758 LocalTemporary temporary;
759 bool prepared;
761 public Indirection (Expression expr, Location l)
763 this.expr = expr;
764 loc = l;
767 public override Expression CreateExpressionTree (EmitContext ec)
769 Error_PointerInsideExpressionTree ();
770 return null;
773 protected override void CloneTo (CloneContext clonectx, Expression t)
775 Indirection target = (Indirection) t;
776 target.expr = expr.Clone (clonectx);
779 public override void Emit (EmitContext ec)
781 if (!prepared)
782 expr.Emit (ec);
784 LoadFromPtr (ec.ig, Type);
787 public void Emit (EmitContext ec, bool leave_copy)
789 Emit (ec);
790 if (leave_copy) {
791 ec.ig.Emit (OpCodes.Dup);
792 temporary = new LocalTemporary (expr.Type);
793 temporary.Store (ec);
797 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
799 prepared = prepare_for_load;
801 expr.Emit (ec);
803 if (prepare_for_load)
804 ec.ig.Emit (OpCodes.Dup);
806 source.Emit (ec);
807 if (leave_copy) {
808 ec.ig.Emit (OpCodes.Dup);
809 temporary = new LocalTemporary (expr.Type);
810 temporary.Store (ec);
813 StoreFromPtr (ec.ig, type);
815 if (temporary != null) {
816 temporary.Emit (ec);
817 temporary.Release (ec);
821 public void AddressOf (EmitContext ec, AddressOp Mode)
823 expr.Emit (ec);
826 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
828 return DoResolve (ec);
831 public override Expression DoResolve (EmitContext ec)
833 expr = expr.Resolve (ec);
834 if (expr == null)
835 return null;
837 if (!ec.InUnsafe)
838 UnsafeError (loc);
840 if (!expr.Type.IsPointer) {
841 Error (193, "The * or -> operator must be applied to a pointer");
842 return null;
845 if (expr.Type == TypeManager.void_ptr_type) {
846 Error (242, "The operation in question is undefined on void pointers");
847 return null;
850 type = TypeManager.GetElementType (expr.Type);
851 eclass = ExprClass.Variable;
852 return this;
855 public bool IsFixed {
856 get { return true; }
859 public override string ToString ()
861 return "*(" + expr + ")";
865 /// <summary>
866 /// Unary Mutator expressions (pre and post ++ and --)
867 /// </summary>
869 /// <remarks>
870 /// UnaryMutator implements ++ and -- expressions. It derives from
871 /// ExpressionStatement becuase the pre/post increment/decrement
872 /// operators can be used in a statement context.
874 /// FIXME: Idea, we could split this up in two classes, one simpler
875 /// for the common case, and one with the extra fields for more complex
876 /// classes (indexers require temporary access; overloaded require method)
878 /// </remarks>
879 public class UnaryMutator : ExpressionStatement {
880 [Flags]
881 public enum Mode : byte {
882 IsIncrement = 0,
883 IsDecrement = 1,
884 IsPre = 0,
885 IsPost = 2,
887 PreIncrement = 0,
888 PreDecrement = IsDecrement,
889 PostIncrement = IsPost,
890 PostDecrement = IsPost | IsDecrement
893 Mode mode;
894 bool is_expr = false;
895 bool recurse = false;
897 Expression expr;
900 // This is expensive for the simplest case.
902 UserOperatorCall method;
904 public UnaryMutator (Mode m, Expression e)
906 mode = m;
907 loc = e.Location;
908 expr = e;
911 static string OperName (Mode mode)
913 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
914 "++" : "--";
917 /// <summary>
918 /// Returns whether an object of type `t' can be incremented
919 /// or decremented with add/sub (ie, basically whether we can
920 /// use pre-post incr-decr operations on it, but it is not a
921 /// System.Decimal, which we require operator overloading to catch)
922 /// </summary>
923 static bool IsIncrementableNumber (Type t)
925 return (t == TypeManager.sbyte_type) ||
926 (t == TypeManager.byte_type) ||
927 (t == TypeManager.short_type) ||
928 (t == TypeManager.ushort_type) ||
929 (t == TypeManager.int32_type) ||
930 (t == TypeManager.uint32_type) ||
931 (t == TypeManager.int64_type) ||
932 (t == TypeManager.uint64_type) ||
933 (t == TypeManager.char_type) ||
934 (TypeManager.IsSubclassOf (t, TypeManager.enum_type)) ||
935 (t == TypeManager.float_type) ||
936 (t == TypeManager.double_type) ||
937 (t.IsPointer && t != TypeManager.void_ptr_type);
940 Expression ResolveOperator (EmitContext ec)
942 type = expr.Type;
945 // The operand of the prefix/postfix increment decrement operators
946 // should be an expression that is classified as a variable,
947 // a property access or an indexer access
949 if (expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess) {
950 expr = expr.ResolveLValue (ec, expr, Location);
951 } else {
952 Report.Error (1059, loc, "The operand of an increment or decrement operator must be a variable, property or indexer");
956 // Step 1: Perform Operator Overload location
958 MethodGroupExpr mg;
959 string op_name;
961 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
962 op_name = Operator.GetMetadataName (Operator.OpType.Increment);
963 else
964 op_name = Operator.GetMetadataName (Operator.OpType.Decrement);
966 mg = MemberLookup (ec.ContainerType, type, op_name, MemberTypes.Method, AllBindingFlags, loc) as MethodGroupExpr;
968 if (mg != null) {
969 ArrayList args = new ArrayList (1);
970 args.Add (new Argument (expr, Argument.AType.Expression));
971 mg = mg.OverloadResolve (ec, ref args, false, loc);
972 if (mg == null)
973 return null;
975 method = new UserOperatorCall (mg, args, null, loc);
976 Convert.ImplicitConversionRequired (ec, method, type, loc);
977 return this;
980 if (!IsIncrementableNumber (type)) {
981 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
982 TypeManager.CSharpName (type) + "'");
983 return null;
986 return this;
989 public override Expression CreateExpressionTree (EmitContext ec)
991 return new SimpleAssign (this, this).CreateExpressionTree (ec);
994 public override Expression DoResolve (EmitContext ec)
996 expr = expr.Resolve (ec);
998 if (expr == null)
999 return null;
1001 eclass = ExprClass.Value;
1003 #if GMCS_SOURCE
1004 if (TypeManager.IsNullableValueType (expr.Type))
1005 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
1006 #endif
1008 return ResolveOperator (ec);
1012 // Loads the proper "1" into the stack based on the type, then it emits the
1013 // opcode for the operation requested
1015 void LoadOneAndEmitOp (EmitContext ec, Type t)
1018 // Measure if getting the typecode and using that is more/less efficient
1019 // that comparing types. t.GetTypeCode() is an internal call.
1021 ILGenerator ig = ec.ig;
1023 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
1024 LongConstant.EmitLong (ig, 1);
1025 else if (t == TypeManager.double_type)
1026 ig.Emit (OpCodes.Ldc_R8, 1.0);
1027 else if (t == TypeManager.float_type)
1028 ig.Emit (OpCodes.Ldc_R4, 1.0F);
1029 else if (t.IsPointer){
1030 Type et = TypeManager.GetElementType (t);
1031 int n = GetTypeSize (et);
1033 if (n == 0)
1034 ig.Emit (OpCodes.Sizeof, et);
1035 else {
1036 IntConstant.EmitInt (ig, n);
1037 ig.Emit (OpCodes.Conv_I);
1039 } else
1040 ig.Emit (OpCodes.Ldc_I4_1);
1043 // Now emit the operation
1046 Binary.Operator op = (mode & Mode.IsDecrement) != 0 ? Binary.Operator.Subtraction : Binary.Operator.Addition;
1047 Binary.EmitOperatorOpcode (ec, op, t);
1049 if (t == TypeManager.sbyte_type){
1050 if (ec.CheckState)
1051 ig.Emit (OpCodes.Conv_Ovf_I1);
1052 else
1053 ig.Emit (OpCodes.Conv_I1);
1054 } else if (t == TypeManager.byte_type){
1055 if (ec.CheckState)
1056 ig.Emit (OpCodes.Conv_Ovf_U1);
1057 else
1058 ig.Emit (OpCodes.Conv_U1);
1059 } else if (t == TypeManager.short_type){
1060 if (ec.CheckState)
1061 ig.Emit (OpCodes.Conv_Ovf_I2);
1062 else
1063 ig.Emit (OpCodes.Conv_I2);
1064 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
1065 if (ec.CheckState)
1066 ig.Emit (OpCodes.Conv_Ovf_U2);
1067 else
1068 ig.Emit (OpCodes.Conv_U2);
1073 void EmitCode (EmitContext ec, bool is_expr)
1075 recurse = true;
1076 this.is_expr = is_expr;
1077 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1080 public override void Emit (EmitContext ec)
1083 // We use recurse to allow ourselfs to be the source
1084 // of an assignment. This little hack prevents us from
1085 // having to allocate another expression
1087 if (recurse) {
1088 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1089 if (method == null)
1090 LoadOneAndEmitOp (ec, expr.Type);
1091 else
1092 ec.ig.Emit (OpCodes.Call, (MethodInfo)method.Method);
1093 recurse = false;
1094 return;
1097 EmitCode (ec, true);
1100 public override void EmitStatement (EmitContext ec)
1102 EmitCode (ec, false);
1105 protected override void CloneTo (CloneContext clonectx, Expression t)
1107 UnaryMutator target = (UnaryMutator) t;
1109 target.expr = expr.Clone (clonectx);
1113 /// <summary>
1114 /// Base class for the `Is' and `As' classes.
1115 /// </summary>
1117 /// <remarks>
1118 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1119 /// size.
1120 /// </remarks>
1121 public abstract class Probe : Expression {
1122 public Expression ProbeType;
1123 protected Expression expr;
1124 protected TypeExpr probe_type_expr;
1126 public Probe (Expression expr, Expression probe_type, Location l)
1128 ProbeType = probe_type;
1129 loc = l;
1130 this.expr = expr;
1133 public Expression Expr {
1134 get {
1135 return expr;
1139 public override Expression DoResolve (EmitContext ec)
1141 probe_type_expr = ProbeType.ResolveAsTypeTerminal (ec, false);
1142 if (probe_type_expr == null)
1143 return null;
1145 expr = expr.Resolve (ec);
1146 if (expr == null)
1147 return null;
1149 if ((probe_type_expr.Type.Attributes & Class.StaticClassAttribute) == Class.StaticClassAttribute) {
1150 Report.Error (-244, loc, "The `{0}' operator cannot be applied to an operand of a static type",
1151 OperatorName);
1154 if (expr.Type.IsPointer || probe_type_expr.Type.IsPointer) {
1155 Report.Error (244, loc, "The `{0}' operator cannot be applied to an operand of pointer type",
1156 OperatorName);
1157 return null;
1160 if (expr.Type == TypeManager.anonymous_method_type) {
1161 Report.Error (837, loc, "The `{0}' operator cannot be applied to a lambda expression or anonymous method",
1162 OperatorName);
1163 return null;
1166 return this;
1169 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1171 expr.MutateHoistedGenericType (storey);
1172 probe_type_expr.MutateHoistedGenericType (storey);
1175 protected abstract string OperatorName { get; }
1177 protected override void CloneTo (CloneContext clonectx, Expression t)
1179 Probe target = (Probe) t;
1181 target.expr = expr.Clone (clonectx);
1182 target.ProbeType = ProbeType.Clone (clonectx);
1187 /// <summary>
1188 /// Implementation of the `is' operator.
1189 /// </summary>
1190 public class Is : Probe {
1191 Nullable.Unwrap expr_unwrap;
1193 public Is (Expression expr, Expression probe_type, Location l)
1194 : base (expr, probe_type, l)
1198 public override Expression CreateExpressionTree (EmitContext ec)
1200 ArrayList args = new ArrayList (2);
1201 args.Add (new Argument (expr.CreateExpressionTree (ec)));
1202 args.Add (new Argument (new TypeOf (probe_type_expr, loc)));
1203 return CreateExpressionFactoryCall ("TypeIs", args);
1206 public override void Emit (EmitContext ec)
1208 ILGenerator ig = ec.ig;
1209 if (expr_unwrap != null) {
1210 expr_unwrap.EmitCheck (ec);
1211 return;
1214 expr.Emit (ec);
1215 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1216 ig.Emit (OpCodes.Ldnull);
1217 ig.Emit (OpCodes.Cgt_Un);
1220 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
1222 ILGenerator ig = ec.ig;
1223 if (expr_unwrap != null) {
1224 expr_unwrap.EmitCheck (ec);
1225 } else {
1226 expr.Emit (ec);
1227 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1229 ig.Emit (on_true ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1232 Expression CreateConstantResult (bool result)
1234 if (result)
1235 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
1236 TypeManager.CSharpName (probe_type_expr.Type));
1237 else
1238 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
1239 TypeManager.CSharpName (probe_type_expr.Type));
1241 return ReducedExpression.Create (new BoolConstant (result, loc), this);
1244 public override Expression DoResolve (EmitContext ec)
1246 if (base.DoResolve (ec) == null)
1247 return null;
1249 Type d = expr.Type;
1250 bool d_is_nullable = false;
1253 // If E is a method group or the null literal, or if the type of E is a reference
1254 // type or a nullable type and the value of E is null, the result is false
1256 if (expr.IsNull || expr.eclass == ExprClass.MethodGroup)
1257 return CreateConstantResult (false);
1259 if (TypeManager.IsNullableType (d) && !TypeManager.ContainsGenericParameters (d)) {
1260 d = TypeManager.GetTypeArguments (d) [0];
1261 d_is_nullable = true;
1264 type = TypeManager.bool_type;
1265 eclass = ExprClass.Value;
1266 Type t = probe_type_expr.Type;
1267 bool t_is_nullable = false;
1268 if (TypeManager.IsNullableType (t) && !TypeManager.ContainsGenericParameters (t)) {
1269 t = TypeManager.GetTypeArguments (t) [0];
1270 t_is_nullable = true;
1273 if (TypeManager.IsStruct (t)) {
1274 if (d == t) {
1276 // D and T are the same value types but D can be null
1278 if (d_is_nullable && !t_is_nullable) {
1279 expr_unwrap = Nullable.Unwrap.Create (expr, ec);
1280 return this;
1284 // The result is true if D and T are the same value types
1286 return CreateConstantResult (true);
1289 if (TypeManager.IsGenericParameter (d))
1290 return ResolveGenericParameter (t, d);
1293 // An unboxing conversion exists
1295 if (Convert.ExplicitReferenceConversionExists (d, t))
1296 return this;
1297 } else {
1298 if (TypeManager.IsGenericParameter (t))
1299 return ResolveGenericParameter (d, t);
1301 if (TypeManager.IsStruct (d)) {
1302 bool temp;
1303 if (Convert.ImplicitBoxingConversionExists (expr, t, out temp))
1304 return CreateConstantResult (true);
1305 } else {
1306 if (TypeManager.IsGenericParameter (d))
1307 return ResolveGenericParameter (t, d);
1309 if (TypeManager.ContainsGenericParameters (d))
1310 return this;
1312 if (Convert.ImplicitReferenceConversionExists (expr, t) ||
1313 Convert.ExplicitReferenceConversionExists (d, t)) {
1314 return this;
1319 return CreateConstantResult (false);
1322 Expression ResolveGenericParameter (Type d, Type t)
1324 #if GMCS_SOURCE
1325 GenericConstraints constraints = TypeManager.GetTypeParameterConstraints (t);
1326 if (constraints != null) {
1327 if (constraints.IsReferenceType && TypeManager.IsStruct (d))
1328 return CreateConstantResult (false);
1330 if (constraints.IsValueType && !TypeManager.IsStruct (d))
1331 return CreateConstantResult (TypeManager.IsEqual (d, t));
1334 if (!TypeManager.IsReferenceType (expr.Type))
1335 expr = new BoxedCast (expr, d);
1337 return this;
1338 #else
1339 return null;
1340 #endif
1343 protected override string OperatorName {
1344 get { return "is"; }
1348 /// <summary>
1349 /// Implementation of the `as' operator.
1350 /// </summary>
1351 public class As : Probe {
1352 bool do_isinst;
1353 Expression resolved_type;
1355 public As (Expression expr, Expression probe_type, Location l)
1356 : base (expr, probe_type, l)
1360 public override Expression CreateExpressionTree (EmitContext ec)
1362 ArrayList args = new ArrayList (2);
1363 args.Add (new Argument (expr.CreateExpressionTree (ec)));
1364 args.Add (new Argument (new TypeOf (probe_type_expr, loc)));
1365 return CreateExpressionFactoryCall ("TypeAs", args);
1368 public override void Emit (EmitContext ec)
1370 ILGenerator ig = ec.ig;
1372 expr.Emit (ec);
1374 if (do_isinst)
1375 ig.Emit (OpCodes.Isinst, type);
1377 #if GMCS_SOURCE
1378 if (TypeManager.IsGenericParameter (type) || TypeManager.IsNullableType (type))
1379 ig.Emit (OpCodes.Unbox_Any, type);
1380 #endif
1383 public override Expression DoResolve (EmitContext ec)
1385 // Because expr is modified
1386 if (eclass != ExprClass.Invalid)
1387 return this;
1389 if (resolved_type == null) {
1390 resolved_type = base.DoResolve (ec);
1392 if (resolved_type == null)
1393 return null;
1396 type = probe_type_expr.Type;
1397 eclass = ExprClass.Value;
1398 Type etype = expr.Type;
1400 if (!TypeManager.IsReferenceType (type) && !TypeManager.IsNullableType (type)) {
1401 if (probe_type_expr is TypeParameterExpr) {
1402 Report.Error (413, loc,
1403 "The `as' operator cannot be used with a non-reference type parameter `{0}'. Consider adding `class' or a reference type constraint",
1404 probe_type_expr.GetSignatureForError ());
1405 } else {
1406 Report.Error (77, loc,
1407 "The `as' operator cannot be used with a non-nullable value type `{0}'",
1408 TypeManager.CSharpName (type));
1410 return null;
1413 if (expr.IsNull && TypeManager.IsNullableType (type)) {
1414 return Nullable.LiftedNull.CreateFromExpression (this);
1417 Expression e = Convert.ImplicitConversion (ec, expr, type, loc);
1418 if (e != null){
1419 expr = e;
1420 do_isinst = false;
1421 return this;
1424 if (Convert.ExplicitReferenceConversionExists (etype, type)){
1425 if (TypeManager.IsGenericParameter (etype))
1426 expr = new BoxedCast (expr, etype);
1428 do_isinst = true;
1429 return this;
1432 if (TypeManager.ContainsGenericParameters (etype) ||
1433 TypeManager.ContainsGenericParameters (type)) {
1434 expr = new BoxedCast (expr, etype);
1435 do_isinst = true;
1436 return this;
1439 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1440 TypeManager.CSharpName (etype), TypeManager.CSharpName (type));
1442 return null;
1445 protected override string OperatorName {
1446 get { return "as"; }
1449 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1451 type = storey.MutateType (type);
1452 base.MutateHoistedGenericType (storey);
1455 public override bool GetAttributableValue (EmitContext ec, Type value_type, out object value)
1457 return expr.GetAttributableValue (ec, value_type, out value);
1461 /// <summary>
1462 /// This represents a typecast in the source language.
1464 /// FIXME: Cast expressions have an unusual set of parsing
1465 /// rules, we need to figure those out.
1466 /// </summary>
1467 public class Cast : Expression {
1468 Expression target_type;
1469 Expression expr;
1471 public Cast (Expression cast_type, Expression expr)
1472 : this (cast_type, expr, cast_type.Location)
1476 public Cast (Expression cast_type, Expression expr, Location loc)
1478 this.target_type = cast_type;
1479 this.expr = expr;
1480 this.loc = loc;
1483 public Expression TargetType {
1484 get { return target_type; }
1487 public Expression Expr {
1488 get { return expr; }
1491 public override Expression CreateExpressionTree (EmitContext ec)
1493 throw new NotSupportedException ("ET");
1496 public override Expression DoResolve (EmitContext ec)
1498 expr = expr.Resolve (ec);
1499 if (expr == null)
1500 return null;
1502 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1503 if (target == null)
1504 return null;
1506 type = target.Type;
1508 if (type.IsAbstract && type.IsSealed) {
1509 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1510 return null;
1513 eclass = ExprClass.Value;
1515 Constant c = expr as Constant;
1516 if (c != null) {
1517 c = c.TryReduce (ec, type, loc);
1518 if (c != null)
1519 return c;
1522 if (type.IsPointer && !ec.InUnsafe) {
1523 UnsafeError (loc);
1524 return null;
1526 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1527 return expr;
1530 public override void Emit (EmitContext ec)
1532 throw new Exception ("Should not happen");
1535 protected override void CloneTo (CloneContext clonectx, Expression t)
1537 Cast target = (Cast) t;
1539 target.target_type = target_type.Clone (clonectx);
1540 target.expr = expr.Clone (clonectx);
1545 // C# 2.0 Default value expression
1547 public class DefaultValueExpression : Expression
1549 sealed class DefaultValueNullLiteral : NullLiteral
1551 public DefaultValueNullLiteral (DefaultValueExpression expr)
1552 : base (expr.type, expr.loc)
1556 public override void Error_ValueCannotBeConverted (EmitContext ec, Location loc, Type t, bool expl)
1558 Error_ValueCannotBeConvertedCore (ec, loc, t, expl);
1563 Expression expr;
1565 public DefaultValueExpression (Expression expr, Location loc)
1567 this.expr = expr;
1568 this.loc = loc;
1571 public override Expression CreateExpressionTree (EmitContext ec)
1573 ArrayList args = new ArrayList (2);
1574 args.Add (new Argument (this));
1575 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
1576 return CreateExpressionFactoryCall ("Constant", args);
1579 public override Expression DoResolve (EmitContext ec)
1581 TypeExpr texpr = expr.ResolveAsTypeTerminal (ec, false);
1582 if (texpr == null)
1583 return null;
1585 type = texpr.Type;
1587 if ((type.Attributes & Class.StaticClassAttribute) == Class.StaticClassAttribute) {
1588 Report.Error (-244, loc, "The `default value' operator cannot be applied to an operand of a static type");
1591 if (type.IsPointer)
1592 return new NullLiteral (Location).ConvertImplicitly (type);
1594 if (TypeManager.IsReferenceType (type))
1595 return new DefaultValueNullLiteral (this);
1597 Constant c = New.Constantify (type);
1598 if (c != null)
1599 return c;
1601 eclass = ExprClass.Variable;
1602 return this;
1605 public override void Emit (EmitContext ec)
1607 LocalTemporary temp_storage = new LocalTemporary(type);
1609 temp_storage.AddressOf(ec, AddressOp.LoadStore);
1610 ec.ig.Emit(OpCodes.Initobj, type);
1611 temp_storage.Emit(ec);
1614 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1616 type = storey.MutateType (type);
1619 protected override void CloneTo (CloneContext clonectx, Expression t)
1621 DefaultValueExpression target = (DefaultValueExpression) t;
1623 target.expr = expr.Clone (clonectx);
1627 /// <summary>
1628 /// Binary operators
1629 /// </summary>
1630 public class Binary : Expression {
1632 protected class PredefinedOperator {
1633 protected readonly Type left;
1634 protected readonly Type right;
1635 public readonly Operator OperatorsMask;
1636 public Type ReturnType;
1638 public PredefinedOperator (Type ltype, Type rtype, Operator op_mask)
1639 : this (ltype, rtype, op_mask, ltype)
1643 public PredefinedOperator (Type type, Operator op_mask, Type return_type)
1644 : this (type, type, op_mask, return_type)
1648 public PredefinedOperator (Type type, Operator op_mask)
1649 : this (type, type, op_mask, type)
1653 public PredefinedOperator (Type ltype, Type rtype, Operator op_mask, Type return_type)
1655 if ((op_mask & Operator.ValuesOnlyMask) != 0)
1656 throw new InternalErrorException ("Only masked values can be used");
1658 this.left = ltype;
1659 this.right = rtype;
1660 this.OperatorsMask = op_mask;
1661 this.ReturnType = return_type;
1664 public virtual Expression ConvertResult (EmitContext ec, Binary b)
1666 b.type = ReturnType;
1668 b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
1669 b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
1672 // A user operators does not support multiple user conversions, but decimal type
1673 // is considered to be predefined type therefore we apply predefined operators rules
1674 // and then look for decimal user-operator implementation
1676 if (left == TypeManager.decimal_type)
1677 return b.ResolveUserOperator (ec, b.left.Type, b.right.Type);
1679 return b;
1682 public bool IsPrimitiveApplicable (Type ltype, Type rtype)
1685 // We are dealing with primitive types only
1687 return left == ltype && ltype == rtype;
1690 public virtual bool IsApplicable (EmitContext ec, Expression lexpr, Expression rexpr)
1692 if (TypeManager.IsEqual (left, lexpr.Type) &&
1693 TypeManager.IsEqual (right, rexpr.Type))
1694 return true;
1696 return Convert.ImplicitConversionExists (ec, lexpr, left) &&
1697 Convert.ImplicitConversionExists (ec, rexpr, right);
1700 public PredefinedOperator ResolveBetterOperator (EmitContext ec, PredefinedOperator best_operator)
1702 int result = 0;
1703 if (left != null && best_operator.left != null) {
1704 result = MethodGroupExpr.BetterTypeConversion (ec, best_operator.left, left);
1708 // When second arguments are same as the first one, the result is same
1710 if (right != null && (left != right || best_operator.left != best_operator.right)) {
1711 result |= MethodGroupExpr.BetterTypeConversion (ec, best_operator.right, right);
1714 if (result == 0 || result > 2)
1715 return null;
1717 return result == 1 ? best_operator : this;
1721 class PredefinedStringOperator : PredefinedOperator {
1722 public PredefinedStringOperator (Type type, Operator op_mask)
1723 : base (type, op_mask, type)
1725 ReturnType = TypeManager.string_type;
1728 public PredefinedStringOperator (Type ltype, Type rtype, Operator op_mask)
1729 : base (ltype, rtype, op_mask)
1731 ReturnType = TypeManager.string_type;
1734 public override Expression ConvertResult (EmitContext ec, Binary b)
1737 // Use original expression for nullable arguments
1739 Nullable.Unwrap unwrap = b.left as Nullable.Unwrap;
1740 if (unwrap != null)
1741 b.left = unwrap.Original;
1743 unwrap = b.right as Nullable.Unwrap;
1744 if (unwrap != null)
1745 b.right = unwrap.Original;
1747 b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
1748 b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
1751 // Start a new concat expression using converted expression
1753 return new StringConcat (ec, b.loc, b.left, b.right).Resolve (ec);
1757 class PredefinedShiftOperator : PredefinedOperator {
1758 public PredefinedShiftOperator (Type ltype, Operator op_mask) :
1759 base (ltype, TypeManager.int32_type, op_mask)
1763 public override Expression ConvertResult (EmitContext ec, Binary b)
1765 b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
1767 Expression expr_tree_expr = EmptyCast.Create (b.right, TypeManager.int32_type);
1769 int right_mask = left == TypeManager.int32_type || left == TypeManager.uint32_type ? 0x1f : 0x3f;
1772 // b = b.left >> b.right & (0x1f|0x3f)
1774 b.right = new Binary (Operator.BitwiseAnd,
1775 b.right, new IntConstant (right_mask, b.right.Location)).Resolve (ec);
1778 // Expression tree representation does not use & mask
1780 b.right = ReducedExpression.Create (b.right, expr_tree_expr).Resolve (ec);
1781 b.type = ReturnType;
1782 return b;
1786 class PredefinedPointerOperator : PredefinedOperator {
1787 public PredefinedPointerOperator (Type ltype, Type rtype, Operator op_mask)
1788 : base (ltype, rtype, op_mask)
1792 public PredefinedPointerOperator (Type ltype, Type rtype, Operator op_mask, Type retType)
1793 : base (ltype, rtype, op_mask, retType)
1797 public PredefinedPointerOperator (Type type, Operator op_mask, Type return_type)
1798 : base (type, op_mask, return_type)
1802 public override bool IsApplicable (EmitContext ec, Expression lexpr, Expression rexpr)
1804 if (left == null) {
1805 if (!lexpr.Type.IsPointer)
1806 return false;
1807 } else {
1808 if (!Convert.ImplicitConversionExists (ec, lexpr, left))
1809 return false;
1812 if (right == null) {
1813 if (!rexpr.Type.IsPointer)
1814 return false;
1815 } else {
1816 if (!Convert.ImplicitConversionExists (ec, rexpr, right))
1817 return false;
1820 return true;
1823 public override Expression ConvertResult (EmitContext ec, Binary b)
1825 if (left != null) {
1826 b.left = EmptyCast.Create (b.left, left);
1827 } else if (right != null) {
1828 b.right = EmptyCast.Create (b.right, right);
1831 Type r_type = ReturnType;
1832 Expression left_arg, right_arg;
1833 if (r_type == null) {
1834 if (left == null) {
1835 left_arg = b.left;
1836 right_arg = b.right;
1837 r_type = b.left.Type;
1838 } else {
1839 left_arg = b.right;
1840 right_arg = b.left;
1841 r_type = b.right.Type;
1843 } else {
1844 left_arg = b.left;
1845 right_arg = b.right;
1848 return new PointerArithmetic (b.oper, left_arg, right_arg, r_type, b.loc).Resolve (ec);
1852 [Flags]
1853 public enum Operator {
1854 Multiply = 0 | ArithmeticMask,
1855 Division = 1 | ArithmeticMask,
1856 Modulus = 2 | ArithmeticMask,
1857 Addition = 3 | ArithmeticMask | AdditionMask,
1858 Subtraction = 4 | ArithmeticMask | SubtractionMask,
1860 LeftShift = 5 | ShiftMask,
1861 RightShift = 6 | ShiftMask,
1863 LessThan = 7 | ComparisonMask | RelationalMask,
1864 GreaterThan = 8 | ComparisonMask | RelationalMask,
1865 LessThanOrEqual = 9 | ComparisonMask | RelationalMask,
1866 GreaterThanOrEqual = 10 | ComparisonMask | RelationalMask,
1867 Equality = 11 | ComparisonMask | EqualityMask,
1868 Inequality = 12 | ComparisonMask | EqualityMask,
1870 BitwiseAnd = 13 | BitwiseMask,
1871 ExclusiveOr = 14 | BitwiseMask,
1872 BitwiseOr = 15 | BitwiseMask,
1874 LogicalAnd = 16 | LogicalMask,
1875 LogicalOr = 17 | LogicalMask,
1878 // Operator masks
1880 ValuesOnlyMask = ArithmeticMask - 1,
1881 ArithmeticMask = 1 << 5,
1882 ShiftMask = 1 << 6,
1883 ComparisonMask = 1 << 7,
1884 EqualityMask = 1 << 8,
1885 BitwiseMask = 1 << 9,
1886 LogicalMask = 1 << 10,
1887 AdditionMask = 1 << 11,
1888 SubtractionMask = 1 << 12,
1889 RelationalMask = 1 << 13
1892 readonly Operator oper;
1893 protected Expression left, right;
1894 readonly bool is_compound;
1895 Expression enum_conversion;
1897 static PredefinedOperator [] standard_operators;
1898 static PredefinedOperator [] pointer_operators;
1900 public Binary (Operator oper, Expression left, Expression right, bool isCompound)
1901 : this (oper, left, right)
1903 this.is_compound = isCompound;
1906 public Binary (Operator oper, Expression left, Expression right)
1908 this.oper = oper;
1909 this.left = left;
1910 this.right = right;
1911 this.loc = left.Location;
1914 public Operator Oper {
1915 get {
1916 return oper;
1920 /// <summary>
1921 /// Returns a stringified representation of the Operator
1922 /// </summary>
1923 string OperName (Operator oper)
1925 string s;
1926 switch (oper){
1927 case Operator.Multiply:
1928 s = "*";
1929 break;
1930 case Operator.Division:
1931 s = "/";
1932 break;
1933 case Operator.Modulus:
1934 s = "%";
1935 break;
1936 case Operator.Addition:
1937 s = "+";
1938 break;
1939 case Operator.Subtraction:
1940 s = "-";
1941 break;
1942 case Operator.LeftShift:
1943 s = "<<";
1944 break;
1945 case Operator.RightShift:
1946 s = ">>";
1947 break;
1948 case Operator.LessThan:
1949 s = "<";
1950 break;
1951 case Operator.GreaterThan:
1952 s = ">";
1953 break;
1954 case Operator.LessThanOrEqual:
1955 s = "<=";
1956 break;
1957 case Operator.GreaterThanOrEqual:
1958 s = ">=";
1959 break;
1960 case Operator.Equality:
1961 s = "==";
1962 break;
1963 case Operator.Inequality:
1964 s = "!=";
1965 break;
1966 case Operator.BitwiseAnd:
1967 s = "&";
1968 break;
1969 case Operator.BitwiseOr:
1970 s = "|";
1971 break;
1972 case Operator.ExclusiveOr:
1973 s = "^";
1974 break;
1975 case Operator.LogicalOr:
1976 s = "||";
1977 break;
1978 case Operator.LogicalAnd:
1979 s = "&&";
1980 break;
1981 default:
1982 s = oper.ToString ();
1983 break;
1986 if (is_compound)
1987 return s + "=";
1989 return s;
1992 public static void Error_OperatorCannotBeApplied (Expression left, Expression right, Operator oper, Location loc)
1994 new Binary (oper, left, right).Error_OperatorCannotBeApplied (left, right);
1997 public static void Error_OperatorCannotBeApplied (Expression left, Expression right, string oper, Location loc)
1999 string l, r;
2000 // TODO: This should be handled as Type of method group in CSharpName
2001 if (left.eclass == ExprClass.MethodGroup)
2002 l = left.ExprClassName;
2003 else
2004 l = TypeManager.CSharpName (left.Type);
2006 if (right.eclass == ExprClass.MethodGroup)
2007 r = right.ExprClassName;
2008 else
2009 r = TypeManager.CSharpName (right.Type);
2011 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
2012 oper, l, r);
2015 protected void Error_OperatorCannotBeApplied (Expression left, Expression right)
2017 Error_OperatorCannotBeApplied (left, right, OperName (oper), loc);
2020 static string GetOperatorMetadataName (Operator op)
2022 CSharp.Operator.OpType op_type;
2023 switch (op) {
2024 case Operator.Addition:
2025 op_type = CSharp.Operator.OpType.Addition; break;
2026 case Operator.BitwiseAnd:
2027 op_type = CSharp.Operator.OpType.BitwiseAnd; break;
2028 case Operator.BitwiseOr:
2029 op_type = CSharp.Operator.OpType.BitwiseOr; break;
2030 case Operator.Division:
2031 op_type = CSharp.Operator.OpType.Division; break;
2032 case Operator.Equality:
2033 op_type = CSharp.Operator.OpType.Equality; break;
2034 case Operator.ExclusiveOr:
2035 op_type = CSharp.Operator.OpType.ExclusiveOr; break;
2036 case Operator.GreaterThan:
2037 op_type = CSharp.Operator.OpType.GreaterThan; break;
2038 case Operator.GreaterThanOrEqual:
2039 op_type = CSharp.Operator.OpType.GreaterThanOrEqual; break;
2040 case Operator.Inequality:
2041 op_type = CSharp.Operator.OpType.Inequality; break;
2042 case Operator.LeftShift:
2043 op_type = CSharp.Operator.OpType.LeftShift; break;
2044 case Operator.LessThan:
2045 op_type = CSharp.Operator.OpType.LessThan; break;
2046 case Operator.LessThanOrEqual:
2047 op_type = CSharp.Operator.OpType.LessThanOrEqual; break;
2048 case Operator.Modulus:
2049 op_type = CSharp.Operator.OpType.Modulus; break;
2050 case Operator.Multiply:
2051 op_type = CSharp.Operator.OpType.Multiply; break;
2052 case Operator.RightShift:
2053 op_type = CSharp.Operator.OpType.RightShift; break;
2054 case Operator.Subtraction:
2055 op_type = CSharp.Operator.OpType.Subtraction; break;
2056 default:
2057 throw new InternalErrorException (op.ToString ());
2060 return CSharp.Operator.GetMetadataName (op_type);
2063 public static void EmitOperatorOpcode (EmitContext ec, Operator oper, Type l)
2065 OpCode opcode;
2066 ILGenerator ig = ec.ig;
2068 switch (oper){
2069 case Operator.Multiply:
2070 if (ec.CheckState){
2071 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2072 opcode = OpCodes.Mul_Ovf;
2073 else if (!IsFloat (l))
2074 opcode = OpCodes.Mul_Ovf_Un;
2075 else
2076 opcode = OpCodes.Mul;
2077 } else
2078 opcode = OpCodes.Mul;
2080 break;
2082 case Operator.Division:
2083 if (IsUnsigned (l))
2084 opcode = OpCodes.Div_Un;
2085 else
2086 opcode = OpCodes.Div;
2087 break;
2089 case Operator.Modulus:
2090 if (IsUnsigned (l))
2091 opcode = OpCodes.Rem_Un;
2092 else
2093 opcode = OpCodes.Rem;
2094 break;
2096 case Operator.Addition:
2097 if (ec.CheckState){
2098 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2099 opcode = OpCodes.Add_Ovf;
2100 else if (!IsFloat (l))
2101 opcode = OpCodes.Add_Ovf_Un;
2102 else
2103 opcode = OpCodes.Add;
2104 } else
2105 opcode = OpCodes.Add;
2106 break;
2108 case Operator.Subtraction:
2109 if (ec.CheckState){
2110 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2111 opcode = OpCodes.Sub_Ovf;
2112 else if (!IsFloat (l))
2113 opcode = OpCodes.Sub_Ovf_Un;
2114 else
2115 opcode = OpCodes.Sub;
2116 } else
2117 opcode = OpCodes.Sub;
2118 break;
2120 case Operator.RightShift:
2121 if (IsUnsigned (l))
2122 opcode = OpCodes.Shr_Un;
2123 else
2124 opcode = OpCodes.Shr;
2125 break;
2127 case Operator.LeftShift:
2128 opcode = OpCodes.Shl;
2129 break;
2131 case Operator.Equality:
2132 opcode = OpCodes.Ceq;
2133 break;
2135 case Operator.Inequality:
2136 ig.Emit (OpCodes.Ceq);
2137 ig.Emit (OpCodes.Ldc_I4_0);
2139 opcode = OpCodes.Ceq;
2140 break;
2142 case Operator.LessThan:
2143 if (IsUnsigned (l))
2144 opcode = OpCodes.Clt_Un;
2145 else
2146 opcode = OpCodes.Clt;
2147 break;
2149 case Operator.GreaterThan:
2150 if (IsUnsigned (l))
2151 opcode = OpCodes.Cgt_Un;
2152 else
2153 opcode = OpCodes.Cgt;
2154 break;
2156 case Operator.LessThanOrEqual:
2157 if (IsUnsigned (l) || IsFloat (l))
2158 ig.Emit (OpCodes.Cgt_Un);
2159 else
2160 ig.Emit (OpCodes.Cgt);
2161 ig.Emit (OpCodes.Ldc_I4_0);
2163 opcode = OpCodes.Ceq;
2164 break;
2166 case Operator.GreaterThanOrEqual:
2167 if (IsUnsigned (l) || IsFloat (l))
2168 ig.Emit (OpCodes.Clt_Un);
2169 else
2170 ig.Emit (OpCodes.Clt);
2172 ig.Emit (OpCodes.Ldc_I4_0);
2174 opcode = OpCodes.Ceq;
2175 break;
2177 case Operator.BitwiseOr:
2178 opcode = OpCodes.Or;
2179 break;
2181 case Operator.BitwiseAnd:
2182 opcode = OpCodes.And;
2183 break;
2185 case Operator.ExclusiveOr:
2186 opcode = OpCodes.Xor;
2187 break;
2189 default:
2190 throw new InternalErrorException (oper.ToString ());
2193 ig.Emit (opcode);
2196 static bool IsUnsigned (Type t)
2198 if (t.IsPointer)
2199 return true;
2201 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
2202 t == TypeManager.ushort_type || t == TypeManager.byte_type);
2205 static bool IsFloat (Type t)
2207 return t == TypeManager.float_type || t == TypeManager.double_type;
2210 Expression ResolveOperator (EmitContext ec)
2212 Type l = left.Type;
2213 Type r = right.Type;
2214 Expression expr;
2215 bool primitives_only = false;
2217 if (standard_operators == null)
2218 CreateStandardOperatorsTable ();
2221 // Handles predefined primitive types
2223 if (TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r)) {
2224 if ((oper & Operator.ShiftMask) == 0) {
2225 if (l != TypeManager.bool_type && !DoBinaryOperatorPromotion (ec))
2226 return null;
2228 primitives_only = true;
2230 } else {
2231 // Pointers
2232 if (l.IsPointer || r.IsPointer)
2233 return ResolveOperatorPointer (ec, l, r);
2235 // Enums
2236 bool lenum = TypeManager.IsEnumType (l);
2237 bool renum = TypeManager.IsEnumType (r);
2238 if (lenum || renum) {
2239 expr = ResolveOperatorEnum (ec, lenum, renum, l, r);
2241 // TODO: Can this be ambiguous
2242 if (expr != null)
2243 return expr;
2246 // Delegates
2247 if ((oper == Operator.Addition || oper == Operator.Subtraction || (oper & Operator.EqualityMask) != 0) &&
2248 (TypeManager.IsDelegateType (l) || TypeManager.IsDelegateType (r))) {
2250 expr = ResolveOperatorDelegate (ec, l, r);
2252 // TODO: Can this be ambiguous
2253 if (expr != null)
2254 return expr;
2257 // User operators
2258 expr = ResolveUserOperator (ec, l, r);
2259 if (expr != null)
2260 return expr;
2262 // Predefined reference types equality
2263 if ((oper & Operator.EqualityMask) != 0) {
2264 expr = ResolveOperatorEqualityRerefence (ec, l, r);
2265 if (expr != null)
2266 return expr;
2270 return ResolveOperatorPredefined (ec, standard_operators, primitives_only, null);
2273 // at least one of 'left' or 'right' is an enumeration constant (EnumConstant or SideEffectConstant or ...)
2274 // if 'left' is not an enumeration constant, create one from the type of 'right'
2275 Constant EnumLiftUp (EmitContext ec, Constant left, Constant right, Location loc)
2277 switch (oper) {
2278 case Operator.BitwiseOr:
2279 case Operator.BitwiseAnd:
2280 case Operator.ExclusiveOr:
2281 case Operator.Equality:
2282 case Operator.Inequality:
2283 case Operator.LessThan:
2284 case Operator.LessThanOrEqual:
2285 case Operator.GreaterThan:
2286 case Operator.GreaterThanOrEqual:
2287 if (TypeManager.IsEnumType (left.Type))
2288 return left;
2290 if (left.IsZeroInteger)
2291 return left.TryReduce (ec, right.Type, loc);
2293 break;
2295 case Operator.Addition:
2296 case Operator.Subtraction:
2297 return left;
2299 case Operator.Multiply:
2300 case Operator.Division:
2301 case Operator.Modulus:
2302 case Operator.LeftShift:
2303 case Operator.RightShift:
2304 if (TypeManager.IsEnumType (right.Type) || TypeManager.IsEnumType (left.Type))
2305 break;
2306 return left;
2308 Error_OperatorCannotBeApplied (this.left, this.right);
2309 return null;
2313 // The `|' operator used on types which were extended is dangerous
2315 void CheckBitwiseOrOnSignExtended ()
2317 OpcodeCast lcast = left as OpcodeCast;
2318 if (lcast != null) {
2319 if (IsUnsigned (lcast.UnderlyingType))
2320 lcast = null;
2323 OpcodeCast rcast = right as OpcodeCast;
2324 if (rcast != null) {
2325 if (IsUnsigned (rcast.UnderlyingType))
2326 rcast = null;
2329 if (lcast == null && rcast == null)
2330 return;
2332 // FIXME: consider constants
2334 Report.Warning (675, 3, loc,
2335 "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first",
2336 TypeManager.CSharpName (lcast != null ? lcast.UnderlyingType : rcast.UnderlyingType));
2339 static void CreatePointerOperatorsTable ()
2341 ArrayList temp = new ArrayList ();
2344 // Pointer arithmetic:
2346 // T* operator + (T* x, int y); T* operator - (T* x, int y);
2347 // T* operator + (T* x, uint y); T* operator - (T* x, uint y);
2348 // T* operator + (T* x, long y); T* operator - (T* x, long y);
2349 // T* operator + (T* x, ulong y); T* operator - (T* x, ulong y);
2351 temp.Add (new PredefinedPointerOperator (null, TypeManager.int32_type, Operator.AdditionMask | Operator.SubtractionMask));
2352 temp.Add (new PredefinedPointerOperator (null, TypeManager.uint32_type, Operator.AdditionMask | Operator.SubtractionMask));
2353 temp.Add (new PredefinedPointerOperator (null, TypeManager.int64_type, Operator.AdditionMask | Operator.SubtractionMask));
2354 temp.Add (new PredefinedPointerOperator (null, TypeManager.uint64_type, Operator.AdditionMask | Operator.SubtractionMask));
2357 // T* operator + (int y, T* x);
2358 // T* operator + (uint y, T *x);
2359 // T* operator + (long y, T *x);
2360 // T* operator + (ulong y, T *x);
2362 temp.Add (new PredefinedPointerOperator (TypeManager.int32_type, null, Operator.AdditionMask, null));
2363 temp.Add (new PredefinedPointerOperator (TypeManager.uint32_type, null, Operator.AdditionMask, null));
2364 temp.Add (new PredefinedPointerOperator (TypeManager.int64_type, null, Operator.AdditionMask, null));
2365 temp.Add (new PredefinedPointerOperator (TypeManager.uint64_type, null, Operator.AdditionMask, null));
2368 // long operator - (T* x, T *y)
2370 temp.Add (new PredefinedPointerOperator (null, Operator.SubtractionMask, TypeManager.int64_type));
2372 pointer_operators = (PredefinedOperator []) temp.ToArray (typeof (PredefinedOperator));
2375 static void CreateStandardOperatorsTable ()
2377 ArrayList temp = new ArrayList ();
2378 Type bool_type = TypeManager.bool_type;
2380 temp.Add (new PredefinedOperator (TypeManager.int32_type, Operator.ArithmeticMask | Operator.BitwiseMask));
2381 temp.Add (new PredefinedOperator (TypeManager.uint32_type, Operator.ArithmeticMask | Operator.BitwiseMask));
2382 temp.Add (new PredefinedOperator (TypeManager.int64_type, Operator.ArithmeticMask | Operator.BitwiseMask));
2383 temp.Add (new PredefinedOperator (TypeManager.uint64_type, Operator.ArithmeticMask | Operator.BitwiseMask));
2384 temp.Add (new PredefinedOperator (TypeManager.float_type, Operator.ArithmeticMask));
2385 temp.Add (new PredefinedOperator (TypeManager.double_type, Operator.ArithmeticMask));
2386 temp.Add (new PredefinedOperator (TypeManager.decimal_type, Operator.ArithmeticMask));
2388 temp.Add (new PredefinedOperator (TypeManager.int32_type, Operator.ComparisonMask, bool_type));
2389 temp.Add (new PredefinedOperator (TypeManager.uint32_type, Operator.ComparisonMask, bool_type));
2390 temp.Add (new PredefinedOperator (TypeManager.int64_type, Operator.ComparisonMask, bool_type));
2391 temp.Add (new PredefinedOperator (TypeManager.uint64_type, Operator.ComparisonMask, bool_type));
2392 temp.Add (new PredefinedOperator (TypeManager.float_type, Operator.ComparisonMask, bool_type));
2393 temp.Add (new PredefinedOperator (TypeManager.double_type, Operator.ComparisonMask, bool_type));
2394 temp.Add (new PredefinedOperator (TypeManager.decimal_type, Operator.ComparisonMask, bool_type));
2396 temp.Add (new PredefinedOperator (TypeManager.string_type, Operator.EqualityMask, bool_type));
2398 temp.Add (new PredefinedStringOperator (TypeManager.string_type, Operator.AdditionMask));
2399 temp.Add (new PredefinedStringOperator (TypeManager.string_type, TypeManager.object_type, Operator.AdditionMask));
2400 temp.Add (new PredefinedStringOperator (TypeManager.object_type, TypeManager.string_type, Operator.AdditionMask));
2402 temp.Add (new PredefinedOperator (bool_type,
2403 Operator.BitwiseMask | Operator.LogicalMask | Operator.EqualityMask, bool_type));
2405 temp.Add (new PredefinedShiftOperator (TypeManager.int32_type, Operator.ShiftMask));
2406 temp.Add (new PredefinedShiftOperator (TypeManager.uint32_type, Operator.ShiftMask));
2407 temp.Add (new PredefinedShiftOperator (TypeManager.int64_type, Operator.ShiftMask));
2408 temp.Add (new PredefinedShiftOperator (TypeManager.uint64_type, Operator.ShiftMask));
2410 standard_operators = (PredefinedOperator []) temp.ToArray (typeof (PredefinedOperator));
2414 // Rules used during binary numeric promotion
2416 static bool DoNumericPromotion (ref Expression prim_expr, ref Expression second_expr, Type type)
2418 Expression temp;
2419 Type etype;
2421 Constant c = prim_expr as Constant;
2422 if (c != null) {
2423 temp = c.ConvertImplicitly (type);
2424 if (temp != null) {
2425 prim_expr = temp;
2426 return true;
2430 if (type == TypeManager.uint32_type) {
2431 etype = prim_expr.Type;
2432 if (etype == TypeManager.int32_type || etype == TypeManager.short_type || etype == TypeManager.sbyte_type) {
2433 type = TypeManager.int64_type;
2435 if (type != second_expr.Type) {
2436 c = second_expr as Constant;
2437 if (c != null)
2438 temp = c.ConvertImplicitly (type);
2439 else
2440 temp = Convert.ImplicitNumericConversion (second_expr, type);
2441 if (temp == null)
2442 return false;
2443 second_expr = temp;
2446 } else if (type == TypeManager.uint64_type) {
2448 // A compile-time error occurs if the other operand is of type sbyte, short, int, or long
2450 if (type == TypeManager.int32_type || type == TypeManager.int64_type ||
2451 type == TypeManager.sbyte_type || type == TypeManager.sbyte_type)
2452 return false;
2455 temp = Convert.ImplicitNumericConversion (prim_expr, type);
2456 if (temp == null)
2457 return false;
2459 prim_expr = temp;
2460 return true;
2464 // 7.2.6.2 Binary numeric promotions
2466 public bool DoBinaryOperatorPromotion (EmitContext ec)
2468 Type ltype = left.Type;
2469 Type rtype = right.Type;
2470 Expression temp;
2472 foreach (Type t in ConstantFold.binary_promotions) {
2473 if (t == ltype)
2474 return t == rtype || DoNumericPromotion (ref right, ref left, t);
2476 if (t == rtype)
2477 return t == ltype || DoNumericPromotion (ref left, ref right, t);
2480 Type int32 = TypeManager.int32_type;
2481 if (ltype != int32) {
2482 Constant c = left as Constant;
2483 if (c != null)
2484 temp = c.ConvertImplicitly (int32);
2485 else
2486 temp = Convert.ImplicitNumericConversion (left, int32);
2488 if (temp == null)
2489 return false;
2490 left = temp;
2493 if (rtype != int32) {
2494 Constant c = right as Constant;
2495 if (c != null)
2496 temp = c.ConvertImplicitly (int32);
2497 else
2498 temp = Convert.ImplicitNumericConversion (right, int32);
2500 if (temp == null)
2501 return false;
2502 right = temp;
2505 return true;
2508 public override Expression DoResolve (EmitContext ec)
2510 if (left == null)
2511 return null;
2513 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2514 left = ((ParenthesizedExpression) left).Expr;
2515 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2516 if (left == null)
2517 return null;
2519 if (left.eclass == ExprClass.Type) {
2520 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2521 return null;
2523 } else
2524 left = left.Resolve (ec);
2526 if (left == null)
2527 return null;
2529 Constant lc = left as Constant;
2531 if (lc != null && lc.Type == TypeManager.bool_type &&
2532 ((oper == Operator.LogicalAnd && lc.IsDefaultValue) ||
2533 (oper == Operator.LogicalOr && !lc.IsDefaultValue))) {
2535 // FIXME: resolve right expression as unreachable
2536 // right.Resolve (ec);
2538 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2539 return left;
2542 right = right.Resolve (ec);
2543 if (right == null)
2544 return null;
2546 eclass = ExprClass.Value;
2547 Constant rc = right as Constant;
2549 // The conversion rules are ignored in enum context but why
2550 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2551 lc = EnumLiftUp (ec, lc, rc, loc);
2552 if (lc != null)
2553 rc = EnumLiftUp (ec, rc, lc, loc);
2556 if (rc != null && lc != null) {
2557 int prev_e = Report.Errors;
2558 Expression e = ConstantFold.BinaryFold (
2559 ec, oper, lc, rc, loc);
2560 if (e != null || Report.Errors != prev_e)
2561 return e;
2562 } else {
2563 if ((oper == Operator.BitwiseAnd || oper == Operator.LogicalAnd) &&
2564 ((lc != null && lc.IsDefaultValue) || (rc != null && rc.IsDefaultValue))) {
2566 if ((ResolveOperator (ec)) == null) {
2567 Error_OperatorCannotBeApplied (left, right);
2568 return null;
2572 // The result is a constant with side-effect
2574 Constant side_effect = rc == null ?
2575 new SideEffectConstant (lc, right, loc) :
2576 new SideEffectConstant (rc, left, loc);
2578 return ReducedExpression.Create (side_effect, this);
2582 // Comparison warnings
2583 if ((oper & Operator.ComparisonMask) != 0) {
2584 if (left.Equals (right)) {
2585 Report.Warning (1718, 3, loc, "A comparison made to same variable. Did you mean to compare something else?");
2587 CheckUselessComparison (lc, right.Type);
2588 CheckUselessComparison (rc, left.Type);
2591 if (RootContext.Version >= LanguageVersion.ISO_2 &&
2592 ((TypeManager.IsNullableType (left.Type) && (right is NullLiteral || TypeManager.IsNullableType (right.Type) || TypeManager.IsValueType (right.Type))) ||
2593 (TypeManager.IsValueType (left.Type) && right is NullLiteral) ||
2594 (TypeManager.IsNullableType (right.Type) && (left is NullLiteral || TypeManager.IsNullableType (left.Type) || TypeManager.IsValueType (left.Type))) ||
2595 (TypeManager.IsValueType (right.Type) && left is NullLiteral)))
2596 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2598 return DoResolveCore (ec, left, right);
2601 protected Expression DoResolveCore (EmitContext ec, Expression left_orig, Expression right_orig)
2603 Expression expr = ResolveOperator (ec);
2604 if (expr == null)
2605 Error_OperatorCannotBeApplied (left_orig, right_orig);
2607 if (left == null || right == null)
2608 throw new InternalErrorException ("Invalid conversion");
2610 if (oper == Operator.BitwiseOr)
2611 CheckBitwiseOrOnSignExtended ();
2613 return expr;
2616 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2618 left.MutateHoistedGenericType (storey);
2619 right.MutateHoistedGenericType (storey);
2623 // D operator + (D x, D y)
2624 // D operator - (D x, D y)
2625 // bool operator == (D x, D y)
2626 // bool operator != (D x, D y)
2628 Expression ResolveOperatorDelegate (EmitContext ec, Type l, Type r)
2630 bool is_equality = (oper & Operator.EqualityMask) != 0;
2631 if (!TypeManager.IsEqual (l, r)) {
2632 Expression tmp;
2633 if (right.eclass == ExprClass.MethodGroup || (r == TypeManager.anonymous_method_type && !is_equality)) {
2634 tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
2635 if (tmp == null)
2636 return null;
2637 right = tmp;
2638 r = right.Type;
2639 } else if (left.eclass == ExprClass.MethodGroup || (l == TypeManager.anonymous_method_type && !is_equality)) {
2640 tmp = Convert.ImplicitConversionRequired (ec, left, r, loc);
2641 if (tmp == null)
2642 return null;
2643 left = tmp;
2644 l = left.Type;
2645 } else {
2646 return null;
2651 // Resolve delegate equality as a user operator
2653 if (is_equality)
2654 return ResolveUserOperator (ec, l, r);
2656 MethodInfo method;
2657 ArrayList args = new ArrayList (2);
2658 args.Add (new Argument (left, Argument.AType.Expression));
2659 args.Add (new Argument (right, Argument.AType.Expression));
2661 if (oper == Operator.Addition) {
2662 if (TypeManager.delegate_combine_delegate_delegate == null) {
2663 TypeManager.delegate_combine_delegate_delegate = TypeManager.GetPredefinedMethod (
2664 TypeManager.delegate_type, "Combine", loc, TypeManager.delegate_type, TypeManager.delegate_type);
2667 method = TypeManager.delegate_combine_delegate_delegate;
2668 } else {
2669 if (TypeManager.delegate_remove_delegate_delegate == null) {
2670 TypeManager.delegate_remove_delegate_delegate = TypeManager.GetPredefinedMethod (
2671 TypeManager.delegate_type, "Remove", loc, TypeManager.delegate_type, TypeManager.delegate_type);
2674 method = TypeManager.delegate_remove_delegate_delegate;
2677 MethodGroupExpr mg = new MethodGroupExpr (new MemberInfo [] { method }, TypeManager.delegate_type, loc);
2678 mg = mg.OverloadResolve (ec, ref args, false, loc);
2680 return new ClassCast (new UserOperatorCall (mg, args, CreateExpressionTree, loc), l);
2684 // Enumeration operators
2686 Expression ResolveOperatorEnum (EmitContext ec, bool lenum, bool renum, Type ltype, Type rtype)
2689 // bool operator == (E x, E y);
2690 // bool operator != (E x, E y);
2691 // bool operator < (E x, E y);
2692 // bool operator > (E x, E y);
2693 // bool operator <= (E x, E y);
2694 // bool operator >= (E x, E y);
2696 // E operator & (E x, E y);
2697 // E operator | (E x, E y);
2698 // E operator ^ (E x, E y);
2700 // U operator - (E e, E f)
2701 // E operator - (E e, U x)
2703 // E operator + (U x, E e)
2704 // E operator + (E e, U x)
2706 if (!((oper & (Operator.ComparisonMask | Operator.BitwiseMask)) != 0 ||
2707 (oper == Operator.Subtraction && lenum) || (oper == Operator.Addition && lenum != renum)))
2708 return null;
2710 Expression ltemp = left;
2711 Expression rtemp = right;
2712 Type underlying_type;
2713 Expression expr;
2715 if ((oper & Operator.ComparisonMask | Operator.BitwiseMask) != 0) {
2716 if (renum) {
2717 expr = Convert.ImplicitConversion (ec, left, rtype, loc);
2718 if (expr != null) {
2719 left = expr;
2720 ltype = expr.Type;
2722 } else if (lenum) {
2723 expr = Convert.ImplicitConversion (ec, right, ltype, loc);
2724 if (expr != null) {
2725 right = expr;
2726 rtype = expr.Type;
2731 if (TypeManager.IsEqual (ltype, rtype)) {
2732 underlying_type = TypeManager.GetEnumUnderlyingType (ltype);
2734 if (left is Constant)
2735 left = ((Constant) left).ConvertExplicitly (false, underlying_type);
2736 else
2737 left = EmptyCast.Create (left, underlying_type);
2739 if (right is Constant)
2740 right = ((Constant) right).ConvertExplicitly (false, underlying_type);
2741 else
2742 right = EmptyCast.Create (right, underlying_type);
2743 } else if (lenum) {
2744 underlying_type = TypeManager.GetEnumUnderlyingType (ltype);
2746 if (oper != Operator.Subtraction && oper != Operator.Addition) {
2747 Constant c = right as Constant;
2748 if (c == null || !c.IsDefaultValue)
2749 return null;
2750 } else {
2751 if (!Convert.ImplicitStandardConversionExists (right, underlying_type))
2752 return null;
2754 right = Convert.ImplicitConversionStandard (ec, right, underlying_type, right.Location);
2757 if (left is Constant)
2758 left = ((Constant) left).ConvertExplicitly (false, underlying_type);
2759 else
2760 left = EmptyCast.Create (left, underlying_type);
2762 } else if (renum) {
2763 underlying_type = TypeManager.GetEnumUnderlyingType (rtype);
2765 if (oper != Operator.Addition) {
2766 Constant c = left as Constant;
2767 if (c == null || !c.IsDefaultValue)
2768 return null;
2769 } else {
2770 if (!Convert.ImplicitStandardConversionExists (left, underlying_type))
2771 return null;
2773 left = Convert.ImplicitConversionStandard (ec, left, underlying_type, left.Location);
2776 if (right is Constant)
2777 right = ((Constant) right).ConvertExplicitly (false, underlying_type);
2778 else
2779 right = EmptyCast.Create (right, underlying_type);
2781 } else {
2782 return null;
2786 // C# specification uses explicit cast syntax which means binary promotion
2787 // should happen, however it seems that csc does not do that
2789 if (!DoBinaryOperatorPromotion (ec)) {
2790 left = ltemp;
2791 right = rtemp;
2792 return null;
2795 Type res_type = null;
2796 if ((oper & Operator.BitwiseMask) != 0 || oper == Operator.Subtraction || oper == Operator.Addition) {
2797 Type promoted_type = lenum ? left.Type : right.Type;
2798 enum_conversion = Convert.ExplicitNumericConversion (
2799 new EmptyExpression (promoted_type), underlying_type);
2801 if (oper == Operator.Subtraction && renum && lenum)
2802 res_type = underlying_type;
2803 else if (oper == Operator.Addition && renum)
2804 res_type = rtype;
2805 else
2806 res_type = ltype;
2809 expr = ResolveOperatorPredefined (ec, standard_operators, true, res_type);
2810 if (!is_compound || expr == null)
2811 return expr;
2814 // TODO: Need to corectly implemented Coumpound Assigment for all operators
2815 // Section: 7.16.2
2817 if (Convert.ImplicitConversionExists (ec, left, rtype))
2818 return expr;
2820 if (!Convert.ImplicitConversionExists (ec, ltemp, rtype))
2821 return null;
2823 expr = Convert.ExplicitConversion (ec, expr, rtype, loc);
2824 return expr;
2828 // 7.9.6 Reference type equality operators
2830 Binary ResolveOperatorEqualityRerefence (EmitContext ec, Type l, Type r)
2833 // operator != (object a, object b)
2834 // operator == (object a, object b)
2837 // TODO: this method is almost equivalent to Convert.ImplicitReferenceConversion
2839 if (left.eclass == ExprClass.MethodGroup || right.eclass == ExprClass.MethodGroup)
2840 return null;
2842 type = TypeManager.bool_type;
2843 GenericConstraints constraints;
2845 bool lgen = TypeManager.IsGenericParameter (l);
2847 if (TypeManager.IsEqual (l, r)) {
2848 if (lgen) {
2850 // Only allow to compare same reference type parameter
2852 constraints = TypeManager.GetTypeParameterConstraints (l);
2853 if (constraints != null && constraints.IsReferenceType)
2854 return this;
2856 return null;
2859 if (l == TypeManager.anonymous_method_type)
2860 return null;
2862 if (TypeManager.IsValueType (l))
2863 return null;
2865 return this;
2868 bool rgen = TypeManager.IsGenericParameter (r);
2871 // a, Both operands are reference-type values or the value null
2872 // b, One operand is a value of type T where T is a type-parameter and
2873 // the other operand is the value null. Furthermore T does not have the
2874 // value type constrain
2876 if (left is NullLiteral || right is NullLiteral) {
2877 if (lgen) {
2878 constraints = TypeManager.GetTypeParameterConstraints (l);
2879 if (constraints != null && constraints.HasValueTypeConstraint)
2880 return null;
2882 left = new BoxedCast (left, TypeManager.object_type);
2883 return this;
2886 if (rgen) {
2887 constraints = TypeManager.GetTypeParameterConstraints (r);
2888 if (constraints != null && constraints.HasValueTypeConstraint)
2889 return null;
2891 right = new BoxedCast (right, TypeManager.object_type);
2892 return this;
2897 // An interface is converted to the object before the
2898 // standard conversion is applied. It's not clear from the
2899 // standard but it looks like it works like that.
2901 if (lgen) {
2902 constraints = TypeManager.GetTypeParameterConstraints (l);
2903 if (constraints == null || constraints.IsReferenceType)
2904 return null;
2905 } else if (l.IsInterface) {
2906 l = TypeManager.object_type;
2907 } else if (TypeManager.IsStruct (l)) {
2908 return null;
2911 if (rgen) {
2912 constraints = TypeManager.GetTypeParameterConstraints (r);
2913 if (constraints == null || constraints.IsReferenceType)
2914 return null;
2915 } else if (r.IsInterface) {
2916 r = TypeManager.object_type;
2917 } else if (TypeManager.IsStruct (r)) {
2918 return null;
2922 const string ref_comparison = "Possible unintended reference comparison. " +
2923 "Consider casting the {0} side of the expression to `string' to compare the values";
2926 // A standard implicit conversion exists from the type of either
2927 // operand to the type of the other operand
2929 if (Convert.ImplicitReferenceConversionExists (left, r)) {
2930 if (l == TypeManager.string_type)
2931 Report.Warning (253, 2, loc, ref_comparison, "right");
2933 return this;
2936 if (Convert.ImplicitReferenceConversionExists (right, l)) {
2937 if (r == TypeManager.string_type)
2938 Report.Warning (252, 2, loc, ref_comparison, "left");
2940 return this;
2943 return null;
2947 Expression ResolveOperatorPointer (EmitContext ec, Type l, Type r)
2950 // bool operator == (void* x, void* y);
2951 // bool operator != (void* x, void* y);
2952 // bool operator < (void* x, void* y);
2953 // bool operator > (void* x, void* y);
2954 // bool operator <= (void* x, void* y);
2955 // bool operator >= (void* x, void* y);
2957 if ((oper & Operator.ComparisonMask) != 0) {
2958 Expression temp;
2959 if (!l.IsPointer) {
2960 temp = Convert.ImplicitConversion (ec, left, r, left.Location);
2961 if (temp == null)
2962 return null;
2963 left = temp;
2966 if (!r.IsPointer) {
2967 temp = Convert.ImplicitConversion (ec, right, l, right.Location);
2968 if (temp == null)
2969 return null;
2970 right = temp;
2973 type = TypeManager.bool_type;
2974 return this;
2977 if (pointer_operators == null)
2978 CreatePointerOperatorsTable ();
2980 return ResolveOperatorPredefined (ec, pointer_operators, false, null);
2984 // Build-in operators method overloading
2986 protected virtual Expression ResolveOperatorPredefined (EmitContext ec, PredefinedOperator [] operators, bool primitives_only, Type enum_type)
2988 PredefinedOperator best_operator = null;
2989 Type l = left.Type;
2990 Type r = right.Type;
2991 Operator oper_mask = oper & ~Operator.ValuesOnlyMask;
2993 foreach (PredefinedOperator po in operators) {
2994 if ((po.OperatorsMask & oper_mask) == 0)
2995 continue;
2997 if (primitives_only) {
2998 if (!po.IsPrimitiveApplicable (l, r))
2999 continue;
3000 } else {
3001 if (!po.IsApplicable (ec, left, right))
3002 continue;
3005 if (best_operator == null) {
3006 best_operator = po;
3007 if (primitives_only)
3008 break;
3010 continue;
3013 best_operator = po.ResolveBetterOperator (ec, best_operator);
3015 if (best_operator == null) {
3016 Report.Error (34, loc, "Operator `{0}' is ambiguous on operands of type `{1}' and `{2}'",
3017 OperName (oper), left.GetSignatureForError (), right.GetSignatureForError ());
3019 best_operator = po;
3020 break;
3024 if (best_operator == null)
3025 return null;
3027 Expression expr = best_operator.ConvertResult (ec, this);
3028 if (enum_type == null)
3029 return expr;
3032 // HACK: required by enum_conversion
3034 expr.Type = enum_type;
3035 return EmptyCast.Create (expr, enum_type);
3039 // Performs user-operator overloading
3041 protected virtual Expression ResolveUserOperator (EmitContext ec, Type l, Type r)
3043 Operator user_oper;
3044 if (oper == Operator.LogicalAnd)
3045 user_oper = Operator.BitwiseAnd;
3046 else if (oper == Operator.LogicalOr)
3047 user_oper = Operator.BitwiseOr;
3048 else
3049 user_oper = oper;
3051 string op = GetOperatorMetadataName (user_oper);
3053 MethodGroupExpr left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc) as MethodGroupExpr;
3054 MethodGroupExpr right_operators = null;
3056 if (!TypeManager.IsEqual (r, l)) {
3057 right_operators = MemberLookup (ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc) as MethodGroupExpr;
3058 if (right_operators == null && left_operators == null)
3059 return null;
3060 } else if (left_operators == null) {
3061 return null;
3064 ArrayList args = new ArrayList (2);
3065 Argument larg = new Argument (left);
3066 args.Add (larg);
3067 Argument rarg = new Argument (right);
3068 args.Add (rarg);
3070 MethodGroupExpr union;
3073 // User-defined operator implementations always take precedence
3074 // over predefined operator implementations
3076 if (left_operators != null && right_operators != null) {
3077 if (IsPredefinedUserOperator (l, user_oper)) {
3078 union = right_operators.OverloadResolve (ec, ref args, true, loc);
3079 if (union == null)
3080 union = left_operators;
3081 } else if (IsPredefinedUserOperator (r, user_oper)) {
3082 union = left_operators.OverloadResolve (ec, ref args, true, loc);
3083 if (union == null)
3084 union = right_operators;
3085 } else {
3086 union = MethodGroupExpr.MakeUnionSet (left_operators, right_operators, loc);
3088 } else if (left_operators != null) {
3089 union = left_operators;
3090 } else {
3091 union = right_operators;
3094 union = union.OverloadResolve (ec, ref args, true, loc);
3095 if (union == null)
3096 return null;
3098 Expression oper_expr;
3100 // TODO: CreateExpressionTree is allocated every time
3101 if (user_oper != oper) {
3102 oper_expr = new ConditionalLogicalOperator (union, args, CreateExpressionTree,
3103 oper == Operator.LogicalAnd, loc).Resolve (ec);
3104 } else {
3105 oper_expr = new UserOperatorCall (union, args, CreateExpressionTree, loc);
3108 // This is used to check if a test 'x == null' can be optimized to a reference equals,
3109 // and not invoke user operator
3111 if ((oper & Operator.EqualityMask) != 0) {
3112 if ((left is NullLiteral && IsBuildInEqualityOperator (r)) ||
3113 (right is NullLiteral && IsBuildInEqualityOperator (l))) {
3114 type = TypeManager.bool_type;
3115 if (left is NullLiteral || right is NullLiteral)
3116 oper_expr = ReducedExpression.Create (this, oper_expr).Resolve (ec);
3117 } else if (union.DeclaringType == TypeManager.delegate_type && l != r) {
3119 // Two System.Delegate(s) are never equal
3121 return null;
3126 left = larg.Expr;
3127 right = rarg.Expr;
3128 return oper_expr;
3131 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3133 return null;
3136 private void CheckUselessComparison (Constant c, Type type)
3138 if (c == null || !IsTypeIntegral (type)
3139 || c is StringConstant
3140 || c is BoolConstant
3141 || c is FloatConstant
3142 || c is DoubleConstant
3143 || c is DecimalConstant
3145 return;
3147 long value = 0;
3149 if (c is ULongConstant) {
3150 ulong uvalue = ((ULongConstant) c).Value;
3151 if (uvalue > long.MaxValue) {
3152 if (type == TypeManager.byte_type ||
3153 type == TypeManager.sbyte_type ||
3154 type == TypeManager.short_type ||
3155 type == TypeManager.ushort_type ||
3156 type == TypeManager.int32_type ||
3157 type == TypeManager.uint32_type ||
3158 type == TypeManager.int64_type ||
3159 type == TypeManager.char_type)
3160 WarnUselessComparison (type);
3161 return;
3163 value = (long) uvalue;
3165 else if (c is ByteConstant)
3166 value = ((ByteConstant) c).Value;
3167 else if (c is SByteConstant)
3168 value = ((SByteConstant) c).Value;
3169 else if (c is ShortConstant)
3170 value = ((ShortConstant) c).Value;
3171 else if (c is UShortConstant)
3172 value = ((UShortConstant) c).Value;
3173 else if (c is IntConstant)
3174 value = ((IntConstant) c).Value;
3175 else if (c is UIntConstant)
3176 value = ((UIntConstant) c).Value;
3177 else if (c is LongConstant)
3178 value = ((LongConstant) c).Value;
3179 else if (c is CharConstant)
3180 value = ((CharConstant)c).Value;
3182 if (value == 0)
3183 return;
3185 if (IsValueOutOfRange (value, type))
3186 WarnUselessComparison (type);
3189 static bool IsValueOutOfRange (long value, Type type)
3191 if (IsTypeUnsigned (type) && value < 0)
3192 return true;
3193 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
3194 type == TypeManager.byte_type && value >= 0x100 ||
3195 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
3196 type == TypeManager.ushort_type && value >= 0x10000 ||
3197 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
3198 type == TypeManager.uint32_type && value >= 0x100000000;
3201 static bool IsBuildInEqualityOperator (Type t)
3203 return t == TypeManager.object_type || t == TypeManager.string_type ||
3204 t == TypeManager.delegate_type || TypeManager.IsDelegateType (t);
3207 static bool IsPredefinedUserOperator (Type t, Operator op)
3210 // Some predefined types have user operators
3212 return (op & Operator.EqualityMask) != 0 && (t == TypeManager.string_type || t == TypeManager.decimal_type);
3215 private static bool IsTypeIntegral (Type type)
3217 return type == TypeManager.uint64_type ||
3218 type == TypeManager.int64_type ||
3219 type == TypeManager.uint32_type ||
3220 type == TypeManager.int32_type ||
3221 type == TypeManager.ushort_type ||
3222 type == TypeManager.short_type ||
3223 type == TypeManager.sbyte_type ||
3224 type == TypeManager.byte_type ||
3225 type == TypeManager.char_type;
3228 private static bool IsTypeUnsigned (Type type)
3230 return type == TypeManager.uint64_type ||
3231 type == TypeManager.uint32_type ||
3232 type == TypeManager.ushort_type ||
3233 type == TypeManager.byte_type ||
3234 type == TypeManager.char_type;
3237 private void WarnUselessComparison (Type type)
3239 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}'",
3240 TypeManager.CSharpName (type));
3243 /// <remarks>
3244 /// EmitBranchable is called from Statement.EmitBoolExpression in the
3245 /// context of a conditional bool expression. This function will return
3246 /// false if it is was possible to use EmitBranchable, or true if it was.
3248 /// The expression's code is generated, and we will generate a branch to `target'
3249 /// if the resulting expression value is equal to isTrue
3250 /// </remarks>
3251 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
3253 ILGenerator ig = ec.ig;
3256 // This is more complicated than it looks, but its just to avoid
3257 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
3258 // but on top of that we want for == and != to use a special path
3259 // if we are comparing against null
3261 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
3262 bool my_on_true = oper == Operator.Inequality ? on_true : !on_true;
3265 // put the constant on the rhs, for simplicity
3267 if (left is Constant) {
3268 Expression swap = right;
3269 right = left;
3270 left = swap;
3273 if (((Constant) right).IsZeroInteger) {
3274 left.EmitBranchable (ec, target, my_on_true);
3275 return;
3277 if (right.Type == TypeManager.bool_type) {
3278 // right is a boolean, and it's not 'false' => it is 'true'
3279 left.EmitBranchable (ec, target, !my_on_true);
3280 return;
3283 } else if (oper == Operator.LogicalAnd) {
3285 if (on_true) {
3286 Label tests_end = ig.DefineLabel ();
3288 left.EmitBranchable (ec, tests_end, false);
3289 right.EmitBranchable (ec, target, true);
3290 ig.MarkLabel (tests_end);
3291 } else {
3293 // This optimizes code like this
3294 // if (true && i > 4)
3296 if (!(left is Constant))
3297 left.EmitBranchable (ec, target, false);
3299 if (!(right is Constant))
3300 right.EmitBranchable (ec, target, false);
3303 return;
3305 } else if (oper == Operator.LogicalOr){
3306 if (on_true) {
3307 left.EmitBranchable (ec, target, true);
3308 right.EmitBranchable (ec, target, true);
3310 } else {
3311 Label tests_end = ig.DefineLabel ();
3312 left.EmitBranchable (ec, tests_end, true);
3313 right.EmitBranchable (ec, target, false);
3314 ig.MarkLabel (tests_end);
3317 return;
3319 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
3320 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
3321 oper == Operator.Equality || oper == Operator.Inequality)) {
3322 base.EmitBranchable (ec, target, on_true);
3323 return;
3326 left.Emit (ec);
3327 right.Emit (ec);
3329 Type t = left.Type;
3330 bool is_float = IsFloat (t);
3331 bool is_unsigned = is_float || IsUnsigned (t);
3333 switch (oper){
3334 case Operator.Equality:
3335 if (on_true)
3336 ig.Emit (OpCodes.Beq, target);
3337 else
3338 ig.Emit (OpCodes.Bne_Un, target);
3339 break;
3341 case Operator.Inequality:
3342 if (on_true)
3343 ig.Emit (OpCodes.Bne_Un, target);
3344 else
3345 ig.Emit (OpCodes.Beq, target);
3346 break;
3348 case Operator.LessThan:
3349 if (on_true)
3350 if (is_unsigned && !is_float)
3351 ig.Emit (OpCodes.Blt_Un, target);
3352 else
3353 ig.Emit (OpCodes.Blt, target);
3354 else
3355 if (is_unsigned)
3356 ig.Emit (OpCodes.Bge_Un, target);
3357 else
3358 ig.Emit (OpCodes.Bge, target);
3359 break;
3361 case Operator.GreaterThan:
3362 if (on_true)
3363 if (is_unsigned && !is_float)
3364 ig.Emit (OpCodes.Bgt_Un, target);
3365 else
3366 ig.Emit (OpCodes.Bgt, target);
3367 else
3368 if (is_unsigned)
3369 ig.Emit (OpCodes.Ble_Un, target);
3370 else
3371 ig.Emit (OpCodes.Ble, target);
3372 break;
3374 case Operator.LessThanOrEqual:
3375 if (on_true)
3376 if (is_unsigned && !is_float)
3377 ig.Emit (OpCodes.Ble_Un, target);
3378 else
3379 ig.Emit (OpCodes.Ble, target);
3380 else
3381 if (is_unsigned)
3382 ig.Emit (OpCodes.Bgt_Un, target);
3383 else
3384 ig.Emit (OpCodes.Bgt, target);
3385 break;
3388 case Operator.GreaterThanOrEqual:
3389 if (on_true)
3390 if (is_unsigned && !is_float)
3391 ig.Emit (OpCodes.Bge_Un, target);
3392 else
3393 ig.Emit (OpCodes.Bge, target);
3394 else
3395 if (is_unsigned)
3396 ig.Emit (OpCodes.Blt_Un, target);
3397 else
3398 ig.Emit (OpCodes.Blt, target);
3399 break;
3400 default:
3401 throw new InternalErrorException (oper.ToString ());
3405 public override void Emit (EmitContext ec)
3407 EmitOperator (ec, left.Type);
3410 protected virtual void EmitOperator (EmitContext ec, Type l)
3412 ILGenerator ig = ec.ig;
3415 // Handle short-circuit operators differently
3416 // than the rest
3418 if ((oper & Operator.LogicalMask) != 0) {
3419 Label load_result = ig.DefineLabel ();
3420 Label end = ig.DefineLabel ();
3422 bool is_or = oper == Operator.LogicalOr;
3423 left.EmitBranchable (ec, load_result, is_or);
3424 right.Emit (ec);
3425 ig.Emit (OpCodes.Br_S, end);
3427 ig.MarkLabel (load_result);
3428 ig.Emit (is_or ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
3429 ig.MarkLabel (end);
3430 return;
3433 left.Emit (ec);
3436 // Optimize zero-based operations
3438 // TODO: Implement more optimizations, but it should probably go to PredefinedOperators
3440 if ((oper & Operator.ShiftMask) != 0 || oper == Operator.Addition || oper == Operator.Subtraction) {
3441 Constant rc = right as Constant;
3442 if (rc != null && rc.IsDefaultValue) {
3443 return;
3447 right.Emit (ec);
3448 EmitOperatorOpcode (ec, oper, l);
3451 // Nullable enum could require underlying type cast and we cannot simply wrap binary
3452 // expression because that would wrap lifted binary operation
3454 if (enum_conversion != null)
3455 enum_conversion.Emit (ec);
3458 public override void EmitSideEffect (EmitContext ec)
3460 if ((oper & Operator.LogicalMask) != 0 ||
3461 (ec.CheckState && (oper == Operator.Multiply || oper == Operator.Addition || oper == Operator.Subtraction))) {
3462 base.EmitSideEffect (ec);
3463 } else {
3464 left.EmitSideEffect (ec);
3465 right.EmitSideEffect (ec);
3469 protected override void CloneTo (CloneContext clonectx, Expression t)
3471 Binary target = (Binary) t;
3473 target.left = left.Clone (clonectx);
3474 target.right = right.Clone (clonectx);
3477 public override Expression CreateExpressionTree (EmitContext ec)
3479 return CreateExpressionTree (ec, null);
3482 Expression CreateExpressionTree (EmitContext ec, MethodGroupExpr method)
3484 string method_name;
3485 bool lift_arg = false;
3487 switch (oper) {
3488 case Operator.Addition:
3489 if (method == null && ec.CheckState && !IsFloat (type))
3490 method_name = "AddChecked";
3491 else
3492 method_name = "Add";
3493 break;
3494 case Operator.BitwiseAnd:
3495 method_name = "And";
3496 break;
3497 case Operator.BitwiseOr:
3498 method_name = "Or";
3499 break;
3500 case Operator.Division:
3501 method_name = "Divide";
3502 break;
3503 case Operator.Equality:
3504 method_name = "Equal";
3505 lift_arg = true;
3506 break;
3507 case Operator.ExclusiveOr:
3508 method_name = "ExclusiveOr";
3509 break;
3510 case Operator.GreaterThan:
3511 method_name = "GreaterThan";
3512 lift_arg = true;
3513 break;
3514 case Operator.GreaterThanOrEqual:
3515 method_name = "GreaterThanOrEqual";
3516 lift_arg = true;
3517 break;
3518 case Operator.Inequality:
3519 method_name = "NotEqual";
3520 lift_arg = true;
3521 break;
3522 case Operator.LeftShift:
3523 method_name = "LeftShift";
3524 break;
3525 case Operator.LessThan:
3526 method_name = "LessThan";
3527 lift_arg = true;
3528 break;
3529 case Operator.LessThanOrEqual:
3530 method_name = "LessThanOrEqual";
3531 lift_arg = true;
3532 break;
3533 case Operator.LogicalAnd:
3534 method_name = "AndAlso";
3535 break;
3536 case Operator.LogicalOr:
3537 method_name = "OrElse";
3538 break;
3539 case Operator.Modulus:
3540 method_name = "Modulo";
3541 break;
3542 case Operator.Multiply:
3543 if (method == null && ec.CheckState && !IsFloat (type))
3544 method_name = "MultiplyChecked";
3545 else
3546 method_name = "Multiply";
3547 break;
3548 case Operator.RightShift:
3549 method_name = "RightShift";
3550 break;
3551 case Operator.Subtraction:
3552 if (method == null && ec.CheckState && !IsFloat (type))
3553 method_name = "SubtractChecked";
3554 else
3555 method_name = "Subtract";
3556 break;
3558 default:
3559 throw new InternalErrorException ("Unknown expression tree binary operator " + oper);
3562 ArrayList args = new ArrayList (2);
3563 args.Add (new Argument (left.CreateExpressionTree (ec)));
3564 args.Add (new Argument (right.CreateExpressionTree (ec)));
3565 if (method != null) {
3566 if (lift_arg)
3567 args.Add (new Argument (new BoolConstant (false, loc)));
3569 args.Add (new Argument (method.CreateExpressionTree (ec)));
3572 return CreateExpressionFactoryCall (method_name, args);
3577 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
3578 // b, c, d... may be strings or objects.
3580 public class StringConcat : Expression {
3581 ArrayList arguments;
3583 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
3585 this.loc = loc;
3586 type = TypeManager.string_type;
3587 eclass = ExprClass.Value;
3589 arguments = new ArrayList (2);
3590 Append (ec, left);
3591 Append (ec, right);
3594 public override Expression CreateExpressionTree (EmitContext ec)
3596 Argument arg = (Argument) arguments [0];
3597 return CreateExpressionAddCall (ec, arg, arg.Expr.CreateExpressionTree (ec), 1);
3601 // Creates nested calls tree from an array of arguments used for IL emit
3603 Expression CreateExpressionAddCall (EmitContext ec, Argument left, Expression left_etree, int pos)
3605 ArrayList concat_args = new ArrayList (2);
3606 ArrayList add_args = new ArrayList (3);
3608 concat_args.Add (left);
3609 add_args.Add (new Argument (left_etree));
3611 concat_args.Add (arguments [pos]);
3612 add_args.Add (new Argument (((Argument) arguments [pos]).Expr.CreateExpressionTree (ec)));
3614 MethodGroupExpr method = CreateConcatMemberExpression ().Resolve (ec) as MethodGroupExpr;
3615 if (method == null)
3616 return null;
3618 method = method.OverloadResolve (ec, ref concat_args, false, loc);
3619 if (method == null)
3620 return null;
3622 add_args.Add (new Argument (method.CreateExpressionTree (ec)));
3624 Expression expr = CreateExpressionFactoryCall ("Add", add_args);
3625 if (++pos == arguments.Count)
3626 return expr;
3628 left = new Argument (new EmptyExpression (method.Type));
3629 return CreateExpressionAddCall (ec, left, expr, pos);
3632 public override Expression DoResolve (EmitContext ec)
3634 return this;
3637 public void Append (EmitContext ec, Expression operand)
3640 // Constant folding
3642 StringConstant sc = operand as StringConstant;
3643 if (sc != null) {
3644 if (arguments.Count != 0) {
3645 Argument last_argument = (Argument) arguments [arguments.Count - 1];
3646 StringConstant last_expr_constant = last_argument.Expr as StringConstant;
3647 if (last_expr_constant != null) {
3648 last_argument.Expr = new StringConstant (
3649 last_expr_constant.Value + sc.Value, sc.Location);
3650 return;
3653 } else {
3655 // Multiple (3+) concatenation are resolved as multiple StringConcat instances
3657 StringConcat concat_oper = operand as StringConcat;
3658 if (concat_oper != null) {
3659 arguments.AddRange (concat_oper.arguments);
3660 return;
3664 arguments.Add (new Argument (operand));
3667 Expression CreateConcatMemberExpression ()
3669 return new MemberAccess (new MemberAccess (new QualifiedAliasMember ("global", "System", loc), "String", loc), "Concat", loc);
3672 public override void Emit (EmitContext ec)
3674 Expression concat = new Invocation (CreateConcatMemberExpression (), arguments, true);
3675 concat = concat.Resolve (ec);
3676 if (concat != null)
3677 concat.Emit (ec);
3680 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3682 foreach (Argument a in arguments)
3683 a.Expr.MutateHoistedGenericType (storey);
3688 // User-defined conditional logical operator
3690 public class ConditionalLogicalOperator : UserOperatorCall {
3691 readonly bool is_and;
3692 Expression oper;
3694 public ConditionalLogicalOperator (MethodGroupExpr oper_method, ArrayList arguments,
3695 ExpressionTreeExpression expr_tree, bool is_and, Location loc)
3696 : base (oper_method, arguments, expr_tree, loc)
3698 this.is_and = is_and;
3701 public override Expression DoResolve (EmitContext ec)
3703 MethodInfo method = (MethodInfo)mg;
3704 type = TypeManager.TypeToCoreType (method.ReturnType);
3705 AParametersCollection pd = TypeManager.GetParameterData (method);
3706 if (!TypeManager.IsEqual (type, type) || !TypeManager.IsEqual (type, pd.Types [0]) || !TypeManager.IsEqual (type, pd.Types [1])) {
3707 Report.Error (217, loc,
3708 "A user-defined operator `{0}' must have parameters and return values of the same type in order to be applicable as a short circuit operator",
3709 TypeManager.CSharpSignature (method));
3710 return null;
3713 Expression left_dup = new EmptyExpression (type);
3714 Expression op_true = GetOperatorTrue (ec, left_dup, loc);
3715 Expression op_false = GetOperatorFalse (ec, left_dup, loc);
3716 if (op_true == null || op_false == null) {
3717 Report.Error (218, loc,
3718 "The type `{0}' must have operator `true' and operator `false' defined when `{1}' is used as a short circuit operator",
3719 TypeManager.CSharpName (type), TypeManager.CSharpSignature (method));
3720 return null;
3723 oper = is_and ? op_false : op_true;
3724 eclass = ExprClass.Value;
3725 return this;
3728 public override void Emit (EmitContext ec)
3730 ILGenerator ig = ec.ig;
3731 Label end_target = ig.DefineLabel ();
3734 // Emit and duplicate left argument
3736 ((Argument)arguments [0]).Expr.Emit (ec);
3737 ig.Emit (OpCodes.Dup);
3738 arguments.RemoveAt (0);
3740 oper.EmitBranchable (ec, end_target, true);
3741 base.Emit (ec);
3742 ig.MarkLabel (end_target);
3746 public class PointerArithmetic : Expression {
3747 Expression left, right;
3748 Binary.Operator op;
3751 // We assume that `l' is always a pointer
3753 public PointerArithmetic (Binary.Operator op, Expression l, Expression r, Type t, Location loc)
3755 type = t;
3756 this.loc = loc;
3757 left = l;
3758 right = r;
3759 this.op = op;
3762 public override Expression CreateExpressionTree (EmitContext ec)
3764 Error_PointerInsideExpressionTree ();
3765 return null;
3768 public override Expression DoResolve (EmitContext ec)
3770 eclass = ExprClass.Variable;
3772 if (left.Type == TypeManager.void_ptr_type) {
3773 Error (242, "The operation in question is undefined on void pointers");
3774 return null;
3777 return this;
3780 public override void Emit (EmitContext ec)
3782 Type op_type = left.Type;
3783 ILGenerator ig = ec.ig;
3785 // It must be either array or fixed buffer
3786 Type element;
3787 if (TypeManager.HasElementType (op_type)) {
3788 element = TypeManager.GetElementType (op_type);
3789 } else {
3790 FieldExpr fe = left as FieldExpr;
3791 if (fe != null)
3792 element = AttributeTester.GetFixedBuffer (fe.FieldInfo).ElementType;
3793 else
3794 element = op_type;
3797 int size = GetTypeSize (element);
3798 Type rtype = right.Type;
3800 if ((op & Binary.Operator.SubtractionMask) != 0 && rtype.IsPointer){
3802 // handle (pointer - pointer)
3804 left.Emit (ec);
3805 right.Emit (ec);
3806 ig.Emit (OpCodes.Sub);
3808 if (size != 1){
3809 if (size == 0)
3810 ig.Emit (OpCodes.Sizeof, element);
3811 else
3812 IntLiteral.EmitInt (ig, size);
3813 ig.Emit (OpCodes.Div);
3815 ig.Emit (OpCodes.Conv_I8);
3816 } else {
3818 // handle + and - on (pointer op int)
3820 Constant left_const = left as Constant;
3821 if (left_const != null) {
3823 // Optimize ((T*)null) pointer operations
3825 if (left_const.IsDefaultValue) {
3826 left = EmptyExpression.Null;
3827 } else {
3828 left_const = null;
3832 left.Emit (ec);
3834 Constant right_const = right as Constant;
3835 if (right_const != null) {
3837 // Optimize 0-based arithmetic
3839 if (right_const.IsDefaultValue)
3840 return;
3842 if (size != 0) {
3843 right = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3844 if (right == null)
3845 return;
3846 } else {
3847 ig.Emit (OpCodes.Sizeof, element);
3848 right = EmptyExpression.Null;
3852 right.Emit (ec);
3853 if (rtype == TypeManager.sbyte_type || rtype == TypeManager.byte_type ||
3854 rtype == TypeManager.short_type || rtype == TypeManager.ushort_type) {
3855 ig.Emit (OpCodes.Conv_I);
3856 } else if (rtype == TypeManager.uint32_type) {
3857 ig.Emit (OpCodes.Conv_U);
3860 if (right_const == null && size != 1){
3861 if (size == 0)
3862 ig.Emit (OpCodes.Sizeof, element);
3863 else
3864 IntLiteral.EmitInt (ig, size);
3865 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3866 ig.Emit (OpCodes.Conv_I8);
3868 Binary.EmitOperatorOpcode (ec, Binary.Operator.Multiply, rtype);
3871 if (left_const == null) {
3872 if (rtype == TypeManager.int64_type)
3873 ig.Emit (OpCodes.Conv_I);
3874 else if (rtype == TypeManager.uint64_type)
3875 ig.Emit (OpCodes.Conv_U);
3877 Binary.EmitOperatorOpcode (ec, op, op_type);
3883 /// <summary>
3884 /// Implements the ternary conditional operator (?:)
3885 /// </summary>
3886 public class Conditional : Expression {
3887 Expression expr, true_expr, false_expr;
3889 public Conditional (Expression expr, Expression true_expr, Expression false_expr)
3891 this.expr = expr;
3892 this.true_expr = true_expr;
3893 this.false_expr = false_expr;
3894 this.loc = expr.Location;
3897 public Expression Expr {
3898 get {
3899 return expr;
3903 public Expression TrueExpr {
3904 get {
3905 return true_expr;
3909 public Expression FalseExpr {
3910 get {
3911 return false_expr;
3915 public override Expression CreateExpressionTree (EmitContext ec)
3917 ArrayList args = new ArrayList (3);
3918 args.Add (new Argument (expr.CreateExpressionTree (ec)));
3919 args.Add (new Argument (true_expr.CreateExpressionTree (ec)));
3920 args.Add (new Argument (false_expr.CreateExpressionTree (ec)));
3921 return CreateExpressionFactoryCall ("Condition", args);
3924 public override Expression DoResolve (EmitContext ec)
3926 expr = expr.Resolve (ec);
3928 if (expr == null)
3929 return null;
3931 if (expr.Type != TypeManager.bool_type){
3932 expr = Expression.ResolveBoolean (
3933 ec, expr, loc);
3935 if (expr == null)
3936 return null;
3939 Assign ass = expr as Assign;
3940 if (ass != null && ass.Source is Constant) {
3941 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3944 true_expr = true_expr.Resolve (ec);
3945 false_expr = false_expr.Resolve (ec);
3947 if (true_expr == null || false_expr == null)
3948 return null;
3950 eclass = ExprClass.Value;
3951 Type true_type = true_expr.Type;
3952 Type false_type = false_expr.Type;
3953 type = true_type;
3956 // First, if an implicit conversion exists from true_expr
3957 // to false_expr, then the result type is of type false_expr.Type
3959 if (!TypeManager.IsEqual (true_type, false_type)) {
3960 Expression conv = Convert.ImplicitConversion (ec, true_expr, false_type, loc);
3961 if (conv != null) {
3963 // Check if both can convert implicitl to each other's type
3965 if (Convert.ImplicitConversion (ec, false_expr, true_type, loc) != null) {
3966 Error (172,
3967 "Can not compute type of conditional expression " +
3968 "as `" + TypeManager.CSharpName (true_expr.Type) +
3969 "' and `" + TypeManager.CSharpName (false_expr.Type) +
3970 "' convert implicitly to each other");
3971 return null;
3973 type = false_type;
3974 true_expr = conv;
3975 } else if ((conv = Convert.ImplicitConversion (ec, false_expr, true_type, loc)) != null) {
3976 false_expr = conv;
3977 } else {
3978 Report.Error (173, loc,
3979 "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3980 true_expr.GetSignatureForError (), false_expr.GetSignatureForError ());
3981 return null;
3985 // Dead code optimalization
3986 Constant c = expr as Constant;
3987 if (c != null){
3988 bool is_false = c.IsDefaultValue;
3989 Report.Warning (429, 4, is_false ? true_expr.Location : false_expr.Location, "Unreachable expression code detected");
3990 return ReducedExpression.Create (is_false ? false_expr : true_expr, this).Resolve (ec);
3993 return this;
3996 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3998 expr.MutateHoistedGenericType (storey);
3999 true_expr.MutateHoistedGenericType (storey);
4000 false_expr.MutateHoistedGenericType (storey);
4001 type = storey.MutateType (type);
4004 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
4006 return null;
4009 public override void Emit (EmitContext ec)
4011 ILGenerator ig = ec.ig;
4012 Label false_target = ig.DefineLabel ();
4013 Label end_target = ig.DefineLabel ();
4015 expr.EmitBranchable (ec, false_target, false);
4016 true_expr.Emit (ec);
4018 if (type.IsInterface) {
4019 LocalBuilder temp = ec.GetTemporaryLocal (type);
4020 ig.Emit (OpCodes.Stloc, temp);
4021 ig.Emit (OpCodes.Ldloc, temp);
4022 ec.FreeTemporaryLocal (temp, type);
4025 ig.Emit (OpCodes.Br, end_target);
4026 ig.MarkLabel (false_target);
4027 false_expr.Emit (ec);
4028 ig.MarkLabel (end_target);
4031 protected override void CloneTo (CloneContext clonectx, Expression t)
4033 Conditional target = (Conditional) t;
4035 target.expr = expr.Clone (clonectx);
4036 target.true_expr = true_expr.Clone (clonectx);
4037 target.false_expr = false_expr.Clone (clonectx);
4041 public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation, IVariableReference {
4042 LocalTemporary temp;
4044 #region Abstract
4045 public abstract HoistedVariable GetHoistedVariable (EmitContext ec);
4046 public abstract bool IsFixed { get; }
4047 public abstract bool IsRef { get; }
4048 public abstract string Name { get; }
4049 public abstract void SetHasAddressTaken ();
4052 // Variable IL data, it has to be protected to encapsulate hoisted variables
4054 protected abstract ILocalVariable Variable { get; }
4057 // Variable flow-analysis data
4059 public abstract VariableInfo VariableInfo { get; }
4060 #endregion
4062 public void AddressOf (EmitContext ec, AddressOp mode)
4064 HoistedVariable hv = GetHoistedVariable (ec);
4065 if (hv != null) {
4066 hv.AddressOf (ec, mode);
4067 return;
4070 Variable.EmitAddressOf (ec);
4073 public override void Emit (EmitContext ec)
4075 Emit (ec, false);
4078 public override void EmitSideEffect (EmitContext ec)
4080 // do nothing
4084 // This method is used by parameters that are references, that are
4085 // being passed as references: we only want to pass the pointer (that
4086 // is already stored in the parameter, not the address of the pointer,
4087 // and not the value of the variable).
4089 public void EmitLoad (EmitContext ec)
4091 Variable.Emit (ec);
4094 public void Emit (EmitContext ec, bool leave_copy)
4096 Report.Debug (64, "VARIABLE EMIT", this, Variable, type, IsRef, loc);
4098 HoistedVariable hv = GetHoistedVariable (ec);
4099 if (hv != null) {
4100 hv.Emit (ec, leave_copy);
4101 return;
4104 EmitLoad (ec);
4106 if (IsRef) {
4108 // If we are a reference, we loaded on the stack a pointer
4109 // Now lets load the real value
4111 LoadFromPtr (ec.ig, type);
4114 if (leave_copy) {
4115 ec.ig.Emit (OpCodes.Dup);
4117 if (IsRef) {
4118 temp = new LocalTemporary (Type);
4119 temp.Store (ec);
4124 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
4125 bool prepare_for_load)
4127 HoistedVariable hv = GetHoistedVariable (ec);
4128 if (hv != null) {
4129 hv.EmitAssign (ec, source, leave_copy, prepare_for_load);
4130 return;
4133 New n_source = source as New;
4134 if (n_source != null) {
4135 if (!n_source.Emit (ec, this)) {
4136 if (leave_copy)
4137 EmitLoad (ec);
4138 return;
4140 } else {
4141 if (IsRef)
4142 EmitLoad (ec);
4144 source.Emit (ec);
4147 if (leave_copy) {
4148 ec.ig.Emit (OpCodes.Dup);
4149 if (IsRef) {
4150 temp = new LocalTemporary (Type);
4151 temp.Store (ec);
4155 if (IsRef)
4156 StoreFromPtr (ec.ig, type);
4157 else
4158 Variable.EmitAssign (ec);
4160 if (temp != null) {
4161 temp.Emit (ec);
4162 temp.Release (ec);
4166 public bool IsHoisted {
4167 get { return GetHoistedVariable (null) != null; }
4170 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4172 type = storey.MutateType (type);
4176 /// <summary>
4177 /// Local variables
4178 /// </summary>
4179 public class LocalVariableReference : VariableReference {
4180 readonly string name;
4181 public Block Block;
4182 public LocalInfo local_info;
4183 bool is_readonly;
4185 public LocalVariableReference (Block block, string name, Location l)
4187 Block = block;
4188 this.name = name;
4189 loc = l;
4190 eclass = ExprClass.Variable;
4194 // Setting `is_readonly' to false will allow you to create a writable
4195 // reference to a read-only variable. This is used by foreach and using.
4197 public LocalVariableReference (Block block, string name, Location l,
4198 LocalInfo local_info, bool is_readonly)
4199 : this (block, name, l)
4201 this.local_info = local_info;
4202 this.is_readonly = is_readonly;
4205 public override VariableInfo VariableInfo {
4206 get { return local_info.VariableInfo; }
4209 public override HoistedVariable GetHoistedVariable (EmitContext ec)
4211 return local_info.HoistedVariableReference;
4215 // A local variable is always fixed
4217 public override bool IsFixed {
4218 get { return true; }
4221 public override bool IsRef {
4222 get { return false; }
4225 public bool IsReadOnly {
4226 get { return is_readonly; }
4229 public override string Name {
4230 get { return name; }
4233 public bool VerifyAssigned (EmitContext ec)
4235 VariableInfo variable_info = local_info.VariableInfo;
4236 return variable_info == null || variable_info.IsAssigned (ec, loc);
4239 void ResolveLocalInfo ()
4241 if (local_info == null) {
4242 local_info = Block.GetLocalInfo (Name);
4243 type = local_info.VariableType;
4244 is_readonly = local_info.ReadOnly;
4248 public override void SetHasAddressTaken ()
4250 local_info.AddressTaken = true;
4253 public override Expression CreateExpressionTree (EmitContext ec)
4255 HoistedVariable hv = GetHoistedVariable (ec);
4256 if (hv != null)
4257 return hv.CreateExpressionTree (ec);
4259 ArrayList arg = new ArrayList (1);
4260 arg.Add (new Argument (this));
4261 return CreateExpressionFactoryCall ("Constant", arg);
4264 Expression DoResolveBase (EmitContext ec)
4266 type = local_info.VariableType;
4268 Expression e = Block.GetConstantExpression (Name);
4269 if (e != null)
4270 return e.Resolve (ec);
4272 VerifyAssigned (ec);
4275 // If we are referencing a variable from the external block
4276 // flag it for capturing
4278 if (ec.MustCaptureVariable (local_info)) {
4279 if (local_info.AddressTaken)
4280 AnonymousMethodExpression.Error_AddressOfCapturedVar (this, loc);
4282 if (ec.IsVariableCapturingRequired) {
4283 AnonymousMethodStorey storey = local_info.Block.Explicit.CreateAnonymousMethodStorey (ec);
4284 storey.CaptureLocalVariable (ec, local_info);
4288 return this;
4291 public override Expression DoResolve (EmitContext ec)
4293 ResolveLocalInfo ();
4294 local_info.Used = true;
4296 if (type == null && local_info.Type is VarExpr) {
4297 local_info.VariableType = TypeManager.object_type;
4298 Error_VariableIsUsedBeforeItIsDeclared (Name);
4299 return null;
4302 return DoResolveBase (ec);
4305 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
4307 ResolveLocalInfo ();
4309 // is out param
4310 if (right_side == EmptyExpression.OutAccess)
4311 local_info.Used = true;
4313 // Infer implicitly typed local variable
4314 if (type == null) {
4315 VarExpr ve = local_info.Type as VarExpr;
4316 if (ve != null) {
4317 if (!ve.InferType (ec, right_side))
4318 return null;
4319 type = local_info.VariableType = ve.Type;
4323 if (is_readonly) {
4324 int code;
4325 string msg;
4326 if (right_side == EmptyExpression.OutAccess) {
4327 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
4328 } else if (right_side == EmptyExpression.LValueMemberAccess) {
4329 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
4330 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
4331 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
4332 } else if (right_side == EmptyExpression.UnaryAddress) {
4333 code = 459; msg = "Cannot take the address of {1} `{0}'";
4334 } else {
4335 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
4337 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
4338 } else if (VariableInfo != null) {
4339 VariableInfo.SetAssigned (ec);
4342 return DoResolveBase (ec);
4345 public override int GetHashCode ()
4347 return Name.GetHashCode ();
4350 public override bool Equals (object obj)
4352 LocalVariableReference lvr = obj as LocalVariableReference;
4353 if (lvr == null)
4354 return false;
4356 return Name == lvr.Name && Block == lvr.Block;
4359 protected override ILocalVariable Variable {
4360 get { return local_info; }
4363 public override string ToString ()
4365 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
4368 protected override void CloneTo (CloneContext clonectx, Expression t)
4370 LocalVariableReference target = (LocalVariableReference) t;
4372 target.Block = clonectx.LookupBlock (Block);
4373 if (local_info != null)
4374 target.local_info = clonectx.LookupVariable (local_info);
4378 /// <summary>
4379 /// This represents a reference to a parameter in the intermediate
4380 /// representation.
4381 /// </summary>
4382 public class ParameterReference : VariableReference {
4383 readonly ToplevelParameterInfo pi;
4385 public ParameterReference (ToplevelParameterInfo pi, Location loc)
4387 this.pi = pi;
4388 this.loc = loc;
4391 public override bool IsRef {
4392 get { return (pi.Parameter.ModFlags & Parameter.Modifier.ISBYREF) != 0; }
4395 bool HasOutModifier {
4396 get { return pi.Parameter.ModFlags == Parameter.Modifier.OUT; }
4399 public override HoistedVariable GetHoistedVariable (EmitContext ec)
4401 return pi.Parameter.HoistedVariableReference;
4405 // A ref or out parameter is classified as a moveable variable, even
4406 // if the argument given for the parameter is a fixed variable
4408 public override bool IsFixed {
4409 get { return !IsRef; }
4412 public override string Name {
4413 get { return Parameter.Name; }
4416 public Parameter Parameter {
4417 get { return pi.Parameter; }
4420 public override VariableInfo VariableInfo {
4421 get { return pi.VariableInfo; }
4424 protected override ILocalVariable Variable {
4425 get { return Parameter; }
4428 public bool IsAssigned (EmitContext ec, Location loc)
4430 // HACK: Variables are not captured in probing mode
4431 if (ec.IsInProbingMode)
4432 return true;
4434 if (!ec.DoFlowAnalysis || !HasOutModifier || ec.CurrentBranching.IsAssigned (VariableInfo))
4435 return true;
4437 Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
4438 return false;
4441 public override void SetHasAddressTaken ()
4443 Parameter.HasAddressTaken = true;
4446 void SetAssigned (EmitContext ec)
4448 if (HasOutModifier && ec.DoFlowAnalysis)
4449 ec.CurrentBranching.SetAssigned (VariableInfo);
4452 bool DoResolveBase (EmitContext ec)
4454 type = pi.ParameterType;
4455 eclass = ExprClass.Variable;
4457 AnonymousExpression am = ec.CurrentAnonymousMethod;
4458 if (am == null)
4459 return true;
4461 Block b = ec.CurrentBlock;
4462 while (b != null) {
4463 IParameterData[] p = b.Toplevel.Parameters.FixedParameters;
4464 for (int i = 0; i < p.Length; ++i) {
4465 if (p [i] != Parameter)
4466 continue;
4469 // Skip closest anonymous method parameters
4471 if (b == ec.CurrentBlock && !am.IsIterator)
4472 return true;
4474 if (IsRef) {
4475 Report.Error (1628, loc,
4476 "Parameter `{0}' cannot be used inside `{1}' when using `ref' or `out' modifier",
4477 Name, am.ContainerType);
4480 b = null;
4481 break;
4484 if (b != null)
4485 b = b.Toplevel.Parent;
4488 if (pi.Parameter.HasAddressTaken)
4489 AnonymousMethodExpression.Error_AddressOfCapturedVar (this, loc);
4491 if (ec.IsVariableCapturingRequired) {
4492 AnonymousMethodStorey storey = pi.Block.CreateAnonymousMethodStorey (ec);
4493 storey.CaptureParameter (ec, this);
4496 return true;
4499 public override int GetHashCode ()
4501 return Name.GetHashCode ();
4504 public override bool Equals (object obj)
4506 ParameterReference pr = obj as ParameterReference;
4507 if (pr == null)
4508 return false;
4510 return Name == pr.Name;
4513 protected override void CloneTo (CloneContext clonectx, Expression target)
4515 // Nothing to clone
4518 public override Expression CreateExpressionTree (EmitContext ec)
4520 HoistedVariable hv = GetHoistedVariable (ec);
4521 if (hv != null)
4522 return hv.CreateExpressionTree (ec);
4524 return Parameter.ExpressionTreeVariableReference ();
4528 // Notice that for ref/out parameters, the type exposed is not the
4529 // same type exposed externally.
4531 // for "ref int a":
4532 // externally we expose "int&"
4533 // here we expose "int".
4535 // We record this in "is_ref". This means that the type system can treat
4536 // the type as it is expected, but when we generate the code, we generate
4537 // the alternate kind of code.
4539 public override Expression DoResolve (EmitContext ec)
4541 if (!DoResolveBase (ec))
4542 return null;
4544 // HACK: Variables are not captured in probing mode
4545 if (ec.IsInProbingMode)
4546 return this;
4548 if (HasOutModifier && ec.DoFlowAnalysis &&
4549 (!ec.OmitStructFlowAnalysis || !VariableInfo.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
4550 return null;
4552 return this;
4555 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
4557 if (!DoResolveBase (ec))
4558 return null;
4560 // HACK: parameters are not captured when probing is on
4561 if (!ec.IsInProbingMode)
4562 SetAssigned (ec);
4564 return this;
4567 static public void EmitLdArg (ILGenerator ig, int x)
4569 switch (x) {
4570 case 0: ig.Emit (OpCodes.Ldarg_0); break;
4571 case 1: ig.Emit (OpCodes.Ldarg_1); break;
4572 case 2: ig.Emit (OpCodes.Ldarg_2); break;
4573 case 3: ig.Emit (OpCodes.Ldarg_3); break;
4574 default:
4575 if (x > byte.MaxValue)
4576 ig.Emit (OpCodes.Ldarg, x);
4577 else
4578 ig.Emit (OpCodes.Ldarg_S, (byte) x);
4579 break;
4584 /// <summary>
4585 /// Used for arguments to New(), Invocation()
4586 /// </summary>
4587 public class Argument {
4588 public enum AType : byte {
4589 Expression,
4590 Ref,
4591 Out,
4592 ArgList
4595 public static readonly Argument[] Empty = new Argument [0];
4597 public readonly AType ArgType;
4598 public Expression Expr;
4600 public Argument (Expression expr, AType type)
4602 this.Expr = expr;
4603 this.ArgType = type;
4606 public Argument (Expression expr)
4608 this.Expr = expr;
4609 this.ArgType = AType.Expression;
4612 public Type Type {
4613 get { return Expr.Type; }
4616 public Parameter.Modifier Modifier
4618 get {
4619 switch (ArgType) {
4620 case AType.Out:
4621 return Parameter.Modifier.OUT;
4623 case AType.Ref:
4624 return Parameter.Modifier.REF;
4626 default:
4627 return Parameter.Modifier.NONE;
4632 public string GetSignatureForError ()
4634 if (Expr.eclass == ExprClass.MethodGroup)
4635 return Expr.ExprClassName;
4637 return TypeManager.CSharpName (Expr.Type);
4640 public bool ResolveMethodGroup (EmitContext ec)
4642 SimpleName sn = Expr as SimpleName;
4643 if (sn != null)
4644 Expr = sn.GetMethodGroup ();
4646 // FIXME: csc doesn't report any error if you try to use `ref' or
4647 // `out' in a delegate creation expression.
4648 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4649 if (Expr == null)
4650 return false;
4652 return true;
4655 public bool Resolve (EmitContext ec, Location loc)
4657 if (Expr == null)
4658 return false;
4660 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
4661 // Verify that the argument is readable
4662 if (ArgType != AType.Out)
4663 Expr = Expr.Resolve (ec);
4665 // Verify that the argument is writeable
4666 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
4667 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
4669 return Expr != null;
4673 public void Emit (EmitContext ec)
4675 if (ArgType != AType.Ref && ArgType != AType.Out) {
4676 Expr.Emit (ec);
4677 return;
4680 AddressOp mode = AddressOp.Store;
4681 if (ArgType == AType.Ref)
4682 mode |= AddressOp.Load;
4684 IMemoryLocation ml = (IMemoryLocation) Expr;
4685 ParameterReference pr = ml as ParameterReference;
4688 // ParameterReferences might already be references, so we want
4689 // to pass just the value
4691 if (pr != null && pr.IsRef)
4692 pr.EmitLoad (ec);
4693 else
4694 ml.AddressOf (ec, mode);
4697 public Argument Clone (CloneContext clonectx)
4699 return new Argument (Expr.Clone (clonectx), ArgType);
4703 /// <summary>
4704 /// Invocation of methods or delegates.
4705 /// </summary>
4706 public class Invocation : ExpressionStatement {
4707 protected ArrayList Arguments;
4708 protected Expression expr;
4709 protected MethodGroupExpr mg;
4710 bool arguments_resolved;
4713 // arguments is an ArrayList, but we do not want to typecast,
4714 // as it might be null.
4716 public Invocation (Expression expr, ArrayList arguments)
4718 SimpleName sn = expr as SimpleName;
4719 if (sn != null)
4720 this.expr = sn.GetMethodGroup ();
4721 else
4722 this.expr = expr;
4724 Arguments = arguments;
4725 if (expr != null)
4726 loc = expr.Location;
4729 public Invocation (Expression expr, ArrayList arguments, bool arguments_resolved)
4730 : this (expr, arguments)
4732 this.arguments_resolved = arguments_resolved;
4735 public override Expression CreateExpressionTree (EmitContext ec)
4737 ArrayList args;
4740 // Special conversion for nested expression trees
4742 if (TypeManager.DropGenericTypeArguments (type) == TypeManager.expression_type) {
4743 args = new ArrayList (1);
4744 args.Add (new Argument (this));
4745 return CreateExpressionFactoryCall ("Quote", args);
4748 ExtensionMethodGroupExpr emg = mg as ExtensionMethodGroupExpr;
4750 int arg_count = Arguments == null ? 2 : Arguments.Count + 2;
4751 if (emg != null)
4752 ++arg_count;
4753 args = new ArrayList (arg_count);
4755 if (mg.IsInstance)
4756 args.Add (new Argument (mg.InstanceExpression.CreateExpressionTree (ec)));
4757 else
4758 args.Add (new Argument (new NullLiteral (loc)));
4760 args.Add (new Argument (mg.CreateExpressionTree (ec)));
4763 // Use extension argument when exists
4765 if (emg != null) {
4766 Expression e = emg.ExtensionExpression.CreateExpressionTree (ec);
4767 if (e != null)
4768 args.Add (new Argument (e));
4771 if (Arguments != null) {
4772 foreach (Argument a in Arguments) {
4773 Expression e = a.Expr.CreateExpressionTree (ec);
4774 if (e != null)
4775 args.Add (new Argument (e));
4779 if (mg.IsBase)
4780 MemberExpr.Error_BaseAccessInExpressionTree (loc);
4782 return CreateExpressionFactoryCall ("Call", args);
4785 public override Expression DoResolve (EmitContext ec)
4787 // Don't resolve already resolved expression
4788 if (eclass != ExprClass.Invalid)
4789 return this;
4791 Expression expr_resolved = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4792 if (expr_resolved == null)
4793 return null;
4795 mg = expr_resolved as MethodGroupExpr;
4796 if (mg == null) {
4797 Type expr_type = expr_resolved.Type;
4799 if (expr_type != null && TypeManager.IsDelegateType (expr_type)){
4800 return (new DelegateInvocation (
4801 expr_resolved, Arguments, loc)).Resolve (ec);
4804 MemberExpr me = expr_resolved as MemberExpr;
4805 if (me == null) {
4806 expr_resolved.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4807 return null;
4810 mg = ec.TypeContainer.LookupExtensionMethod (me.Type, me.Name, loc);
4811 if (mg == null) {
4812 Report.Error (1955, loc, "The member `{0}' cannot be used as method or delegate",
4813 expr_resolved.GetSignatureForError ());
4814 return null;
4817 ((ExtensionMethodGroupExpr)mg).ExtensionExpression = me.InstanceExpression;
4821 // Next, evaluate all the expressions in the argument list
4823 if (Arguments != null && !arguments_resolved) {
4824 for (int i = 0; i < Arguments.Count; ++i)
4826 if (!((Argument)Arguments[i]).Resolve(ec, loc))
4827 return null;
4831 mg = DoResolveOverload (ec);
4832 if (mg == null)
4833 return null;
4835 MethodInfo method = (MethodInfo)mg;
4836 if (method != null) {
4837 type = TypeManager.TypeToCoreType (method.ReturnType);
4839 // TODO: this is a copy of mg.ResolveMemberAccess method
4840 Expression iexpr = mg.InstanceExpression;
4841 if (method.IsStatic) {
4842 if (iexpr == null ||
4843 iexpr is This || iexpr is EmptyExpression ||
4844 mg.IdenticalTypeName) {
4845 mg.InstanceExpression = null;
4846 } else {
4847 MemberExpr.error176 (loc, mg.GetSignatureForError ());
4848 return null;
4850 } else {
4851 if (iexpr == null || iexpr == EmptyExpression.Null) {
4852 SimpleName.Error_ObjectRefRequired (ec, loc, mg.GetSignatureForError ());
4857 if (type.IsPointer){
4858 if (!ec.InUnsafe){
4859 UnsafeError (loc);
4860 return null;
4865 // Only base will allow this invocation to happen.
4867 if (mg.IsBase && method.IsAbstract){
4868 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
4869 return null;
4872 if (Arguments == null && method.DeclaringType == TypeManager.object_type && method.Name == Destructor.MetadataName) {
4873 if (mg.IsBase)
4874 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
4875 else
4876 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
4877 return null;
4880 IsSpecialMethodInvocation (method, loc);
4882 if (mg.InstanceExpression != null)
4883 mg.InstanceExpression.CheckMarshalByRefAccess (ec);
4885 eclass = ExprClass.Value;
4886 return this;
4889 protected virtual MethodGroupExpr DoResolveOverload (EmitContext ec)
4891 return mg.OverloadResolve (ec, ref Arguments, false, loc);
4894 public static bool IsSpecialMethodInvocation (MethodBase method, Location loc)
4896 if (!TypeManager.IsSpecialMethod (method))
4897 return false;
4899 Report.SymbolRelatedToPreviousError (method);
4900 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
4901 TypeManager.CSharpSignature (method, true));
4903 return true;
4906 /// <summary>
4907 /// Emits a list of resolved Arguments that are in the arguments
4908 /// ArrayList.
4909 ///
4910 /// The MethodBase argument might be null if the
4911 /// emission of the arguments is known not to contain
4912 /// a `params' field (for example in constructors or other routines
4913 /// that keep their arguments in this structure)
4914 ///
4915 /// if `dup_args' is true, a copy of the arguments will be left
4916 /// on the stack. If `dup_args' is true, you can specify `this_arg'
4917 /// which will be duplicated before any other args. Only EmitCall
4918 /// should be using this interface.
4919 /// </summary>
4920 public static void EmitArguments (EmitContext ec, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
4922 if (arguments == null)
4923 return;
4925 int top = arguments.Count;
4926 LocalTemporary [] temps = null;
4928 if (dup_args && top != 0)
4929 temps = new LocalTemporary [top];
4931 int argument_index = 0;
4932 Argument a;
4933 for (int i = 0; i < top; i++) {
4934 a = (Argument) arguments [argument_index++];
4935 a.Emit (ec);
4936 if (dup_args) {
4937 ec.ig.Emit (OpCodes.Dup);
4938 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
4942 if (dup_args) {
4943 if (this_arg != null)
4944 this_arg.Emit (ec);
4946 for (int i = 0; i < top; i ++) {
4947 temps [i].Emit (ec);
4948 temps [i].Release (ec);
4953 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
4955 AParametersCollection pd = TypeManager.GetParameterData (mb);
4957 Argument a = (Argument) arguments [pd.Count - 1];
4958 Arglist list = (Arglist) a.Expr;
4960 return list.ArgumentTypes;
4963 /// <summary>
4964 /// This checks the ConditionalAttribute on the method
4965 /// </summary>
4966 public static bool IsMethodExcluded (MethodBase method, Location loc)
4968 if (method.IsConstructor)
4969 return false;
4971 method = TypeManager.DropGenericMethodArguments (method);
4972 if (method.DeclaringType.Module == CodeGen.Module.Builder) {
4973 IMethodData md = TypeManager.GetMethod (method);
4974 if (md != null)
4975 return md.IsExcluded ();
4977 // For some methods (generated by delegate class) GetMethod returns null
4978 // because they are not included in builder_to_method table
4979 return false;
4982 return AttributeTester.IsConditionalMethodExcluded (method, loc);
4985 /// <remarks>
4986 /// is_base tells whether we want to force the use of the `call'
4987 /// opcode instead of using callvirt. Call is required to call
4988 /// a specific method, while callvirt will always use the most
4989 /// recent method in the vtable.
4991 /// is_static tells whether this is an invocation on a static method
4993 /// instance_expr is an expression that represents the instance
4994 /// it must be non-null if is_static is false.
4996 /// method is the method to invoke.
4998 /// Arguments is the list of arguments to pass to the method or constructor.
4999 /// </remarks>
5000 public static void EmitCall (EmitContext ec, bool is_base,
5001 Expression instance_expr,
5002 MethodBase method, ArrayList Arguments, Location loc)
5004 EmitCall (ec, is_base, instance_expr, method, Arguments, loc, false, false);
5007 // `dup_args' leaves an extra copy of the arguments on the stack
5008 // `omit_args' does not leave any arguments at all.
5009 // So, basically, you could make one call with `dup_args' set to true,
5010 // and then another with `omit_args' set to true, and the two calls
5011 // would have the same set of arguments. However, each argument would
5012 // only have been evaluated once.
5013 public static void EmitCall (EmitContext ec, bool is_base,
5014 Expression instance_expr,
5015 MethodBase method, ArrayList Arguments, Location loc,
5016 bool dup_args, bool omit_args)
5018 ILGenerator ig = ec.ig;
5019 bool struct_call = false;
5020 bool this_call = false;
5021 LocalTemporary this_arg = null;
5023 Type decl_type = method.DeclaringType;
5025 if (IsMethodExcluded (method, loc))
5026 return;
5028 bool is_static = method.IsStatic;
5029 if (!is_static){
5030 this_call = instance_expr is This;
5031 if (TypeManager.IsStruct (decl_type) || TypeManager.IsEnumType (decl_type))
5032 struct_call = true;
5035 // If this is ourselves, push "this"
5037 if (!omit_args) {
5038 Type t = null;
5039 Type iexpr_type = instance_expr.Type;
5042 // Push the instance expression
5044 if (TypeManager.IsValueType (iexpr_type) || TypeManager.IsGenericParameter (iexpr_type)) {
5046 // Special case: calls to a function declared in a
5047 // reference-type with a value-type argument need
5048 // to have their value boxed.
5049 if (TypeManager.IsStruct (decl_type) ||
5050 TypeManager.IsGenericParameter (iexpr_type)) {
5052 // If the expression implements IMemoryLocation, then
5053 // we can optimize and use AddressOf on the
5054 // return.
5056 // If not we have to use some temporary storage for
5057 // it.
5058 if (instance_expr is IMemoryLocation) {
5059 ((IMemoryLocation)instance_expr).
5060 AddressOf (ec, AddressOp.LoadStore);
5061 } else {
5062 LocalTemporary temp = new LocalTemporary (iexpr_type);
5063 instance_expr.Emit (ec);
5064 temp.Store (ec);
5065 temp.AddressOf (ec, AddressOp.Load);
5068 // avoid the overhead of doing this all the time.
5069 if (dup_args)
5070 t = TypeManager.GetReferenceType (iexpr_type);
5071 } else {
5072 instance_expr.Emit (ec);
5074 // FIXME: should use instance_expr is IMemoryLocation + constraint.
5075 // to help JIT to produce better code
5076 ig.Emit (OpCodes.Box, instance_expr.Type);
5077 t = TypeManager.object_type;
5079 } else {
5080 instance_expr.Emit (ec);
5081 t = instance_expr.Type;
5084 if (dup_args) {
5085 ig.Emit (OpCodes.Dup);
5086 if (Arguments != null && Arguments.Count != 0) {
5087 this_arg = new LocalTemporary (t);
5088 this_arg.Store (ec);
5094 if (!omit_args)
5095 EmitArguments (ec, Arguments, dup_args, this_arg);
5097 OpCode call_op;
5098 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual)) {
5099 call_op = OpCodes.Call;
5100 } else {
5101 call_op = OpCodes.Callvirt;
5103 #if GMCS_SOURCE
5104 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
5105 ig.Emit (OpCodes.Constrained, instance_expr.Type);
5106 #endif
5109 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5110 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5111 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5112 return;
5116 // If you have:
5117 // this.DoFoo ();
5118 // and DoFoo is not virtual, you can omit the callvirt,
5119 // because you don't need the null checking behavior.
5121 if (method is MethodInfo)
5122 ig.Emit (call_op, (MethodInfo) method);
5123 else
5124 ig.Emit (call_op, (ConstructorInfo) method);
5127 public override void Emit (EmitContext ec)
5129 mg.EmitCall (ec, Arguments);
5132 public override void EmitStatement (EmitContext ec)
5134 Emit (ec);
5137 // Pop the return value if there is one
5139 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
5140 ec.ig.Emit (OpCodes.Pop);
5143 protected override void CloneTo (CloneContext clonectx, Expression t)
5145 Invocation target = (Invocation) t;
5147 if (Arguments != null) {
5148 target.Arguments = new ArrayList (Arguments.Count);
5149 foreach (Argument a in Arguments)
5150 target.Arguments.Add (a.Clone (clonectx));
5153 target.expr = expr.Clone (clonectx);
5156 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5158 mg.MutateHoistedGenericType (storey);
5159 if (Arguments != null) {
5160 foreach (Argument a in Arguments)
5161 a.Expr.MutateHoistedGenericType (storey);
5167 // It's either a cast or delegate invocation
5169 public class InvocationOrCast : ExpressionStatement
5171 Expression expr;
5172 Expression argument;
5174 public InvocationOrCast (Expression expr, Expression argument)
5176 this.expr = expr;
5177 this.argument = argument;
5178 this.loc = expr.Location;
5181 public override Expression CreateExpressionTree (EmitContext ec)
5183 throw new NotSupportedException ("ET");
5186 public override Expression DoResolve (EmitContext ec)
5188 Expression e = ResolveCore (ec);
5189 if (e == null)
5190 return null;
5192 return e.Resolve (ec);
5195 Expression ResolveCore (EmitContext ec)
5198 // First try to resolve it as a cast.
5200 TypeExpr te = expr.ResolveAsBaseTerminal (ec, true);
5201 if (te != null) {
5202 return new Cast (te, argument, loc);
5206 // This can either be a type or a delegate invocation.
5207 // Let's just resolve it and see what we'll get.
5209 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5210 if (expr == null)
5211 return null;
5214 // Ok, so it's a Cast.
5216 if (expr.eclass == ExprClass.Type || expr.eclass == ExprClass.TypeParameter) {
5217 return new Cast (expr, argument, loc);
5220 if (expr.eclass == ExprClass.Namespace) {
5221 expr.Error_UnexpectedKind (null, "type", loc);
5222 return null;
5226 // It's a delegate invocation.
5228 if (!TypeManager.IsDelegateType (expr.Type)) {
5229 Error (149, "Method name expected");
5230 return null;
5233 ArrayList args = new ArrayList (1);
5234 args.Add (new Argument (argument, Argument.AType.Expression));
5235 return new DelegateInvocation (expr, args, loc);
5238 public override ExpressionStatement ResolveStatement (EmitContext ec)
5240 Expression e = ResolveCore (ec);
5241 if (e == null)
5242 return null;
5244 ExpressionStatement s = e as ExpressionStatement;
5245 if (s == null) {
5246 Error_InvalidExpressionStatement ();
5247 return null;
5250 return s.ResolveStatement (ec);
5253 public override void Emit (EmitContext ec)
5255 throw new Exception ("Cannot happen");
5258 public override void EmitStatement (EmitContext ec)
5260 throw new Exception ("Cannot happen");
5263 protected override void CloneTo (CloneContext clonectx, Expression t)
5265 InvocationOrCast target = (InvocationOrCast) t;
5267 target.expr = expr.Clone (clonectx);
5268 target.argument = argument.Clone (clonectx);
5273 /// <summary>
5274 /// Implements the new expression
5275 /// </summary>
5276 public class New : ExpressionStatement, IMemoryLocation {
5277 ArrayList Arguments;
5280 // During bootstrap, it contains the RequestedType,
5281 // but if `type' is not null, it *might* contain a NewDelegate
5282 // (because of field multi-initialization)
5284 Expression RequestedType;
5286 MethodGroupExpr method;
5288 bool is_type_parameter;
5290 public New (Expression requested_type, ArrayList arguments, Location l)
5292 RequestedType = requested_type;
5293 Arguments = arguments;
5294 loc = l;
5297 /// <summary>
5298 /// Converts complex core type syntax like 'new int ()' to simple constant
5299 /// </summary>
5300 public static Constant Constantify (Type t)
5302 if (t == TypeManager.int32_type)
5303 return new IntConstant (0, Location.Null);
5304 if (t == TypeManager.uint32_type)
5305 return new UIntConstant (0, Location.Null);
5306 if (t == TypeManager.int64_type)
5307 return new LongConstant (0, Location.Null);
5308 if (t == TypeManager.uint64_type)
5309 return new ULongConstant (0, Location.Null);
5310 if (t == TypeManager.float_type)
5311 return new FloatConstant (0, Location.Null);
5312 if (t == TypeManager.double_type)
5313 return new DoubleConstant (0, Location.Null);
5314 if (t == TypeManager.short_type)
5315 return new ShortConstant (0, Location.Null);
5316 if (t == TypeManager.ushort_type)
5317 return new UShortConstant (0, Location.Null);
5318 if (t == TypeManager.sbyte_type)
5319 return new SByteConstant (0, Location.Null);
5320 if (t == TypeManager.byte_type)
5321 return new ByteConstant (0, Location.Null);
5322 if (t == TypeManager.char_type)
5323 return new CharConstant ('\0', Location.Null);
5324 if (t == TypeManager.bool_type)
5325 return new BoolConstant (false, Location.Null);
5326 if (t == TypeManager.decimal_type)
5327 return new DecimalConstant (0, Location.Null);
5328 if (TypeManager.IsEnumType (t))
5329 return new EnumConstant (Constantify (TypeManager.GetEnumUnderlyingType (t)), t);
5330 if (TypeManager.IsNullableType (t))
5331 return Nullable.LiftedNull.Create (t, Location.Null);
5333 return null;
5337 // Checks whether the type is an interface that has the
5338 // [ComImport, CoClass] attributes and must be treated
5339 // specially
5341 public Expression CheckComImport (EmitContext ec)
5343 if (!type.IsInterface)
5344 return null;
5347 // Turn the call into:
5348 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5350 Type real_class = AttributeTester.GetCoClassAttribute (type);
5351 if (real_class == null)
5352 return null;
5354 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5355 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5356 return cast.Resolve (ec);
5359 public override Expression CreateExpressionTree (EmitContext ec)
5361 ArrayList args = Arguments == null ?
5362 new ArrayList (1) : new ArrayList (Arguments.Count + 1);
5364 if (method == null) {
5365 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
5366 } else {
5367 args.Add (new Argument (method.CreateExpressionTree (ec)));
5368 if (Arguments != null) {
5369 Expression expr;
5370 foreach (Argument a in Arguments) {
5371 expr = a.Expr.CreateExpressionTree (ec);
5372 if (expr != null)
5373 args.Add (new Argument (expr));
5378 return CreateExpressionFactoryCall ("New", args);
5381 public override Expression DoResolve (EmitContext ec)
5384 // The New DoResolve might be called twice when initializing field
5385 // expressions (see EmitFieldInitializers, the call to
5386 // GetInitializerExpression will perform a resolve on the expression,
5387 // and later the assign will trigger another resolution
5389 // This leads to bugs (#37014)
5391 if (type != null){
5392 if (RequestedType is NewDelegate)
5393 return RequestedType;
5394 return this;
5397 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5398 if (texpr == null)
5399 return null;
5401 type = texpr.Type;
5403 if (type.IsPointer) {
5404 Report.Error (1919, loc, "Unsafe type `{0}' cannot be used in an object creation expression",
5405 TypeManager.CSharpName (type));
5406 return null;
5409 if (Arguments == null) {
5410 Constant c = Constantify (type);
5411 if (c != null)
5412 return ReducedExpression.Create (c, this);
5415 if (TypeManager.IsDelegateType (type)) {
5416 return (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5419 #if GMCS_SOURCE
5420 if (type.IsGenericParameter) {
5421 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5423 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5424 Error (304, String.Format (
5425 "Cannot create an instance of the " +
5426 "variable type '{0}' because it " +
5427 "doesn't have the new() constraint",
5428 type));
5429 return null;
5432 if ((Arguments != null) && (Arguments.Count != 0)) {
5433 Error (417, String.Format (
5434 "`{0}': cannot provide arguments " +
5435 "when creating an instance of a " +
5436 "variable type.", type));
5437 return null;
5440 if (TypeManager.activator_create_instance == null) {
5441 Type activator_type = TypeManager.CoreLookupType ("System", "Activator", Kind.Class, true);
5442 if (activator_type != null) {
5443 TypeManager.activator_create_instance = TypeManager.GetPredefinedMethod (
5444 activator_type, "CreateInstance", loc, Type.EmptyTypes);
5448 is_type_parameter = true;
5449 eclass = ExprClass.Value;
5450 return this;
5452 #endif
5454 if (type.IsAbstract && type.IsSealed) {
5455 Report.SymbolRelatedToPreviousError (type);
5456 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5457 return null;
5460 if (type.IsInterface || type.IsAbstract){
5461 if (!TypeManager.IsGenericType (type)) {
5462 RequestedType = CheckComImport (ec);
5463 if (RequestedType != null)
5464 return RequestedType;
5467 Report.SymbolRelatedToPreviousError (type);
5468 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5469 return null;
5472 bool is_struct = TypeManager.IsStruct (type);
5473 eclass = ExprClass.Value;
5476 // SRE returns a match for .ctor () on structs (the object constructor),
5477 // so we have to manually ignore it.
5479 if (is_struct && Arguments == null)
5480 return this;
5482 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5483 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5484 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5486 if (Arguments != null){
5487 foreach (Argument a in Arguments){
5488 if (!a.Resolve (ec, loc))
5489 return null;
5493 if (ml == null)
5494 return null;
5496 method = ml as MethodGroupExpr;
5497 if (method == null) {
5498 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5499 return null;
5502 method = method.OverloadResolve (ec, ref Arguments, false, loc);
5503 if (method == null)
5504 return null;
5506 return this;
5509 bool DoEmitTypeParameter (EmitContext ec)
5511 #if GMCS_SOURCE
5512 ILGenerator ig = ec.ig;
5513 // IMemoryLocation ml;
5515 MethodInfo ci = TypeManager.activator_create_instance.MakeGenericMethod (
5516 new Type [] { type });
5518 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5519 if (gc.HasReferenceTypeConstraint || gc.HasClassConstraint) {
5520 ig.Emit (OpCodes.Call, ci);
5521 return true;
5524 // Allow DoEmit() to be called multiple times.
5525 // We need to create a new LocalTemporary each time since
5526 // you can't share LocalBuilders among ILGeneators.
5527 LocalTemporary temp = new LocalTemporary (type);
5529 Label label_activator = ig.DefineLabel ();
5530 Label label_end = ig.DefineLabel ();
5532 temp.AddressOf (ec, AddressOp.Store);
5533 ig.Emit (OpCodes.Initobj, type);
5535 temp.Emit (ec);
5536 ig.Emit (OpCodes.Box, type);
5537 ig.Emit (OpCodes.Brfalse, label_activator);
5539 temp.AddressOf (ec, AddressOp.Store);
5540 ig.Emit (OpCodes.Initobj, type);
5541 temp.Emit (ec);
5542 ig.Emit (OpCodes.Br, label_end);
5544 ig.MarkLabel (label_activator);
5546 ig.Emit (OpCodes.Call, ci);
5547 ig.MarkLabel (label_end);
5548 return true;
5549 #else
5550 throw new InternalErrorException ();
5551 #endif
5555 // This Emit can be invoked in two contexts:
5556 // * As a mechanism that will leave a value on the stack (new object)
5557 // * As one that wont (init struct)
5559 // If we are dealing with a ValueType, we have a few
5560 // situations to deal with:
5562 // * The target is a ValueType, and we have been provided
5563 // the instance (this is easy, we are being assigned).
5565 // * The target of New is being passed as an argument,
5566 // to a boxing operation or a function that takes a
5567 // ValueType.
5569 // In this case, we need to create a temporary variable
5570 // that is the argument of New.
5572 // Returns whether a value is left on the stack
5574 // *** Implementation note ***
5576 // To benefit from this optimization, each assignable expression
5577 // has to manually cast to New and call this Emit.
5579 // TODO: It's worth to implement it for arrays and fields
5581 public virtual bool Emit (EmitContext ec, IMemoryLocation target)
5583 if (is_type_parameter)
5584 return DoEmitTypeParameter (ec);
5586 bool is_value_type = TypeManager.IsValueType (type);
5587 ILGenerator ig = ec.ig;
5588 VariableReference vr = target as VariableReference;
5590 if (target != null && is_value_type && (vr != null || method == null)) {
5591 target.AddressOf (ec, AddressOp.Store);
5592 } else if (vr != null && vr.IsRef) {
5593 vr.EmitLoad (ec);
5596 if (method != null)
5597 method.EmitArguments (ec, Arguments);
5599 if (is_value_type) {
5600 if (method == null) {
5601 ig.Emit (OpCodes.Initobj, type);
5602 return false;
5605 if (vr != null) {
5606 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5607 return false;
5611 ConstructorInfo ci = (ConstructorInfo) method;
5612 #if MS_COMPATIBLE
5613 if (TypeManager.IsGenericType (type))
5614 ci = TypeBuilder.GetConstructor (type, ci);
5615 #endif
5617 ig.Emit (OpCodes.Newobj, ci);
5618 return true;
5621 public override void Emit (EmitContext ec)
5623 LocalTemporary v = null;
5624 if (method == null && TypeManager.IsValueType (type)) {
5625 // TODO: Use temporary variable from pool
5626 v = new LocalTemporary (type);
5629 if (!Emit (ec, v))
5630 v.Emit (ec);
5633 public override void EmitStatement (EmitContext ec)
5635 LocalTemporary v = null;
5636 if (method == null && TypeManager.IsValueType (type)) {
5637 // TODO: Use temporary variable from pool
5638 v = new LocalTemporary (type);
5641 if (Emit (ec, v))
5642 ec.ig.Emit (OpCodes.Pop);
5645 public virtual bool HasInitializer {
5646 get {
5647 return false;
5651 public void AddressOf (EmitContext ec, AddressOp Mode)
5653 if (is_type_parameter) {
5654 LocalTemporary temp = new LocalTemporary (type);
5655 DoEmitTypeParameter (ec);
5656 temp.Store (ec);
5657 temp.AddressOf (ec, Mode);
5658 return;
5661 if (!TypeManager.IsStruct (type)){
5663 // We throw an exception. So far, I believe we only need to support
5664 // value types:
5665 // foreach (int j in new StructType ())
5666 // see bug 42390
5668 throw new Exception ("AddressOf should not be used for classes");
5671 LocalTemporary value_target = new LocalTemporary (type);
5672 IMemoryLocation ml = (IMemoryLocation) value_target;
5674 ml.AddressOf (ec, AddressOp.Store);
5675 if (method == null) {
5676 ec.ig.Emit (OpCodes.Initobj, type);
5677 } else {
5678 method.EmitArguments (ec, Arguments);
5679 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5682 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5685 protected override void CloneTo (CloneContext clonectx, Expression t)
5687 New target = (New) t;
5689 target.RequestedType = RequestedType.Clone (clonectx);
5690 if (Arguments != null){
5691 target.Arguments = new ArrayList ();
5692 foreach (Argument a in Arguments){
5693 target.Arguments.Add (a.Clone (clonectx));
5698 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5700 if (method != null) {
5701 method.MutateHoistedGenericType (storey);
5702 if (Arguments != null) {
5703 foreach (Argument a in Arguments)
5704 a.Expr.MutateHoistedGenericType (storey);
5708 type = storey.MutateType (type);
5712 /// <summary>
5713 /// 14.5.10.2: Represents an array creation expression.
5714 /// </summary>
5716 /// <remarks>
5717 /// There are two possible scenarios here: one is an array creation
5718 /// expression that specifies the dimensions and optionally the
5719 /// initialization data and the other which does not need dimensions
5720 /// specified but where initialization data is mandatory.
5721 /// </remarks>
5722 public class ArrayCreation : Expression {
5723 FullNamedExpression requested_base_type;
5724 ArrayList initializers;
5727 // The list of Argument types.
5728 // This is used to construct the `newarray' or constructor signature
5730 protected ArrayList arguments;
5732 protected Type array_element_type;
5733 bool expect_initializers = false;
5734 int num_arguments = 0;
5735 protected int dimensions;
5736 protected readonly string rank;
5738 protected ArrayList array_data;
5740 IDictionary bounds;
5742 // The number of constants in array initializers
5743 int const_initializers_count;
5744 bool only_constant_initializers;
5746 public ArrayCreation (FullNamedExpression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5748 this.requested_base_type = requested_base_type;
5749 this.initializers = initializers;
5750 this.rank = rank;
5751 loc = l;
5753 arguments = new ArrayList (exprs.Count);
5755 foreach (Expression e in exprs) {
5756 arguments.Add (new Argument (e, Argument.AType.Expression));
5757 num_arguments++;
5761 public ArrayCreation (FullNamedExpression requested_base_type, string rank, ArrayList initializers, Location l)
5763 this.requested_base_type = requested_base_type;
5764 this.initializers = initializers;
5765 this.rank = rank;
5766 loc = l;
5768 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5770 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5772 //dimensions = tmp.Length - 1;
5773 expect_initializers = true;
5776 public static void Error_IncorrectArrayInitializer (Location loc)
5778 Report.Error (178, loc, "Invalid rank specifier: expected `,' or `]'");
5781 protected override void Error_NegativeArrayIndex (Location loc)
5783 Report.Error (248, loc, "Cannot create an array with a negative size");
5786 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5788 if (specified_dims) {
5789 Argument a = (Argument) arguments [idx];
5791 if (!a.Resolve (ec, loc))
5792 return false;
5794 Constant c = a.Expr as Constant;
5795 if (c != null) {
5796 c = c.ImplicitConversionRequired (ec, TypeManager.int32_type, a.Expr.Location);
5799 if (c == null) {
5800 Report.Error (150, a.Expr.Location, "A constant value is expected");
5801 return false;
5804 int value = (int) c.GetValue ();
5806 if (value != probe.Count) {
5807 Error_IncorrectArrayInitializer (loc);
5808 return false;
5811 bounds [idx] = value;
5814 int child_bounds = -1;
5815 only_constant_initializers = true;
5816 for (int i = 0; i < probe.Count; ++i) {
5817 object o = probe [i];
5818 if (o is ArrayList) {
5819 ArrayList sub_probe = o as ArrayList;
5820 int current_bounds = sub_probe.Count;
5822 if (child_bounds == -1)
5823 child_bounds = current_bounds;
5825 else if (child_bounds != current_bounds){
5826 Error_IncorrectArrayInitializer (loc);
5827 return false;
5829 if (idx + 1 >= dimensions){
5830 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
5831 return false;
5834 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
5835 if (!ret)
5836 return false;
5837 } else {
5838 if (child_bounds != -1){
5839 Error_IncorrectArrayInitializer (loc);
5840 return false;
5843 Expression element = ResolveArrayElement (ec, (Expression) o);
5844 if (element == null)
5845 continue;
5847 // Initializers with the default values can be ignored
5848 Constant c = element as Constant;
5849 if (c != null) {
5850 if (c.IsDefaultInitializer (array_element_type)) {
5851 element = null;
5853 else {
5854 ++const_initializers_count;
5856 } else {
5857 only_constant_initializers = false;
5860 array_data.Add (element);
5864 return true;
5867 public override Expression CreateExpressionTree (EmitContext ec)
5869 ArrayList args;
5871 if (array_data == null) {
5872 args = new ArrayList (arguments.Count + 1);
5873 args.Add (new Argument (new TypeOf (new TypeExpression (array_element_type, loc), loc)));
5874 foreach (Argument a in arguments) {
5875 if (arguments.Count == 1) {
5876 Constant c = a.Expr as Constant;
5877 if (c.IsDefaultValue)
5878 return CreateExpressionFactoryCall ("NewArrayInit", args);
5880 args.Add (new Argument (a.Expr.CreateExpressionTree (ec)));
5883 return CreateExpressionFactoryCall ("NewArrayBounds", args);
5886 if (dimensions > 1) {
5887 Report.Error (838, loc, "An expression tree cannot contain a multidimensional array initializer");
5888 return null;
5891 args = new ArrayList (array_data == null ? 1 : array_data.Count + 1);
5892 args.Add (new Argument (new TypeOf (new TypeExpression (array_element_type, loc), loc)));
5893 if (array_data != null) {
5894 for (int i = 0; i < array_data.Count; ++i) {
5895 Expression e = (Expression) array_data [i];
5896 if (e == null)
5897 e = Convert.ImplicitConversion (ec, (Expression) initializers [i], array_element_type, loc);
5899 args.Add (new Argument (e.CreateExpressionTree (ec)));
5903 return CreateExpressionFactoryCall ("NewArrayInit", args);
5906 public void UpdateIndices ()
5908 int i = 0;
5909 for (ArrayList probe = initializers; probe != null;) {
5910 if (probe.Count > 0 && probe [0] is ArrayList) {
5911 Expression e = new IntConstant (probe.Count, Location.Null);
5912 arguments.Add (new Argument (e, Argument.AType.Expression));
5914 bounds [i++] = probe.Count;
5916 probe = (ArrayList) probe [0];
5918 } else {
5919 Expression e = new IntConstant (probe.Count, Location.Null);
5920 arguments.Add (new Argument (e, Argument.AType.Expression));
5922 bounds [i++] = probe.Count;
5923 return;
5929 Expression first_emit;
5930 LocalTemporary first_emit_temp;
5932 protected virtual Expression ResolveArrayElement (EmitContext ec, Expression element)
5934 element = element.Resolve (ec);
5935 if (element == null)
5936 return null;
5938 if (element is CompoundAssign.TargetExpression) {
5939 if (first_emit != null)
5940 throw new InternalErrorException ("Can only handle one mutator at a time");
5941 first_emit = element;
5942 element = first_emit_temp = new LocalTemporary (element.Type);
5945 return Convert.ImplicitConversionRequired (
5946 ec, element, array_element_type, loc);
5949 protected bool ResolveInitializers (EmitContext ec)
5951 if (initializers == null) {
5952 return !expect_initializers;
5956 // We use this to store all the date values in the order in which we
5957 // will need to store them in the byte blob later
5959 array_data = new ArrayList ();
5960 bounds = new System.Collections.Specialized.HybridDictionary ();
5962 if (arguments != null)
5963 return CheckIndices (ec, initializers, 0, true);
5965 arguments = new ArrayList ();
5967 if (!CheckIndices (ec, initializers, 0, false))
5968 return false;
5970 UpdateIndices ();
5972 return true;
5976 // Resolved the type of the array
5978 bool ResolveArrayType (EmitContext ec)
5980 if (requested_base_type == null) {
5981 Report.Error (622, loc, "Can only use array initializer expressions to assign to array types. Try using a new expression instead");
5982 return false;
5985 if (requested_base_type is VarExpr) {
5986 Report.Error (820, loc, "An implicitly typed local variable declarator cannot use an array initializer");
5987 return false;
5990 StringBuilder array_qualifier = new StringBuilder (rank);
5993 // `In the first form allocates an array instace of the type that results
5994 // from deleting each of the individual expression from the expression list'
5996 if (num_arguments > 0) {
5997 array_qualifier.Append ("[");
5998 for (int i = num_arguments-1; i > 0; i--)
5999 array_qualifier.Append (",");
6000 array_qualifier.Append ("]");
6004 // Lookup the type
6006 TypeExpr array_type_expr;
6007 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
6008 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
6009 if (array_type_expr == null)
6010 return false;
6012 type = array_type_expr.Type;
6013 array_element_type = TypeManager.GetElementType (type);
6014 dimensions = type.GetArrayRank ();
6016 return true;
6019 public override Expression DoResolve (EmitContext ec)
6021 if (type != null)
6022 return this;
6024 if (!ResolveArrayType (ec))
6025 return null;
6028 // First step is to validate the initializers and fill
6029 // in any missing bits
6031 if (!ResolveInitializers (ec))
6032 return null;
6034 if (arguments.Count != dimensions) {
6035 Error_IncorrectArrayInitializer (loc);
6038 foreach (Argument a in arguments){
6039 if (!a.Resolve (ec, loc))
6040 continue;
6042 a.Expr = ConvertExpressionToArrayIndex (ec, a.Expr);
6045 eclass = ExprClass.Value;
6046 return this;
6049 MethodInfo GetArrayMethod (int arguments)
6051 ModuleBuilder mb = CodeGen.Module.Builder;
6053 Type[] arg_types = new Type[arguments];
6054 for (int i = 0; i < arguments; i++)
6055 arg_types[i] = TypeManager.int32_type;
6057 MethodInfo mi = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6058 arg_types);
6060 if (mi == null) {
6061 Report.Error (-6, "New invocation: Can not find a constructor for " +
6062 "this argument list");
6063 return null;
6066 return mi;
6069 byte [] MakeByteBlob ()
6071 int factor;
6072 byte [] data;
6073 byte [] element;
6074 int count = array_data.Count;
6076 if (TypeManager.IsEnumType (array_element_type))
6077 array_element_type = TypeManager.GetEnumUnderlyingType (array_element_type);
6079 factor = GetTypeSize (array_element_type);
6080 if (factor == 0)
6081 throw new Exception ("unrecognized type in MakeByteBlob: " + array_element_type);
6083 data = new byte [(count * factor + 3) & ~3];
6084 int idx = 0;
6086 for (int i = 0; i < count; ++i) {
6087 object v = array_data [i];
6089 if (v is EnumConstant)
6090 v = ((EnumConstant) v).Child;
6092 if (v is Constant && !(v is StringConstant))
6093 v = ((Constant) v).GetValue ();
6094 else {
6095 idx += factor;
6096 continue;
6099 if (array_element_type == TypeManager.int64_type){
6100 if (!(v is Expression)){
6101 long val = (long) v;
6103 for (int j = 0; j < factor; ++j) {
6104 data [idx + j] = (byte) (val & 0xFF);
6105 val = (val >> 8);
6108 } else if (array_element_type == TypeManager.uint64_type){
6109 if (!(v is Expression)){
6110 ulong val = (ulong) v;
6112 for (int j = 0; j < factor; ++j) {
6113 data [idx + j] = (byte) (val & 0xFF);
6114 val = (val >> 8);
6117 } else if (array_element_type == TypeManager.float_type) {
6118 if (!(v is Expression)){
6119 element = BitConverter.GetBytes ((float) v);
6121 for (int j = 0; j < factor; ++j)
6122 data [idx + j] = element [j];
6123 if (!BitConverter.IsLittleEndian)
6124 System.Array.Reverse (data, idx, 4);
6126 } else if (array_element_type == TypeManager.double_type) {
6127 if (!(v is Expression)){
6128 element = BitConverter.GetBytes ((double) v);
6130 for (int j = 0; j < factor; ++j)
6131 data [idx + j] = element [j];
6133 // FIXME: Handle the ARM float format.
6134 if (!BitConverter.IsLittleEndian)
6135 System.Array.Reverse (data, idx, 8);
6137 } else if (array_element_type == TypeManager.char_type){
6138 if (!(v is Expression)){
6139 int val = (int) ((char) v);
6141 data [idx] = (byte) (val & 0xff);
6142 data [idx+1] = (byte) (val >> 8);
6144 } else if (array_element_type == TypeManager.short_type){
6145 if (!(v is Expression)){
6146 int val = (int) ((short) v);
6148 data [idx] = (byte) (val & 0xff);
6149 data [idx+1] = (byte) (val >> 8);
6151 } else if (array_element_type == TypeManager.ushort_type){
6152 if (!(v is Expression)){
6153 int val = (int) ((ushort) v);
6155 data [idx] = (byte) (val & 0xff);
6156 data [idx+1] = (byte) (val >> 8);
6158 } else if (array_element_type == TypeManager.int32_type) {
6159 if (!(v is Expression)){
6160 int val = (int) v;
6162 data [idx] = (byte) (val & 0xff);
6163 data [idx+1] = (byte) ((val >> 8) & 0xff);
6164 data [idx+2] = (byte) ((val >> 16) & 0xff);
6165 data [idx+3] = (byte) (val >> 24);
6167 } else if (array_element_type == TypeManager.uint32_type) {
6168 if (!(v is Expression)){
6169 uint val = (uint) v;
6171 data [idx] = (byte) (val & 0xff);
6172 data [idx+1] = (byte) ((val >> 8) & 0xff);
6173 data [idx+2] = (byte) ((val >> 16) & 0xff);
6174 data [idx+3] = (byte) (val >> 24);
6176 } else if (array_element_type == TypeManager.sbyte_type) {
6177 if (!(v is Expression)){
6178 sbyte val = (sbyte) v;
6179 data [idx] = (byte) val;
6181 } else if (array_element_type == TypeManager.byte_type) {
6182 if (!(v is Expression)){
6183 byte val = (byte) v;
6184 data [idx] = (byte) val;
6186 } else if (array_element_type == TypeManager.bool_type) {
6187 if (!(v is Expression)){
6188 bool val = (bool) v;
6189 data [idx] = (byte) (val ? 1 : 0);
6191 } else if (array_element_type == TypeManager.decimal_type){
6192 if (!(v is Expression)){
6193 int [] bits = Decimal.GetBits ((decimal) v);
6194 int p = idx;
6196 // FIXME: For some reason, this doesn't work on the MS runtime.
6197 int [] nbits = new int [4];
6198 nbits [0] = bits [3];
6199 nbits [1] = bits [2];
6200 nbits [2] = bits [0];
6201 nbits [3] = bits [1];
6203 for (int j = 0; j < 4; j++){
6204 data [p++] = (byte) (nbits [j] & 0xff);
6205 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6206 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6207 data [p++] = (byte) (nbits [j] >> 24);
6210 } else
6211 throw new Exception ("Unrecognized type in MakeByteBlob: " + array_element_type);
6213 idx += factor;
6216 return data;
6219 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
6221 array_element_type = storey.MutateType (array_element_type);
6222 type = storey.MutateType (type);
6223 if (arguments != null) {
6224 foreach (Argument a in arguments)
6225 a.Expr.MutateHoistedGenericType (storey);
6228 if (array_data != null) {
6229 foreach (Expression e in array_data) {
6230 // Don't mutate values optimized away
6231 if (e == null)
6232 continue;
6234 e.MutateHoistedGenericType (storey);
6240 // Emits the initializers for the array
6242 void EmitStaticInitializers (EmitContext ec)
6244 // FIXME: This should go to Resolve !
6245 if (TypeManager.void_initializearray_array_fieldhandle == null) {
6246 TypeManager.void_initializearray_array_fieldhandle = TypeManager.GetPredefinedMethod (
6247 TypeManager.runtime_helpers_type, "InitializeArray", loc,
6248 TypeManager.array_type, TypeManager.runtime_field_handle_type);
6249 if (TypeManager.void_initializearray_array_fieldhandle == null)
6250 return;
6254 // First, the static data
6256 FieldBuilder fb;
6257 ILGenerator ig = ec.ig;
6259 byte [] data = MakeByteBlob ();
6261 fb = RootContext.MakeStaticData (data);
6263 ig.Emit (OpCodes.Dup);
6264 ig.Emit (OpCodes.Ldtoken, fb);
6265 ig.Emit (OpCodes.Call,
6266 TypeManager.void_initializearray_array_fieldhandle);
6270 // Emits pieces of the array that can not be computed at compile
6271 // time (variables and string locations).
6273 // This always expect the top value on the stack to be the array
6275 void EmitDynamicInitializers (EmitContext ec, bool emitConstants)
6277 ILGenerator ig = ec.ig;
6278 int dims = bounds.Count;
6279 int [] current_pos = new int [dims];
6281 MethodInfo set = null;
6283 if (dims != 1){
6284 Type [] args = new Type [dims + 1];
6286 for (int j = 0; j < dims; j++)
6287 args [j] = TypeManager.int32_type;
6288 args [dims] = array_element_type;
6290 set = CodeGen.Module.Builder.GetArrayMethod (
6291 type, "Set",
6292 CallingConventions.HasThis | CallingConventions.Standard,
6293 TypeManager.void_type, args);
6296 for (int i = 0; i < array_data.Count; i++){
6298 Expression e = (Expression)array_data [i];
6300 // Constant can be initialized via StaticInitializer
6301 if (e != null && !(!emitConstants && e is Constant)) {
6302 Type etype = e.Type;
6304 ig.Emit (OpCodes.Dup);
6306 for (int idx = 0; idx < dims; idx++)
6307 IntConstant.EmitInt (ig, current_pos [idx]);
6310 // If we are dealing with a struct, get the
6311 // address of it, so we can store it.
6313 if ((dims == 1) && TypeManager.IsStruct (etype) &&
6314 (!TypeManager.IsBuiltinOrEnum (etype) ||
6315 etype == TypeManager.decimal_type)) {
6317 ig.Emit (OpCodes.Ldelema, etype);
6320 e.Emit (ec);
6322 if (dims == 1) {
6323 bool is_stobj, has_type_arg;
6324 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
6325 if (is_stobj)
6326 ig.Emit (OpCodes.Stobj, etype);
6327 else if (has_type_arg)
6328 ig.Emit (op, etype);
6329 else
6330 ig.Emit (op);
6331 } else
6332 ig.Emit (OpCodes.Call, set);
6337 // Advance counter
6339 for (int j = dims - 1; j >= 0; j--){
6340 current_pos [j]++;
6341 if (current_pos [j] < (int) bounds [j])
6342 break;
6343 current_pos [j] = 0;
6348 public override void Emit (EmitContext ec)
6350 ILGenerator ig = ec.ig;
6352 if (first_emit != null) {
6353 first_emit.Emit (ec);
6354 first_emit_temp.Store (ec);
6357 foreach (Argument a in arguments)
6358 a.Emit (ec);
6360 if (arguments.Count == 1)
6361 ig.Emit (OpCodes.Newarr, array_element_type);
6362 else {
6363 ig.Emit (OpCodes.Newobj, GetArrayMethod (arguments.Count));
6366 if (initializers == null)
6367 return;
6369 // Emit static initializer for arrays which have contain more than 4 items and
6370 // the static initializer will initialize at least 25% of array values.
6371 // NOTE: const_initializers_count does not contain default constant values.
6372 if (const_initializers_count >= 4 && const_initializers_count * 4 > (array_data.Count) &&
6373 TypeManager.IsPrimitiveType (array_element_type)) {
6374 EmitStaticInitializers (ec);
6376 if (!only_constant_initializers)
6377 EmitDynamicInitializers (ec, false);
6378 } else {
6379 EmitDynamicInitializers (ec, true);
6382 if (first_emit_temp != null)
6383 first_emit_temp.Release (ec);
6386 public override bool GetAttributableValue (EmitContext ec, Type value_type, out object value)
6388 if (arguments.Count != 1) {
6389 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6390 return base.GetAttributableValue (ec, null, out value);
6393 if (array_data == null) {
6394 Constant c = (Constant)((Argument)arguments [0]).Expr;
6395 if (c.IsDefaultValue) {
6396 value = Array.CreateInstance (array_element_type, 0);
6397 return true;
6399 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6400 return base.GetAttributableValue (ec, null, out value);
6403 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6404 object element_value;
6405 for (int i = 0; i < ret.Length; ++i)
6407 Expression e = (Expression)array_data [i];
6409 // Is null when an initializer is optimized (value == predefined value)
6410 if (e == null)
6411 continue;
6413 if (!e.GetAttributableValue (ec, array_element_type, out element_value)) {
6414 value = null;
6415 return false;
6417 ret.SetValue (element_value, i);
6419 value = ret;
6420 return true;
6423 protected override void CloneTo (CloneContext clonectx, Expression t)
6425 ArrayCreation target = (ArrayCreation) t;
6427 if (requested_base_type != null)
6428 target.requested_base_type = (FullNamedExpression)requested_base_type.Clone (clonectx);
6430 if (arguments != null){
6431 target.arguments = new ArrayList (arguments.Count);
6432 foreach (Argument a in arguments)
6433 target.arguments.Add (a.Clone (clonectx));
6436 if (initializers != null){
6437 target.initializers = new ArrayList (initializers.Count);
6438 foreach (object initializer in initializers)
6439 if (initializer is ArrayList) {
6440 ArrayList this_al = (ArrayList)initializer;
6441 ArrayList al = new ArrayList (this_al.Count);
6442 target.initializers.Add (al);
6443 foreach (Expression e in this_al)
6444 al.Add (e.Clone (clonectx));
6445 } else {
6446 target.initializers.Add (((Expression)initializer).Clone (clonectx));
6453 // Represents an implicitly typed array epxression
6455 public class ImplicitlyTypedArrayCreation : ArrayCreation
6457 public ImplicitlyTypedArrayCreation (string rank, ArrayList initializers, Location loc)
6458 : base (null, rank, initializers, loc)
6460 if (RootContext.Version <= LanguageVersion.ISO_2)
6461 Report.FeatureIsNotAvailable (loc, "implicitly typed arrays");
6463 if (rank.Length > 2) {
6464 while (rank [++dimensions] == ',');
6465 } else {
6466 dimensions = 1;
6470 public override Expression DoResolve (EmitContext ec)
6472 if (type != null)
6473 return this;
6475 if (!ResolveInitializers (ec))
6476 return null;
6478 if (array_element_type == null || array_element_type == TypeManager.null_type ||
6479 array_element_type == TypeManager.void_type || array_element_type == TypeManager.anonymous_method_type ||
6480 arguments.Count != dimensions) {
6481 Error_NoBestType ();
6482 return null;
6486 // At this point we found common base type for all initializer elements
6487 // but we have to be sure that all static initializer elements are of
6488 // same type
6490 UnifyInitializerElement (ec);
6492 type = TypeManager.GetConstructedType (array_element_type, rank);
6493 eclass = ExprClass.Value;
6494 return this;
6497 void Error_NoBestType ()
6499 Report.Error (826, loc,
6500 "The type of an implicitly typed array cannot be inferred from the initializer. Try specifying array type explicitly");
6504 // Converts static initializer only
6506 void UnifyInitializerElement (EmitContext ec)
6508 for (int i = 0; i < array_data.Count; ++i) {
6509 Expression e = (Expression)array_data[i];
6510 if (e != null)
6511 array_data [i] = Convert.ImplicitConversion (ec, e, array_element_type, Location.Null);
6515 protected override Expression ResolveArrayElement (EmitContext ec, Expression element)
6517 element = element.Resolve (ec);
6518 if (element == null)
6519 return null;
6521 if (array_element_type == null) {
6522 if (element.Type != TypeManager.null_type)
6523 array_element_type = element.Type;
6525 return element;
6528 if (Convert.ImplicitConversionExists (ec, element, array_element_type)) {
6529 return element;
6532 if (Convert.ImplicitConversionExists (ec, new TypeExpression (array_element_type, loc), element.Type)) {
6533 array_element_type = element.Type;
6534 return element;
6537 Error_NoBestType ();
6538 return null;
6542 public sealed class CompilerGeneratedThis : This
6544 public static This Instance = new CompilerGeneratedThis ();
6546 private CompilerGeneratedThis ()
6547 : base (Location.Null)
6551 public CompilerGeneratedThis (Type type, Location loc)
6552 : base (loc)
6554 this.type = type;
6557 public override Expression DoResolve (EmitContext ec)
6559 eclass = ExprClass.Variable;
6560 if (type == null)
6561 type = ec.ContainerType;
6562 return this;
6565 public override HoistedVariable GetHoistedVariable (EmitContext ec)
6567 return null;
6571 /// <summary>
6572 /// Represents the `this' construct
6573 /// </summary>
6575 public class This : VariableReference
6577 sealed class ThisVariable : ILocalVariable
6579 public static readonly ILocalVariable Instance = new ThisVariable ();
6581 public void Emit (EmitContext ec)
6583 ec.ig.Emit (OpCodes.Ldarg_0);
6586 public void EmitAssign (EmitContext ec)
6588 throw new InvalidOperationException ();
6591 public void EmitAddressOf (EmitContext ec)
6593 ec.ig.Emit (OpCodes.Ldarg_0);
6597 Block block;
6598 VariableInfo variable_info;
6599 bool is_struct;
6601 public This (Block block, Location loc)
6603 this.loc = loc;
6604 this.block = block;
6607 public This (Location loc)
6609 this.loc = loc;
6612 public override VariableInfo VariableInfo {
6613 get { return variable_info; }
6616 public override bool IsFixed {
6617 get { return false; }
6620 public override HoistedVariable GetHoistedVariable (EmitContext ec)
6622 // Is null when probing IsHoisted
6623 if (ec == null)
6624 return null;
6626 if (ec.CurrentAnonymousMethod == null)
6627 return null;
6629 AnonymousMethodStorey storey = ec.CurrentAnonymousMethod.Storey;
6630 while (storey != null) {
6631 AnonymousMethodStorey temp = storey.Parent as AnonymousMethodStorey;
6632 if (temp == null)
6633 return storey.HoistedThis;
6635 storey = temp;
6638 return null;
6641 public override bool IsRef {
6642 get { return is_struct; }
6645 protected override ILocalVariable Variable {
6646 get { return ThisVariable.Instance; }
6649 public static bool IsThisAvailable (EmitContext ec)
6651 if (ec.IsStatic || ec.IsInFieldInitializer)
6652 return false;
6654 if (ec.CurrentAnonymousMethod == null)
6655 return true;
6657 if (ec.TypeContainer is Struct && ec.CurrentIterator == null)
6658 return false;
6660 return true;
6663 public bool ResolveBase (EmitContext ec)
6665 if (eclass != ExprClass.Invalid)
6666 return true;
6668 eclass = ExprClass.Variable;
6670 if (ec.TypeContainer.CurrentType != null)
6671 type = ec.TypeContainer.CurrentType;
6672 else
6673 type = ec.ContainerType;
6675 if (!IsThisAvailable (ec)) {
6676 if (ec.IsStatic) {
6677 Error (26, "Keyword `this' is not valid in a static property, static method, or static field initializer");
6678 } else {
6679 Report.Error (1673, loc,
6680 "Anonymous methods inside structs cannot access instance members of `this'. " +
6681 "Consider copying `this' to a local variable outside the anonymous method and using the local instead");
6685 is_struct = ec.TypeContainer is Struct;
6687 if (block != null) {
6688 if (block.Toplevel.ThisVariable != null)
6689 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6691 AnonymousExpression am = ec.CurrentAnonymousMethod;
6692 if (am != null && ec.IsVariableCapturingRequired) {
6693 am.SetHasThisAccess ();
6697 return true;
6701 // Called from Invocation to check if the invocation is correct
6703 public override void CheckMarshalByRefAccess (EmitContext ec)
6705 if ((variable_info != null) && !(TypeManager.IsStruct (type) && ec.OmitStructFlowAnalysis) &&
6706 !variable_info.IsAssigned (ec)) {
6707 Error (188, "The `this' object cannot be used before all of its " +
6708 "fields are assigned to");
6709 variable_info.SetAssigned (ec);
6713 public override Expression CreateExpressionTree (EmitContext ec)
6715 ArrayList args = new ArrayList (1);
6716 args.Add (new Argument (this));
6718 // Use typeless constant for ldarg.0 to save some
6719 // space and avoid problems with anonymous stories
6720 return CreateExpressionFactoryCall ("Constant", args);
6723 public override Expression DoResolve (EmitContext ec)
6725 if (!ResolveBase (ec))
6726 return null;
6729 if (ec.IsInFieldInitializer) {
6730 Error (27, "Keyword `this' is not available in the current context");
6731 return null;
6734 return this;
6737 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6739 if (!ResolveBase (ec))
6740 return null;
6742 if (variable_info != null)
6743 variable_info.SetAssigned (ec);
6745 if (ec.TypeContainer is Class){
6746 if (right_side == EmptyExpression.UnaryAddress)
6747 Report.Error (459, loc, "Cannot take the address of `this' because it is read-only");
6748 else if (right_side == EmptyExpression.OutAccess)
6749 Report.Error (1605, loc, "Cannot pass `this' as a ref or out argument because it is read-only");
6750 else
6751 Report.Error (1604, loc, "Cannot assign to `this' because it is read-only");
6754 return this;
6757 public override int GetHashCode()
6759 return block.GetHashCode ();
6762 public override string Name {
6763 get { return "this"; }
6766 public override bool Equals (object obj)
6768 This t = obj as This;
6769 if (t == null)
6770 return false;
6772 return block == t.block;
6775 protected override void CloneTo (CloneContext clonectx, Expression t)
6777 This target = (This) t;
6779 target.block = clonectx.LookupBlock (block);
6782 public override void SetHasAddressTaken ()
6784 // Nothing
6788 /// <summary>
6789 /// Represents the `__arglist' construct
6790 /// </summary>
6791 public class ArglistAccess : Expression
6793 public ArglistAccess (Location loc)
6795 this.loc = loc;
6798 public override Expression CreateExpressionTree (EmitContext ec)
6800 throw new NotSupportedException ("ET");
6803 public override Expression DoResolve (EmitContext ec)
6805 eclass = ExprClass.Variable;
6806 type = TypeManager.runtime_argument_handle_type;
6808 if (ec.IsInFieldInitializer || !ec.CurrentBlock.Toplevel.Parameters.HasArglist)
6810 Error (190, "The __arglist construct is valid only within " +
6811 "a variable argument method");
6812 return null;
6815 return this;
6818 public override void Emit (EmitContext ec)
6820 ec.ig.Emit (OpCodes.Arglist);
6823 protected override void CloneTo (CloneContext clonectx, Expression target)
6825 // nothing.
6829 /// <summary>
6830 /// Represents the `__arglist (....)' construct
6831 /// </summary>
6832 public class Arglist : Expression
6834 Argument[] Arguments;
6836 public Arglist (Location loc)
6837 : this (Argument.Empty, loc)
6841 public Arglist (Argument[] args, Location l)
6843 Arguments = args;
6844 loc = l;
6847 public Type[] ArgumentTypes {
6848 get {
6849 Type[] retval = new Type [Arguments.Length];
6850 for (int i = 0; i < Arguments.Length; i++)
6851 retval [i] = Arguments [i].Type;
6852 return retval;
6856 public override Expression CreateExpressionTree (EmitContext ec)
6858 Report.Error (1952, loc, "An expression tree cannot contain a method with variable arguments");
6859 return null;
6862 public override Expression DoResolve (EmitContext ec)
6864 eclass = ExprClass.Variable;
6865 type = TypeManager.runtime_argument_handle_type;
6867 foreach (Argument arg in Arguments) {
6868 if (!arg.Resolve (ec, loc))
6869 return null;
6872 return this;
6875 public override void Emit (EmitContext ec)
6877 foreach (Argument arg in Arguments)
6878 arg.Emit (ec);
6881 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
6883 foreach (Argument arg in Arguments)
6884 arg.Expr.MutateHoistedGenericType (storey);
6887 protected override void CloneTo (CloneContext clonectx, Expression t)
6889 Arglist target = (Arglist) t;
6891 target.Arguments = new Argument [Arguments.Length];
6892 for (int i = 0; i < Arguments.Length; i++)
6893 target.Arguments [i] = Arguments [i].Clone (clonectx);
6897 /// <summary>
6898 /// Implements the typeof operator
6899 /// </summary>
6900 public class TypeOf : Expression {
6901 Expression QueriedType;
6902 protected Type typearg;
6904 public TypeOf (Expression queried_type, Location l)
6906 QueriedType = queried_type;
6907 loc = l;
6910 public override Expression CreateExpressionTree (EmitContext ec)
6912 ArrayList args = new ArrayList (2);
6913 args.Add (new Argument (this));
6914 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
6915 return CreateExpressionFactoryCall ("Constant", args);
6918 public override Expression DoResolve (EmitContext ec)
6920 if (eclass != ExprClass.Invalid)
6921 return this;
6923 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6924 if (texpr == null)
6925 return null;
6927 typearg = texpr.Type;
6929 if (typearg == TypeManager.void_type) {
6930 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6931 return null;
6934 if (typearg.IsPointer && !ec.InUnsafe){
6935 UnsafeError (loc);
6936 return null;
6939 type = TypeManager.type_type;
6941 return DoResolveBase ();
6944 protected Expression DoResolveBase ()
6946 if (TypeManager.system_type_get_type_from_handle == null) {
6947 TypeManager.system_type_get_type_from_handle = TypeManager.GetPredefinedMethod (
6948 TypeManager.type_type, "GetTypeFromHandle", loc, TypeManager.runtime_handle_type);
6951 // Even though what is returned is a type object, it's treated as a value by the compiler.
6952 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6953 eclass = ExprClass.Value;
6954 return this;
6957 public override void Emit (EmitContext ec)
6959 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6960 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6963 public override bool GetAttributableValue (EmitContext ec, Type value_type, out object value)
6965 if (TypeManager.ContainsGenericParameters (typearg) &&
6966 !TypeManager.IsGenericTypeDefinition (typearg)) {
6967 Report.SymbolRelatedToPreviousError (typearg);
6968 Report.Error (416, loc, "`{0}': an attribute argument cannot use type parameters",
6969 TypeManager.CSharpName (typearg));
6970 value = null;
6971 return false;
6974 if (value_type == TypeManager.object_type) {
6975 value = (object)typearg;
6976 return true;
6978 value = typearg;
6979 return true;
6982 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
6984 typearg = storey.MutateType (typearg);
6987 public Type TypeArgument {
6988 get {
6989 return typearg;
6993 protected override void CloneTo (CloneContext clonectx, Expression t)
6995 TypeOf target = (TypeOf) t;
6996 if (QueriedType != null)
6997 target.QueriedType = QueriedType.Clone (clonectx);
7001 /// <summary>
7002 /// Implements the `typeof (void)' operator
7003 /// </summary>
7004 public class TypeOfVoid : TypeOf {
7005 public TypeOfVoid (Location l) : base (null, l)
7007 loc = l;
7010 public override Expression DoResolve (EmitContext ec)
7012 type = TypeManager.type_type;
7013 typearg = TypeManager.void_type;
7015 return DoResolveBase ();
7019 class TypeOfMethodInfo : TypeOfMethod
7021 public TypeOfMethodInfo (MethodBase method, Location loc)
7022 : base (method, loc)
7026 public override Expression DoResolve (EmitContext ec)
7028 type = typeof (MethodInfo);
7029 return base.DoResolve (ec);
7032 public override void Emit (EmitContext ec)
7034 ec.ig.Emit (OpCodes.Ldtoken, (MethodInfo) method);
7035 base.Emit (ec);
7036 ec.ig.Emit (OpCodes.Castclass, type);
7040 class TypeOfConstructorInfo : TypeOfMethod
7042 public TypeOfConstructorInfo (MethodBase method, Location loc)
7043 : base (method, loc)
7047 public override Expression DoResolve (EmitContext ec)
7049 type = typeof (ConstructorInfo);
7050 return base.DoResolve (ec);
7053 public override void Emit (EmitContext ec)
7055 ec.ig.Emit (OpCodes.Ldtoken, (ConstructorInfo) method);
7056 base.Emit (ec);
7057 ec.ig.Emit (OpCodes.Castclass, type);
7061 abstract class TypeOfMethod : Expression
7063 protected readonly MethodBase method;
7065 protected TypeOfMethod (MethodBase method, Location loc)
7067 this.method = method;
7068 this.loc = loc;
7071 public override Expression CreateExpressionTree (EmitContext ec)
7073 ArrayList args = new ArrayList (2);
7074 args.Add (new Argument (this));
7075 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
7076 return CreateExpressionFactoryCall ("Constant", args);
7079 public override Expression DoResolve (EmitContext ec)
7081 bool is_generic = TypeManager.IsGenericType (method.DeclaringType);
7082 MethodInfo mi = is_generic ?
7083 TypeManager.methodbase_get_type_from_handle_generic :
7084 TypeManager.methodbase_get_type_from_handle;
7086 if (mi == null) {
7087 Type t = TypeManager.CoreLookupType ("System.Reflection", "MethodBase", Kind.Class, true);
7088 Type handle_type = TypeManager.CoreLookupType ("System", "RuntimeMethodHandle", Kind.Class, true);
7090 if (t == null || handle_type == null)
7091 return null;
7093 mi = TypeManager.GetPredefinedMethod (t, "GetMethodFromHandle", loc,
7094 is_generic ?
7095 new Type[] { handle_type, TypeManager.runtime_handle_type } :
7096 new Type[] { handle_type } );
7098 if (is_generic)
7099 TypeManager.methodbase_get_type_from_handle_generic = mi;
7100 else
7101 TypeManager.methodbase_get_type_from_handle = mi;
7104 eclass = ExprClass.Value;
7105 return this;
7108 public override void Emit (EmitContext ec)
7110 bool is_generic = TypeManager.IsGenericType (method.DeclaringType);
7111 MethodInfo mi;
7112 if (is_generic) {
7113 mi = TypeManager.methodbase_get_type_from_handle_generic;
7114 ec.ig.Emit (OpCodes.Ldtoken, method.DeclaringType);
7115 } else {
7116 mi = TypeManager.methodbase_get_type_from_handle;
7119 ec.ig.Emit (OpCodes.Call, mi);
7123 internal class TypeOfField : Expression
7125 readonly FieldInfo field;
7127 public TypeOfField (FieldInfo field, Location loc)
7129 this.field = field;
7130 this.loc = loc;
7133 public override Expression CreateExpressionTree (EmitContext ec)
7135 throw new NotSupportedException ("ET");
7138 public override Expression DoResolve (EmitContext ec)
7140 if (TypeManager.fieldinfo_get_field_from_handle == null) {
7141 Type t = TypeManager.CoreLookupType ("System.Reflection", "FieldInfo", Kind.Class, true);
7142 Type handle_type = TypeManager.CoreLookupType ("System", "RuntimeFieldHandle", Kind.Class, true);
7144 if (t != null && handle_type != null)
7145 TypeManager.fieldinfo_get_field_from_handle = TypeManager.GetPredefinedMethod (t,
7146 "GetFieldFromHandle", loc, handle_type);
7149 type = typeof (FieldInfo);
7150 eclass = ExprClass.Value;
7151 return this;
7154 public override void Emit (EmitContext ec)
7156 ec.ig.Emit (OpCodes.Ldtoken, field);
7157 ec.ig.Emit (OpCodes.Call, TypeManager.fieldinfo_get_field_from_handle);
7161 /// <summary>
7162 /// Implements the sizeof expression
7163 /// </summary>
7164 public class SizeOf : Expression {
7165 readonly Expression QueriedType;
7166 Type type_queried;
7168 public SizeOf (Expression queried_type, Location l)
7170 this.QueriedType = queried_type;
7171 loc = l;
7174 public override Expression CreateExpressionTree (EmitContext ec)
7176 Error_PointerInsideExpressionTree ();
7177 return null;
7180 public override Expression DoResolve (EmitContext ec)
7182 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
7183 if (texpr == null)
7184 return null;
7186 type_queried = texpr.Type;
7187 if (TypeManager.IsEnumType (type_queried))
7188 type_queried = TypeManager.GetEnumUnderlyingType (type_queried);
7190 int size_of = GetTypeSize (type_queried);
7191 if (size_of > 0) {
7192 return new IntConstant (size_of, loc);
7195 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
7196 return null;
7199 if (!ec.InUnsafe) {
7200 Report.Error (233, loc,
7201 "`{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf)",
7202 TypeManager.CSharpName (type_queried));
7205 type = TypeManager.int32_type;
7206 eclass = ExprClass.Value;
7207 return this;
7210 public override void Emit (EmitContext ec)
7212 int size = GetTypeSize (type_queried);
7214 if (size == 0)
7215 ec.ig.Emit (OpCodes.Sizeof, type_queried);
7216 else
7217 IntConstant.EmitInt (ec.ig, size);
7220 protected override void CloneTo (CloneContext clonectx, Expression t)
7225 /// <summary>
7226 /// Implements the qualified-alias-member (::) expression.
7227 /// </summary>
7228 public class QualifiedAliasMember : MemberAccess
7230 readonly string alias;
7231 public static readonly string GlobalAlias = "global";
7233 public QualifiedAliasMember (string alias, string identifier, TypeArguments targs, Location l)
7234 : base (null, identifier, targs, l)
7236 this.alias = alias;
7239 public QualifiedAliasMember (string alias, string identifier, Location l)
7240 : base (null, identifier, l)
7242 this.alias = alias;
7245 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7247 if (alias == GlobalAlias) {
7248 expr = RootNamespace.Global;
7249 return base.ResolveAsTypeStep (ec, silent);
7252 int errors = Report.Errors;
7253 expr = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7254 if (expr == null) {
7255 if (errors == Report.Errors)
7256 Report.Error (432, loc, "Alias `{0}' not found", alias);
7257 return null;
7260 FullNamedExpression fne = base.ResolveAsTypeStep (ec, silent);
7261 if (fne == null)
7262 return null;
7264 if (expr.eclass == ExprClass.Type) {
7265 if (!silent) {
7266 Report.Error (431, loc,
7267 "Alias `{0}' cannot be used with '::' since it denotes a type. Consider replacing '::' with '.'", alias);
7269 return null;
7272 return fne;
7275 public override Expression DoResolve (EmitContext ec)
7277 return ResolveAsTypeStep (ec, false);
7280 protected override void Error_IdentifierNotFound (IResolveContext rc, FullNamedExpression expr_type, string identifier)
7282 Report.Error (687, loc,
7283 "A namespace alias qualifier `{0}' did not resolve to a namespace or a type",
7284 GetSignatureForError ());
7287 public override string GetSignatureForError ()
7289 string name = Name;
7290 if (targs != null) {
7291 name = TypeManager.RemoveGenericArity (Name) + "<" +
7292 targs.GetSignatureForError () + ">";
7295 return alias + "::" + name;
7298 protected override void CloneTo (CloneContext clonectx, Expression t)
7300 // Nothing
7304 /// <summary>
7305 /// Implements the member access expression
7306 /// </summary>
7307 public class MemberAccess : ATypeNameExpression {
7308 protected Expression expr;
7310 public MemberAccess (Expression expr, string id)
7311 : base (id, expr.Location)
7313 this.expr = expr;
7316 public MemberAccess (Expression expr, string identifier, Location loc)
7317 : base (identifier, loc)
7319 this.expr = expr;
7322 public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
7323 : base (identifier, args, loc)
7325 this.expr = expr;
7328 Expression DoResolve (EmitContext ec, Expression right_side)
7330 if (type != null)
7331 throw new Exception ();
7334 // Resolve the expression with flow analysis turned off, we'll do the definite
7335 // assignment checks later. This is because we don't know yet what the expression
7336 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7337 // definite assignment check on the actual field and not on the whole struct.
7340 SimpleName original = expr as SimpleName;
7341 Expression expr_resolved = expr.Resolve (ec,
7342 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7343 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7345 if (expr_resolved == null)
7346 return null;
7348 string LookupIdentifier = MemberName.MakeName (Name, targs);
7350 Namespace ns = expr_resolved as Namespace;
7351 if (ns != null) {
7352 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, LookupIdentifier, loc);
7354 if (retval == null)
7355 ns.Error_NamespaceDoesNotExist (ec.DeclContainer, loc, LookupIdentifier);
7356 else if (targs != null)
7357 retval = new GenericTypeExpr (retval.Type, targs, loc).ResolveAsTypeStep (ec, false);
7359 return retval;
7362 Type expr_type = expr_resolved.Type;
7363 if (expr_type.IsPointer || expr_type == TypeManager.void_type ||
7364 expr_type == TypeManager.null_type || expr_type == TypeManager.anonymous_method_type) {
7365 Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type);
7366 return null;
7369 Constant c = expr_resolved as Constant;
7370 if (c != null && c.GetValue () == null) {
7371 Report.Warning (1720, 1, loc, "Expression will always cause a `{0}'",
7372 "System.NullReferenceException");
7375 if (targs != null) {
7376 if (!targs.Resolve (ec))
7377 return null;
7380 Expression member_lookup;
7381 member_lookup = MemberLookup (
7382 ec.ContainerType, expr_type, expr_type, Name, loc);
7383 #if GMCS_SOURCE
7384 if ((member_lookup == null) && (targs != null)) {
7385 member_lookup = MemberLookup (
7386 ec.ContainerType, expr_type, expr_type, LookupIdentifier, loc);
7388 #endif
7389 if (member_lookup == null) {
7390 ExprClass expr_eclass = expr_resolved.eclass;
7393 // Extension methods are not allowed on all expression types
7395 if (expr_eclass == ExprClass.Value || expr_eclass == ExprClass.Variable ||
7396 expr_eclass == ExprClass.IndexerAccess || expr_eclass == ExprClass.PropertyAccess ||
7397 expr_eclass == ExprClass.EventAccess) {
7398 ExtensionMethodGroupExpr ex_method_lookup = ec.TypeContainer.LookupExtensionMethod (expr_type, Name, loc);
7399 if (ex_method_lookup != null) {
7400 ex_method_lookup.ExtensionExpression = expr_resolved;
7402 if (targs != null) {
7403 ex_method_lookup.SetTypeArguments (targs);
7406 return ex_method_lookup.DoResolve (ec);
7410 expr = expr_resolved;
7411 member_lookup = Error_MemberLookupFailed (
7412 ec.ContainerType, expr_type, expr_type, Name, null,
7413 AllMemberTypes, AllBindingFlags);
7414 if (member_lookup == null)
7415 return null;
7418 TypeExpr texpr = member_lookup as TypeExpr;
7419 if (texpr != null) {
7420 if (!(expr_resolved is TypeExpr) &&
7421 (original == null || !original.IdenticalNameAndTypeName (ec, expr_resolved, loc))) {
7422 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7423 Name, member_lookup.GetSignatureForError ());
7424 return null;
7427 if (!texpr.CheckAccessLevel (ec.DeclContainer)) {
7428 Report.SymbolRelatedToPreviousError (member_lookup.Type);
7429 ErrorIsInaccesible (loc, TypeManager.CSharpName (member_lookup.Type));
7430 return null;
7433 #if GMCS_SOURCE
7434 GenericTypeExpr ct = expr_resolved as GenericTypeExpr;
7435 if (ct != null) {
7437 // When looking up a nested type in a generic instance
7438 // via reflection, we always get a generic type definition
7439 // and not a generic instance - so we have to do this here.
7441 // See gtest-172-lib.cs and gtest-172.cs for an example.
7443 ct = new GenericTypeExpr (
7444 member_lookup.Type, ct.TypeArguments, loc);
7446 return ct.ResolveAsTypeStep (ec, false);
7448 #endif
7449 return member_lookup;
7452 MemberExpr me = (MemberExpr) member_lookup;
7453 me = me.ResolveMemberAccess (ec, expr_resolved, loc, original);
7454 if (me == null)
7455 return null;
7457 if (targs != null) {
7458 me.SetTypeArguments (targs);
7461 if (original != null && !TypeManager.IsValueType (expr_type)) {
7462 if (me.IsInstance) {
7463 LocalVariableReference var = expr_resolved as LocalVariableReference;
7464 if (var != null && !var.VerifyAssigned (ec))
7465 return null;
7469 // The following DoResolve/DoResolveLValue will do the definite assignment
7470 // check.
7472 if (right_side != null)
7473 return me.DoResolveLValue (ec, right_side);
7474 else
7475 return me.DoResolve (ec);
7478 public override Expression DoResolve (EmitContext ec)
7480 return DoResolve (ec, null);
7483 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7485 return DoResolve (ec, right_side);
7488 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7490 return ResolveNamespaceOrType (ec, silent);
7493 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7495 FullNamedExpression expr_resolved = expr.ResolveAsTypeStep (rc, silent);
7497 if (expr_resolved == null)
7498 return null;
7500 string LookupIdentifier = MemberName.MakeName (Name, targs);
7502 Namespace ns = expr_resolved as Namespace;
7503 if (ns != null) {
7504 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, LookupIdentifier, loc);
7506 if (retval == null && !silent)
7507 ns.Error_NamespaceDoesNotExist (rc.DeclContainer, loc, LookupIdentifier);
7508 else if (targs != null)
7509 retval = new GenericTypeExpr (retval.Type, targs, loc).ResolveAsTypeStep (rc, silent);
7511 return retval;
7514 TypeExpr tnew_expr = expr_resolved.ResolveAsTypeTerminal (rc, false);
7515 if (tnew_expr == null)
7516 return null;
7518 if (tnew_expr is TypeParameterExpr) {
7519 Report.Error (704, loc, "A nested type cannot be specified through a type parameter `{0}'",
7520 tnew_expr.GetSignatureForError ());
7521 return null;
7524 Type expr_type = tnew_expr.Type;
7525 Expression member_lookup = MemberLookup (
7526 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
7527 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7528 if (member_lookup == null) {
7529 if (silent)
7530 return null;
7532 Error_IdentifierNotFound (rc, expr_resolved, LookupIdentifier);
7533 return null;
7536 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
7537 if (texpr == null)
7538 return null;
7540 #if GMCS_SOURCE
7541 TypeArguments the_args = targs;
7542 Type declaring_type = texpr.Type.DeclaringType;
7543 if (TypeManager.HasGenericArguments (declaring_type) && !TypeManager.IsGenericTypeDefinition (expr_type)) {
7544 while (!TypeManager.IsEqual (TypeManager.DropGenericTypeArguments (expr_type), declaring_type)) {
7545 expr_type = expr_type.BaseType;
7548 TypeArguments new_args = new TypeArguments ();
7549 foreach (Type decl in TypeManager.GetTypeArguments (expr_type))
7550 new_args.Add (new TypeExpression (decl, loc));
7552 if (targs != null)
7553 new_args.Add (targs);
7555 the_args = new_args;
7558 if (the_args != null) {
7559 GenericTypeExpr ctype = new GenericTypeExpr (texpr.Type, the_args, loc);
7560 return ctype.ResolveAsTypeStep (rc, false);
7562 #endif
7564 return texpr;
7567 protected virtual void Error_IdentifierNotFound (IResolveContext rc, FullNamedExpression expr_type, string identifier)
7569 Expression member_lookup = MemberLookup (
7570 rc.DeclContainer.TypeBuilder, expr_type.Type, expr_type.Type, SimpleName.RemoveGenericArity (identifier),
7571 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7573 if (member_lookup != null) {
7574 expr_type = member_lookup.ResolveAsTypeTerminal (rc, false);
7575 if (expr_type == null)
7576 return;
7578 Namespace.Error_TypeArgumentsCannotBeUsed (expr_type, loc);
7579 return;
7582 member_lookup = MemberLookup (
7583 rc.DeclContainer.TypeBuilder, expr_type.Type, expr_type.Type, identifier,
7584 MemberTypes.All, BindingFlags.Public | BindingFlags.NonPublic, loc);
7586 if (member_lookup == null) {
7587 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7588 Name, expr_type.GetSignatureForError ());
7589 } else {
7590 // TODO: Report.SymbolRelatedToPreviousError
7591 member_lookup.Error_UnexpectedKind (null, "type", loc);
7595 protected override void Error_TypeDoesNotContainDefinition (Type type, string name)
7597 if (RootContext.Version > LanguageVersion.ISO_2 &&
7598 ((expr.eclass & (ExprClass.Value | ExprClass.Variable)) != 0)) {
7599 Report.Error (1061, loc, "Type `{0}' does not contain a definition for `{1}' and no " +
7600 "extension method `{1}' of type `{0}' could be found " +
7601 "(are you missing a using directive or an assembly reference?)",
7602 TypeManager.CSharpName (type), name);
7603 return;
7606 base.Error_TypeDoesNotContainDefinition (type, name);
7609 public override string GetSignatureForError ()
7611 return expr.GetSignatureForError () + "." + base.GetSignatureForError ();
7614 protected override void CloneTo (CloneContext clonectx, Expression t)
7616 MemberAccess target = (MemberAccess) t;
7618 target.expr = expr.Clone (clonectx);
7622 /// <summary>
7623 /// Implements checked expressions
7624 /// </summary>
7625 public class CheckedExpr : Expression {
7627 public Expression Expr;
7629 public CheckedExpr (Expression e, Location l)
7631 Expr = e;
7632 loc = l;
7635 public override Expression CreateExpressionTree (EmitContext ec)
7637 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7638 return Expr.CreateExpressionTree (ec);
7641 public override Expression DoResolve (EmitContext ec)
7643 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7644 Expr = Expr.Resolve (ec);
7646 if (Expr == null)
7647 return null;
7649 if (Expr is Constant || Expr is MethodGroupExpr || Expr is AnonymousMethodExpression)
7650 return Expr;
7652 eclass = Expr.eclass;
7653 type = Expr.Type;
7654 return this;
7657 public override void Emit (EmitContext ec)
7659 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7660 Expr.Emit (ec);
7663 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
7665 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7666 Expr.EmitBranchable (ec, target, on_true);
7669 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
7671 Expr.MutateHoistedGenericType (storey);
7674 protected override void CloneTo (CloneContext clonectx, Expression t)
7676 CheckedExpr target = (CheckedExpr) t;
7678 target.Expr = Expr.Clone (clonectx);
7682 /// <summary>
7683 /// Implements the unchecked expression
7684 /// </summary>
7685 public class UnCheckedExpr : Expression {
7687 public Expression Expr;
7689 public UnCheckedExpr (Expression e, Location l)
7691 Expr = e;
7692 loc = l;
7695 public override Expression CreateExpressionTree (EmitContext ec)
7697 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7698 return Expr.CreateExpressionTree (ec);
7701 public override Expression DoResolve (EmitContext ec)
7703 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7704 Expr = Expr.Resolve (ec);
7706 if (Expr == null)
7707 return null;
7709 if (Expr is Constant || Expr is MethodGroupExpr || Expr is AnonymousMethodExpression)
7710 return Expr;
7712 eclass = Expr.eclass;
7713 type = Expr.Type;
7714 return this;
7717 public override void Emit (EmitContext ec)
7719 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7720 Expr.Emit (ec);
7723 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
7725 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7726 Expr.EmitBranchable (ec, target, on_true);
7729 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
7731 Expr.MutateHoistedGenericType (storey);
7734 protected override void CloneTo (CloneContext clonectx, Expression t)
7736 UnCheckedExpr target = (UnCheckedExpr) t;
7738 target.Expr = Expr.Clone (clonectx);
7742 /// <summary>
7743 /// An Element Access expression.
7745 /// During semantic analysis these are transformed into
7746 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7747 /// </summary>
7748 public class ElementAccess : Expression {
7749 public ArrayList Arguments;
7750 public Expression Expr;
7752 public ElementAccess (Expression e, ArrayList e_list)
7754 Expr = e;
7755 loc = e.Location;
7757 if (e_list == null)
7758 return;
7760 Arguments = new ArrayList (e_list.Count);
7761 foreach (Expression tmp in e_list)
7762 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7765 bool CommonResolve (EmitContext ec)
7767 Expr = Expr.Resolve (ec);
7769 if (Arguments == null)
7770 return false;
7772 foreach (Argument a in Arguments){
7773 if (!a.Resolve (ec, loc))
7774 return false;
7777 return Expr != null;
7780 public override Expression CreateExpressionTree (EmitContext ec)
7782 ArrayList args = new ArrayList (Arguments.Count + 1);
7783 args.Add (new Argument (Expr.CreateExpressionTree (ec)));
7784 foreach (Argument a in Arguments)
7785 args.Add (new Argument (a.Expr.CreateExpressionTree (ec)));
7787 return CreateExpressionFactoryCall ("ArrayIndex", args);
7790 Expression MakePointerAccess (EmitContext ec, Type t)
7792 if (Arguments.Count != 1){
7793 Error (196, "A pointer must be indexed by only one value");
7794 return null;
7797 Expression p = new PointerArithmetic (Binary.Operator.Addition, Expr, ((Argument) Arguments [0]).Expr, t, loc).Resolve (ec);
7798 if (p == null)
7799 return null;
7800 return new Indirection (p, loc).Resolve (ec);
7803 public override Expression DoResolve (EmitContext ec)
7805 if (!CommonResolve (ec))
7806 return null;
7809 // We perform some simple tests, and then to "split" the emit and store
7810 // code we create an instance of a different class, and return that.
7812 // I am experimenting with this pattern.
7814 Type t = Expr.Type;
7816 if (t == TypeManager.array_type){
7817 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7818 return null;
7821 if (t.IsArray)
7822 return (new ArrayAccess (this, loc)).Resolve (ec);
7823 if (t.IsPointer)
7824 return MakePointerAccess (ec, t);
7826 FieldExpr fe = Expr as FieldExpr;
7827 if (fe != null) {
7828 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7829 if (ff != null) {
7830 return MakePointerAccess (ec, ff.ElementType);
7833 return (new IndexerAccess (this, loc)).Resolve (ec);
7836 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7838 if (!CommonResolve (ec))
7839 return null;
7841 type = Expr.Type;
7842 if (type.IsArray)
7843 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7845 if (type.IsPointer)
7846 return MakePointerAccess (ec, type);
7848 if (Expr.eclass != ExprClass.Variable && TypeManager.IsStruct (type))
7849 Error_CannotModifyIntermediateExpressionValue (ec);
7851 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7854 public override void Emit (EmitContext ec)
7856 throw new Exception ("Should never be reached");
7859 public override string GetSignatureForError ()
7861 return Expr.GetSignatureForError ();
7864 protected override void CloneTo (CloneContext clonectx, Expression t)
7866 ElementAccess target = (ElementAccess) t;
7868 target.Expr = Expr.Clone (clonectx);
7869 target.Arguments = new ArrayList (Arguments.Count);
7870 foreach (Argument a in Arguments)
7871 target.Arguments.Add (a.Clone (clonectx));
7875 /// <summary>
7876 /// Implements array access
7877 /// </summary>
7878 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7880 // Points to our "data" repository
7882 ElementAccess ea;
7884 LocalTemporary temp;
7886 bool prepared;
7888 public ArrayAccess (ElementAccess ea_data, Location l)
7890 ea = ea_data;
7891 loc = l;
7894 public override Expression CreateExpressionTree (EmitContext ec)
7896 return ea.CreateExpressionTree (ec);
7899 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7901 return DoResolve (ec);
7904 public override Expression DoResolve (EmitContext ec)
7906 #if false
7907 ExprClass eclass = ea.Expr.eclass;
7909 // As long as the type is valid
7910 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7911 eclass == ExprClass.Value)) {
7912 ea.Expr.Error_UnexpectedKind ("variable or value");
7913 return null;
7915 #endif
7917 if (eclass != ExprClass.Invalid)
7918 return this;
7920 Type t = ea.Expr.Type;
7921 int rank = ea.Arguments.Count;
7922 if (t.GetArrayRank () != rank) {
7923 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7924 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7925 return null;
7928 type = TypeManager.GetElementType (t);
7929 if (type.IsPointer && !ec.InUnsafe) {
7930 UnsafeError (ea.Location);
7931 return null;
7934 foreach (Argument a in ea.Arguments) {
7935 a.Expr = ConvertExpressionToArrayIndex (ec, a.Expr);
7938 eclass = ExprClass.Variable;
7940 return this;
7943 /// <summary>
7944 /// Emits the right opcode to load an object of Type `t'
7945 /// from an array of T
7946 /// </summary>
7947 void EmitLoadOpcode (ILGenerator ig, Type type, int rank)
7949 if (rank > 1) {
7950 MethodInfo get = FetchGetMethod ();
7951 ig.Emit (OpCodes.Call, get);
7952 return;
7955 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7956 ig.Emit (OpCodes.Ldelem_U1);
7957 else if (type == TypeManager.sbyte_type)
7958 ig.Emit (OpCodes.Ldelem_I1);
7959 else if (type == TypeManager.short_type)
7960 ig.Emit (OpCodes.Ldelem_I2);
7961 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7962 ig.Emit (OpCodes.Ldelem_U2);
7963 else if (type == TypeManager.int32_type)
7964 ig.Emit (OpCodes.Ldelem_I4);
7965 else if (type == TypeManager.uint32_type)
7966 ig.Emit (OpCodes.Ldelem_U4);
7967 else if (type == TypeManager.uint64_type)
7968 ig.Emit (OpCodes.Ldelem_I8);
7969 else if (type == TypeManager.int64_type)
7970 ig.Emit (OpCodes.Ldelem_I8);
7971 else if (type == TypeManager.float_type)
7972 ig.Emit (OpCodes.Ldelem_R4);
7973 else if (type == TypeManager.double_type)
7974 ig.Emit (OpCodes.Ldelem_R8);
7975 else if (type == TypeManager.intptr_type)
7976 ig.Emit (OpCodes.Ldelem_I);
7977 else if (TypeManager.IsEnumType (type)){
7978 EmitLoadOpcode (ig, TypeManager.GetEnumUnderlyingType (type), rank);
7979 } else if (TypeManager.IsStruct (type)){
7980 ig.Emit (OpCodes.Ldelema, type);
7981 ig.Emit (OpCodes.Ldobj, type);
7982 #if GMCS_SOURCE
7983 } else if (type.IsGenericParameter) {
7984 ig.Emit (OpCodes.Ldelem, type);
7985 #endif
7986 } else if (type.IsPointer)
7987 ig.Emit (OpCodes.Ldelem_I);
7988 else
7989 ig.Emit (OpCodes.Ldelem_Ref);
7992 protected override void Error_NegativeArrayIndex (Location loc)
7994 Report.Warning (251, 2, loc, "Indexing an array with a negative index (array indices always start at zero)");
7997 /// <summary>
7998 /// Returns the right opcode to store an object of Type `t'
7999 /// from an array of T.
8000 /// </summary>
8001 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
8003 //Console.WriteLine (new System.Diagnostics.StackTrace ());
8004 has_type_arg = false; is_stobj = false;
8005 t = TypeManager.TypeToCoreType (t);
8006 if (TypeManager.IsEnumType (t))
8007 t = TypeManager.GetEnumUnderlyingType (t);
8008 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
8009 t == TypeManager.bool_type)
8010 return OpCodes.Stelem_I1;
8011 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
8012 t == TypeManager.char_type)
8013 return OpCodes.Stelem_I2;
8014 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
8015 return OpCodes.Stelem_I4;
8016 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
8017 return OpCodes.Stelem_I8;
8018 else if (t == TypeManager.float_type)
8019 return OpCodes.Stelem_R4;
8020 else if (t == TypeManager.double_type)
8021 return OpCodes.Stelem_R8;
8022 else if (t == TypeManager.intptr_type) {
8023 has_type_arg = true;
8024 is_stobj = true;
8025 return OpCodes.Stobj;
8026 } else if (TypeManager.IsStruct (t)) {
8027 has_type_arg = true;
8028 is_stobj = true;
8029 return OpCodes.Stobj;
8030 #if GMCS_SOURCE
8031 } else if (t.IsGenericParameter) {
8032 has_type_arg = true;
8033 return OpCodes.Stelem;
8034 #endif
8036 } else if (t.IsPointer)
8037 return OpCodes.Stelem_I;
8038 else
8039 return OpCodes.Stelem_Ref;
8042 MethodInfo FetchGetMethod ()
8044 ModuleBuilder mb = CodeGen.Module.Builder;
8045 int arg_count = ea.Arguments.Count;
8046 Type [] args = new Type [arg_count];
8047 MethodInfo get;
8049 for (int i = 0; i < arg_count; i++){
8050 //args [i++] = a.Type;
8051 args [i] = TypeManager.int32_type;
8054 get = mb.GetArrayMethod (
8055 ea.Expr.Type, "Get",
8056 CallingConventions.HasThis |
8057 CallingConventions.Standard,
8058 type, args);
8059 return get;
8063 MethodInfo FetchAddressMethod ()
8065 ModuleBuilder mb = CodeGen.Module.Builder;
8066 int arg_count = ea.Arguments.Count;
8067 Type [] args = new Type [arg_count];
8068 MethodInfo address;
8069 Type ret_type;
8071 ret_type = TypeManager.GetReferenceType (type);
8073 for (int i = 0; i < arg_count; i++){
8074 //args [i++] = a.Type;
8075 args [i] = TypeManager.int32_type;
8078 address = mb.GetArrayMethod (
8079 ea.Expr.Type, "Address",
8080 CallingConventions.HasThis |
8081 CallingConventions.Standard,
8082 ret_type, args);
8084 return address;
8088 // Load the array arguments into the stack.
8090 void LoadArrayAndArguments (EmitContext ec)
8092 ea.Expr.Emit (ec);
8094 for (int i = 0; i < ea.Arguments.Count; ++i) {
8095 ((Argument)ea.Arguments [i]).Emit (ec);
8099 public void Emit (EmitContext ec, bool leave_copy)
8101 int rank = ea.Expr.Type.GetArrayRank ();
8102 ILGenerator ig = ec.ig;
8104 if (prepared) {
8105 LoadFromPtr (ig, this.type);
8106 } else {
8107 LoadArrayAndArguments (ec);
8108 EmitLoadOpcode (ig, type, rank);
8111 if (leave_copy) {
8112 ig.Emit (OpCodes.Dup);
8113 temp = new LocalTemporary (this.type);
8114 temp.Store (ec);
8118 public override void Emit (EmitContext ec)
8120 Emit (ec, false);
8123 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8125 int rank = ea.Expr.Type.GetArrayRank ();
8126 ILGenerator ig = ec.ig;
8127 Type t = source.Type;
8128 prepared = prepare_for_load;
8130 if (prepared) {
8131 AddressOf (ec, AddressOp.LoadStore);
8132 ec.ig.Emit (OpCodes.Dup);
8133 } else {
8134 LoadArrayAndArguments (ec);
8137 if (rank == 1) {
8138 bool is_stobj, has_type_arg;
8139 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
8141 if (!prepared) {
8143 // The stobj opcode used by value types will need
8144 // an address on the stack, not really an array/array
8145 // pair
8147 if (is_stobj)
8148 ig.Emit (OpCodes.Ldelema, t);
8151 source.Emit (ec);
8152 if (leave_copy) {
8153 ec.ig.Emit (OpCodes.Dup);
8154 temp = new LocalTemporary (this.type);
8155 temp.Store (ec);
8158 if (prepared)
8159 StoreFromPtr (ig, t);
8160 else if (is_stobj)
8161 ig.Emit (OpCodes.Stobj, t);
8162 else if (has_type_arg)
8163 ig.Emit (op, t);
8164 else
8165 ig.Emit (op);
8166 } else {
8167 source.Emit (ec);
8168 if (leave_copy) {
8169 ec.ig.Emit (OpCodes.Dup);
8170 temp = new LocalTemporary (this.type);
8171 temp.Store (ec);
8174 if (prepared) {
8175 StoreFromPtr (ig, t);
8176 } else {
8177 int arg_count = ea.Arguments.Count;
8178 Type [] args = new Type [arg_count + 1];
8179 for (int i = 0; i < arg_count; i++) {
8180 //args [i++] = a.Type;
8181 args [i] = TypeManager.int32_type;
8183 args [arg_count] = type;
8185 MethodInfo set = CodeGen.Module.Builder.GetArrayMethod (
8186 ea.Expr.Type, "Set",
8187 CallingConventions.HasThis |
8188 CallingConventions.Standard,
8189 TypeManager.void_type, args);
8191 ig.Emit (OpCodes.Call, set);
8195 if (temp != null) {
8196 temp.Emit (ec);
8197 temp.Release (ec);
8201 public void EmitNew (EmitContext ec, New source, bool leave_copy)
8203 if (!source.Emit (ec, this)) {
8204 if (leave_copy)
8205 throw new NotImplementedException ();
8207 return;
8210 throw new NotImplementedException ();
8213 public void AddressOf (EmitContext ec, AddressOp mode)
8215 int rank = ea.Expr.Type.GetArrayRank ();
8216 ILGenerator ig = ec.ig;
8218 LoadArrayAndArguments (ec);
8220 if (rank == 1){
8221 ig.Emit (OpCodes.Ldelema, type);
8222 } else {
8223 MethodInfo address = FetchAddressMethod ();
8224 ig.Emit (OpCodes.Call, address);
8228 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
8230 type = storey.MutateType (type);
8231 ea.Expr.Type = storey.MutateType (ea.Expr.Type);
8235 /// <summary>
8236 /// Expressions that represent an indexer call.
8237 /// </summary>
8238 public class IndexerAccess : Expression, IAssignMethod
8240 class IndexerMethodGroupExpr : MethodGroupExpr
8242 public IndexerMethodGroupExpr (Indexers indexers, Location loc)
8243 : base (null, loc)
8245 Methods = (MethodBase []) indexers.Methods.ToArray (typeof (MethodBase));
8248 public override string Name {
8249 get {
8250 return "this";
8254 protected override int GetApplicableParametersCount (MethodBase method, AParametersCollection parameters)
8257 // Here is the trick, decrease number of arguments by 1 when only
8258 // available property method is setter. This makes overload resolution
8259 // work correctly for indexers.
8262 if (method.Name [0] == 'g')
8263 return parameters.Count;
8265 return parameters.Count - 1;
8269 class Indexers
8271 // Contains either property getter or setter
8272 public ArrayList Methods;
8273 public ArrayList Properties;
8275 Indexers ()
8279 void Append (Type caller_type, MemberInfo [] mi)
8281 if (mi == null)
8282 return;
8284 foreach (PropertyInfo property in mi) {
8285 MethodInfo accessor = property.GetGetMethod (true);
8286 if (accessor == null)
8287 accessor = property.GetSetMethod (true);
8289 if (Methods == null) {
8290 Methods = new ArrayList ();
8291 Properties = new ArrayList ();
8294 Methods.Add (accessor);
8295 Properties.Add (property);
8299 static MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
8301 string p_name = TypeManager.IndexerPropertyName (lookup_type);
8303 return TypeManager.MemberLookup (
8304 caller_type, caller_type, lookup_type, MemberTypes.Property,
8305 BindingFlags.Public | BindingFlags.Instance |
8306 BindingFlags.DeclaredOnly, p_name, null);
8309 public static Indexers GetIndexersForType (Type caller_type, Type lookup_type)
8311 Indexers ix = new Indexers ();
8313 #if GMCS_SOURCE
8314 if (lookup_type.IsGenericParameter) {
8315 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
8316 if (gc == null)
8317 return ix;
8319 if (gc.HasClassConstraint)
8320 ix.Append (caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
8322 Type[] ifaces = gc.InterfaceConstraints;
8323 foreach (Type itype in ifaces)
8324 ix.Append (caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8326 return ix;
8328 #endif
8330 Type copy = lookup_type;
8331 while (copy != TypeManager.object_type && copy != null){
8332 ix.Append (caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
8333 copy = copy.BaseType;
8336 if (lookup_type.IsInterface) {
8337 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
8338 if (ifaces != null) {
8339 foreach (Type itype in ifaces)
8340 ix.Append (caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8344 return ix;
8348 enum AccessorType
8350 Get,
8355 // Points to our "data" repository
8357 MethodInfo get, set;
8358 bool is_base_indexer;
8359 bool prepared;
8360 LocalTemporary temp;
8361 LocalTemporary prepared_value;
8362 Expression set_expr;
8364 protected Type indexer_type;
8365 protected Type current_type;
8366 protected Expression instance_expr;
8367 protected ArrayList arguments;
8369 public IndexerAccess (ElementAccess ea, Location loc)
8370 : this (ea.Expr, false, loc)
8372 this.arguments = ea.Arguments;
8375 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
8376 Location loc)
8378 this.instance_expr = instance_expr;
8379 this.is_base_indexer = is_base_indexer;
8380 this.eclass = ExprClass.Value;
8381 this.loc = loc;
8384 static string GetAccessorName (AccessorType at)
8386 if (at == AccessorType.Set)
8387 return "set";
8389 if (at == AccessorType.Get)
8390 return "get";
8392 throw new NotImplementedException (at.ToString ());
8395 public override Expression CreateExpressionTree (EmitContext ec)
8397 ArrayList args = new ArrayList (arguments.Count + 2);
8398 args.Add (new Argument (instance_expr.CreateExpressionTree (ec)));
8399 args.Add (new Argument (new TypeOfMethodInfo (get, loc)));
8400 foreach (Argument a in arguments)
8401 args.Add (new Argument (a.Expr.CreateExpressionTree (ec)));
8403 return CreateExpressionFactoryCall ("Call", args);
8406 protected virtual bool CommonResolve (EmitContext ec)
8408 indexer_type = instance_expr.Type;
8409 current_type = ec.ContainerType;
8411 return true;
8414 public override Expression DoResolve (EmitContext ec)
8416 return ResolveAccessor (ec, AccessorType.Get);
8419 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8421 if (right_side == EmptyExpression.OutAccess) {
8422 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
8423 GetSignatureForError ());
8424 return null;
8427 // if the indexer returns a value type, and we try to set a field in it
8428 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
8429 Error_CannotModifyIntermediateExpressionValue (ec);
8432 Expression e = ResolveAccessor (ec, AccessorType.Set);
8433 if (e == null)
8434 return null;
8436 set_expr = Convert.ImplicitConversion (ec, right_side, type, loc);
8437 return e;
8440 Expression ResolveAccessor (EmitContext ec, AccessorType accessorType)
8442 if (!CommonResolve (ec))
8443 return null;
8445 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8446 if (ilist.Methods == null) {
8447 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8448 TypeManager.CSharpName (indexer_type));
8449 return null;
8452 MethodGroupExpr mg = new IndexerMethodGroupExpr (ilist, loc);
8453 mg = mg.OverloadResolve (ec, ref arguments, false, loc);
8454 if (mg == null)
8455 return null;
8457 MethodInfo mi = (MethodInfo) mg;
8458 PropertyInfo pi = null;
8459 for (int i = 0; i < ilist.Methods.Count; ++i) {
8460 if (ilist.Methods [i] == mi) {
8461 pi = (PropertyInfo) ilist.Properties [i];
8462 break;
8466 type = TypeManager.TypeToCoreType (pi.PropertyType);
8467 if (type.IsPointer && !ec.InUnsafe)
8468 UnsafeError (loc);
8470 MethodInfo accessor;
8471 if (accessorType == AccessorType.Get) {
8472 accessor = get = pi.GetGetMethod (true);
8473 } else {
8474 accessor = set = pi.GetSetMethod (true);
8475 if (accessor == null && pi.GetGetMethod (true) != null) {
8476 Report.SymbolRelatedToPreviousError (pi);
8477 Report.Error (200, loc, "The read only property or indexer `{0}' cannot be assigned to",
8478 TypeManager.GetFullNameSignature (pi));
8479 return null;
8483 if (accessor == null) {
8484 Report.SymbolRelatedToPreviousError (pi);
8485 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks a `{1}' accessor",
8486 TypeManager.GetFullNameSignature (pi), GetAccessorName (accessorType));
8487 return null;
8491 // Only base will allow this invocation to happen.
8493 if (accessor.IsAbstract && this is BaseIndexerAccess) {
8494 Error_CannotCallAbstractBase (TypeManager.GetFullNameSignature (pi));
8497 bool must_do_cs1540_check;
8498 if (!IsAccessorAccessible (ec.ContainerType, accessor, out must_do_cs1540_check)) {
8499 if (set == null)
8500 set = pi.GetSetMethod (true);
8501 else
8502 get = pi.GetGetMethod (true);
8504 if (set != null && get != null &&
8505 (set.Attributes & MethodAttributes.MemberAccessMask) != (get.Attributes & MethodAttributes.MemberAccessMask)) {
8506 Report.SymbolRelatedToPreviousError (accessor);
8507 Report.Error (271, loc, "The property or indexer `{0}' cannot be used in this context because a `{1}' accessor is inaccessible",
8508 TypeManager.GetFullNameSignature (pi), GetAccessorName (accessorType));
8509 } else {
8510 Report.SymbolRelatedToPreviousError (pi);
8511 ErrorIsInaccesible (loc, TypeManager.GetFullNameSignature (pi));
8515 instance_expr.CheckMarshalByRefAccess (ec);
8516 eclass = ExprClass.IndexerAccess;
8517 return this;
8520 public void Emit (EmitContext ec, bool leave_copy)
8522 if (prepared) {
8523 prepared_value.Emit (ec);
8524 } else {
8525 Invocation.EmitCall (ec, is_base_indexer, instance_expr, get,
8526 arguments, loc, false, false);
8529 if (leave_copy) {
8530 ec.ig.Emit (OpCodes.Dup);
8531 temp = new LocalTemporary (Type);
8532 temp.Store (ec);
8537 // source is ignored, because we already have a copy of it from the
8538 // LValue resolution and we have already constructed a pre-cached
8539 // version of the arguments (ea.set_arguments);
8541 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8543 prepared = prepare_for_load;
8544 Expression value = set_expr;
8546 if (prepared) {
8547 Invocation.EmitCall (ec, is_base_indexer, instance_expr, get,
8548 arguments, loc, true, false);
8550 prepared_value = new LocalTemporary (type);
8551 prepared_value.Store (ec);
8552 source.Emit (ec);
8553 prepared_value.Release (ec);
8555 if (leave_copy) {
8556 ec.ig.Emit (OpCodes.Dup);
8557 temp = new LocalTemporary (Type);
8558 temp.Store (ec);
8560 } else if (leave_copy) {
8561 temp = new LocalTemporary (Type);
8562 source.Emit (ec);
8563 temp.Store (ec);
8564 value = temp;
8567 arguments.Add (new Argument (value, Argument.AType.Expression));
8568 Invocation.EmitCall (ec, is_base_indexer, instance_expr, set, arguments, loc, false, prepared);
8570 if (temp != null) {
8571 temp.Emit (ec);
8572 temp.Release (ec);
8576 public override void Emit (EmitContext ec)
8578 Emit (ec, false);
8581 public override string GetSignatureForError ()
8583 return TypeManager.CSharpSignature (get != null ? get : set, false);
8586 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
8588 if (get != null)
8589 get = storey.MutateGenericMethod (get);
8590 if (set != null)
8591 set = storey.MutateGenericMethod (set);
8593 instance_expr.MutateHoistedGenericType (storey);
8594 foreach (Argument a in arguments)
8595 a.Expr.MutateHoistedGenericType (storey);
8597 type = storey.MutateType (type);
8600 protected override void CloneTo (CloneContext clonectx, Expression t)
8602 IndexerAccess target = (IndexerAccess) t;
8604 if (arguments != null){
8605 target.arguments = new ArrayList ();
8606 foreach (Argument a in arguments)
8607 target.arguments.Add (a.Clone (clonectx));
8609 if (instance_expr != null)
8610 target.instance_expr = instance_expr.Clone (clonectx);
8614 /// <summary>
8615 /// The base operator for method names
8616 /// </summary>
8617 public class BaseAccess : Expression {
8618 public readonly string Identifier;
8619 TypeArguments args;
8621 public BaseAccess (string member, Location l)
8623 this.Identifier = member;
8624 loc = l;
8627 public BaseAccess (string member, TypeArguments args, Location l)
8628 : this (member, l)
8630 this.args = args;
8633 public override Expression CreateExpressionTree (EmitContext ec)
8635 throw new NotSupportedException ("ET");
8638 public override Expression DoResolve (EmitContext ec)
8640 Expression c = CommonResolve (ec);
8642 if (c == null)
8643 return null;
8646 // MethodGroups use this opportunity to flag an error on lacking ()
8648 if (!(c is MethodGroupExpr))
8649 return c.Resolve (ec);
8650 return c;
8653 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8655 Expression c = CommonResolve (ec);
8657 if (c == null)
8658 return null;
8661 // MethodGroups use this opportunity to flag an error on lacking ()
8663 if (! (c is MethodGroupExpr))
8664 return c.DoResolveLValue (ec, right_side);
8666 return c;
8669 Expression CommonResolve (EmitContext ec)
8671 Expression member_lookup;
8672 Type current_type = ec.ContainerType;
8673 Type base_type = current_type.BaseType;
8675 if (!This.IsThisAvailable (ec)) {
8676 if (ec.IsStatic) {
8677 Error (1511, "Keyword `base' is not available in a static method");
8678 } else {
8679 Error (1512, "Keyword `base' is not available in the current context");
8681 return null;
8684 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8685 AllMemberTypes, AllBindingFlags, loc);
8686 if (member_lookup == null) {
8687 Error_MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier,
8688 null, AllMemberTypes, AllBindingFlags);
8689 return null;
8692 Expression left;
8694 if (ec.IsStatic)
8695 left = new TypeExpression (base_type, loc);
8696 else
8697 left = ec.GetThis (loc);
8699 MemberExpr me = (MemberExpr) member_lookup;
8700 me = me.ResolveMemberAccess (ec, left, loc, null);
8701 if (me == null)
8702 return null;
8704 me.IsBase = true;
8705 if (args != null) {
8706 args.Resolve (ec);
8707 me.SetTypeArguments (args);
8710 return me;
8713 public override void Emit (EmitContext ec)
8715 throw new Exception ("Should never be called");
8718 protected override void CloneTo (CloneContext clonectx, Expression t)
8720 BaseAccess target = (BaseAccess) t;
8722 if (args != null)
8723 target.args = args.Clone ();
8727 /// <summary>
8728 /// The base indexer operator
8729 /// </summary>
8730 public class BaseIndexerAccess : IndexerAccess {
8731 public BaseIndexerAccess (ArrayList args, Location loc)
8732 : base (null, true, loc)
8734 arguments = new ArrayList ();
8735 foreach (Expression tmp in args)
8736 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8739 protected override bool CommonResolve (EmitContext ec)
8741 instance_expr = ec.GetThis (loc);
8743 current_type = ec.ContainerType.BaseType;
8744 indexer_type = current_type;
8746 foreach (Argument a in arguments){
8747 if (!a.Resolve (ec, loc))
8748 return false;
8751 return true;
8754 public override Expression CreateExpressionTree (EmitContext ec)
8756 MemberExpr.Error_BaseAccessInExpressionTree (loc);
8757 return base.CreateExpressionTree (ec);
8761 /// <summary>
8762 /// This class exists solely to pass the Type around and to be a dummy
8763 /// that can be passed to the conversion functions (this is used by
8764 /// foreach implementation to typecast the object return value from
8765 /// get_Current into the proper type. All code has been generated and
8766 /// we only care about the side effect conversions to be performed
8768 /// This is also now used as a placeholder where a no-action expression
8769 /// is needed (the `New' class).
8770 /// </summary>
8771 public class EmptyExpression : Expression {
8772 public static readonly Expression Null = new EmptyExpression ();
8774 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8775 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8776 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8777 public static readonly EmptyExpression UnaryAddress = new EmptyExpression ();
8779 static EmptyExpression temp = new EmptyExpression ();
8780 public static EmptyExpression Grab ()
8782 EmptyExpression retval = temp == null ? new EmptyExpression () : temp;
8783 temp = null;
8784 return retval;
8787 public static void Release (EmptyExpression e)
8789 temp = e;
8792 EmptyExpression ()
8794 // FIXME: Don't set to object
8795 type = TypeManager.object_type;
8796 eclass = ExprClass.Value;
8797 loc = Location.Null;
8800 public EmptyExpression (Type t)
8802 type = t;
8803 eclass = ExprClass.Value;
8804 loc = Location.Null;
8807 public override Expression CreateExpressionTree (EmitContext ec)
8809 throw new NotSupportedException ("ET");
8812 public override Expression DoResolve (EmitContext ec)
8814 return this;
8817 public override void Emit (EmitContext ec)
8819 // nothing, as we only exist to not do anything.
8822 public override void EmitSideEffect (EmitContext ec)
8827 // This is just because we might want to reuse this bad boy
8828 // instead of creating gazillions of EmptyExpressions.
8829 // (CanImplicitConversion uses it)
8831 public void SetType (Type t)
8833 type = t;
8838 // Empty statement expression
8840 public sealed class EmptyExpressionStatement : ExpressionStatement
8842 public static readonly EmptyExpressionStatement Instance = new EmptyExpressionStatement ();
8844 private EmptyExpressionStatement ()
8846 eclass = ExprClass.Value;
8847 loc = Location.Null;
8850 public override Expression CreateExpressionTree (EmitContext ec)
8852 return null;
8855 public override void EmitStatement (EmitContext ec)
8857 // Do nothing
8860 public override Expression DoResolve (EmitContext ec)
8862 type = TypeManager.object_type;
8863 return this;
8866 public override void Emit (EmitContext ec)
8868 // Do nothing
8872 public class UserCast : Expression {
8873 MethodInfo method;
8874 Expression source;
8876 public UserCast (MethodInfo method, Expression source, Location l)
8878 this.method = method;
8879 this.source = source;
8880 type = TypeManager.TypeToCoreType (method.ReturnType);
8881 loc = l;
8884 public Expression Source {
8885 get {
8886 return source;
8890 public override Expression CreateExpressionTree (EmitContext ec)
8892 ArrayList args = new ArrayList (3);
8893 args.Add (new Argument (source.CreateExpressionTree (ec)));
8894 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
8895 args.Add (new Argument (new TypeOfMethodInfo (method, loc)));
8896 return CreateExpressionFactoryCall ("Convert", args);
8899 public override Expression DoResolve (EmitContext ec)
8901 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
8902 if (oa != null)
8903 AttributeTester.Report_ObsoleteMessage (oa, GetSignatureForError (), loc);
8905 eclass = ExprClass.Value;
8906 return this;
8909 public override void Emit (EmitContext ec)
8911 source.Emit (ec);
8912 ec.ig.Emit (OpCodes.Call, method);
8915 public override string GetSignatureForError ()
8917 return TypeManager.CSharpSignature (method);
8920 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
8922 source.MutateHoistedGenericType (storey);
8923 method = storey.MutateGenericMethod (method);
8927 // <summary>
8928 // This class is used to "construct" the type during a typecast
8929 // operation. Since the Type.GetType class in .NET can parse
8930 // the type specification, we just use this to construct the type
8931 // one bit at a time.
8932 // </summary>
8933 public class ComposedCast : TypeExpr {
8934 FullNamedExpression left;
8935 string dim;
8937 public ComposedCast (FullNamedExpression left, string dim)
8938 : this (left, dim, left.Location)
8942 public ComposedCast (FullNamedExpression left, string dim, Location l)
8944 this.left = left;
8945 this.dim = dim;
8946 loc = l;
8949 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8951 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8952 if (lexpr == null)
8953 return null;
8955 Type ltype = lexpr.Type;
8956 #if GMCS_SOURCE
8957 if ((dim.Length > 0) && (dim [0] == '?')) {
8958 TypeExpr nullable = new Nullable.NullableType (lexpr, loc);
8959 if (dim.Length > 1)
8960 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8961 return nullable.ResolveAsTypeTerminal (ec, false);
8963 #endif
8965 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc))
8966 return null;
8968 if (dim.Length != 0 && dim [0] == '[') {
8969 if (TypeManager.IsSpecialType (ltype)) {
8970 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (ltype));
8971 return null;
8974 if ((ltype.Attributes & Class.StaticClassAttribute) == Class.StaticClassAttribute) {
8975 Report.SymbolRelatedToPreviousError (ltype);
8976 Report.Error (719, loc, "Array elements cannot be of static type `{0}'",
8977 TypeManager.CSharpName (ltype));
8981 if (dim != "")
8982 type = TypeManager.GetConstructedType (ltype, dim);
8983 else
8984 type = ltype;
8986 if (type == null)
8987 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8989 if (type.IsPointer && !ec.IsInUnsafeScope){
8990 UnsafeError (loc);
8993 eclass = ExprClass.Type;
8994 return this;
8997 public override string GetSignatureForError ()
8999 return left.GetSignatureForError () + dim;
9002 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
9004 return ResolveAsBaseTerminal (ec, silent);
9008 public class FixedBufferPtr : Expression {
9009 Expression array;
9011 public FixedBufferPtr (Expression array, Type array_type, Location l)
9013 this.array = array;
9014 this.loc = l;
9016 type = TypeManager.GetPointerType (array_type);
9017 eclass = ExprClass.Value;
9020 public override Expression CreateExpressionTree (EmitContext ec)
9022 Error_PointerInsideExpressionTree ();
9023 return null;
9026 public override void Emit(EmitContext ec)
9028 array.Emit (ec);
9031 public override Expression DoResolve (EmitContext ec)
9034 // We are born fully resolved
9036 return this;
9042 // This class is used to represent the address of an array, used
9043 // only by the Fixed statement, this generates "&a [0]" construct
9044 // for fixed (char *pa = a)
9046 public class ArrayPtr : FixedBufferPtr {
9047 Type array_type;
9049 public ArrayPtr (Expression array, Type array_type, Location l):
9050 base (array, array_type, l)
9052 this.array_type = array_type;
9055 public override void Emit (EmitContext ec)
9057 base.Emit (ec);
9059 ILGenerator ig = ec.ig;
9060 IntLiteral.EmitInt (ig, 0);
9061 ig.Emit (OpCodes.Ldelema, array_type);
9066 // Encapsulates a conversion rules required for array indexes
9068 public class ArrayIndexCast : TypeCast
9070 public ArrayIndexCast (Expression expr)
9071 : base (expr, expr.Type)
9075 public override Expression CreateExpressionTree (EmitContext ec)
9077 ArrayList args = new ArrayList (2);
9078 args.Add (new Argument (child.CreateExpressionTree (ec)));
9079 args.Add (new Argument (new TypeOf (new TypeExpression (TypeManager.int32_type, loc), loc)));
9080 return CreateExpressionFactoryCall ("ConvertChecked", args);
9083 public override void Emit (EmitContext ec)
9085 child.Emit (ec);
9087 if (type == TypeManager.int32_type)
9088 return;
9090 if (type == TypeManager.uint32_type)
9091 ec.ig.Emit (OpCodes.Conv_U);
9092 else if (type == TypeManager.int64_type)
9093 ec.ig.Emit (OpCodes.Conv_Ovf_I);
9094 else if (type == TypeManager.uint64_type)
9095 ec.ig.Emit (OpCodes.Conv_Ovf_I_Un);
9096 else
9097 throw new InternalErrorException ("Cannot emit cast to unknown array element type", type);
9102 // Implements the `stackalloc' keyword
9104 public class StackAlloc : Expression {
9105 Type otype;
9106 Expression t;
9107 Expression count;
9109 public StackAlloc (Expression type, Expression count, Location l)
9111 t = type;
9112 this.count = count;
9113 loc = l;
9116 public override Expression CreateExpressionTree (EmitContext ec)
9118 throw new NotSupportedException ("ET");
9121 public override Expression DoResolve (EmitContext ec)
9123 count = count.Resolve (ec);
9124 if (count == null)
9125 return null;
9127 if (count.Type != TypeManager.uint32_type){
9128 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
9129 if (count == null)
9130 return null;
9133 Constant c = count as Constant;
9134 if (c != null && c.IsNegative) {
9135 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
9136 return null;
9139 if (ec.InCatch || ec.InFinally) {
9140 Error (255, "Cannot use stackalloc in finally or catch");
9141 return null;
9144 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
9145 if (texpr == null)
9146 return null;
9148 otype = texpr.Type;
9150 if (!TypeManager.VerifyUnManaged (otype, loc))
9151 return null;
9153 type = TypeManager.GetPointerType (otype);
9154 eclass = ExprClass.Value;
9156 return this;
9159 public override void Emit (EmitContext ec)
9161 int size = GetTypeSize (otype);
9162 ILGenerator ig = ec.ig;
9164 count.Emit (ec);
9166 if (size == 0)
9167 ig.Emit (OpCodes.Sizeof, otype);
9168 else
9169 IntConstant.EmitInt (ig, size);
9171 ig.Emit (OpCodes.Mul_Ovf_Un);
9172 ig.Emit (OpCodes.Localloc);
9175 protected override void CloneTo (CloneContext clonectx, Expression t)
9177 StackAlloc target = (StackAlloc) t;
9178 target.count = count.Clone (clonectx);
9179 target.t = t.Clone (clonectx);
9184 // An object initializer expression
9186 public class ElementInitializer : Assign
9188 public readonly string Name;
9190 public ElementInitializer (string name, Expression initializer, Location loc)
9191 : base (null, initializer, loc)
9193 this.Name = name;
9196 protected override void CloneTo (CloneContext clonectx, Expression t)
9198 ElementInitializer target = (ElementInitializer) t;
9199 target.source = source.Clone (clonectx);
9202 public override Expression CreateExpressionTree (EmitContext ec)
9204 ArrayList args = new ArrayList (2);
9205 FieldExpr fe = target as FieldExpr;
9206 if (fe != null)
9207 args.Add (new Argument (fe.CreateTypeOfExpression ()));
9208 else
9209 args.Add (new Argument (((PropertyExpr)target).CreateSetterTypeOfExpression ()));
9211 args.Add (new Argument (source.CreateExpressionTree (ec)));
9212 return CreateExpressionFactoryCall (
9213 source is CollectionOrObjectInitializers ? "ListBind" : "Bind",
9214 args);
9217 public override Expression DoResolve (EmitContext ec)
9219 if (source == null)
9220 return EmptyExpressionStatement.Instance;
9222 MemberExpr me = MemberLookupFinal (ec, ec.CurrentInitializerVariable.Type, ec.CurrentInitializerVariable.Type,
9223 Name, MemberTypes.Field | MemberTypes.Property, BindingFlags.Public | BindingFlags.Instance, loc) as MemberExpr;
9225 if (me == null)
9226 return null;
9228 target = me;
9229 me.InstanceExpression = ec.CurrentInitializerVariable;
9231 if (source is CollectionOrObjectInitializers) {
9232 Expression previous = ec.CurrentInitializerVariable;
9233 ec.CurrentInitializerVariable = target;
9234 source = source.Resolve (ec);
9235 ec.CurrentInitializerVariable = previous;
9236 if (source == null)
9237 return null;
9239 eclass = source.eclass;
9240 type = source.Type;
9241 return this;
9244 Expression expr = base.DoResolve (ec);
9245 if (expr == null)
9246 return null;
9249 // Ignore field initializers with default value
9251 Constant c = source as Constant;
9252 if (c != null && c.IsDefaultInitializer (type) && target.eclass == ExprClass.Variable)
9253 return EmptyExpressionStatement.Instance.DoResolve (ec);
9255 return expr;
9258 protected override Expression Error_MemberLookupFailed (Type type, MemberInfo[] members)
9260 MemberInfo member = members [0];
9261 if (member.MemberType != MemberTypes.Property && member.MemberType != MemberTypes.Field)
9262 Report.Error (1913, loc, "Member `{0}' cannot be initialized. An object " +
9263 "initializer may only be used for fields, or properties", TypeManager.GetFullNameSignature (member));
9264 else
9265 Report.Error (1914, loc, " Static field or property `{0}' cannot be assigned in an object initializer",
9266 TypeManager.GetFullNameSignature (member));
9268 return null;
9271 public override void EmitStatement (EmitContext ec)
9273 if (source is CollectionOrObjectInitializers)
9274 source.Emit (ec);
9275 else
9276 base.EmitStatement (ec);
9281 // A collection initializer expression
9283 public class CollectionElementInitializer : Invocation
9285 public class ElementInitializerArgument : Argument
9287 public ElementInitializerArgument (Expression e)
9288 : base (e)
9293 sealed class AddMemberAccess : MemberAccess
9295 public AddMemberAccess (Expression expr, Location loc)
9296 : base (expr, "Add", loc)
9300 protected override void Error_TypeDoesNotContainDefinition (Type type, string name)
9302 if (TypeManager.HasElementType (type))
9303 return;
9305 base.Error_TypeDoesNotContainDefinition (type, name);
9309 public CollectionElementInitializer (Expression argument)
9310 : base (null, new ArrayList (1), true)
9312 Arguments.Add (argument);
9313 this.loc = argument.Location;
9316 public CollectionElementInitializer (ArrayList arguments, Location loc)
9317 : base (null, arguments, true)
9319 this.loc = loc;
9322 public override Expression CreateExpressionTree (EmitContext ec)
9324 ArrayList args = new ArrayList (2);
9325 args.Add (new Argument (mg.CreateExpressionTree (ec)));
9327 ArrayList expr_initializers = new ArrayList (Arguments.Count);
9328 foreach (Argument a in Arguments)
9329 expr_initializers.Add (a.Expr.CreateExpressionTree (ec));
9331 args.Add (new Argument (new ArrayCreation (
9332 CreateExpressionTypeExpression (loc), "[]", expr_initializers, loc)));
9333 return CreateExpressionFactoryCall ("ElementInit", args);
9336 protected override void CloneTo (CloneContext clonectx, Expression t)
9338 CollectionElementInitializer target = (CollectionElementInitializer) t;
9340 target.Arguments = new ArrayList (Arguments.Count);
9341 foreach (Expression e in Arguments)
9342 target.Arguments.Add (e.Clone (clonectx));
9345 public override Expression DoResolve (EmitContext ec)
9347 if (eclass != ExprClass.Invalid)
9348 return this;
9350 // TODO: We could call a constructor which takes element count argument,
9351 // for known types like List<T>, Dictionary<T, U>
9353 for (int i = 0; i < Arguments.Count; ++i) {
9354 Expression expr = Arguments [i] as Expression;
9355 if (expr == null)
9356 return null;
9358 expr = expr.Resolve (ec);
9359 if (expr == null)
9360 return null;
9362 Arguments [i] = new ElementInitializerArgument (expr);
9365 base.expr = new AddMemberAccess (ec.CurrentInitializerVariable, loc);
9367 return base.DoResolve (ec);
9372 // A block of object or collection initializers
9374 public class CollectionOrObjectInitializers : ExpressionStatement
9376 ArrayList initializers;
9377 bool is_collection_initialization;
9379 public static readonly CollectionOrObjectInitializers Empty =
9380 new CollectionOrObjectInitializers (new ArrayList (0), Location.Null);
9382 public CollectionOrObjectInitializers (ArrayList initializers, Location loc)
9384 this.initializers = initializers;
9385 this.loc = loc;
9388 public bool IsEmpty {
9389 get {
9390 return initializers.Count == 0;
9394 public bool IsCollectionInitializer {
9395 get {
9396 return is_collection_initialization;
9400 protected override void CloneTo (CloneContext clonectx, Expression target)
9402 CollectionOrObjectInitializers t = (CollectionOrObjectInitializers) target;
9404 t.initializers = new ArrayList (initializers.Count);
9405 foreach (Expression e in initializers)
9406 t.initializers.Add (e.Clone (clonectx));
9409 public override Expression CreateExpressionTree (EmitContext ec)
9411 ArrayList expr_initializers = new ArrayList (initializers.Count);
9412 foreach (Expression e in initializers) {
9413 Expression expr = e.CreateExpressionTree (ec);
9414 if (expr != null)
9415 expr_initializers.Add (expr);
9418 return new ImplicitlyTypedArrayCreation ("[]", expr_initializers, loc);
9421 public override Expression DoResolve (EmitContext ec)
9423 if (eclass != ExprClass.Invalid)
9424 return this;
9426 ArrayList element_names = null;
9427 for (int i = 0; i < initializers.Count; ++i) {
9428 Expression initializer = (Expression) initializers [i];
9429 ElementInitializer element_initializer = initializer as ElementInitializer;
9431 if (i == 0) {
9432 if (element_initializer != null) {
9433 element_names = new ArrayList (initializers.Count);
9434 element_names.Add (element_initializer.Name);
9435 } else {
9436 if (!TypeManager.ImplementsInterface (ec.CurrentInitializerVariable.Type,
9437 TypeManager.ienumerable_type)) {
9438 Report.Error (1922, loc, "A field or property `{0}' cannot be initialized with a collection " +
9439 "object initializer because type `{1}' does not implement `{2}' interface",
9440 ec.CurrentInitializerVariable.GetSignatureForError (),
9441 TypeManager.CSharpName (ec.CurrentInitializerVariable.Type),
9442 TypeManager.CSharpName (TypeManager.ienumerable_type));
9443 return null;
9445 is_collection_initialization = true;
9447 } else {
9448 if (is_collection_initialization != (element_initializer == null)) {
9449 Report.Error (747, initializer.Location, "Inconsistent `{0}' member declaration",
9450 is_collection_initialization ? "collection initializer" : "object initializer");
9451 continue;
9454 if (!is_collection_initialization) {
9455 if (element_names.Contains (element_initializer.Name)) {
9456 Report.Error (1912, element_initializer.Location,
9457 "An object initializer includes more than one member `{0}' initialization",
9458 element_initializer.Name);
9459 } else {
9460 element_names.Add (element_initializer.Name);
9465 Expression e = initializer.Resolve (ec);
9466 if (e == EmptyExpressionStatement.Instance)
9467 initializers.RemoveAt (i--);
9468 else
9469 initializers [i] = e;
9472 type = ec.CurrentInitializerVariable.Type;
9473 if (is_collection_initialization) {
9474 if (TypeManager.HasElementType (type)) {
9475 Report.Error (1925, loc, "Cannot initialize object of type `{0}' with a collection initializer",
9476 TypeManager.CSharpName (type));
9480 eclass = ExprClass.Variable;
9481 return this;
9484 public override void Emit (EmitContext ec)
9486 EmitStatement (ec);
9489 public override void EmitStatement (EmitContext ec)
9491 foreach (ExpressionStatement e in initializers)
9492 e.EmitStatement (ec);
9495 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
9497 foreach (Expression e in initializers)
9498 e.MutateHoistedGenericType (storey);
9503 // New expression with element/object initializers
9505 public class NewInitialize : New
9508 // This class serves as a proxy for variable initializer target instances.
9509 // A real variable is assigned later when we resolve left side of an
9510 // assignment
9512 sealed class InitializerTargetExpression : Expression, IMemoryLocation
9514 NewInitialize new_instance;
9516 public InitializerTargetExpression (NewInitialize newInstance)
9518 this.type = newInstance.type;
9519 this.loc = newInstance.loc;
9520 this.eclass = newInstance.eclass;
9521 this.new_instance = newInstance;
9524 public override Expression CreateExpressionTree (EmitContext ec)
9526 // Should not be reached
9527 throw new NotSupportedException ("ET");
9530 public override Expression DoResolve (EmitContext ec)
9532 return this;
9535 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
9537 return this;
9540 public override void Emit (EmitContext ec)
9542 Expression e = (Expression) new_instance.instance;
9543 e.Emit (ec);
9546 #region IMemoryLocation Members
9548 public void AddressOf (EmitContext ec, AddressOp mode)
9550 new_instance.instance.AddressOf (ec, mode);
9553 #endregion
9556 CollectionOrObjectInitializers initializers;
9557 IMemoryLocation instance;
9559 public NewInitialize (Expression requested_type, ArrayList arguments, CollectionOrObjectInitializers initializers, Location l)
9560 : base (requested_type, arguments, l)
9562 this.initializers = initializers;
9565 protected override void CloneTo (CloneContext clonectx, Expression t)
9567 base.CloneTo (clonectx, t);
9569 NewInitialize target = (NewInitialize) t;
9570 target.initializers = (CollectionOrObjectInitializers) initializers.Clone (clonectx);
9573 public override Expression CreateExpressionTree (EmitContext ec)
9575 ArrayList args = new ArrayList (2);
9576 args.Add (new Argument (base.CreateExpressionTree (ec)));
9577 if (!initializers.IsEmpty)
9578 args.Add (new Argument (initializers.CreateExpressionTree (ec)));
9580 return CreateExpressionFactoryCall (
9581 initializers.IsCollectionInitializer ? "ListInit" : "MemberInit",
9582 args);
9585 public override Expression DoResolve (EmitContext ec)
9587 if (eclass != ExprClass.Invalid)
9588 return this;
9590 Expression e = base.DoResolve (ec);
9591 if (type == null)
9592 return null;
9594 Expression previous = ec.CurrentInitializerVariable;
9595 ec.CurrentInitializerVariable = new InitializerTargetExpression (this);
9596 initializers.Resolve (ec);
9598 // Empty initializer can be optimized to simple new
9599 if (initializers.IsEmpty) {
9600 e = ReducedExpression.Create (e, this).Resolve (ec);
9603 ec.CurrentInitializerVariable = previous;
9604 return e;
9607 public override bool Emit (EmitContext ec, IMemoryLocation target)
9609 bool left_on_stack = base.Emit (ec, target);
9611 if (initializers.IsEmpty)
9612 return left_on_stack;
9614 LocalTemporary temp = null;
9617 // If target is non-hoisted variable, let's use it
9619 VariableReference variable = target as VariableReference;
9620 if (variable != null && !variable.IsRef) {
9621 instance = target;
9623 if (left_on_stack) {
9624 variable.EmitAssign (ec, EmptyExpression.Null, false, false);
9625 left_on_stack = false;
9627 } else {
9628 temp = target as LocalTemporary;
9629 bool is_address = false;
9630 if (temp == null) {
9631 if (!left_on_stack) {
9632 is_address = true;
9633 target.AddressOf (ec, AddressOp.Load);
9634 left_on_stack = true;
9637 temp = new LocalTemporary (type);
9640 instance = temp;
9641 if (left_on_stack && !is_address)
9642 temp.Store (ec);
9645 initializers.Emit (ec);
9647 if (left_on_stack) {
9648 temp.Emit (ec);
9649 temp.Release (ec);
9652 return left_on_stack;
9655 public override bool HasInitializer {
9656 get {
9657 return !initializers.IsEmpty;
9661 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
9663 base.MutateHoistedGenericType (storey);
9664 initializers.MutateHoistedGenericType (storey);
9668 public class AnonymousTypeDeclaration : Expression
9670 ArrayList parameters;
9671 readonly TypeContainer parent;
9672 static readonly ArrayList EmptyParameters = new ArrayList (0);
9674 public AnonymousTypeDeclaration (ArrayList parameters, TypeContainer parent, Location loc)
9676 this.parameters = parameters;
9677 this.parent = parent;
9678 this.loc = loc;
9681 protected override void CloneTo (CloneContext clonectx, Expression target)
9683 if (parameters == null)
9684 return;
9686 AnonymousTypeDeclaration t = (AnonymousTypeDeclaration) target;
9687 t.parameters = new ArrayList (parameters.Count);
9688 foreach (AnonymousTypeParameter atp in parameters)
9689 t.parameters.Add (atp.Clone (clonectx));
9692 AnonymousTypeClass CreateAnonymousType (ArrayList parameters)
9694 AnonymousTypeClass type = RootContext.ToplevelTypes.GetAnonymousType (parameters);
9695 if (type != null)
9696 return type;
9698 type = AnonymousTypeClass.Create (parent, parameters, loc);
9699 if (type == null)
9700 return null;
9702 type.DefineType ();
9703 type.Define ();
9704 type.EmitType ();
9705 if (Report.Errors == 0)
9706 type.CloseType ();
9708 RootContext.ToplevelTypes.AddAnonymousType (type);
9709 return type;
9712 public override Expression CreateExpressionTree (EmitContext ec)
9714 throw new NotSupportedException ("ET");
9717 public override Expression DoResolve (EmitContext ec)
9719 AnonymousTypeClass anonymous_type;
9721 if (!ec.IsAnonymousMethodAllowed) {
9722 Report.Error (836, loc, "Anonymous types cannot be used in this expression");
9723 return null;
9726 if (parameters == null) {
9727 anonymous_type = CreateAnonymousType (EmptyParameters);
9728 return new New (new TypeExpression (anonymous_type.TypeBuilder, loc),
9729 null, loc).Resolve (ec);
9732 bool error = false;
9733 ArrayList arguments = new ArrayList (parameters.Count);
9734 TypeExpression [] t_args = new TypeExpression [parameters.Count];
9735 for (int i = 0; i < parameters.Count; ++i) {
9736 Expression e = ((AnonymousTypeParameter) parameters [i]).Resolve (ec);
9737 if (e == null) {
9738 error = true;
9739 continue;
9742 arguments.Add (new Argument (e));
9743 t_args [i] = new TypeExpression (e.Type, e.Location);
9746 if (error)
9747 return null;
9749 anonymous_type = CreateAnonymousType (parameters);
9750 if (anonymous_type == null)
9751 return null;
9753 GenericTypeExpr te = new GenericTypeExpr (anonymous_type.TypeBuilder,
9754 new TypeArguments (t_args), loc);
9756 return new New (te, arguments, loc).Resolve (ec);
9759 public override void Emit (EmitContext ec)
9761 throw new InternalErrorException ("Should not be reached");
9765 public class AnonymousTypeParameter : Expression
9767 public readonly string Name;
9768 Expression initializer;
9770 public AnonymousTypeParameter (Expression initializer, string name, Location loc)
9772 this.Name = name;
9773 this.loc = loc;
9774 this.initializer = initializer;
9777 public AnonymousTypeParameter (Parameter parameter)
9779 this.Name = parameter.Name;
9780 this.loc = parameter.Location;
9781 this.initializer = new SimpleName (Name, loc);
9784 protected override void CloneTo (CloneContext clonectx, Expression target)
9786 AnonymousTypeParameter t = (AnonymousTypeParameter) target;
9787 t.initializer = initializer.Clone (clonectx);
9790 public override Expression CreateExpressionTree (EmitContext ec)
9792 throw new NotSupportedException ("ET");
9795 public override bool Equals (object o)
9797 AnonymousTypeParameter other = o as AnonymousTypeParameter;
9798 return other != null && Name == other.Name;
9801 public override int GetHashCode ()
9803 return Name.GetHashCode ();
9806 public override Expression DoResolve (EmitContext ec)
9808 Expression e = initializer.Resolve (ec);
9809 if (e == null)
9810 return null;
9812 if (e.eclass == ExprClass.MethodGroup) {
9813 Error_InvalidInitializer (e.ExprClassName);
9814 return null;
9817 type = e.Type;
9818 if (type == TypeManager.void_type || type == TypeManager.null_type ||
9819 type == TypeManager.anonymous_method_type || type.IsPointer) {
9820 Error_InvalidInitializer (e.GetSignatureForError ());
9821 return null;
9824 return e;
9827 protected virtual void Error_InvalidInitializer (string initializer)
9829 Report.Error (828, loc, "An anonymous type property `{0}' cannot be initialized with `{1}'",
9830 Name, initializer);
9833 public override void Emit (EmitContext ec)
9835 throw new InternalErrorException ("Should not be reached");