In ilasm/codegen:
[mcs.git] / bmcs / expression.cs
blobd8562f5155e6527a8d43bf1bdd13b2e67e33cd9d
1 //
2 // expression.cs: Expression representation for the IL tree.
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2001, 2002, 2003 Ximian, Inc.
8 // (C) 2003, 2004 Novell, Inc.
9 //
10 #define USE_OLD
12 namespace Mono.CSharp {
13 using System;
14 using System.Collections;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Text;
18 using Microsoft.VisualBasic;
20 /// <summary>
21 /// This is just a helper class, it is generated by Unary, UnaryMutator
22 /// when an overloaded method has been found. It just emits the code for a
23 /// static call.
24 /// </summary>
25 public class StaticCallExpr : ExpressionStatement {
26 ArrayList args;
27 MethodInfo mi;
29 public StaticCallExpr (MethodInfo m, ArrayList a, Location l)
31 mi = m;
32 args = a;
34 type = m.ReturnType;
35 eclass = ExprClass.Value;
36 loc = l;
39 public override Expression DoResolve (EmitContext ec)
42 // We are born fully resolved
44 return this;
47 public override void Emit (EmitContext ec)
49 if (args != null)
50 Invocation.EmitArguments (ec, mi, args, false, null);
52 ec.ig.Emit (OpCodes.Call, mi);
53 return;
56 static public StaticCallExpr MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
57 Expression e, Location loc)
59 ArrayList args;
60 MethodBase method;
62 args = new ArrayList (1);
63 Argument a = new Argument (e, Argument.AType.Expression);
65 // We need to resolve the arguments before sending them in !
66 if (!a.Resolve (ec, loc))
67 return null;
69 args.Add (a);
70 method = Invocation.OverloadResolve (
71 ec, (MethodGroupExpr) mg, args, false, loc);
73 if (method == null)
74 return null;
76 return new StaticCallExpr ((MethodInfo) method, args, loc);
79 public override void EmitStatement (EmitContext ec)
81 Emit (ec);
82 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
83 ec.ig.Emit (OpCodes.Pop);
86 public MethodInfo Method {
87 get { return mi; }
91 public class ParenthesizedExpression : Expression
93 public Expression Expr;
95 public ParenthesizedExpression (Expression expr, Location loc)
97 this.Expr = expr;
98 this.loc = loc;
101 public override Expression DoResolve (EmitContext ec)
103 Expr = Expr.Resolve (ec);
104 return Expr;
107 public override void Emit (EmitContext ec)
109 throw new Exception ("Should not happen");
113 /// <summary>
114 /// Unary expressions.
115 /// </summary>
117 /// <remarks>
118 /// Unary implements unary expressions. It derives from
119 /// ExpressionStatement becuase the pre/post increment/decrement
120 /// operators can be used in a statement context.
121 /// </remarks>
122 public class Unary : Expression {
123 public enum Operator : byte {
124 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
125 Indirection, AddressOf, TOP
128 public Operator Oper;
129 public Expression Expr;
131 public Unary (Operator op, Expression expr, Location loc)
133 this.Oper = op;
134 this.Expr = expr;
135 this.loc = loc;
138 /// <summary>
139 /// Returns a stringified representation of the Operator
140 /// </summary>
141 static public string OperName (Operator oper)
143 switch (oper){
144 case Operator.UnaryPlus:
145 return "+";
146 case Operator.UnaryNegation:
147 return "-";
148 case Operator.LogicalNot:
149 return "!";
150 case Operator.OnesComplement:
151 return "~";
152 case Operator.AddressOf:
153 return "&";
154 case Operator.Indirection:
155 return "*";
158 return oper.ToString ();
161 public static readonly string [] oper_names;
163 static Unary ()
165 oper_names = new string [(int)Operator.TOP];
167 oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus";
168 oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation";
169 oper_names [(int) Operator.LogicalNot] = "op_LogicalNot";
170 oper_names [(int) Operator.OnesComplement] = "op_OnesComplement";
171 oper_names [(int) Operator.Indirection] = "op_Indirection";
172 oper_names [(int) Operator.AddressOf] = "op_AddressOf";
175 void Error23 (Type t)
177 Error (
178 23, "Operator " + OperName (Oper) +
179 " cannot be applied to operand of type `" +
180 TypeManager.CSharpName (t) + "'");
183 /// <remarks>
184 /// The result has been already resolved:
186 /// FIXME: a minus constant -128 sbyte cant be turned into a
187 /// constant byte.
188 /// </remarks>
189 static Expression TryReduceNegative (Constant expr)
191 Expression e = null;
193 if (expr is IntConstant)
194 e = new IntConstant (-((IntConstant) expr).Value);
195 else if (expr is UIntConstant){
196 uint value = ((UIntConstant) expr).Value;
198 if (value < 2147483649)
199 return new IntConstant (-(int)value);
200 else
201 e = new LongConstant (-value);
203 else if (expr is LongConstant)
204 e = new LongConstant (-((LongConstant) expr).Value);
205 else if (expr is ULongConstant){
206 ulong value = ((ULongConstant) expr).Value;
208 if (value < 9223372036854775809)
209 return new LongConstant(-(long)value);
211 else if (expr is FloatConstant)
212 e = new FloatConstant (-((FloatConstant) expr).Value);
213 else if (expr is DoubleConstant)
214 e = new DoubleConstant (-((DoubleConstant) expr).Value);
215 else if (expr is DecimalConstant)
216 e = new DecimalConstant (-((DecimalConstant) expr).Value);
217 else if (expr is ShortConstant)
218 e = new IntConstant (-((ShortConstant) expr).Value);
219 else if (expr is UShortConstant)
220 e = new IntConstant (-((UShortConstant) expr).Value);
221 else if (expr is SByteConstant)
222 e = new IntConstant (-((SByteConstant) expr).Value);
223 else if (expr is ByteConstant)
224 e = new IntConstant (-((ByteConstant) expr).Value);
225 return e;
228 // <summary>
229 // This routine will attempt to simplify the unary expression when the
230 // argument is a constant. The result is returned in `result' and the
231 // function returns true or false depending on whether a reduction
232 // was performed or not
233 // </summary>
234 bool Reduce (EmitContext ec, Constant e, out Expression result)
236 Type expr_type = e.Type;
238 switch (Oper){
239 case Operator.UnaryPlus:
240 result = e;
241 return true;
243 case Operator.UnaryNegation:
244 result = TryReduceNegative (e);
245 return result != null;
247 case Operator.LogicalNot:
248 if (expr_type != TypeManager.bool_type) {
249 result = null;
250 Error23 (expr_type);
251 return false;
254 BoolConstant b = (BoolConstant) e;
255 result = new BoolConstant (!(b.Value));
256 return true;
258 case Operator.OnesComplement:
259 if (!((expr_type == TypeManager.int32_type) ||
260 (expr_type == TypeManager.uint32_type) ||
261 (expr_type == TypeManager.int64_type) ||
262 (expr_type == TypeManager.uint64_type) ||
263 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
265 result = null;
266 if (Convert.WideningConversionExists (ec, e, TypeManager.int32_type)){
267 result = new Cast (new TypeExpression (TypeManager.int32_type, loc), e, loc);
268 result = result.Resolve (ec);
269 } else if (Convert.WideningConversionExists (ec, e, TypeManager.uint32_type)){
270 result = new Cast (new TypeExpression (TypeManager.uint32_type, loc), e, loc);
271 result = result.Resolve (ec);
272 } else if (Convert.WideningConversionExists (ec, e, TypeManager.int64_type)){
273 result = new Cast (new TypeExpression (TypeManager.int64_type, loc), e, loc);
274 result = result.Resolve (ec);
275 } else if (Convert.WideningConversionExists (ec, e, TypeManager.uint64_type)){
276 result = new Cast (new TypeExpression (TypeManager.uint64_type, loc), e, loc);
277 result = result.Resolve (ec);
280 if (result == null || !(result is Constant)){
281 result = null;
282 Error23 (expr_type);
283 return false;
286 expr_type = result.Type;
287 e = (Constant) result;
290 if (e is EnumConstant){
291 EnumConstant enum_constant = (EnumConstant) e;
292 Expression reduced;
294 if (Reduce (ec, enum_constant.Child, out reduced)){
295 result = new EnumConstant ((Constant) reduced, enum_constant.Type);
296 return true;
297 } else {
298 result = null;
299 return false;
303 if (expr_type == TypeManager.int32_type){
304 result = new IntConstant (~ ((IntConstant) e).Value);
305 } else if (expr_type == TypeManager.uint32_type){
306 result = new UIntConstant (~ ((UIntConstant) e).Value);
307 } else if (expr_type == TypeManager.int64_type){
308 result = new LongConstant (~ ((LongConstant) e).Value);
309 } else if (expr_type == TypeManager.uint64_type){
310 result = new ULongConstant (~ ((ULongConstant) e).Value);
311 } else {
312 result = null;
313 Error23 (expr_type);
314 return false;
316 return true;
318 case Operator.AddressOf:
319 result = this;
320 return false;
322 case Operator.Indirection:
323 result = this;
324 return false;
326 throw new Exception ("Can not constant fold: " + Oper.ToString());
329 Expression ResolveOperator (EmitContext ec)
332 // Step 1: Default operations on CLI native types.
335 // Attempt to use a constant folding operation.
336 if (Expr is Constant){
337 Expression result;
339 if (Reduce (ec, (Constant) Expr, out result))
340 return result;
344 // Step 2: Perform Operator Overload location
346 Type expr_type = Expr.Type;
347 Expression mg;
348 string op_name;
350 op_name = oper_names [(int) Oper];
352 mg = MemberLookup (ec, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
354 if (mg != null) {
355 Expression e = StaticCallExpr.MakeSimpleCall (
356 ec, (MethodGroupExpr) mg, Expr, loc);
358 if (e == null){
359 Error23 (expr_type);
360 return null;
363 return e;
366 // Only perform numeric promotions on:
367 // +, -
369 if (expr_type == null)
370 return null;
372 switch (Oper){
373 case Operator.LogicalNot:
374 if (expr_type != TypeManager.bool_type) {
375 Expr = ResolveBoolean (ec, Expr, loc);
376 if (Expr == null){
377 Error23 (expr_type);
378 return null;
382 type = TypeManager.bool_type;
383 return this;
385 case Operator.OnesComplement:
386 if (!((expr_type == TypeManager.int32_type) ||
387 (expr_type == TypeManager.uint32_type) ||
388 (expr_type == TypeManager.int64_type) ||
389 (expr_type == TypeManager.uint64_type) ||
390 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
391 Expression e;
393 e = Convert.WideningConversion (ec, Expr, TypeManager.int32_type, loc);
394 if (e != null){
395 type = TypeManager.int32_type;
396 return this;
398 e = Convert.WideningConversion (ec, Expr, TypeManager.uint32_type, loc);
399 if (e != null){
400 type = TypeManager.uint32_type;
401 return this;
403 e = Convert.WideningConversion (ec, Expr, TypeManager.int64_type, loc);
404 if (e != null){
405 type = TypeManager.int64_type;
406 return this;
408 e = Convert.WideningConversion (ec, Expr, TypeManager.uint64_type, loc);
409 if (e != null){
410 type = TypeManager.uint64_type;
411 return this;
413 Error23 (expr_type);
414 return null;
416 type = expr_type;
417 return this;
419 case Operator.AddressOf:
420 if (Expr.eclass != ExprClass.Variable){
421 Error (211, "Cannot take the address of non-variables");
422 return null;
425 if (!ec.InUnsafe) {
426 UnsafeError (loc);
427 return null;
430 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){
431 return null;
434 IVariable variable = Expr as IVariable;
435 bool is_fixed = variable != null && variable.VerifyFixed (false);
437 if (!ec.InFixedInitializer && !is_fixed) {
438 Error (212, "You can only take the address of an unfixed expression inside " +
439 "of a fixed statement initializer");
440 return null;
443 if (ec.InFixedInitializer && is_fixed) {
444 Error (213, "You can not fix an already fixed expression");
445 return null;
448 LocalVariableReference lr = Expr as LocalVariableReference;
449 if (lr != null){
450 if (lr.local_info.IsCaptured){
451 AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc);
452 return null;
454 lr.local_info.AddressTaken = true;
455 lr.local_info.Used = true;
458 // According to the specs, a variable is considered definitely assigned if you take
459 // its address.
460 if ((variable != null) && (variable.VariableInfo != null))
461 variable.VariableInfo.SetAssigned (ec);
463 type = TypeManager.GetPointerType (Expr.Type);
464 return this;
466 case Operator.Indirection:
467 if (!ec.InUnsafe){
468 UnsafeError (loc);
469 return null;
472 if (!expr_type.IsPointer){
473 Error (193, "The * or -> operator can only be applied to pointers");
474 return null;
478 // We create an Indirection expression, because
479 // it can implement the IMemoryLocation.
481 return new Indirection (Expr, loc);
483 case Operator.UnaryPlus:
485 // A plus in front of something is just a no-op, so return the child.
487 return Expr;
489 case Operator.UnaryNegation:
491 // Deals with -literals
492 // int operator- (int x)
493 // long operator- (long x)
494 // float operator- (float f)
495 // double operator- (double d)
496 // decimal operator- (decimal d)
498 Expression expr = null;
501 // transform - - expr into expr
503 if (Expr is Unary){
504 Unary unary = (Unary) Expr;
506 if (unary.Oper == Operator.UnaryNegation)
507 return unary.Expr;
511 // perform numeric promotions to int,
512 // long, double.
515 // The following is inneficient, because we call
516 // WideningConversion too many times.
518 // It is also not clear if we should convert to Float
519 // or Double initially.
521 if (expr_type == TypeManager.uint32_type){
523 // FIXME: handle exception to this rule that
524 // permits the int value -2147483648 (-2^31) to
525 // bt wrote as a decimal interger literal
527 type = TypeManager.int64_type;
528 Expr = Convert.WideningConversion (ec, Expr, type, loc);
529 return this;
532 if (expr_type == TypeManager.uint64_type){
534 // FIXME: Handle exception of `long value'
535 // -92233720368547758087 (-2^63) to be wrote as
536 // decimal integer literal.
538 Error23 (expr_type);
539 return null;
542 if (expr_type == TypeManager.float_type){
543 type = expr_type;
544 return this;
547 expr = Convert.WideningConversion (ec, Expr, TypeManager.int32_type, loc);
548 if (expr != null){
549 Expr = expr;
550 type = expr.Type;
551 return this;
554 expr = Convert.WideningConversion (ec, Expr, TypeManager.int64_type, loc);
555 if (expr != null){
556 Expr = expr;
557 type = expr.Type;
558 return this;
561 expr = Convert.WideningConversion (ec, Expr, TypeManager.double_type, loc);
562 if (expr != null){
563 Expr = expr;
564 type = expr.Type;
565 return this;
568 Error23 (expr_type);
569 return null;
572 Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" +
573 TypeManager.CSharpName (expr_type) + "'");
574 return null;
577 public override Expression DoResolve (EmitContext ec)
579 if (Oper == Operator.AddressOf)
580 Expr = Expr.ResolveLValue (ec, new EmptyExpression ());
581 else
582 Expr = Expr.Resolve (ec);
584 if (Expr == null)
585 return null;
587 if (TypeManager.IsNullableType (Expr.Type))
588 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
590 eclass = ExprClass.Value;
591 return ResolveOperator (ec);
594 public override Expression DoResolveLValue (EmitContext ec, Expression right)
596 if (Oper == Operator.Indirection)
597 return base.DoResolveLValue (ec, right);
599 Error (131, "The left-hand side of an assignment must be a " +
600 "variable, property or indexer");
601 return null;
604 public override void Emit (EmitContext ec)
606 ILGenerator ig = ec.ig;
608 switch (Oper) {
609 case Operator.UnaryPlus:
610 throw new Exception ("This should be caught by Resolve");
612 case Operator.UnaryNegation:
613 if (ec.CheckState) {
614 ig.Emit (OpCodes.Ldc_I4_0);
615 if (type == TypeManager.int64_type)
616 ig.Emit (OpCodes.Conv_U8);
617 Expr.Emit (ec);
618 ig.Emit (OpCodes.Sub_Ovf);
619 } else {
620 Expr.Emit (ec);
621 ig.Emit (OpCodes.Neg);
624 break;
626 case Operator.LogicalNot:
627 Expr.Emit (ec);
628 ig.Emit (OpCodes.Ldc_I4_0);
629 ig.Emit (OpCodes.Ceq);
630 break;
632 case Operator.OnesComplement:
633 Expr.Emit (ec);
634 ig.Emit (OpCodes.Not);
635 break;
637 case Operator.AddressOf:
638 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
639 break;
641 default:
642 throw new Exception ("This should not happen: Operator = "
643 + Oper.ToString ());
647 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
649 if (Oper == Operator.LogicalNot)
650 Expr.EmitBranchable (ec, target, !onTrue);
651 else
652 base.EmitBranchable (ec, target, onTrue);
655 public override string ToString ()
657 return "Unary (" + Oper + ", " + Expr + ")";
663 // Unary operators are turned into Indirection expressions
664 // after semantic analysis (this is so we can take the address
665 // of an indirection).
667 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
668 Expression expr;
669 LocalTemporary temporary;
670 bool prepared;
672 public Indirection (Expression expr, Location l)
674 this.expr = expr;
675 this.type = TypeManager.GetElementType (expr.Type);
676 eclass = ExprClass.Variable;
677 loc = l;
680 void LoadExprValue (EmitContext ec)
684 public override void Emit (EmitContext ec)
686 if (!prepared)
687 expr.Emit (ec);
689 LoadFromPtr (ec.ig, Type);
692 public void Emit (EmitContext ec, bool leave_copy)
694 Emit (ec);
695 if (leave_copy) {
696 ec.ig.Emit (OpCodes.Dup);
697 temporary = new LocalTemporary (ec, expr.Type);
698 temporary.Store (ec);
702 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
704 prepared = prepare_for_load;
706 expr.Emit (ec);
708 if (prepare_for_load)
709 ec.ig.Emit (OpCodes.Dup);
711 source.Emit (ec);
712 if (leave_copy) {
713 ec.ig.Emit (OpCodes.Dup);
714 temporary = new LocalTemporary (ec, expr.Type);
715 temporary.Store (ec);
718 StoreFromPtr (ec.ig, type);
720 if (temporary != null)
721 temporary.Emit (ec);
724 public void AddressOf (EmitContext ec, AddressOp Mode)
726 expr.Emit (ec);
729 public override Expression DoResolve (EmitContext ec)
732 // Born fully resolved
734 return this;
737 public override string ToString ()
739 return "*(" + expr + ")";
742 #region IVariable Members
744 public VariableInfo VariableInfo {
745 get {
746 return null;
750 public bool VerifyFixed (bool is_expression)
752 return true;
755 #endregion
758 /// <summary>
759 /// Unary Mutator expressions (pre and post ++ and --)
760 /// </summary>
762 /// <remarks>
763 /// UnaryMutator implements ++ and -- expressions. It derives from
764 /// ExpressionStatement becuase the pre/post increment/decrement
765 /// operators can be used in a statement context.
767 /// FIXME: Idea, we could split this up in two classes, one simpler
768 /// for the common case, and one with the extra fields for more complex
769 /// classes (indexers require temporary access; overloaded require method)
771 /// </remarks>
772 public class UnaryMutator : ExpressionStatement {
773 [Flags]
774 public enum Mode : byte {
775 IsIncrement = 0,
776 IsDecrement = 1,
777 IsPre = 0,
778 IsPost = 2,
780 PreIncrement = 0,
781 PreDecrement = IsDecrement,
782 PostIncrement = IsPost,
783 PostDecrement = IsPost | IsDecrement
786 Mode mode;
787 bool is_expr = false;
788 bool recurse = false;
790 Expression expr;
793 // This is expensive for the simplest case.
795 StaticCallExpr method;
797 public UnaryMutator (Mode m, Expression e, Location l)
799 mode = m;
800 loc = l;
801 expr = e;
804 static string OperName (Mode mode)
806 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
807 "++" : "--";
810 void Error23 (Type t)
812 Error (
813 23, "Operator " + OperName (mode) +
814 " cannot be applied to operand of type `" +
815 TypeManager.CSharpName (t) + "'");
818 /// <summary>
819 /// Returns whether an object of type `t' can be incremented
820 /// or decremented with add/sub (ie, basically whether we can
821 /// use pre-post incr-decr operations on it, but it is not a
822 /// System.Decimal, which we require operator overloading to catch)
823 /// </summary>
824 static bool IsIncrementableNumber (Type t)
826 return (t == TypeManager.sbyte_type) ||
827 (t == TypeManager.byte_type) ||
828 (t == TypeManager.short_type) ||
829 (t == TypeManager.ushort_type) ||
830 (t == TypeManager.int32_type) ||
831 (t == TypeManager.uint32_type) ||
832 (t == TypeManager.int64_type) ||
833 (t == TypeManager.uint64_type) ||
834 (t == TypeManager.char_type) ||
835 (t.IsSubclassOf (TypeManager.enum_type)) ||
836 (t == TypeManager.float_type) ||
837 (t == TypeManager.double_type) ||
838 (t.IsPointer && t != TypeManager.void_ptr_type);
841 Expression ResolveOperator (EmitContext ec)
843 Type expr_type = expr.Type;
846 // Step 1: Perform Operator Overload location
848 Expression mg;
849 string op_name;
851 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
852 op_name = "op_Increment";
853 else
854 op_name = "op_Decrement";
856 mg = MemberLookup (ec, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
858 if (mg == null && expr_type.BaseType != null)
859 mg = MemberLookup (ec, expr_type.BaseType, op_name,
860 MemberTypes.Method, AllBindingFlags, loc);
862 if (mg != null) {
863 method = StaticCallExpr.MakeSimpleCall (
864 ec, (MethodGroupExpr) mg, expr, loc);
866 type = method.Type;
867 return this;
871 // The operand of the prefix/postfix increment decrement operators
872 // should be an expression that is classified as a variable,
873 // a property access or an indexer access
875 type = expr_type;
876 if (expr.eclass == ExprClass.Variable){
877 LocalVariableReference var = expr as LocalVariableReference;
878 if ((var != null) && var.IsReadOnly)
879 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
880 if (IsIncrementableNumber (expr_type) ||
881 expr_type == TypeManager.decimal_type){
882 return this;
884 } else if (expr.eclass == ExprClass.IndexerAccess){
885 IndexerAccess ia = (IndexerAccess) expr;
887 expr = ia.ResolveLValue (ec, this);
888 if (expr == null)
889 return null;
891 return this;
892 } else if (expr.eclass == ExprClass.PropertyAccess){
893 PropertyExpr pe = (PropertyExpr) expr;
895 if (pe.VerifyAssignable ())
896 return this;
898 return null;
899 } else {
900 expr.Error_UnexpectedKind ("variable, indexer or property access", loc);
901 return null;
904 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
905 TypeManager.CSharpName (expr_type) + "'");
906 return null;
909 public override Expression DoResolve (EmitContext ec)
911 expr = expr.Resolve (ec);
913 if (expr == null)
914 return null;
916 eclass = ExprClass.Value;
918 if (TypeManager.IsNullableType (expr.Type))
919 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
921 return ResolveOperator (ec);
924 static int PtrTypeSize (Type t)
926 return GetTypeSize (TypeManager.GetElementType (t));
930 // Loads the proper "1" into the stack based on the type, then it emits the
931 // opcode for the operation requested
933 void LoadOneAndEmitOp (EmitContext ec, Type t)
936 // Measure if getting the typecode and using that is more/less efficient
937 // that comparing types. t.GetTypeCode() is an internal call.
939 ILGenerator ig = ec.ig;
941 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
942 LongConstant.EmitLong (ig, 1);
943 else if (t == TypeManager.double_type)
944 ig.Emit (OpCodes.Ldc_R8, 1.0);
945 else if (t == TypeManager.float_type)
946 ig.Emit (OpCodes.Ldc_R4, 1.0F);
947 else if (t.IsPointer){
948 int n = PtrTypeSize (t);
950 if (n == 0)
951 ig.Emit (OpCodes.Sizeof, t);
952 else
953 IntConstant.EmitInt (ig, n);
954 } else
955 ig.Emit (OpCodes.Ldc_I4_1);
958 // Now emit the operation
960 if (ec.CheckState){
961 if (t == TypeManager.int32_type ||
962 t == TypeManager.int64_type){
963 if ((mode & Mode.IsDecrement) != 0)
964 ig.Emit (OpCodes.Sub_Ovf);
965 else
966 ig.Emit (OpCodes.Add_Ovf);
967 } else if (t == TypeManager.uint32_type ||
968 t == TypeManager.uint64_type){
969 if ((mode & Mode.IsDecrement) != 0)
970 ig.Emit (OpCodes.Sub_Ovf_Un);
971 else
972 ig.Emit (OpCodes.Add_Ovf_Un);
973 } else {
974 if ((mode & Mode.IsDecrement) != 0)
975 ig.Emit (OpCodes.Sub_Ovf);
976 else
977 ig.Emit (OpCodes.Add_Ovf);
979 } else {
980 if ((mode & Mode.IsDecrement) != 0)
981 ig.Emit (OpCodes.Sub);
982 else
983 ig.Emit (OpCodes.Add);
986 if (t == TypeManager.sbyte_type){
987 if (ec.CheckState)
988 ig.Emit (OpCodes.Conv_Ovf_I1);
989 else
990 ig.Emit (OpCodes.Conv_I1);
991 } else if (t == TypeManager.byte_type){
992 if (ec.CheckState)
993 ig.Emit (OpCodes.Conv_Ovf_U1);
994 else
995 ig.Emit (OpCodes.Conv_U1);
996 } else if (t == TypeManager.short_type){
997 if (ec.CheckState)
998 ig.Emit (OpCodes.Conv_Ovf_I2);
999 else
1000 ig.Emit (OpCodes.Conv_I2);
1001 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
1002 if (ec.CheckState)
1003 ig.Emit (OpCodes.Conv_Ovf_U2);
1004 else
1005 ig.Emit (OpCodes.Conv_U2);
1010 void EmitCode (EmitContext ec, bool is_expr)
1012 recurse = true;
1013 this.is_expr = is_expr;
1014 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1017 public override void Emit (EmitContext ec)
1020 // We use recurse to allow ourselfs to be the source
1021 // of an assignment. This little hack prevents us from
1022 // having to allocate another expression
1024 if (recurse) {
1025 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1026 if (method == null)
1027 LoadOneAndEmitOp (ec, expr.Type);
1028 else
1029 ec.ig.Emit (OpCodes.Call, method.Method);
1030 recurse = false;
1031 return;
1034 EmitCode (ec, true);
1037 public override void EmitStatement (EmitContext ec)
1039 EmitCode (ec, false);
1043 /// <summary>
1044 /// Base class for the `Is' and `As' classes.
1045 /// </summary>
1047 /// <remarks>
1048 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1049 /// size.
1050 /// </remarks>
1051 public abstract class Probe : Expression {
1052 public Expression ProbeType;
1053 protected Expression expr;
1054 protected Type probe_type;
1056 public Probe (Expression expr, Expression probe_type, Location l)
1058 ProbeType = probe_type;
1059 loc = l;
1060 this.expr = expr;
1063 public Expression Expr {
1064 get {
1065 return expr;
1069 public override Expression DoResolve (EmitContext ec)
1071 TypeExpr texpr = ProbeType.ResolveAsTypeTerminal (ec);
1072 if (texpr == null)
1073 return null;
1074 probe_type = texpr.Type;
1076 CheckObsoleteAttribute (probe_type);
1078 expr = expr.Resolve (ec);
1079 if (expr == null)
1080 return null;
1082 if (expr.Type.IsPointer) {
1083 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1084 return null;
1086 return this;
1090 /// <summary>
1091 /// Implementation of the `is' operator.
1092 /// </summary>
1093 public class Is : Probe {
1094 public Is (Expression expr, Expression probe_type, Location l)
1095 : base (expr, probe_type, l)
1099 enum Action {
1100 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1103 Action action;
1105 public override void Emit (EmitContext ec)
1107 ILGenerator ig = ec.ig;
1109 expr.Emit (ec);
1111 switch (action){
1112 case Action.AlwaysFalse:
1113 ig.Emit (OpCodes.Pop);
1114 IntConstant.EmitInt (ig, 0);
1115 return;
1116 case Action.AlwaysTrue:
1117 ig.Emit (OpCodes.Pop);
1118 IntConstant.EmitInt (ig, 1);
1119 return;
1120 case Action.LeaveOnStack:
1121 // the `e != null' rule.
1122 ig.Emit (OpCodes.Ldnull);
1123 ig.Emit (OpCodes.Ceq);
1124 ig.Emit (OpCodes.Ldc_I4_0);
1125 ig.Emit (OpCodes.Ceq);
1126 return;
1127 case Action.Probe:
1128 ig.Emit (OpCodes.Isinst, probe_type);
1129 ig.Emit (OpCodes.Ldnull);
1130 ig.Emit (OpCodes.Cgt_Un);
1131 return;
1133 throw new Exception ("never reached");
1136 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1138 ILGenerator ig = ec.ig;
1140 switch (action){
1141 case Action.AlwaysFalse:
1142 if (! onTrue)
1143 ig.Emit (OpCodes.Br, target);
1145 return;
1146 case Action.AlwaysTrue:
1147 if (onTrue)
1148 ig.Emit (OpCodes.Br, target);
1150 return;
1151 case Action.LeaveOnStack:
1152 // the `e != null' rule.
1153 expr.Emit (ec);
1154 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1155 return;
1156 case Action.Probe:
1157 expr.Emit (ec);
1158 ig.Emit (OpCodes.Isinst, probe_type);
1159 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1160 return;
1162 throw new Exception ("never reached");
1165 public override Expression DoResolve (EmitContext ec)
1167 Expression e = base.DoResolve (ec);
1169 if ((e == null) || (expr == null))
1170 return null;
1172 Type etype = expr.Type;
1173 bool warning_always_matches = false;
1174 bool warning_never_matches = false;
1176 type = TypeManager.bool_type;
1177 eclass = ExprClass.Value;
1180 // First case, if at compile time, there is an implicit conversion
1181 // then e != null (objects) or true (value types)
1183 e = Convert.WideningConversionStandard (ec, expr, probe_type, loc);
1184 if (e != null){
1185 expr = e;
1186 if (etype.IsValueType)
1187 action = Action.AlwaysTrue;
1188 else
1189 action = Action.LeaveOnStack;
1191 warning_always_matches = true;
1192 } else if (Convert.NarrowingReferenceConversionExists (etype, probe_type)){
1193 if (etype.IsGenericParameter)
1194 expr = new BoxedCast (expr, etype);
1197 // Second case: explicit reference convresion
1199 if (expr is NullLiteral)
1200 action = Action.AlwaysFalse;
1201 else
1202 action = Action.Probe;
1203 } else {
1204 action = Action.AlwaysFalse;
1205 warning_never_matches = true;
1208 if (warning_always_matches)
1209 Warning (183, "The given expression is always of the provided ('{0}') type", TypeManager.CSharpName (probe_type));
1210 else if (warning_never_matches){
1211 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1212 Warning (184, "The given expression is never of the provided ('{0}') type", TypeManager.CSharpName (probe_type));
1215 return this;
1219 /// <summary>
1220 /// Implementation of the `as' operator.
1221 /// </summary>
1222 public class As : Probe {
1223 public As (Expression expr, Expression probe_type, Location l)
1224 : base (expr, probe_type, l)
1228 bool do_isinst = false;
1230 public override void Emit (EmitContext ec)
1232 ILGenerator ig = ec.ig;
1234 expr.Emit (ec);
1236 if (do_isinst)
1237 ig.Emit (OpCodes.Isinst, probe_type);
1240 static void Error_CannotConvertType (Type source, Type target, Location loc)
1242 Report.Error (
1243 39, loc, "as operator can not convert from `" +
1244 TypeManager.CSharpName (source) + "' to `" +
1245 TypeManager.CSharpName (target) + "'");
1248 public override Expression DoResolve (EmitContext ec)
1250 Expression e = base.DoResolve (ec);
1252 if (e == null)
1253 return null;
1255 type = probe_type;
1256 eclass = ExprClass.Value;
1257 Type etype = expr.Type;
1259 if (TypeManager.IsValueType (probe_type)){
1260 Report.Error (77, loc, "The as operator should be used with a reference type only (" +
1261 TypeManager.CSharpName (probe_type) + " is a value type)");
1262 return null;
1266 e = Convert.WideningConversion (ec, expr, probe_type, loc);
1267 if (e != null){
1268 expr = e;
1269 do_isinst = false;
1270 return this;
1273 if (Convert.NarrowingReferenceConversionExists (etype, probe_type)){
1274 if (etype.IsGenericParameter)
1275 expr = new BoxedCast (expr, etype);
1277 do_isinst = true;
1278 return this;
1281 Error_CannotConvertType (etype, probe_type, loc);
1282 return null;
1286 /// <summary>
1287 /// This represents a typecast in the source language.
1289 /// FIXME: Cast expressions have an unusual set of parsing
1290 /// rules, we need to figure those out.
1291 /// </summary>
1292 public class Cast : Expression {
1293 Expression target_type;
1294 Expression expr;
1296 public Cast (Expression cast_type, Expression expr, Location loc)
1298 this.target_type = cast_type;
1299 this.expr = expr;
1300 this.loc = loc;
1303 public Expression TargetType {
1304 get {
1305 return target_type;
1309 public Expression Expr {
1310 get {
1311 return expr;
1313 set {
1314 expr = value;
1318 bool CheckRange (EmitContext ec, long value, Type type, long min, long max)
1320 if (!ec.ConstantCheckState)
1321 return true;
1323 if ((value < min) || (value > max)) {
1324 Error (221, "Constant value `" + value + "' cannot be converted " +
1325 "to a `" + TypeManager.CSharpName (type) + "' (use `unchecked' " +
1326 "syntax to override)");
1327 return false;
1330 return true;
1333 bool CheckRange (EmitContext ec, ulong value, Type type, ulong max)
1335 if (!ec.ConstantCheckState)
1336 return true;
1338 if (value > max) {
1339 Error (221, "Constant value `" + value + "' cannot be converted " +
1340 "to a `" + TypeManager.CSharpName (type) + "' (use `unchecked' " +
1341 "syntax to override)");
1342 return false;
1345 return true;
1348 bool CheckUnsigned (EmitContext ec, long value, Type type)
1350 if (!ec.ConstantCheckState)
1351 return true;
1353 if (value < 0) {
1354 Error (221, "Constant value `" + value + "' cannot be converted " +
1355 "to a `" + TypeManager.CSharpName (type) + "' (use `unchecked' " +
1356 "syntax to override)");
1357 return false;
1360 return true;
1363 /// <summary>
1364 /// Attempts to do a compile-time folding of a constant cast.
1365 /// </summary>
1366 Expression TryReduce (EmitContext ec, Type target_type)
1368 Expression real_expr = expr;
1369 if (real_expr is EnumConstant)
1370 real_expr = ((EnumConstant) real_expr).Child;
1372 if (real_expr is ByteConstant){
1373 byte v = ((ByteConstant) real_expr).Value;
1375 if (target_type == TypeManager.sbyte_type) {
1376 if (!CheckRange (ec, v, target_type, SByte.MinValue, SByte.MaxValue))
1377 return null;
1378 return new SByteConstant ((sbyte) v);
1380 if (target_type == TypeManager.short_type)
1381 return new ShortConstant ((short) v);
1382 if (target_type == TypeManager.ushort_type)
1383 return new UShortConstant ((ushort) v);
1384 if (target_type == TypeManager.int32_type)
1385 return new IntConstant ((int) v);
1386 if (target_type == TypeManager.uint32_type)
1387 return new UIntConstant ((uint) v);
1388 if (target_type == TypeManager.int64_type)
1389 return new LongConstant ((long) v);
1390 if (target_type == TypeManager.uint64_type)
1391 return new ULongConstant ((ulong) v);
1392 if (target_type == TypeManager.float_type)
1393 return new FloatConstant ((float) v);
1394 if (target_type == TypeManager.double_type)
1395 return new DoubleConstant ((double) v);
1396 if (target_type == TypeManager.char_type)
1397 return new CharConstant ((char) v);
1398 if (target_type == TypeManager.decimal_type)
1399 return new DecimalConstant ((decimal) v);
1401 if (real_expr is SByteConstant){
1402 sbyte v = ((SByteConstant) real_expr).Value;
1404 if (target_type == TypeManager.byte_type) {
1405 if (!CheckUnsigned (ec, v, target_type))
1406 return null;
1407 return new ByteConstant ((byte) v);
1409 if (target_type == TypeManager.short_type)
1410 return new ShortConstant ((short) v);
1411 if (target_type == TypeManager.ushort_type) {
1412 if (!CheckUnsigned (ec, v, target_type))
1413 return null;
1414 return new UShortConstant ((ushort) v);
1415 } if (target_type == TypeManager.int32_type)
1416 return new IntConstant ((int) v);
1417 if (target_type == TypeManager.uint32_type) {
1418 if (!CheckUnsigned (ec, v, target_type))
1419 return null;
1420 return new UIntConstant ((uint) v);
1421 } if (target_type == TypeManager.int64_type)
1422 return new LongConstant ((long) v);
1423 if (target_type == TypeManager.uint64_type) {
1424 if (!CheckUnsigned (ec, v, target_type))
1425 return null;
1426 return new ULongConstant ((ulong) v);
1428 if (target_type == TypeManager.float_type)
1429 return new FloatConstant ((float) v);
1430 if (target_type == TypeManager.double_type)
1431 return new DoubleConstant ((double) v);
1432 if (target_type == TypeManager.char_type) {
1433 if (!CheckUnsigned (ec, v, target_type))
1434 return null;
1435 return new CharConstant ((char) v);
1437 if (target_type == TypeManager.decimal_type)
1438 return new DecimalConstant ((decimal) v);
1440 if (real_expr is ShortConstant){
1441 short v = ((ShortConstant) real_expr).Value;
1443 if (target_type == TypeManager.byte_type) {
1444 if (!CheckRange (ec, v, target_type, Byte.MinValue, Byte.MaxValue))
1445 return null;
1446 return new ByteConstant ((byte) v);
1448 if (target_type == TypeManager.sbyte_type) {
1449 if (!CheckRange (ec, v, target_type, SByte.MinValue, SByte.MaxValue))
1450 return null;
1451 return new SByteConstant ((sbyte) v);
1453 if (target_type == TypeManager.ushort_type) {
1454 if (!CheckUnsigned (ec, v, target_type))
1455 return null;
1456 return new UShortConstant ((ushort) v);
1458 if (target_type == TypeManager.int32_type)
1459 return new IntConstant ((int) v);
1460 if (target_type == TypeManager.uint32_type) {
1461 if (!CheckUnsigned (ec, v, target_type))
1462 return null;
1463 return new UIntConstant ((uint) v);
1465 if (target_type == TypeManager.int64_type)
1466 return new LongConstant ((long) v);
1467 if (target_type == TypeManager.uint64_type) {
1468 if (!CheckUnsigned (ec, v, target_type))
1469 return null;
1470 return new ULongConstant ((ulong) v);
1472 if (target_type == TypeManager.float_type)
1473 return new FloatConstant ((float) v);
1474 if (target_type == TypeManager.double_type)
1475 return new DoubleConstant ((double) v);
1476 if (target_type == TypeManager.char_type) {
1477 if (!CheckRange (ec, v, target_type, Char.MinValue, Char.MaxValue))
1478 return null;
1479 return new CharConstant ((char) v);
1481 if (target_type == TypeManager.decimal_type)
1482 return new DecimalConstant ((decimal) v);
1484 if (real_expr is UShortConstant){
1485 ushort v = ((UShortConstant) real_expr).Value;
1487 if (target_type == TypeManager.byte_type) {
1488 if (!CheckRange (ec, v, target_type, Byte.MinValue, Byte.MaxValue))
1489 return null;
1490 return new ByteConstant ((byte) v);
1492 if (target_type == TypeManager.sbyte_type) {
1493 if (!CheckRange (ec, v, target_type, SByte.MinValue, SByte.MaxValue))
1494 return null;
1495 return new SByteConstant ((sbyte) v);
1497 if (target_type == TypeManager.short_type) {
1498 if (!CheckRange (ec, v, target_type, Int16.MinValue, Int16.MaxValue))
1499 return null;
1500 return new ShortConstant ((short) v);
1502 if (target_type == TypeManager.int32_type)
1503 return new IntConstant ((int) v);
1504 if (target_type == TypeManager.uint32_type)
1505 return new UIntConstant ((uint) v);
1506 if (target_type == TypeManager.int64_type)
1507 return new LongConstant ((long) v);
1508 if (target_type == TypeManager.uint64_type)
1509 return new ULongConstant ((ulong) v);
1510 if (target_type == TypeManager.float_type)
1511 return new FloatConstant ((float) v);
1512 if (target_type == TypeManager.double_type)
1513 return new DoubleConstant ((double) v);
1514 if (target_type == TypeManager.char_type) {
1515 if (!CheckRange (ec, v, target_type, Char.MinValue, Char.MaxValue))
1516 return null;
1517 return new CharConstant ((char) v);
1519 if (target_type == TypeManager.decimal_type)
1520 return new DecimalConstant ((decimal) v);
1522 if (real_expr is IntConstant){
1523 int v = ((IntConstant) real_expr).Value;
1525 if (target_type == TypeManager.byte_type) {
1526 if (!CheckRange (ec, v, target_type, Byte.MinValue, Byte.MaxValue))
1527 return null;
1528 return new ByteConstant ((byte) v);
1530 if (target_type == TypeManager.sbyte_type) {
1531 if (!CheckRange (ec, v, target_type, SByte.MinValue, SByte.MaxValue))
1532 return null;
1533 return new SByteConstant ((sbyte) v);
1535 if (target_type == TypeManager.short_type) {
1536 if (!CheckRange (ec, v, target_type, Int16.MinValue, Int16.MaxValue))
1537 return null;
1538 return new ShortConstant ((short) v);
1540 if (target_type == TypeManager.ushort_type) {
1541 if (!CheckRange (ec, v, target_type, UInt16.MinValue, UInt16.MaxValue))
1542 return null;
1543 return new UShortConstant ((ushort) v);
1545 if (target_type == TypeManager.uint32_type) {
1546 if (!CheckRange (ec, v, target_type, Int32.MinValue, Int32.MaxValue))
1547 return null;
1548 return new UIntConstant ((uint) v);
1550 if (target_type == TypeManager.int64_type)
1551 return new LongConstant ((long) v);
1552 if (target_type == TypeManager.uint64_type) {
1553 if (!CheckUnsigned (ec, v, target_type))
1554 return null;
1555 return new ULongConstant ((ulong) v);
1557 if (target_type == TypeManager.float_type)
1558 return new FloatConstant ((float) v);
1559 if (target_type == TypeManager.double_type)
1560 return new DoubleConstant ((double) v);
1561 if (target_type == TypeManager.char_type) {
1562 if (!CheckRange (ec, v, target_type, Char.MinValue, Char.MaxValue))
1563 return null;
1564 return new CharConstant ((char) v);
1566 if (target_type == TypeManager.decimal_type)
1567 return new DecimalConstant ((decimal) v);
1569 if (real_expr is UIntConstant){
1570 uint v = ((UIntConstant) real_expr).Value;
1572 if (target_type == TypeManager.byte_type) {
1573 if (!CheckRange (ec, v, target_type, Char.MinValue, Char.MaxValue))
1574 return null;
1575 return new ByteConstant ((byte) v);
1577 if (target_type == TypeManager.sbyte_type) {
1578 if (!CheckRange (ec, v, target_type, SByte.MinValue, SByte.MaxValue))
1579 return null;
1580 return new SByteConstant ((sbyte) v);
1582 if (target_type == TypeManager.short_type) {
1583 if (!CheckRange (ec, v, target_type, Int16.MinValue, Int16.MaxValue))
1584 return null;
1585 return new ShortConstant ((short) v);
1587 if (target_type == TypeManager.ushort_type) {
1588 if (!CheckRange (ec, v, target_type, UInt16.MinValue, UInt16.MaxValue))
1589 return null;
1590 return new UShortConstant ((ushort) v);
1592 if (target_type == TypeManager.int32_type) {
1593 if (!CheckRange (ec, v, target_type, Int32.MinValue, Int32.MaxValue))
1594 return null;
1595 return new IntConstant ((int) v);
1597 if (target_type == TypeManager.int64_type)
1598 return new LongConstant ((long) v);
1599 if (target_type == TypeManager.uint64_type)
1600 return new ULongConstant ((ulong) v);
1601 if (target_type == TypeManager.float_type)
1602 return new FloatConstant ((float) v);
1603 if (target_type == TypeManager.double_type)
1604 return new DoubleConstant ((double) v);
1605 if (target_type == TypeManager.char_type) {
1606 if (!CheckRange (ec, v, target_type, Char.MinValue, Char.MaxValue))
1607 return null;
1608 return new CharConstant ((char) v);
1610 if (target_type == TypeManager.decimal_type)
1611 return new DecimalConstant ((decimal) v);
1613 if (real_expr is LongConstant){
1614 long v = ((LongConstant) real_expr).Value;
1616 if (target_type == TypeManager.byte_type) {
1617 if (!CheckRange (ec, v, target_type, Byte.MinValue, Byte.MaxValue))
1618 return null;
1619 return new ByteConstant ((byte) v);
1621 if (target_type == TypeManager.sbyte_type) {
1622 if (!CheckRange (ec, v, target_type, SByte.MinValue, SByte.MaxValue))
1623 return null;
1624 return new SByteConstant ((sbyte) v);
1626 if (target_type == TypeManager.short_type) {
1627 if (!CheckRange (ec, v, target_type, Int16.MinValue, Int16.MaxValue))
1628 return null;
1629 return new ShortConstant ((short) v);
1631 if (target_type == TypeManager.ushort_type) {
1632 if (!CheckRange (ec, v, target_type, UInt16.MinValue, UInt16.MaxValue))
1633 return null;
1634 return new UShortConstant ((ushort) v);
1636 if (target_type == TypeManager.int32_type) {
1637 if (!CheckRange (ec, v, target_type, Int32.MinValue, Int32.MaxValue))
1638 return null;
1639 return new IntConstant ((int) v);
1641 if (target_type == TypeManager.uint32_type) {
1642 if (!CheckRange (ec, v, target_type, UInt32.MinValue, UInt32.MaxValue))
1643 return null;
1644 return new UIntConstant ((uint) v);
1646 if (target_type == TypeManager.uint64_type) {
1647 if (!CheckUnsigned (ec, v, target_type))
1648 return null;
1649 return new ULongConstant ((ulong) v);
1651 if (target_type == TypeManager.float_type)
1652 return new FloatConstant ((float) v);
1653 if (target_type == TypeManager.double_type)
1654 return new DoubleConstant ((double) v);
1655 if (target_type == TypeManager.char_type) {
1656 if (!CheckRange (ec, v, target_type, Char.MinValue, Char.MaxValue))
1657 return null;
1658 return new CharConstant ((char) v);
1660 if (target_type == TypeManager.decimal_type)
1661 return new DecimalConstant ((decimal) v);
1663 if (real_expr is ULongConstant){
1664 ulong v = ((ULongConstant) real_expr).Value;
1666 if (target_type == TypeManager.byte_type) {
1667 if (!CheckRange (ec, v, target_type, Byte.MaxValue))
1668 return null;
1669 return new ByteConstant ((byte) v);
1671 if (target_type == TypeManager.sbyte_type) {
1672 if (!CheckRange (ec, v, target_type, (ulong) SByte.MaxValue))
1673 return null;
1674 return new SByteConstant ((sbyte) v);
1676 if (target_type == TypeManager.short_type) {
1677 if (!CheckRange (ec, v, target_type, (ulong) Int16.MaxValue))
1678 return null;
1679 return new ShortConstant ((short) v);
1681 if (target_type == TypeManager.ushort_type) {
1682 if (!CheckRange (ec, v, target_type, UInt16.MaxValue))
1683 return null;
1684 return new UShortConstant ((ushort) v);
1686 if (target_type == TypeManager.int32_type) {
1687 if (!CheckRange (ec, v, target_type, Int32.MaxValue))
1688 return null;
1689 return new IntConstant ((int) v);
1691 if (target_type == TypeManager.uint32_type) {
1692 if (!CheckRange (ec, v, target_type, UInt32.MaxValue))
1693 return null;
1694 return new UIntConstant ((uint) v);
1696 if (target_type == TypeManager.int64_type) {
1697 if (!CheckRange (ec, v, target_type, (ulong) Int64.MaxValue))
1698 return null;
1699 return new LongConstant ((long) v);
1701 if (target_type == TypeManager.float_type)
1702 return new FloatConstant ((float) v);
1703 if (target_type == TypeManager.double_type)
1704 return new DoubleConstant ((double) v);
1705 if (target_type == TypeManager.char_type) {
1706 if (!CheckRange (ec, v, target_type, Char.MaxValue))
1707 return null;
1708 return new CharConstant ((char) v);
1710 if (target_type == TypeManager.decimal_type)
1711 return new DecimalConstant ((decimal) v);
1713 if (real_expr is FloatConstant){
1714 float v = ((FloatConstant) real_expr).Value;
1716 if (target_type == TypeManager.byte_type)
1717 return new ByteConstant ((byte) v);
1718 if (target_type == TypeManager.sbyte_type)
1719 return new SByteConstant ((sbyte) v);
1720 if (target_type == TypeManager.short_type)
1721 return new ShortConstant ((short) v);
1722 if (target_type == TypeManager.ushort_type)
1723 return new UShortConstant ((ushort) v);
1724 if (target_type == TypeManager.int32_type)
1725 return new IntConstant ((int) v);
1726 if (target_type == TypeManager.uint32_type)
1727 return new UIntConstant ((uint) v);
1728 if (target_type == TypeManager.int64_type)
1729 return new LongConstant ((long) v);
1730 if (target_type == TypeManager.uint64_type)
1731 return new ULongConstant ((ulong) v);
1732 if (target_type == TypeManager.double_type)
1733 return new DoubleConstant ((double) v);
1734 if (target_type == TypeManager.char_type)
1735 return new CharConstant ((char) v);
1736 if (target_type == TypeManager.decimal_type)
1737 return new DecimalConstant ((decimal) v);
1739 if (real_expr is DoubleConstant){
1740 double v = ((DoubleConstant) real_expr).Value;
1742 if (target_type == TypeManager.byte_type){
1743 return new ByteConstant ((byte) v);
1744 } if (target_type == TypeManager.sbyte_type)
1745 return new SByteConstant ((sbyte) v);
1746 if (target_type == TypeManager.short_type)
1747 return new ShortConstant ((short) v);
1748 if (target_type == TypeManager.ushort_type)
1749 return new UShortConstant ((ushort) v);
1750 if (target_type == TypeManager.int32_type)
1751 return new IntConstant ((int) v);
1752 if (target_type == TypeManager.uint32_type)
1753 return new UIntConstant ((uint) v);
1754 if (target_type == TypeManager.int64_type)
1755 return new LongConstant ((long) v);
1756 if (target_type == TypeManager.uint64_type)
1757 return new ULongConstant ((ulong) v);
1758 if (target_type == TypeManager.float_type)
1759 return new FloatConstant ((float) v);
1760 if (target_type == TypeManager.char_type)
1761 return new CharConstant ((char) v);
1762 if (target_type == TypeManager.decimal_type)
1763 return new DecimalConstant ((decimal) v);
1766 if (real_expr is CharConstant){
1767 char v = ((CharConstant) real_expr).Value;
1769 if (target_type == TypeManager.byte_type) {
1770 if (!CheckRange (ec, v, target_type, Byte.MinValue, Byte.MaxValue))
1771 return null;
1772 return new ByteConstant ((byte) v);
1774 if (target_type == TypeManager.sbyte_type) {
1775 if (!CheckRange (ec, v, target_type, SByte.MinValue, SByte.MaxValue))
1776 return null;
1777 return new SByteConstant ((sbyte) v);
1779 if (target_type == TypeManager.short_type) {
1780 if (!CheckRange (ec, v, target_type, Int16.MinValue, Int16.MaxValue))
1781 return null;
1782 return new ShortConstant ((short) v);
1784 if (target_type == TypeManager.int32_type)
1785 return new IntConstant ((int) v);
1786 if (target_type == TypeManager.uint32_type)
1787 return new UIntConstant ((uint) v);
1788 if (target_type == TypeManager.int64_type)
1789 return new LongConstant ((long) v);
1790 if (target_type == TypeManager.uint64_type)
1791 return new ULongConstant ((ulong) v);
1792 if (target_type == TypeManager.float_type)
1793 return new FloatConstant ((float) v);
1794 if (target_type == TypeManager.double_type)
1795 return new DoubleConstant ((double) v);
1796 if (target_type == TypeManager.char_type) {
1797 if (!CheckRange (ec, v, target_type, Char.MinValue, Char.MaxValue))
1798 return null;
1799 return new CharConstant ((char) v);
1801 if (target_type == TypeManager.decimal_type)
1802 return new DecimalConstant ((decimal) v);
1805 return null;
1808 public override Expression DoResolve (EmitContext ec)
1810 expr = expr.Resolve (ec);
1811 if (expr == null)
1812 return null;
1814 TypeExpr target = target_type.ResolveAsTypeTerminal (ec);
1815 if (target == null)
1816 return null;
1818 type = target.Type;
1820 CheckObsoleteAttribute (type);
1822 if (type.IsAbstract && type.IsSealed) {
1823 Report.Error (716, loc, "Cannot convert to static type '{0}'", TypeManager.CSharpName (type));
1824 return null;
1827 eclass = ExprClass.Value;
1829 if (expr is Constant){
1830 Expression e = TryReduce (ec, type);
1832 if (e != null)
1833 return e;
1836 if (type.IsPointer && !ec.InUnsafe) {
1837 UnsafeError (loc);
1838 return null;
1840 expr = Convert.WideningAndNarrowingConversion (ec, expr, type, loc);
1841 return expr;
1844 public override void Emit (EmitContext ec)
1847 // This one will never happen
1849 throw new Exception ("Should not happen");
1853 /// <summary>
1854 /// Binary operators
1855 /// </summary>
1856 public class Binary : Expression {
1857 public enum Operator : byte {
1858 Exponentiation,
1859 Multiply, Division,
1860 IntegerDivision,
1861 Modulus,
1862 Addition, Subtraction,
1863 Concatenation,
1864 LeftShift, RightShift,
1865 Equality, Inequality, LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual, Like, Is,
1866 BitwiseAnd, LogicalAndAlso,
1867 BitwiseOr, LogicalOrElse,
1868 ExclusiveOr,
1872 Operator oper;
1873 Expression left, right;
1874 Expression intermediate;
1876 // This must be kept in sync with Operator!!!
1877 public static readonly string [] oper_names;
1879 static Binary ()
1881 oper_names = new string [(int) Operator.TOP];
1883 oper_names [(int) Operator.Multiply] = "op_Multiply";
1884 oper_names [(int) Operator.Division] = "op_Division";
1885 oper_names [(int) Operator.Modulus] = "op_Modulus";
1886 oper_names [(int) Operator.Addition] = "op_Addition";
1887 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1888 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1889 oper_names [(int) Operator.RightShift] = "op_RightShift";
1890 oper_names [(int) Operator.LessThan] = "op_LessThan";
1891 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1892 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1893 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1894 oper_names [(int) Operator.Equality] = "op_Equality";
1895 oper_names [(int) Operator.Inequality] = "op_Inequality";
1896 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1897 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1898 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1899 oper_names [(int) Operator.LogicalOrElse] = "op_LogicalOr";
1900 oper_names [(int) Operator.LogicalAndAlso] = "op_LogicalAnd";
1903 public Binary (Operator oper, Expression left, Expression right, Location loc)
1905 this.oper = oper;
1906 this.left = left;
1907 this.right = right;
1908 this.loc = loc;
1911 public Operator Oper {
1912 get {
1913 return oper;
1915 set {
1916 oper = value;
1920 public Expression Left {
1921 get {
1922 return left;
1924 set {
1925 left = value;
1929 public Expression Right {
1930 get {
1931 return right;
1933 set {
1934 right = value;
1939 /// <summary>
1940 /// Returns a stringified representation of the Operator
1941 /// </summary>
1942 static string OperName (Operator oper)
1944 switch (oper){
1945 case Operator.Exponentiation:
1946 return "^";
1947 case Operator.Multiply:
1948 return "*";
1949 case Operator.Division:
1950 return "/";
1951 case Operator.IntegerDivision:
1952 return "\\";
1953 case Operator.Modulus:
1954 return "Mod";
1955 case Operator.Addition:
1956 return "+";
1957 case Operator.Subtraction:
1958 return "-";
1959 case Operator.LeftShift:
1960 return "<<";
1961 case Operator.RightShift:
1962 return ">>";
1963 case Operator.LessThan:
1964 return "<";
1965 case Operator.GreaterThan:
1966 return ">";
1967 case Operator.LessThanOrEqual:
1968 return "<=";
1969 case Operator.GreaterThanOrEqual:
1970 return ">=";
1971 case Operator.Equality:
1972 return "=";
1973 case Operator.Inequality:
1974 return "<>";
1975 case Operator.Like:
1976 return "Like";
1977 case Operator.BitwiseAnd:
1978 return "And";
1979 case Operator.BitwiseOr:
1980 return "Or";
1981 case Operator.ExclusiveOr:
1982 return "Xor";
1983 case Operator.LogicalOrElse:
1984 return "OrElse";
1985 case Operator.LogicalAndAlso:
1986 return "AndAlso";
1989 return oper.ToString ();
1992 public override string ToString ()
1994 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1995 right.ToString () + ")";
1998 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
2000 if (expr.Type == target_type)
2001 return expr;
2003 return Convert.WideningConversion (ec, expr, target_type, loc);
2006 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
2008 Report.Error (
2009 34, loc, "Operator `" + OperName (oper)
2010 + "' is ambiguous on operands of type `"
2011 + TypeManager.CSharpName (l) + "' "
2012 + "and `" + TypeManager.CSharpName (r)
2013 + "'");
2016 bool IsOfType (EmitContext ec, Type l, Type r, Type t, bool check_user_conversions)
2018 if ((l == t) || (r == t))
2019 return true;
2021 if (!check_user_conversions)
2022 return false;
2025 // VB.NET has no notion of User defined conversions
2028 // if (Convert.ImplicitUserConversionExists (ec, l, t))
2029 // return true;
2030 // else if (Convert.ImplicitUserConversionExists (ec, r, t))
2031 // return true;
2032 // else
2033 // return false;
2035 return false;
2039 // Note that handling the case l == Decimal || r == Decimal
2040 // is taken care of by the Step 1 Operator Overload resolution.
2042 // If `check_user_conv' is true, we also check whether a user-defined conversion
2043 // exists. Note that we only need to do this if both arguments are of a user-defined
2044 // type, otherwise ConvertImplict() already finds the user-defined conversion for us,
2045 // so we don't explicitly check for performance reasons.
2047 bool DoNumericPromotions (EmitContext ec, Type l, Type r, bool check_user_conv)
2049 if (IsOfType (ec, l, r, TypeManager.double_type, check_user_conv)){
2051 // If either operand is of type double, the other operand is
2052 // conveted to type double.
2054 if (r != TypeManager.double_type)
2055 right = Convert.WideningConversion (ec, right, TypeManager.double_type, loc);
2056 if (l != TypeManager.double_type)
2057 left = Convert.WideningConversion (ec, left, TypeManager.double_type, loc);
2059 type = TypeManager.double_type;
2060 } else if (IsOfType (ec, l, r, TypeManager.float_type, check_user_conv)){
2062 // if either operand is of type float, the other operand is
2063 // converted to type float.
2065 if (r != TypeManager.double_type)
2066 right = Convert.WideningConversion (ec, right, TypeManager.float_type, loc);
2067 if (l != TypeManager.double_type)
2068 left = Convert.WideningConversion (ec, left, TypeManager.float_type, loc);
2069 type = TypeManager.float_type;
2070 } else if (IsOfType (ec, l, r, TypeManager.uint64_type, check_user_conv)){
2071 Expression e;
2072 Type other;
2074 // If either operand is of type ulong, the other operand is
2075 // converted to type ulong. or an error ocurrs if the other
2076 // operand is of type sbyte, short, int or long
2078 if (l == TypeManager.uint64_type){
2079 if (r != TypeManager.uint64_type){
2080 if (right is IntConstant){
2081 IntConstant ic = (IntConstant) right;
2083 e = Convert.TryWideningIntConversion (l, ic);
2084 if (e != null)
2085 right = e;
2086 } else if (right is LongConstant){
2087 long ll = ((LongConstant) right).Value;
2089 if (ll >= 0)
2090 right = new ULongConstant ((ulong) ll);
2091 } else {
2092 e = Convert.WideningNumericConversion (ec, right, l, loc);
2093 if (e != null)
2094 right = e;
2097 other = right.Type;
2098 } else {
2099 if (left is IntConstant){
2100 e = Convert.TryWideningIntConversion (r, (IntConstant) left);
2101 if (e != null)
2102 left = e;
2103 } else if (left is LongConstant){
2104 long ll = ((LongConstant) left).Value;
2106 if (ll > 0)
2107 left = new ULongConstant ((ulong) ll);
2108 } else {
2109 e = Convert.WideningNumericConversion (ec, left, r, loc);
2110 if (e != null)
2111 left = e;
2113 other = left.Type;
2116 if ((other == TypeManager.sbyte_type) ||
2117 (other == TypeManager.short_type) ||
2118 (other == TypeManager.int32_type) ||
2119 (other == TypeManager.int64_type))
2120 Error_OperatorAmbiguous (loc, oper, l, r);
2121 else {
2122 left = ForceConversion (ec, left, TypeManager.uint64_type);
2123 right = ForceConversion (ec, right, TypeManager.uint64_type);
2125 type = TypeManager.uint64_type;
2126 } else if (IsOfType (ec, l, r, TypeManager.int64_type, check_user_conv)){
2128 // If either operand is of type long, the other operand is converted
2129 // to type long.
2131 if (l != TypeManager.int64_type)
2132 left = Convert.WideningConversion (ec, left, TypeManager.int64_type, loc);
2133 if (r != TypeManager.int64_type)
2134 right = Convert.WideningConversion (ec, right, TypeManager.int64_type, loc);
2136 type = TypeManager.int64_type;
2137 } else if (IsOfType (ec, l, r, TypeManager.uint32_type, check_user_conv)){
2139 // If either operand is of type uint, and the other
2140 // operand is of type sbyte, short or int, othe operands are
2141 // converted to type long (unless we have an int constant).
2143 Type other = null;
2145 if (l == TypeManager.uint32_type){
2146 if (right is IntConstant){
2147 IntConstant ic = (IntConstant) right;
2148 int val = ic.Value;
2150 if (val >= 0){
2151 right = new UIntConstant ((uint) val);
2152 type = l;
2154 return true;
2157 other = r;
2158 } else if (r == TypeManager.uint32_type){
2159 if (left is IntConstant){
2160 IntConstant ic = (IntConstant) left;
2161 int val = ic.Value;
2163 if (val >= 0){
2164 left = new UIntConstant ((uint) val);
2165 type = r;
2166 return true;
2170 other = l;
2173 if ((other == TypeManager.sbyte_type) ||
2174 (other == TypeManager.short_type) ||
2175 (other == TypeManager.int32_type)){
2176 left = ForceConversion (ec, left, TypeManager.int64_type);
2177 right = ForceConversion (ec, right, TypeManager.int64_type);
2178 type = TypeManager.int64_type;
2179 } else {
2181 // if either operand is of type uint, the other
2182 // operand is converd to type uint
2184 left = ForceConversion (ec, left, TypeManager.uint32_type);
2185 right = ForceConversion (ec, right, TypeManager.uint32_type);
2186 type = TypeManager.uint32_type;
2188 } else if (l == TypeManager.decimal_type || r == TypeManager.decimal_type){
2189 if (l != TypeManager.decimal_type)
2190 left = Convert.WideningConversion (ec, left, TypeManager.decimal_type, loc);
2192 if (r != TypeManager.decimal_type)
2193 right = Convert.WideningConversion (ec, right, TypeManager.decimal_type, loc);
2194 type = TypeManager.decimal_type;
2195 } else {
2196 left = ForceConversion (ec, left, TypeManager.int32_type);
2197 right = ForceConversion (ec, right, TypeManager.int32_type);
2199 type = TypeManager.int32_type;
2202 return (left != null) && (right != null);
2205 public void Error_OperatorCannotBeAppliedToObjectOperands ()
2207 Report.Error (30038, loc,
2208 "Operator " + OperName (oper) + " cannot be applied to operands of type `" +
2209 TypeManager.CSharpName (TypeManager.object_type) + "'");
2212 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
2214 Report.Error (19, loc,
2215 "Operator " + name + " cannot be applied to operands of type `" +
2216 TypeManager.CSharpName (l) + "' and `" +
2217 TypeManager.CSharpName (r) + "'");
2220 void Error_OperatorCannotBeApplied ()
2222 Error_OperatorCannotBeApplied (loc, OperName (oper), left.Type, right.Type);
2225 static bool is_unsigned (Type t)
2227 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
2228 t == TypeManager.short_type || t == TypeManager.byte_type);
2231 static bool is_user_defined (Type t)
2233 if (t.IsSubclassOf (TypeManager.value_type) &&
2234 (!TypeManager.IsBuiltinType (t) || t == TypeManager.decimal_type))
2235 return true;
2236 else
2237 return false;
2240 Expression Make32or64 (EmitContext ec, Expression e)
2242 Type t= e.Type;
2244 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
2245 t == TypeManager.int64_type || t == TypeManager.uint64_type)
2246 return e;
2247 Expression ee = Convert.WideningConversion (ec, e, TypeManager.int32_type, loc);
2248 if (ee != null)
2249 return ee;
2250 ee = Convert.WideningConversion (ec, e, TypeManager.uint32_type, loc);
2251 if (ee != null)
2252 return ee;
2253 ee = Convert.WideningConversion (ec, e, TypeManager.int64_type, loc);
2254 if (ee != null)
2255 return ee;
2256 ee = Convert.WideningConversion (ec, e, TypeManager.uint64_type, loc);
2257 if (ee != null)
2258 return ee;
2259 return null;
2262 void CheckShiftArguments (EmitContext ec)
2264 Expression e;
2265 Type assumed_target_type = right.Type;
2267 e = Convert.ImplicitVBConversion (ec, right, TypeManager.int32_type, Location);
2268 if (e == null){
2269 Error_OperatorCannotBeApplied ();
2270 return;
2272 right = e;
2274 if ( !IsOperatorDefinedForType (left.Type)) {
2275 Expression target_left_expr = ConvertOperandToDefinedType(ec, left);
2277 if (target_left_expr == null) {
2278 Error_OperatorCannotBeApplied();
2279 return;
2282 left = target_left_expr;
2283 } else if (left.Type == TypeManager.null_type)
2284 left = Convert.ImplicitVBConversion (ec, left, assumed_target_type, Location);
2286 type = left.Type;
2288 int mask = 0x1f;
2290 if (type == TypeManager.byte_type)
2291 mask = 0x7;
2292 else if (type == TypeManager.short_type)
2293 mask = 0xf;
2294 else if (type == TypeManager.int32_type)
2295 mask = 0x1f;
2296 else if (type == TypeManager.int64_type)
2297 mask = 0x3f;
2298 else
2299 throw new Exception ("This should not happen");
2301 right = new Binary (Binary.Operator.BitwiseAnd, right, new IntLiteral (mask), loc);
2302 right = right.DoResolve (ec);
2305 void CheckIsArguments (EmitContext ec)
2307 Type l = left.Type;
2308 Type r = right.Type;
2309 Type = TypeManager.bool_type;
2311 bool left_is_null = left is NullLiteral;
2312 bool right_is_null = right is NullLiteral;
2314 if (left_is_null || right_is_null)
2315 return;
2317 if (l.IsValueType || r.IsValueType) {
2318 Error_OperatorCannotBeApplied ();
2319 return;
2323 if (l == r)
2324 return;
2326 if (l.IsSubclassOf (r) || r.IsSubclassOf (l))
2327 return;
2329 if (!(Convert.WideningStandardConversionExists (ec, left, right.Type) ||
2330 Convert.WideningStandardConversionExists (ec, right, left.Type))){
2331 Error_OperatorCannotBeApplied ();
2332 return;
2335 if (left.Type != TypeManager.object_type)
2336 left = new EmptyCast (left, TypeManager.object_type);
2337 if (right.Type != TypeManager.object_type)
2338 right = new EmptyCast (right, TypeManager.object_type);
2340 return;
2344 #if false
2345 Expression ResolveOperator (EmitContext ec)
2347 Type l = left.Type;
2348 Type r = right.Type;
2351 // Special cases: string or type parameter comapred to null
2353 if (oper == Operator.Equality || oper == Operator.Inequality){
2354 if ((!TypeManager.IsValueType (l) && r == TypeManager.null_type) ||
2355 (!TypeManager.IsValueType (r) && l == TypeManager.null_type)) {
2356 Type = TypeManager.bool_type;
2358 return this;
2361 if (l.IsGenericParameter && (right is NullLiteral)) {
2362 if (l.BaseType == TypeManager.value_type) {
2363 Error_OperatorCannotBeApplied ();
2364 return null;
2367 left = new BoxedCast (left);
2368 Type = TypeManager.bool_type;
2369 return this;
2372 if (r.IsGenericParameter && (left is NullLiteral)) {
2373 if (r.BaseType == TypeManager.value_type) {
2374 Error_OperatorCannotBeApplied ();
2375 return null;
2378 right = new BoxedCast (right);
2379 Type = TypeManager.bool_type;
2380 return this;
2383 // IntPtr equality
2384 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
2385 Type = TypeManager.bool_type;
2387 return this;
2392 // Do not perform operator overload resolution when both sides are
2393 // built-in types
2395 if (!(TypeManager.IsCLRType (l) && TypeManager.IsCLRType (r))){
2397 // Step 1: Perform Operator Overload location
2399 Expression left_expr, right_expr;
2401 string op = oper_names [(int) oper];
2403 MethodGroupExpr union;
2404 left_expr = MemberLookup (ec, l, op, MemberTypes.Method, AllBindingFlags, loc);
2405 if (r != l){
2406 right_expr = MemberLookup (
2407 ec, r, op, MemberTypes.Method, AllBindingFlags, loc);
2408 union = Invocation.MakeUnionSet (left_expr, right_expr, loc);
2409 } else
2410 union = (MethodGroupExpr) left_expr;
2412 if (union != null) {
2413 ArrayList args = new ArrayList (2);
2414 args.Add (new Argument (left, Argument.AType.Expression));
2415 args.Add (new Argument (right, Argument.AType.Expression));
2417 MethodBase method = Invocation.OverloadResolve (
2418 ec, union, args, true, Location.Null);
2420 if (method != null) {
2421 MethodInfo mi = (MethodInfo) method;
2423 return new BinaryMethod (mi.ReturnType, method, args);
2429 // Step 0: String concatenation (because overloading will get this wrong)
2431 if (oper == Operator.Addition){
2433 // If any of the arguments is a string, cast to string
2436 // Simple constant folding
2437 if (left is StringConstant && right is StringConstant)
2438 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value);
2440 if (l == TypeManager.string_type || r == TypeManager.string_type) {
2442 if (r == TypeManager.void_type || l == TypeManager.void_type) {
2443 Error_OperatorCannotBeApplied ();
2444 return null;
2447 // try to fold it in on the left
2448 if (left is StringConcat) {
2451 // We have to test here for not-null, since we can be doubly-resolved
2452 // take care of not appending twice
2454 if (type == null){
2455 type = TypeManager.string_type;
2456 ((StringConcat) left).Append (ec, right);
2457 return left.Resolve (ec);
2458 } else {
2459 return left;
2463 // Otherwise, start a new concat expression
2464 return new StringConcat (ec, loc, left, right).Resolve (ec);
2468 // Transform a + ( - b) into a - b
2470 if (right is Unary){
2471 Unary right_unary = (Unary) right;
2473 if (right_unary.Oper == Unary.Operator.UnaryNegation){
2474 oper = Operator.Subtraction;
2475 right = right_unary.Expr;
2476 r = right.Type;
2481 if (oper == Operator.Equality || oper == Operator.Inequality){
2482 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
2483 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
2484 Error_OperatorCannotBeApplied ();
2485 return null;
2488 type = TypeManager.bool_type;
2489 return this;
2492 bool left_is_null = left is NullLiteral;
2493 bool right_is_null = right is NullLiteral;
2494 if (left_is_null || right_is_null) {
2495 if (oper == Operator.Equality)
2496 return new BoolLiteral (left_is_null == right_is_null);
2497 else
2498 return new BoolLiteral (left_is_null != right_is_null);
2502 // operator != (object a, object b)
2503 // operator == (object a, object b)
2505 // For this to be used, both arguments have to be reference-types.
2506 // Read the rationale on the spec (14.9.6)
2508 // Also, if at compile time we know that the classes do not inherit
2509 // one from the other, then we catch the error there.
2511 if (!(l.IsValueType || r.IsValueType)){
2512 type = TypeManager.bool_type;
2514 if (l == r)
2515 return this;
2517 if (l.IsSubclassOf (r) || r.IsSubclassOf (l))
2518 return this;
2521 // Also, a standard conversion must exist from either one
2523 if (!(Convert.WideningStandardConversionExists (ec, left, r) ||
2524 Convert.WideningStandardConversionExists (ec, right, l))){
2525 Error_OperatorCannotBeApplied ();
2526 return null;
2529 // We are going to have to convert to an object to compare
2531 if (l != TypeManager.object_type)
2532 left = new EmptyCast (left, TypeManager.object_type);
2533 if (r != TypeManager.object_type)
2534 right = new EmptyCast (right, TypeManager.object_type);
2537 // FIXME: CSC here catches errors cs254 and cs252
2539 return this;
2543 // One of them is a valuetype, but the other one is not.
2545 if (!l.IsValueType || !r.IsValueType) {
2546 Error_OperatorCannotBeApplied ();
2547 return null;
2551 // Only perform numeric promotions on:
2552 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
2554 if (oper == Operator.Addition || oper == Operator.Subtraction) {
2555 if (TypeManager.IsDelegateType (l)){
2556 if (((right.eclass == ExprClass.MethodGroup) ||
2557 (r == TypeManager.anonymous_method_type))){
2558 if ((RootContext.Version != LanguageVersion.ISO_1)){
2559 Expression tmp = Convert.WideningConversionRequired (ec, right, l, loc);
2560 if (tmp == null)
2561 return null;
2562 right = tmp;
2563 r = right.Type;
2567 if (TypeManager.IsDelegateType (r)){
2568 MethodInfo method;
2569 ArrayList args = new ArrayList (2);
2571 args = new ArrayList (2);
2572 args.Add (new Argument (left, Argument.AType.Expression));
2573 args.Add (new Argument (right, Argument.AType.Expression));
2575 if (oper == Operator.Addition)
2576 method = TypeManager.delegate_combine_delegate_delegate;
2577 else
2578 method = TypeManager.delegate_remove_delegate_delegate;
2580 if (!TypeManager.IsEqual (l, r)) {
2581 Error_OperatorCannotBeApplied ();
2582 return null;
2585 return new BinaryDelegate (l, method, args);
2590 // Pointer arithmetic:
2592 // T* operator + (T* x, int y);
2593 // T* operator + (T* x, uint y);
2594 // T* operator + (T* x, long y);
2595 // T* operator + (T* x, ulong y);
2597 // T* operator + (int y, T* x);
2598 // T* operator + (uint y, T *x);
2599 // T* operator + (long y, T *x);
2600 // T* operator + (ulong y, T *x);
2602 // T* operator - (T* x, int y);
2603 // T* operator - (T* x, uint y);
2604 // T* operator - (T* x, long y);
2605 // T* operator - (T* x, ulong y);
2607 // long operator - (T* x, T *y)
2609 if (l.IsPointer){
2610 if (r.IsPointer && oper == Operator.Subtraction){
2611 if (r == l)
2612 return new PointerArithmetic (
2613 false, left, right, TypeManager.int64_type,
2614 loc).Resolve (ec);
2615 } else {
2616 Expression t = Make32or64 (ec, right);
2617 if (t != null)
2618 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
2620 } else if (r.IsPointer && oper == Operator.Addition){
2621 Expression t = Make32or64 (ec, left);
2622 if (t != null)
2623 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
2628 // Enumeration operators
2630 bool lie = TypeManager.IsEnumType (l);
2631 bool rie = TypeManager.IsEnumType (r);
2632 if (lie || rie){
2633 Expression temp;
2635 // U operator - (E e, E f)
2636 if (lie && rie){
2637 if (oper == Operator.Subtraction){
2638 if (l == r){
2639 type = TypeManager.EnumToUnderlying (l);
2640 return this;
2642 Error_OperatorCannotBeApplied ();
2643 return null;
2648 // operator + (E e, U x)
2649 // operator - (E e, U x)
2651 if (oper == Operator.Addition || oper == Operator.Subtraction){
2652 Type enum_type = lie ? l : r;
2653 Type other_type = lie ? r : l;
2654 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2656 if (underlying_type != other_type){
2657 temp = Convert.WideningConversion (ec, lie ? right : left, underlying_type, loc);
2658 if (temp != null){
2659 if (lie)
2660 right = temp;
2661 else
2662 left = temp;
2663 type = enum_type;
2664 return this;
2667 Error_OperatorCannotBeApplied ();
2668 return null;
2671 type = enum_type;
2672 return this;
2675 if (!rie){
2676 temp = Convert.WideningConversion (ec, right, l, loc);
2677 if (temp != null)
2678 right = temp;
2679 else {
2680 Error_OperatorCannotBeApplied ();
2681 return null;
2683 } if (!lie){
2684 temp = Convert.WideningConversion (ec, left, r, loc);
2685 if (temp != null){
2686 left = temp;
2687 l = r;
2688 } else {
2689 Error_OperatorCannotBeApplied ();
2690 return null;
2694 if (oper == Operator.Equality || oper == Operator.Inequality ||
2695 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2696 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2697 if (left.Type != right.Type){
2698 Error_OperatorCannotBeApplied ();
2699 return null;
2701 type = TypeManager.bool_type;
2702 return this;
2705 if (oper == Operator.BitwiseAnd ||
2706 oper == Operator.BitwiseOr ||
2707 oper == Operator.ExclusiveOr){
2708 type = l;
2709 return this;
2711 Error_OperatorCannotBeApplied ();
2712 return null;
2715 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2716 return CheckShiftArguments (ec);
2718 if (oper == Operator.LogicalOrElse || oper == Operator.LogicalAndAlso){
2719 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2720 type = TypeManager.bool_type;
2721 return this;
2724 if (l != r) {
2725 Error_OperatorCannotBeApplied ();
2726 return null;
2729 Expression e = new ConditionalLogicalOperator (
2730 oper == Operator.LogicalAndAlso, left, right, l, loc);
2731 return e.Resolve (ec);
2735 // operator & (bool x, bool y)
2736 // operator | (bool x, bool y)
2737 // operator ^ (bool x, bool y)
2739 if (l == TypeManager.bool_type && r == TypeManager.bool_type){
2740 if (oper == Operator.BitwiseAnd ||
2741 oper == Operator.BitwiseOr ||
2742 oper == Operator.ExclusiveOr){
2743 type = l;
2744 return this;
2749 // Pointer comparison
2751 if (l.IsPointer && r.IsPointer){
2752 if (oper == Operator.Equality || oper == Operator.Inequality ||
2753 oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2754 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2755 type = TypeManager.bool_type;
2756 return this;
2761 // This will leave left or right set to null if there is an error
2763 bool check_user_conv = is_user_defined (l) && is_user_defined (r);
2764 DoNumericPromotions (ec, l, r, check_user_conv);
2765 if (left == null || right == null){
2766 Error_OperatorCannotBeApplied (loc, OperName (oper), l, r);
2767 return null;
2771 // reload our cached types if required
2773 l = left.Type;
2774 r = right.Type;
2776 if (oper == Operator.BitwiseAnd ||
2777 oper == Operator.BitwiseOr ||
2778 oper == Operator.ExclusiveOr){
2779 if (l == r){
2780 if (((l == TypeManager.int32_type) ||
2781 (l == TypeManager.uint32_type) ||
2782 (l == TypeManager.short_type) ||
2783 (l == TypeManager.ushort_type) ||
2784 (l == TypeManager.int64_type) ||
2785 (l == TypeManager.uint64_type))){
2786 type = l;
2787 } else {
2788 Error_OperatorCannotBeApplied ();
2789 return null;
2791 } else {
2792 Error_OperatorCannotBeApplied ();
2793 return null;
2797 if (oper == Operator.Equality ||
2798 oper == Operator.Inequality ||
2799 oper == Operator.LessThanOrEqual ||
2800 oper == Operator.LessThan ||
2801 oper == Operator.GreaterThanOrEqual ||
2802 oper == Operator.GreaterThan){
2803 type = TypeManager.bool_type;
2806 return this;
2808 #endif
2810 public override Expression DoResolve (EmitContext ec)
2812 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2813 left = ((ParenthesizedExpression) left).Expr;
2814 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2815 if (left == null)
2816 return null;
2818 if (left.eclass == ExprClass.Type) {
2819 Error (75, "Casting a negative value needs to have the value in parentheses.");
2820 return null;
2822 } else
2823 left = left.Resolve (ec);
2825 if (left == null)
2826 return null;
2828 Constant lc = left as Constant;
2829 if (lc != null && lc.Type == TypeManager.bool_type &&
2830 ((oper == Operator.LogicalAndAlso && (bool)lc.GetValue () == false) ||
2831 (oper == Operator.LogicalOrElse && (bool)lc.GetValue () == true))) {
2833 // TODO: make a sense to resolve unreachable expression as we do for statement
2834 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2835 return left;
2838 right = right.Resolve (ec);
2839 if (right == null)
2840 return null;
2842 eclass = ExprClass.Value;
2844 Constant rc = right as Constant;
2845 if (rc != null & lc != null){
2846 Expression e = ConstantFold.BinaryFold (
2847 ec, oper, lc, rc, loc);
2848 if (e != null)
2849 return e;
2852 if (TypeManager.IsNullableType (left.Type) || TypeManager.IsNullableType (right.Type))
2853 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2855 return ResolveVisualBasicOperator (ec);
2858 /// <remarks>
2859 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2860 /// context of a conditional bool expression. This function will return
2861 /// false if it is was possible to use EmitBranchable, or true if it was.
2863 /// The expression's code is generated, and we will generate a branch to `target'
2864 /// if the resulting expression value is equal to isTrue
2865 /// </remarks>
2866 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2868 ILGenerator ig = ec.ig;
2871 // This is more complicated than it looks, but its just to avoid
2872 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2873 // but on top of that we want for == and != to use a special path
2874 // if we are comparing against null
2876 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2877 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2880 // put the constant on the rhs, for simplicity
2882 if (left is Constant) {
2883 Expression swap = right;
2884 right = left;
2885 left = swap;
2888 if (((Constant) right).IsZeroInteger) {
2889 left.Emit (ec);
2890 if (my_on_true)
2891 ig.Emit (OpCodes.Brtrue, target);
2892 else
2893 ig.Emit (OpCodes.Brfalse, target);
2895 return;
2896 } else if (right is BoolConstant){
2897 left.Emit (ec);
2898 if (my_on_true != ((BoolConstant) right).Value)
2899 ig.Emit (OpCodes.Brtrue, target);
2900 else
2901 ig.Emit (OpCodes.Brfalse, target);
2903 return;
2906 } else if (oper == Operator.LogicalAndAlso) {
2908 if (onTrue) {
2909 Label tests_end = ig.DefineLabel ();
2911 left.EmitBranchable (ec, tests_end, false);
2912 right.EmitBranchable (ec, target, true);
2913 ig.MarkLabel (tests_end);
2914 } else {
2915 left.EmitBranchable (ec, target, false);
2916 right.EmitBranchable (ec, target, false);
2919 return;
2921 } else if (oper == Operator.LogicalOrElse){
2922 if (onTrue) {
2923 left.EmitBranchable (ec, target, true);
2924 right.EmitBranchable (ec, target, true);
2926 } else {
2927 Label tests_end = ig.DefineLabel ();
2928 left.EmitBranchable (ec, tests_end, true);
2929 right.EmitBranchable (ec, target, false);
2930 ig.MarkLabel (tests_end);
2933 return;
2935 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2936 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2937 oper == Operator.Equality || oper == Operator.Inequality)) {
2938 base.EmitBranchable (ec, target, onTrue);
2939 return;
2942 left.Emit (ec);
2943 right.Emit (ec);
2945 Type t = left.Type;
2946 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2948 switch (oper){
2949 case Operator.Equality:
2950 if (onTrue)
2951 ig.Emit (OpCodes.Beq, target);
2952 else
2953 ig.Emit (OpCodes.Bne_Un, target);
2954 break;
2956 case Operator.Inequality:
2957 if (onTrue)
2958 ig.Emit (OpCodes.Bne_Un, target);
2959 else
2960 ig.Emit (OpCodes.Beq, target);
2961 break;
2963 case Operator.LessThan:
2964 if (onTrue)
2965 if (isUnsigned)
2966 ig.Emit (OpCodes.Blt_Un, target);
2967 else
2968 ig.Emit (OpCodes.Blt, target);
2969 else
2970 if (isUnsigned)
2971 ig.Emit (OpCodes.Bge_Un, target);
2972 else
2973 ig.Emit (OpCodes.Bge, target);
2974 break;
2976 case Operator.GreaterThan:
2977 if (onTrue)
2978 if (isUnsigned)
2979 ig.Emit (OpCodes.Bgt_Un, target);
2980 else
2981 ig.Emit (OpCodes.Bgt, target);
2982 else
2983 if (isUnsigned)
2984 ig.Emit (OpCodes.Ble_Un, target);
2985 else
2986 ig.Emit (OpCodes.Ble, target);
2987 break;
2989 case Operator.LessThanOrEqual:
2990 if (onTrue)
2991 if (isUnsigned)
2992 ig.Emit (OpCodes.Ble_Un, target);
2993 else
2994 ig.Emit (OpCodes.Ble, target);
2995 else
2996 if (isUnsigned)
2997 ig.Emit (OpCodes.Bgt_Un, target);
2998 else
2999 ig.Emit (OpCodes.Bgt, target);
3000 break;
3003 case Operator.GreaterThanOrEqual:
3004 if (onTrue)
3005 if (isUnsigned)
3006 ig.Emit (OpCodes.Bge_Un, target);
3007 else
3008 ig.Emit (OpCodes.Bge, target);
3009 else
3010 if (isUnsigned)
3011 ig.Emit (OpCodes.Blt_Un, target);
3012 else
3013 ig.Emit (OpCodes.Blt, target);
3014 break;
3015 default:
3016 Console.WriteLine (oper);
3017 throw new Exception ("what is THAT");
3021 public override void Emit (EmitContext ec)
3023 ILGenerator ig = ec.ig;
3024 Type l = left.Type;
3025 OpCode opcode;
3026 OpCode opcode1 = OpCodes.Nop;
3029 // Handle short-circuit operators differently
3030 // than the rest
3032 if (oper == Operator.LogicalAndAlso) {
3033 Label load_zero = ig.DefineLabel ();
3034 Label end = ig.DefineLabel ();
3036 left.EmitBranchable (ec, load_zero, false);
3037 right.Emit (ec);
3038 ig.Emit (OpCodes.Br, end);
3040 ig.MarkLabel (load_zero);
3041 ig.Emit (OpCodes.Ldc_I4_0);
3042 ig.MarkLabel (end);
3043 return;
3044 } else if (oper == Operator.LogicalOrElse) {
3045 Label load_one = ig.DefineLabel ();
3046 Label end = ig.DefineLabel ();
3048 left.EmitBranchable (ec, load_one, true);
3049 right.Emit (ec);
3050 ig.Emit (OpCodes.Br, end);
3052 ig.MarkLabel (load_one);
3053 ig.Emit (OpCodes.Ldc_I4_1);
3054 ig.MarkLabel (end);
3055 return;
3058 if (intermediate != null) {
3059 intermediate.Emit (ec);
3060 ig.Emit (OpCodes.Ldc_I4_0);
3062 else {
3063 left.Emit (ec);
3064 right.Emit (ec);
3067 bool is_int32_or_int64_type = (Type == TypeManager.int32_type) || (Type == TypeManager.int64_type);
3069 switch (oper){
3070 case Operator.Multiply:
3071 if (ec.CheckState){
3072 if (is_int32_or_int64_type)
3073 opcode = OpCodes.Mul_Ovf;
3074 else
3075 opcode = OpCodes.Mul;
3076 } else
3077 opcode = OpCodes.Mul;
3079 break;
3081 case Operator.Division:
3082 case Operator.IntegerDivision:
3083 opcode = OpCodes.Div;
3084 break;
3086 case Operator.Modulus:
3087 opcode = OpCodes.Rem;
3088 break;
3090 case Operator.Addition:
3091 if (ec.CheckState){
3092 if (is_int32_or_int64_type)
3093 opcode = OpCodes.Add_Ovf;
3094 else
3095 opcode = OpCodes.Add;
3096 } else
3097 opcode = OpCodes.Add;
3098 break;
3100 case Operator.Subtraction:
3101 if (ec.CheckState){
3102 if (is_int32_or_int64_type)
3103 opcode = OpCodes.Sub_Ovf;
3104 else
3105 opcode = OpCodes.Sub;
3106 } else
3107 opcode = OpCodes.Sub;
3108 break;
3110 case Operator.RightShift:
3111 opcode = OpCodes.Shr;
3112 break;
3114 case Operator.LeftShift:
3115 opcode = OpCodes.Shl;
3116 break;
3118 case Operator.Is:
3119 case Operator.Equality:
3120 opcode = OpCodes.Ceq;
3121 break;
3123 case Operator.Inequality:
3124 ig.Emit (OpCodes.Ceq);
3125 ig.Emit (OpCodes.Ldc_I4_0);
3127 opcode = OpCodes.Ceq;
3128 break;
3130 case Operator.LessThan:
3131 opcode = OpCodes.Clt;
3132 break;
3134 case Operator.GreaterThan:
3135 opcode = OpCodes.Cgt;
3136 break;
3138 case Operator.LessThanOrEqual:
3139 ig.Emit (OpCodes.Cgt);
3140 ig.Emit (OpCodes.Ldc_I4_0);
3142 opcode = OpCodes.Ceq;
3143 break;
3145 case Operator.GreaterThanOrEqual:
3146 ig.Emit (OpCodes.Clt);
3147 ig.Emit (OpCodes.Ldc_I4_0);
3149 opcode = OpCodes.Ceq;
3150 break;
3152 case Operator.BitwiseOr:
3153 opcode = OpCodes.Or;
3154 break;
3156 case Operator.BitwiseAnd:
3157 opcode = OpCodes.And;
3158 break;
3160 case Operator.ExclusiveOr:
3161 opcode = OpCodes.Xor;
3162 break;
3164 default:
3165 throw new Exception ("This should not happen: Operator = "
3166 + oper.ToString ());
3169 ig.Emit (opcode);
3171 if (!IsArithmeticExpression && !IsShiftExpression)
3172 return;
3174 if (type == TypeManager.byte_type)
3175 ig.Emit (ec.CheckState && ! IsShiftExpression ? OpCodes.Conv_Ovf_U1 : OpCodes.Conv_U1);
3177 if (type == TypeManager.short_type)
3178 ig.Emit (ec.CheckState && ! IsShiftExpression ? OpCodes.Conv_Ovf_I2 : OpCodes.Conv_I2);
3181 Expression ResolveVisualBasicOperator (EmitContext ec)
3183 int errors;
3184 Expression ret_expr;
3186 Type l = left.Type;
3187 Type r = right.Type;
3189 //Console.WriteLine (OperName (oper) +"< "+ l + ", " + r + ">");
3191 errors = Report.Errors;
3192 ret_expr = HandleObjectOperands (ec);
3193 if (Report.Errors > errors)
3194 return null;
3195 if (ret_expr != null)
3196 return ret_expr;
3198 errors = Report.Errors;
3199 CheckArguments (ec);
3200 if (Report.Errors > errors)
3201 return null;
3203 if (oper == Operator.Exponentiation)
3204 return new HelperMethodInvocation (ec, Location, TypeManager.double_type,
3205 TypeManager.math_pow_double_double, left, right);
3207 if (type == TypeManager.decimal_type) {
3208 MethodInfo helper_method = null;
3209 switch (oper) {
3210 case Operator.Addition:
3211 helper_method = TypeManager.decimal_add_decimal_decimal;
3212 break;
3213 case Operator.Subtraction:
3214 helper_method = TypeManager.decimal_subtract_decimal_decimal;
3215 break;
3216 case Operator.Multiply:
3217 helper_method = TypeManager.decimal_multiply_decimal_decimal;
3218 break;
3219 case Operator.Division:
3220 helper_method = TypeManager.decimal_divide_decimal_decimal;
3221 break;
3222 case Operator.Modulus:
3223 helper_method = TypeManager.decimal_remainder_decimal_decimal;
3224 break;
3227 return new HelperMethodInvocation (ec, Location, TypeManager.decimal_type,
3228 helper_method, left, right);
3231 if (IsRelationalExpression) {
3232 Type = TypeManager.bool_type;
3233 if (left.Type == TypeManager.string_type) {
3234 Expression is_text_mode;
3236 is_text_mode = new BoolConstant (RootContext.StringComparisonMode == CompareMethod.Text);
3237 intermediate = new HelperMethodInvocation (ec, Location, TypeManager.int32_type,
3238 TypeManager.msvbcs_stringtype_strcmp_string_string_boolean,
3239 left, right, is_text_mode);
3240 return this;
3242 if (left.Type == TypeManager.decimal_type) {
3243 intermediate = new HelperMethodInvocation (ec, Location, TypeManager.int32_type,
3244 TypeManager.decimal_compare_decimal_decimal, left, right);
3245 return this;
3247 if (left.Type == TypeManager.date_type) {
3248 intermediate = new HelperMethodInvocation (ec, Location, TypeManager.int32_type,
3249 TypeManager.datetime_compare_datetime_datetime, left, right);
3250 return this;
3254 if (IsShiftExpression)
3255 return this;
3257 if (IsShortCircuitedLogicalExpression)
3258 return this;
3260 if (oper == Operator.Like) {
3261 Type = TypeManager.bool_type;
3262 Expression compare_mode = new EnumConstant (new IntConstant ((int) RootContext.StringComparisonMode),
3263 typeof (Microsoft.VisualBasic.CompareMethod));
3264 return new HelperMethodInvocation (ec, Location, TypeManager.bool_type, TypeManager.msvbcs_stringtype_strlike_string_string_comparemethod, left, right, compare_mode);
3269 // Step 0: String concatenation (because overloading will get this wrong)
3271 if (oper == Operator.Addition || oper == Operator.Concatenation){
3274 // If any of the arguments is a string, cast to string
3277 // Simple constant folding
3278 if (left is StringConstant && right is StringConstant)
3279 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value);
3281 if (Type == TypeManager.string_type) {
3283 // try to fold it in on the left
3284 if (left is StringConcat) {
3287 // We have to test here for not-null, since we can be doubly-resolved
3288 // take care of not appending twice
3290 if (type == null){
3291 type = TypeManager.string_type;
3292 ((StringConcat) left).Append (ec, right);
3293 return left.Resolve (ec);
3294 } else {
3295 return left;
3299 // Otherwise, start a new concat expression
3300 return new StringConcat (ec, loc, left, right).Resolve (ec);
3304 // Transform a + ( - b) into a - b
3306 if (right is Unary){
3307 Unary right_unary = (Unary) right;
3309 if (right_unary.Oper == Unary.Operator.UnaryNegation){
3310 oper = Operator.Subtraction;
3311 right = right_unary.Expr;
3312 r = right.Type;
3317 return this;
3320 Expression HandleObjectOperands (EmitContext ec)
3322 Type l = left.Type;
3323 Type r = right.Type;
3325 Expression target_left_expr = left;
3326 Expression target_right_expr = right;
3328 if (IsShortCircuitedLogicalExpression || IsExpression)
3329 return null;
3331 if (l != TypeManager.object_type && r != TypeManager.object_type)
3332 return null;
3334 if (RootContext.StricterTypeChecking)
3335 if (oper != Operator.Equality &&
3336 oper != Operator.Inequality && oper != Operator.Is) {
3337 Error_OperatorCannotBeAppliedToObjectOperands ();
3338 return null;
3341 if (l != TypeManager.object_type && ! IsOperatorDefinedForType (l) && ConvertOperandToDefinedType(ec, target_left_expr) == null) {
3342 Error_OperatorCannotBeApplied ();
3343 return null;
3345 if (!IsShiftExpression && r != TypeManager.object_type && ! IsOperatorDefinedForType (r) && ConvertOperandToDefinedType(ec, target_right_expr) == null) {
3346 Error_OperatorCannotBeApplied ();
3347 return null;
3350 if (l != TypeManager.object_type)
3351 left = Convert.ImplicitVBConversionRequired (ec, left, TypeManager.object_type, Location);
3353 if (IsShiftExpression) {
3354 if (r != TypeManager.int32_type) {
3355 target_right_expr = Convert.ImplicitVBConversionRequired (ec, right, TypeManager.int32_type, Location);
3356 if (target_right_expr == null) {
3357 Error_OperatorCannotBeApplied ();
3358 return null;
3360 right = target_right_expr;
3363 } else if (r != TypeManager.object_type) {
3364 right = Convert.ImplicitVBConversionRequired (ec, right, TypeManager.object_type, Location);
3368 Type = TypeManager.object_type;
3369 if (IsRelationalExpression) {
3370 Type = TypeManager.bool_type;
3371 Expression is_text_mode = new BoolConstant (RootContext.StringComparisonMode == CompareMethod.Text);
3372 intermediate = new HelperMethodInvocation (ec, Location, TypeManager.int32_type, HelperMethod, left, right, is_text_mode);
3373 return this;
3376 if (oper == Operator.Like) {
3377 Type = TypeManager.bool_type;
3378 Expression compare_mode = new EnumConstant (new IntConstant ((int) RootContext.StringComparisonMode),
3379 typeof (Microsoft.VisualBasic.CompareMethod));
3380 return new HelperMethodInvocation (ec, Location, TypeManager.bool_type, HelperMethod, left, right, compare_mode);
3383 if (IsShiftExpression)
3384 return new HelperMethodInvocation (ec, Location, TypeManager.object_type, HelperMethod, left, right);
3386 return new HelperMethodInvocation (ec, Location, TypeManager.object_type, HelperMethod, left, right);
3389 void CheckArguments (EmitContext ec)
3391 int step = 0;
3393 Type l = left.Type;
3394 Type r = right.Type;
3396 Expression target_left_expr = left;
3397 Expression target_right_expr = right;
3399 Type target_left_expr_type = target_left_expr.Type;
3400 Type target_right_expr_type = target_right_expr.Type;
3403 if (IsShiftExpression) {
3404 CheckShiftArguments (ec);
3405 return;
3408 if (IsExpression) {
3409 CheckIsArguments (ec);
3410 return;
3413 while (true) {
3414 ++step;
3416 if (step > 10)
3417 throw new Exception ("FIXME: An Infinite loop when resolving <" + l + "> " + OperName (oper) + " <" + r + ">");
3419 //Console.WriteLine (" STEP " + step + ":");
3420 //Console.WriteLine (" " + "<" + target_left_expr_type + ", " + target_right_expr_type + ">");
3422 if ((target_left_expr_type == target_right_expr_type) &&
3423 IsOperatorDefinedForType (target_left_expr_type)) {
3425 if (target_left_expr_type == TypeManager.null_type) {
3426 target_left_expr = target_right_expr = new IntConstant (0);
3427 Type = TypeManager.int32_type;
3428 return;
3429 } else {
3430 left = target_left_expr;
3431 right = target_right_expr;
3432 type = target_left_expr_type;
3433 return;
3437 if ( !IsOperatorDefinedForType (target_left_expr_type)) {
3438 target_left_expr = ConvertOperandToDefinedType(ec, target_left_expr);
3440 if (target_left_expr == null) {
3441 Error_OperatorCannotBeApplied();
3442 return;
3445 target_left_expr_type = target_left_expr.Type;
3446 continue;
3449 if ( !IsOperatorDefinedForType(target_right_expr_type)) {
3450 target_right_expr = ConvertOperandToDefinedType(ec, target_right_expr);
3452 if(target_right_expr == null) {
3453 Error_OperatorCannotBeApplied();
3454 return;
3457 target_right_expr_type = target_right_expr.Type;
3458 continue;
3461 if (target_left_expr_type == TypeManager.null_type ||
3462 target_right_expr_type == TypeManager.null_type)
3463 break;
3465 if (target_left_expr_type == TypeManager.string_type) {
3466 Type target_type;
3467 if (target_right_expr_type == TypeManager.date_type)
3468 target_type = TypeManager.date_type;
3469 else if (target_right_expr_type == TypeManager.bool_type)
3470 target_type = TypeManager.bool_type;
3471 else
3472 target_type = TypeManager.double_type;
3474 if (l == target_type)
3475 target_left_expr = left;
3476 else
3477 target_left_expr = Convert.ImplicitVBConversionRequired (ec, left, target_type, Location);
3479 if (target_left_expr == null) {
3480 Error_OperatorCannotBeApplied();
3481 return;
3484 target_left_expr_type = target_left_expr.Type;
3485 continue;
3488 if (target_right_expr_type == TypeManager.string_type) {
3489 Type target_type;
3490 if (target_left_expr_type == TypeManager.date_type)
3491 target_type = TypeManager.date_type;
3492 else if (target_left_expr_type == TypeManager.bool_type)
3493 target_type = TypeManager.bool_type;
3494 else
3495 target_type = TypeManager.double_type;
3497 if (r == target_type)
3498 target_right_expr = right;
3499 else
3500 target_right_expr = Convert.ImplicitVBConversionRequired (ec, right, target_type, Location);
3502 if (target_right_expr == null) {
3503 Error_OperatorCannotBeApplied();
3504 return;
3507 target_right_expr_type = target_right_expr.Type;
3508 continue;
3511 break;
3514 if ( !DoOperandPromotions(ec, target_left_expr, target_right_expr))
3515 Error_OperatorCannotBeApplied();
3517 return;
3520 bool IsOperatorDefinedForType (Type t)
3522 if (t == TypeManager.null_type)
3523 return true;
3525 switch (oper) {
3527 case Operator.Exponentiation:
3528 if (t == TypeManager.double_type)
3529 return true;
3530 break;
3532 case Operator.Concatenation:
3533 case Operator.Like:
3534 if (t == TypeManager.string_type)
3535 return true;
3537 break;
3539 case Operator.BitwiseAnd:
3540 case Operator.BitwiseOr:
3541 case Operator.ExclusiveOr:
3542 if (t == TypeManager.bool_type ||
3543 TypeManager.IsFixedNumericType (t))
3544 return true;
3546 break;
3548 case Operator.LogicalAndAlso:
3549 case Operator.LogicalOrElse:
3550 if (t == TypeManager.bool_type)
3551 return true;
3552 break;
3554 case Operator.RightShift:
3555 case Operator.LeftShift:
3557 if (TypeManager.IsFixedNumericType (t))
3558 return true;
3560 break;
3562 case Operator.Equality:
3563 case Operator.Inequality:
3564 case Operator.LessThan:
3565 case Operator.LessThanOrEqual:
3566 case Operator.GreaterThan:
3567 case Operator.GreaterThanOrEqual:
3568 if (t == TypeManager.bool_type ||
3569 t == TypeManager.date_type ||
3570 t == TypeManager.char_type ||
3571 t == TypeManager.string_type ||
3572 TypeManager.IsNumericType (t))
3573 return true;
3575 break;
3577 case Operator.Addition:
3578 if (t == TypeManager.string_type ||
3579 TypeManager.IsNumericType (t))
3580 return true;
3581 break;
3583 case Operator.Subtraction:
3584 case Operator.Multiply:
3585 case Operator.Division:
3586 case Operator.Modulus:
3587 if (TypeManager.IsNumericType (t))
3588 return true;
3589 break;
3591 case Operator.IntegerDivision:
3592 if (TypeManager.IsFixedNumericType (t))
3593 return true;
3595 break;
3598 return false;
3602 Expression ConvertOperandToDefinedType (EmitContext ec, Expression expr)
3604 Type target_type = null;
3605 Type operand_type = expr.Type;
3607 if (IsOperatorDefinedForType (operand_type))
3608 return expr;
3610 switch (oper) {
3611 case Operator.Addition:
3612 case Operator.Subtraction:
3613 case Operator.Multiply:
3614 if (operand_type == TypeManager.bool_type)
3615 target_type = TypeManager.short_type;
3617 if (operand_type == TypeManager.char_type)
3618 target_type = TypeManager.string_type;
3620 if (operand_type == TypeManager.date_type)
3621 target_type = TypeManager.string_type;
3623 break;
3625 case Operator.Like:
3626 case Operator.Concatenation:
3627 return Convert.ExplicitVBConversion(ec, expr, TypeManager.string_type, expr.Location);
3628 break;
3630 case Operator.LogicalAndAlso:
3631 case Operator.LogicalOrElse:
3632 return Convert.ExplicitVBConversion(ec, expr, TypeManager.bool_type, expr.Location);
3633 break;
3635 case Operator.Exponentiation:
3636 return Convert.ExplicitVBConversion(ec, expr, TypeManager.double_type, expr.Location);
3637 break;
3641 if (target_type != null)
3642 return Convert.ImplicitVBConversion(ec, expr, target_type, expr.Location);
3644 return null;
3647 static Type GetWiderOfTypes (Type t1, Type t2)
3649 // char array and Nothing should be handled here ?
3652 if (t1 == t2)
3653 return t1;
3655 if(t1 == TypeManager.null_type)
3656 return t2;
3658 if (t2 == TypeManager.null_type)
3659 return t1;
3661 if (t1 == TypeManager.date_type || t1 == TypeManager.char_type) {
3662 if (t2 == TypeManager.string_type)
3663 return t2;
3664 else
3665 return null;
3668 if (t2 == TypeManager.date_type || t2 == TypeManager.char_type) {
3669 if (t1 == TypeManager.string_type)
3670 return t1;
3671 else
3672 return null;
3675 object order1 = TypeManager.relative_type_order[t1];
3676 if (order1 == null)
3677 return null;
3679 object order2 = TypeManager.relative_type_order[t2];
3681 if (order2 == null)
3682 return null;
3684 if ((int) order1 > (int) order2)
3685 return t1;
3686 else
3687 return t2;
3691 bool DoOperandPromotions (EmitContext ec, Expression target_left_expr, Expression target_right_expr)
3693 Type l = target_left_expr.Type;
3694 Type r = target_right_expr.Type;
3696 Type target_type = GetWiderOfTypes(l, r);
3698 //Console.WriteLine (" DoingOperandPromotions");
3699 //Console.WriteLine (" left => " + l + " right => " + r);
3700 //Console.WriteLine (" target_type => " + target_type);
3702 if (target_type == null) {
3703 throw new Exception ("Types " + l + " " + r +" cannot be compared");
3706 if (r != target_type) {
3707 target_right_expr = Convert.ImplicitVBConversion (ec, target_right_expr, target_type, Location);
3709 if (target_right_expr == null)
3710 return false;
3714 if (l != target_type) {
3715 target_left_expr = Convert.ImplicitVBConversion (ec, target_left_expr, target_type, Location);
3717 if (target_left_expr == null)
3718 return false;
3721 left = target_left_expr;
3722 right = target_right_expr;
3723 type = target_type;
3724 return true;
3727 bool IsArithmeticExpression {
3728 get {
3729 if (oper == Operator.Addition|| oper == Operator.Subtraction||
3730 oper == Operator.Multiply|| oper == Operator.Division||
3731 oper == Operator.IntegerDivision|| oper == Operator.Modulus)
3732 return true;
3734 return false;
3738 bool IsRelationalExpression {
3739 get {
3740 if (oper == Operator.Equality || oper == Operator.Inequality ||
3741 oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
3742 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual)
3743 return true;
3745 return false;
3749 bool IsShiftExpression {
3750 get {
3751 if (oper == Operator.LeftShift || oper == Operator.RightShift)
3752 return true;
3754 return false;
3758 bool IsShortCircuitedLogicalExpression {
3759 get {
3760 if (oper == Operator.LogicalAndAlso|| oper == Operator.LogicalOrElse)
3761 return true;
3763 return false;
3767 bool IsExpression {
3768 get {
3769 return (oper == Operator.Is);
3773 MethodInfo HelperMethod {
3774 get {
3775 MethodInfo helper_method = null;
3776 switch (oper) {
3777 case Operator.Multiply:
3778 helper_method = TypeManager.msvbcs_objecttype_mulobj_object_object;
3779 break;
3780 case Operator.Division:
3781 helper_method = TypeManager.msvbcs_objecttype_divobj_object_object;
3782 break;
3783 case Operator.IntegerDivision:
3784 helper_method = TypeManager.msvbcs_objecttype_idivobj_object_object;
3785 break;
3786 case Operator.Modulus:
3787 helper_method = TypeManager.msvbcs_objecttype_modobj_object_object;
3788 break;
3789 case Operator.Addition:
3790 helper_method = TypeManager.msvbcs_objecttype_addobj_object_object;
3791 break;
3792 case Operator.Subtraction:
3793 helper_method = TypeManager.msvbcs_objecttype_subobj_object_object;
3794 break;
3795 case Operator.LessThan:
3796 case Operator.GreaterThan:
3797 case Operator.LessThanOrEqual:
3798 case Operator.GreaterThanOrEqual:
3799 case Operator.Equality:
3800 case Operator.Inequality:
3801 helper_method = TypeManager.msvbcs_objecttype_objtst_object_object_boolean;
3802 break;
3803 case Operator.BitwiseAnd:
3804 helper_method = TypeManager.msvbcs_objecttype_bitandobj_object_object;
3805 break;
3806 case Operator.BitwiseOr:
3807 helper_method = TypeManager.msvbcs_objecttype_bitorobj_object_object;
3808 break;
3809 case Operator.ExclusiveOr:
3810 helper_method = TypeManager.msvbcs_objecttype_bitxorobj_object_object;
3811 break;
3813 case Operator.Like:
3814 helper_method = TypeManager.msvbcs_objecttype_likeobj_object_object_comparemethod;
3815 break;
3817 case Operator.Concatenation:
3818 helper_method = TypeManager.msvbcs_objecttype_strcatobj_object_object;
3819 break;
3821 case Operator.Exponentiation:
3822 helper_method = TypeManager.msvbcs_objecttype_powobj_object_object;
3823 break;
3824 case Operator.LeftShift:
3825 helper_method = TypeManager.msvbcs_objecttype_shiftleftobj_object_int32;
3826 break;
3827 case Operator.RightShift:
3828 helper_method = TypeManager.msvbcs_objecttype_shiftrightobj_object_int32;
3829 break;
3833 return helper_method;
3839 // Object created by Binary when the binary operator uses an method instead of being
3840 // a binary operation that maps to a CIL binary operation.
3842 public class BinaryMethod : Expression {
3843 public MethodBase method;
3844 public ArrayList Arguments;
3846 public BinaryMethod (Type t, MethodBase m, ArrayList args)
3848 method = m;
3849 Arguments = args;
3850 type = t;
3851 eclass = ExprClass.Value;
3854 public override Expression DoResolve (EmitContext ec)
3856 return this;
3859 public override void Emit (EmitContext ec)
3861 ILGenerator ig = ec.ig;
3863 if (Arguments != null)
3864 Invocation.EmitArguments (ec, method, Arguments, false, null);
3866 if (method is MethodInfo)
3867 ig.Emit (OpCodes.Call, (MethodInfo) method);
3868 else
3869 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
3874 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
3875 // b, c, d... may be strings or objects.
3877 public class StringConcat : Expression {
3878 ArrayList operands;
3879 bool invalid = false;
3880 bool emit_conv_done = false;
3882 // Are we also concating objects?
3884 bool is_strings_only = true;
3886 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
3888 this.loc = loc;
3889 type = TypeManager.string_type;
3890 eclass = ExprClass.Value;
3892 operands = new ArrayList (2);
3893 Append (ec, left);
3894 Append (ec, right);
3897 public override Expression DoResolve (EmitContext ec)
3899 if (invalid)
3900 return null;
3902 return this;
3905 public void Append (EmitContext ec, Expression operand)
3908 // Constant folding
3910 if (operand is StringConstant && operands.Count != 0) {
3911 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
3912 if (last_operand != null) {
3913 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value);
3914 return;
3919 // Conversion to object
3921 if (operand.Type != TypeManager.string_type) {
3922 Expression no = Convert.WideningConversion (ec, operand, TypeManager.object_type, loc);
3924 if (no == null) {
3925 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
3926 invalid = true;
3928 operand = no;
3931 operands.Add (operand);
3934 public override void Emit (EmitContext ec)
3936 MethodInfo concat_method = null;
3939 // Do conversion to arguments; check for strings only
3942 // This can get called multiple times, so we have to deal with that.
3943 if (!emit_conv_done) {
3944 emit_conv_done = true;
3945 for (int i = 0; i < operands.Count; i ++) {
3946 Expression e = (Expression) operands [i];
3947 is_strings_only &= e.Type == TypeManager.string_type;
3950 for (int i = 0; i < operands.Count; i ++) {
3951 Expression e = (Expression) operands [i];
3953 if (! is_strings_only && e.Type == TypeManager.string_type) {
3954 // need to make sure this is an object, because the EmitParams
3955 // method might look at the type of this expression, see it is a
3956 // string and emit a string [] when we want an object [];
3958 e = new EmptyCast (e, TypeManager.object_type);
3960 operands [i] = new Argument (e, Argument.AType.Expression);
3965 // Find the right method
3967 switch (operands.Count) {
3968 case 1:
3970 // This should not be possible, because simple constant folding
3971 // is taken care of in the Binary code.
3973 throw new Exception ("how did you get here?");
3975 case 2:
3976 concat_method = is_strings_only ?
3977 TypeManager.string_concat_string_string :
3978 TypeManager.string_concat_object_object ;
3979 break;
3980 case 3:
3981 concat_method = is_strings_only ?
3982 TypeManager.string_concat_string_string_string :
3983 TypeManager.string_concat_object_object_object ;
3984 break;
3985 case 4:
3987 // There is not a 4 param overlaod for object (the one that there is
3988 // is actually a varargs methods, and is only in corlib because it was
3989 // introduced there before.).
3991 if (!is_strings_only)
3992 goto default;
3994 concat_method = TypeManager.string_concat_string_string_string_string;
3995 break;
3996 default:
3997 concat_method = is_strings_only ?
3998 TypeManager.string_concat_string_dot_dot_dot :
3999 TypeManager.string_concat_object_dot_dot_dot ;
4000 break;
4003 Invocation.EmitArguments (ec, concat_method, operands, false, null);
4004 ec.ig.Emit (OpCodes.Call, concat_method);
4009 // Object created with +/= on delegates
4011 public class BinaryDelegate : Expression {
4012 MethodInfo method;
4013 ArrayList args;
4015 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
4017 method = mi;
4018 this.args = args;
4019 type = t;
4020 eclass = ExprClass.Value;
4023 public override Expression DoResolve (EmitContext ec)
4025 return this;
4028 public override void Emit (EmitContext ec)
4030 ILGenerator ig = ec.ig;
4032 Invocation.EmitArguments (ec, method, args, false, null);
4034 ig.Emit (OpCodes.Call, (MethodInfo) method);
4035 ig.Emit (OpCodes.Castclass, type);
4038 public Expression Right {
4039 get {
4040 Argument arg = (Argument) args [1];
4041 return arg.Expr;
4045 public bool IsAddition {
4046 get {
4047 return method == TypeManager.delegate_combine_delegate_delegate;
4053 // User-defined conditional logical operator
4054 public class ConditionalLogicalOperator : Expression {
4055 Expression left, right;
4056 bool is_and;
4058 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
4060 type = t;
4061 eclass = ExprClass.Value;
4062 this.loc = loc;
4063 this.left = left;
4064 this.right = right;
4065 this.is_and = is_and;
4068 protected void Error19 ()
4070 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", type, type);
4073 protected void Error218 ()
4075 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
4076 "declarations of operator true and operator false");
4079 Expression op_true, op_false, op;
4080 LocalTemporary left_temp;
4082 public override Expression DoResolve (EmitContext ec)
4084 MethodInfo method;
4085 Expression operator_group;
4087 operator_group = MethodLookup (ec, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
4088 if (operator_group == null) {
4089 Error19 ();
4090 return null;
4093 left_temp = new LocalTemporary (ec, type);
4095 ArrayList arguments = new ArrayList ();
4096 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
4097 arguments.Add (new Argument (right, Argument.AType.Expression));
4098 method = Invocation.OverloadResolve (
4099 ec, (MethodGroupExpr) operator_group, arguments, false, loc)
4100 as MethodInfo;
4101 if ((method == null) || (method.ReturnType != type)) {
4102 Error19 ();
4103 return null;
4106 op = new StaticCallExpr (method, arguments, loc);
4108 op_true = GetOperatorTrue (ec, left_temp, loc);
4109 op_false = GetOperatorFalse (ec, left_temp, loc);
4110 if ((op_true == null) || (op_false == null)) {
4111 Error218 ();
4112 return null;
4115 return this;
4118 public override void Emit (EmitContext ec)
4120 ILGenerator ig = ec.ig;
4121 Label false_target = ig.DefineLabel ();
4122 Label end_target = ig.DefineLabel ();
4124 left.Emit (ec);
4125 left_temp.Store (ec);
4127 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
4128 left_temp.Emit (ec);
4129 ig.Emit (OpCodes.Br, end_target);
4130 ig.MarkLabel (false_target);
4131 op.Emit (ec);
4132 ig.MarkLabel (end_target);
4136 public class PointerArithmetic : Expression {
4137 Expression left, right;
4138 bool is_add;
4141 // We assume that `l' is always a pointer
4143 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
4145 type = t;
4146 this.loc = loc;
4147 left = l;
4148 right = r;
4149 is_add = is_addition;
4152 public override Expression DoResolve (EmitContext ec)
4154 eclass = ExprClass.Variable;
4156 if (left.Type == TypeManager.void_ptr_type) {
4157 Error (242, "The operation in question is undefined on void pointers");
4158 return null;
4161 return this;
4164 public override void Emit (EmitContext ec)
4166 Type op_type = left.Type;
4167 ILGenerator ig = ec.ig;
4168 Type element = TypeManager.GetElementType (op_type);
4169 int size = GetTypeSize (element);
4170 Type rtype = right.Type;
4172 if (rtype.IsPointer){
4174 // handle (pointer - pointer)
4176 left.Emit (ec);
4177 right.Emit (ec);
4178 ig.Emit (OpCodes.Sub);
4180 if (size != 1){
4181 if (size == 0)
4182 ig.Emit (OpCodes.Sizeof, element);
4183 else
4184 IntLiteral.EmitInt (ig, size);
4185 ig.Emit (OpCodes.Div);
4187 ig.Emit (OpCodes.Conv_I8);
4188 } else {
4190 // handle + and - on (pointer op int)
4192 left.Emit (ec);
4193 ig.Emit (OpCodes.Conv_I);
4195 Constant right_const = right as Constant;
4196 if (right_const != null && size != 0) {
4197 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size), right_const, loc);
4198 if (ex == null)
4199 return;
4200 ex.Emit (ec);
4201 } else {
4202 right.Emit (ec);
4203 if (size != 1){
4204 if (size == 0)
4205 ig.Emit (OpCodes.Sizeof, element);
4206 else
4207 IntLiteral.EmitInt (ig, size);
4208 if (rtype == TypeManager.int64_type)
4209 ig.Emit (OpCodes.Conv_I8);
4210 else if (rtype == TypeManager.uint64_type)
4211 ig.Emit (OpCodes.Conv_U8);
4212 ig.Emit (OpCodes.Mul);
4216 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
4217 ig.Emit (OpCodes.Conv_I);
4219 if (is_add)
4220 ig.Emit (OpCodes.Add);
4221 else
4222 ig.Emit (OpCodes.Sub);
4227 /// <summary>
4228 /// Implements the ternary conditional operator (?:)
4229 /// </summary>
4230 public class Conditional : Expression {
4231 Expression expr, trueExpr, falseExpr;
4233 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr, Location l)
4235 this.expr = expr;
4236 this.trueExpr = trueExpr;
4237 this.falseExpr = falseExpr;
4238 this.loc = l;
4241 public Expression Expr {
4242 get {
4243 return expr;
4247 public Expression TrueExpr {
4248 get {
4249 return trueExpr;
4253 public Expression FalseExpr {
4254 get {
4255 return falseExpr;
4259 public override Expression DoResolve (EmitContext ec)
4261 expr = expr.Resolve (ec);
4263 if (expr == null)
4264 return null;
4266 if (TypeManager.IsNullableType (expr.Type))
4267 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
4269 if (expr.Type != TypeManager.bool_type){
4270 expr = Expression.ResolveBoolean (
4271 ec, expr, loc);
4273 if (expr == null)
4274 return null;
4277 trueExpr = trueExpr.Resolve (ec);
4278 falseExpr = falseExpr.Resolve (ec);
4280 if (trueExpr == null || falseExpr == null)
4281 return null;
4283 eclass = ExprClass.Value;
4284 if (trueExpr.Type == falseExpr.Type)
4285 type = trueExpr.Type;
4286 else {
4287 Expression conv;
4288 Type true_type = trueExpr.Type;
4289 Type false_type = falseExpr.Type;
4292 // First, if an implicit conversion exists from trueExpr
4293 // to falseExpr, then the result type is of type falseExpr.Type
4295 conv = Convert.WideningConversion (ec, trueExpr, false_type, loc);
4296 if (conv != null){
4298 // Check if both can convert implicitl to each other's type
4300 if (Convert.WideningConversion (ec, falseExpr, true_type, loc) != null){
4301 Error (172,
4302 "Can not compute type of conditional expression " +
4303 "as `" + TypeManager.CSharpName (trueExpr.Type) +
4304 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
4305 "' convert implicitly to each other");
4306 return null;
4308 type = false_type;
4309 trueExpr = conv;
4310 } else if ((conv = Convert.WideningConversion(ec, falseExpr, true_type,loc))!= null){
4311 type = true_type;
4312 falseExpr = conv;
4313 } else {
4314 Error (173, "The type of the conditional expression can " +
4315 "not be computed because there is no implicit conversion" +
4316 " from `" + TypeManager.CSharpName (trueExpr.Type) + "'" +
4317 " and `" + TypeManager.CSharpName (falseExpr.Type) + "'");
4318 return null;
4322 // Dead code optimalization
4323 if (expr is BoolConstant){
4324 BoolConstant bc = (BoolConstant) expr;
4326 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
4327 return bc.Value ? trueExpr : falseExpr;
4330 return this;
4333 public override void Emit (EmitContext ec)
4335 ILGenerator ig = ec.ig;
4336 Label false_target = ig.DefineLabel ();
4337 Label end_target = ig.DefineLabel ();
4339 expr.EmitBranchable (ec, false_target, false);
4340 trueExpr.Emit (ec);
4341 ig.Emit (OpCodes.Br, end_target);
4342 ig.MarkLabel (false_target);
4343 falseExpr.Emit (ec);
4344 ig.MarkLabel (end_target);
4349 /// <summary>
4350 /// Local variables
4351 /// </summary>
4352 public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
4353 public readonly string Name;
4354 public readonly Block Block;
4355 public LocalInfo local_info;
4356 bool is_readonly;
4357 bool prepared;
4358 LocalTemporary temp;
4360 public LocalVariableReference (Block block, string name, Location l)
4362 Block = block;
4363 Name = name;
4364 loc = l;
4365 eclass = ExprClass.Variable;
4369 // Setting `is_readonly' to false will allow you to create a writable
4370 // reference to a read-only variable. This is used by foreach and using.
4372 public LocalVariableReference (Block block, string name, Location l,
4373 LocalInfo local_info, bool is_readonly)
4374 : this (block, name, l)
4376 this.local_info = local_info;
4377 this.is_readonly = is_readonly;
4380 public VariableInfo VariableInfo {
4381 get {
4382 return local_info.VariableInfo;
4386 public bool IsReadOnly {
4387 get {
4388 return is_readonly;
4392 protected Expression DoResolveBase (EmitContext ec, Expression lvalue_right_side)
4394 if (local_info == null) {
4395 local_info = Block.GetLocalInfo (Name);
4397 // is out param
4398 if (lvalue_right_side == EmptyExpression.Null)
4399 local_info.Used = true;
4401 is_readonly = local_info.ReadOnly;
4404 type = local_info.VariableType;
4406 VariableInfo variable_info = local_info.VariableInfo;
4407 if (lvalue_right_side != null){
4408 if (is_readonly){
4409 Error (1604, "cannot assign to `" + Name + "' because it is readonly");
4410 return null;
4413 if (variable_info != null)
4414 variable_info.SetAssigned (ec);
4417 Expression e = Block.GetConstantExpression (Name);
4418 if (e != null) {
4419 local_info.Used = true;
4420 eclass = ExprClass.Value;
4421 return e.Resolve (ec);
4424 if ((variable_info != null) && !variable_info.IsAssigned (ec, loc))
4425 return null;
4427 if (lvalue_right_side == null)
4428 local_info.Used = true;
4430 if (ec.CurrentAnonymousMethod != null){
4432 // If we are referencing a variable from the external block
4433 // flag it for capturing
4435 if (local_info.Block.Toplevel != ec.CurrentBlock.Toplevel){
4436 if (local_info.AddressTaken){
4437 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
4438 return null;
4440 ec.CaptureVariable (local_info);
4444 return this;
4447 public override Expression DoResolve (EmitContext ec)
4449 return DoResolveBase (ec, null);
4452 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
4454 Expression ret = DoResolveBase (ec, right_side);
4455 if (ret != null)
4456 CheckObsoleteAttribute (ret.Type);
4458 return ret;
4461 public bool VerifyFixed (bool is_expression)
4463 return !is_expression || local_info.IsFixed;
4466 public override void Emit (EmitContext ec)
4468 ILGenerator ig = ec.ig;
4470 if (local_info.FieldBuilder == null){
4472 // A local variable on the local CLR stack
4474 ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
4475 } else {
4477 // A local variable captured by anonymous methods.
4479 if (!prepared)
4480 ec.EmitCapturedVariableInstance (local_info);
4482 ig.Emit (OpCodes.Ldfld, local_info.FieldBuilder);
4486 public void Emit (EmitContext ec, bool leave_copy)
4488 Emit (ec);
4489 if (leave_copy){
4490 ec.ig.Emit (OpCodes.Dup);
4491 if (local_info.FieldBuilder != null){
4492 temp = new LocalTemporary (ec, Type);
4493 temp.Store (ec);
4498 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
4500 ILGenerator ig = ec.ig;
4501 prepared = prepare_for_load;
4503 if (local_info.FieldBuilder == null){
4505 // A local variable on the local CLR stack
4507 if (local_info.LocalBuilder == null)
4508 throw new Exception ("This should not happen: both Field and Local are null");
4510 source.Emit (ec);
4511 if (leave_copy)
4512 ec.ig.Emit (OpCodes.Dup);
4513 ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
4514 } else {
4516 // A local variable captured by anonymous methods or itereators.
4518 ec.EmitCapturedVariableInstance (local_info);
4520 if (prepare_for_load)
4521 ig.Emit (OpCodes.Dup);
4522 source.Emit (ec);
4523 if (leave_copy){
4524 ig.Emit (OpCodes.Dup);
4525 temp = new LocalTemporary (ec, Type);
4526 temp.Store (ec);
4528 ig.Emit (OpCodes.Stfld, local_info.FieldBuilder);
4529 if (temp != null)
4530 temp.Emit (ec);
4534 public void AddressOf (EmitContext ec, AddressOp mode)
4536 ILGenerator ig = ec.ig;
4538 if (local_info.FieldBuilder == null){
4540 // A local variable on the local CLR stack
4542 ig.Emit (OpCodes.Ldloca, local_info.LocalBuilder);
4543 } else {
4545 // A local variable captured by anonymous methods or iterators
4547 ec.EmitCapturedVariableInstance (local_info);
4548 ig.Emit (OpCodes.Ldflda, local_info.FieldBuilder);
4552 public override string ToString ()
4554 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
4558 /// <summary>
4559 /// This represents a reference to a parameter in the intermediate
4560 /// representation.
4561 /// </summary>
4562 public class ParameterReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
4563 Parameters pars;
4564 String name;
4565 int idx;
4566 Block block;
4567 VariableInfo vi;
4568 public Parameter.Modifier mod;
4569 public bool is_ref, is_out, prepared;
4571 public bool IsOut {
4572 get {
4573 return is_out;
4577 public bool IsRef {
4578 get {
4579 return is_ref;
4583 LocalTemporary temp;
4585 public ParameterReference (Parameters pars, Block block, int idx, string name, Location loc)
4587 this.pars = pars;
4588 this.block = block;
4589 this.idx = idx;
4590 this.name = name;
4591 this.loc = loc;
4592 eclass = ExprClass.Variable;
4595 public VariableInfo VariableInfo {
4596 get { return vi; }
4599 public bool VerifyFixed (bool is_expression)
4601 return !is_expression || TypeManager.IsValueType (type);
4604 public bool IsAssigned (EmitContext ec, Location loc)
4606 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
4607 return true;
4609 Report.Error (165, loc,
4610 "Use of unassigned parameter `" + name + "'");
4611 return false;
4614 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
4616 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
4617 return true;
4619 Report.Error (170, loc,
4620 "Use of possibly unassigned field `" + field_name + "'");
4621 return false;
4624 public void SetAssigned (EmitContext ec)
4626 if (is_out && ec.DoFlowAnalysis)
4627 ec.CurrentBranching.SetAssigned (vi);
4630 public void SetFieldAssigned (EmitContext ec, string field_name)
4632 if (is_out && ec.DoFlowAnalysis)
4633 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
4636 protected void DoResolveBase (EmitContext ec)
4638 type = pars.GetParameterInfo (ec, idx, out mod);
4639 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
4640 is_out = (mod & Parameter.Modifier.OUT) != 0;
4641 eclass = ExprClass.Variable;
4643 if (is_out)
4644 vi = block.ParameterMap [idx];
4646 if (ec.CurrentAnonymousMethod != null){
4647 if (is_ref){
4648 Report.Error (1628, Location,
4649 "Can not reference a ref or out parameter in an anonymous method");
4650 return;
4654 // If we are referencing the parameter from the external block
4655 // flag it for capturing
4657 //Console.WriteLine ("Is parameter `{0}' local? {1}", name, block.IsLocalParameter (name));
4658 if (!block.IsLocalParameter (name)){
4659 ec.CaptureParameter (name, type, idx);
4665 // Notice that for ref/out parameters, the type exposed is not the
4666 // same type exposed externally.
4668 // for "ref int a":
4669 // externally we expose "int&"
4670 // here we expose "int".
4672 // We record this in "is_ref". This means that the type system can treat
4673 // the type as it is expected, but when we generate the code, we generate
4674 // the alternate kind of code.
4676 public override Expression DoResolve (EmitContext ec)
4678 DoResolveBase (ec);
4680 if (is_out && ec.DoFlowAnalysis && !IsAssigned (ec, loc))
4681 return null;
4683 if (ec.RemapToProxy)
4684 return ec.RemapParameter (idx);
4686 return this;
4689 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
4691 DoResolveBase (ec);
4693 SetAssigned (ec);
4695 if (ec.RemapToProxy)
4696 return ec.RemapParameterLValue (idx, right_side);
4698 return this;
4701 static public void EmitLdArg (ILGenerator ig, int x)
4703 if (x <= 255){
4704 switch (x){
4705 case 0: ig.Emit (OpCodes.Ldarg_0); break;
4706 case 1: ig.Emit (OpCodes.Ldarg_1); break;
4707 case 2: ig.Emit (OpCodes.Ldarg_2); break;
4708 case 3: ig.Emit (OpCodes.Ldarg_3); break;
4709 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
4711 } else
4712 ig.Emit (OpCodes.Ldarg, x);
4716 // This method is used by parameters that are references, that are
4717 // being passed as references: we only want to pass the pointer (that
4718 // is already stored in the parameter, not the address of the pointer,
4719 // and not the value of the variable).
4721 public void EmitLoad (EmitContext ec)
4723 ILGenerator ig = ec.ig;
4724 int arg_idx = idx;
4726 if (!ec.IsStatic)
4727 arg_idx++;
4729 EmitLdArg (ig, arg_idx);
4732 // FIXME: Review for anonymous methods
4736 public override void Emit (EmitContext ec)
4738 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
4739 ec.EmitParameter (name);
4740 return;
4743 Emit (ec, false);
4746 public void Emit (EmitContext ec, bool leave_copy)
4748 ILGenerator ig = ec.ig;
4749 int arg_idx = idx;
4751 if (!ec.IsStatic)
4752 arg_idx++;
4754 EmitLdArg (ig, arg_idx);
4756 if (is_ref) {
4757 if (prepared)
4758 ec.ig.Emit (OpCodes.Dup);
4761 // If we are a reference, we loaded on the stack a pointer
4762 // Now lets load the real value
4764 LoadFromPtr (ig, type);
4767 if (leave_copy) {
4768 ec.ig.Emit (OpCodes.Dup);
4770 if (is_ref) {
4771 temp = new LocalTemporary (ec, type);
4772 temp.Store (ec);
4777 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
4779 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
4780 ec.EmitAssignParameter (name, source, leave_copy, prepare_for_load);
4781 return;
4784 ILGenerator ig = ec.ig;
4785 int arg_idx = idx;
4787 prepared = prepare_for_load;
4789 if (!ec.IsStatic)
4790 arg_idx++;
4792 if (is_ref && !prepared)
4793 EmitLdArg (ig, arg_idx);
4795 source.Emit (ec);
4797 if (leave_copy)
4798 ec.ig.Emit (OpCodes.Dup);
4800 if (is_ref) {
4801 if (leave_copy) {
4802 temp = new LocalTemporary (ec, type);
4803 temp.Store (ec);
4806 StoreFromPtr (ig, type);
4808 if (temp != null)
4809 temp.Emit (ec);
4810 } else {
4811 if (arg_idx <= 255)
4812 ig.Emit (OpCodes.Starg_S, (byte) arg_idx);
4813 else
4814 ig.Emit (OpCodes.Starg, arg_idx);
4818 public void AddressOf (EmitContext ec, AddressOp mode)
4820 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
4821 ec.EmitAddressOfParameter (name);
4822 return;
4825 int arg_idx = idx;
4827 if (!ec.IsStatic)
4828 arg_idx++;
4830 if (is_ref){
4831 if (arg_idx <= 255)
4832 ec.ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx);
4833 else
4834 ec.ig.Emit (OpCodes.Ldarg, arg_idx);
4835 } else {
4836 if (arg_idx <= 255)
4837 ec.ig.Emit (OpCodes.Ldarga_S, (byte) arg_idx);
4838 else
4839 ec.ig.Emit (OpCodes.Ldarga, arg_idx);
4845 /// <summary>
4846 /// Used for arguments to New(), Invocation()
4847 /// </summary>
4848 public class Argument {
4849 public enum AType : byte {
4850 Expression,
4851 Ref,
4852 Out,
4853 ArgList,
4854 //FIXME: These two are mbas specific and the
4855 // related changes need to be propagated
4856 NoArg,
4857 AddressOf
4860 public readonly AType ArgType;
4861 public Expression Expr;
4863 public Argument (Expression expr, AType type)
4865 this.Expr = expr;
4866 this.ArgType = type;
4869 public Argument (Expression expr)
4871 this.Expr = expr;
4872 this.ArgType = AType.Expression;
4875 public Type Type {
4876 get {
4877 if (ArgType == AType.Ref || ArgType == AType.Out)
4878 return TypeManager.GetReferenceType (Expr.Type);
4879 else
4880 return Expr.Type;
4884 public Parameter.Modifier GetParameterModifier ()
4886 switch (ArgType) {
4887 case AType.Out:
4888 return Parameter.Modifier.OUT | Parameter.Modifier.ISBYREF;
4890 case AType.Ref:
4891 return Parameter.Modifier.REF | Parameter.Modifier.ISBYREF;
4893 default:
4894 return Parameter.Modifier.NONE;
4898 public static string FullDesc (Argument a)
4900 if (a.ArgType == AType.ArgList)
4901 return "__arglist";
4903 return (a.ArgType == AType.Ref ? "ref " :
4904 (a.ArgType == AType.Out ? "out " : "")) +
4905 TypeManager.CSharpName (a.Expr.Type);
4908 public bool ResolveMethodGroup (EmitContext ec, Location loc)
4910 ConstructedType ctype = Expr as ConstructedType;
4911 if (ctype != null)
4912 Expr = ctype.GetSimpleName (ec);
4914 // FIXME: csc doesn't report any error if you try to use `ref' or
4915 // `out' in a delegate creation expression.
4916 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4917 if (Expr == null)
4918 return false;
4920 return true;
4923 public bool Resolve (EmitContext ec, Location loc)
4925 if (ArgType == AType.Ref) {
4926 Expr = Expr.Resolve (ec);
4927 if (Expr == null)
4928 return false;
4930 if (!ec.IsConstructor) {
4931 FieldExpr fe = Expr as FieldExpr;
4932 if (fe != null && fe.FieldInfo.IsInitOnly) {
4933 if (fe.FieldInfo.IsStatic)
4934 Report.Error (199, loc, "A static readonly field cannot be passed ref or out (except in a static constructor)");
4935 else
4936 Report.Error (192, loc, "A readonly field cannot be passed ref or out (except in a constructor)");
4937 return false;
4940 Expr = Expr.ResolveLValue (ec, Expr);
4941 } else if (ArgType == AType.Out)
4942 Expr = Expr.ResolveLValue (ec, EmptyExpression.Null);
4943 else
4944 Expr = Expr.Resolve (ec);
4946 if (Expr == null)
4947 return false;
4949 if (ArgType == AType.Expression)
4950 return true;
4951 else {
4953 // Catch errors where fields of a MarshalByRefObject are passed as ref or out
4954 // This is only allowed for `this'
4956 FieldExpr fe = Expr as FieldExpr;
4957 if (fe != null && !fe.IsStatic){
4958 Expression instance = fe.InstanceExpression;
4960 if (instance.GetType () != typeof (This)){
4961 if (fe.InstanceExpression.Type.IsSubclassOf (TypeManager.mbr_type)){
4962 Report.SymbolRelatedToPreviousError (fe.InstanceExpression.Type);
4963 Report.Error (197, loc, "Cannot pass '{0}' as ref or out or take its address because it is a member of a marshal-by-reference class",
4964 fe.Name);
4965 return false;
4971 if (Expr.eclass != ExprClass.Variable){
4973 // We just probe to match the CSC output
4975 if (Expr.eclass == ExprClass.PropertyAccess ||
4976 Expr.eclass == ExprClass.IndexerAccess){
4977 Report.Error (
4978 206, loc,
4979 "A property or indexer can not be passed as an out or ref " +
4980 "parameter");
4981 } else {
4982 Report.Error (
4983 1510, loc,
4984 "An lvalue is required as an argument to out or ref");
4986 return false;
4989 return true;
4992 public void Emit (EmitContext ec)
4995 // Ref and Out parameters need to have their addresses taken.
4997 // ParameterReferences might already be references, so we want
4998 // to pass just the value
5000 if (ArgType == AType.Ref || ArgType == AType.Out){
5001 AddressOp mode = AddressOp.Store;
5003 if (ArgType == AType.Ref)
5004 mode |= AddressOp.Load;
5006 if (Expr is ParameterReference){
5007 ParameterReference pr = (ParameterReference) Expr;
5009 if (pr.IsRef)
5010 pr.EmitLoad (ec);
5011 else {
5013 pr.AddressOf (ec, mode);
5015 } else {
5016 if (Expr is IMemoryLocation)
5017 ((IMemoryLocation) Expr).AddressOf (ec, mode);
5018 else {
5019 Report.Error (
5020 1510, Expr.Location,
5021 "An lvalue is required as an argument to out or ref");
5022 return;
5025 } else
5026 Expr.Emit (ec);
5030 /// <summary>
5031 /// Invocation of methods or delegates.
5032 /// </summary>
5033 public class Invocation : ExpressionStatement {
5034 public readonly ArrayList Arguments;
5036 public Expression expr;
5037 MethodBase method = null;
5039 static Hashtable method_parameter_cache;
5041 static Invocation ()
5043 method_parameter_cache = new PtrHashtable ();
5047 // arguments is an ArrayList, but we do not want to typecast,
5048 // as it might be null.
5050 // FIXME: only allow expr to be a method invocation or a
5051 // delegate invocation (7.5.5)
5053 public Invocation (Expression expr, ArrayList arguments, Location l)
5055 this.expr = expr;
5056 Arguments = arguments;
5057 loc = l;
5060 public Expression Expr {
5061 get {
5062 return expr;
5066 /// <summary>
5067 /// Returns the Parameters (a ParameterData interface) for the
5068 /// Method `mb'
5069 /// </summary>
5070 public static ParameterData GetParameterData (MethodBase mb)
5072 object pd = method_parameter_cache [mb];
5073 object ip;
5075 if (pd != null)
5076 return (ParameterData) pd;
5078 ip = TypeManager.LookupParametersByBuilder (mb);
5079 if (ip != null){
5080 method_parameter_cache [mb] = ip;
5082 return (ParameterData) ip;
5083 } else {
5084 ReflectionParameters rp = new ReflectionParameters (mb);
5085 method_parameter_cache [mb] = rp;
5087 return (ParameterData) rp;
5091 /// <summary>
5092 /// Determines "better conversion" as specified in 7.4.2.3
5094 /// Returns : p if a->p is better,
5095 /// q if a->q is better,
5096 /// null if neither is better
5097 /// </summary>
5098 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q, Location loc)
5100 Type argument_type = TypeManager.TypeToCoreType (a.Type);
5101 Expression argument_expr = a.Expr;
5103 // p = TypeManager.TypeToCoreType (p);
5104 // q = TypeManager.TypeToCoreType (q);
5106 if (argument_type == null)
5107 throw new Exception ("Expression of type " + a.Expr +
5108 " does not resolve its type");
5110 if (p == null || q == null)
5111 throw new InternalErrorException ("BetterConversion Got a null conversion");
5113 if (p == q)
5114 return null;
5116 if (argument_expr is NullLiteral) {
5118 // If the argument is null and one of the types to compare is 'object' and
5119 // the other is a reference type, we prefer the other.
5121 // This follows from the usual rules:
5122 // * There is an implicit conversion from 'null' to type 'object'
5123 // * There is an implicit conversion from 'null' to any reference type
5124 // * There is an implicit conversion from any reference type to type 'object'
5125 // * There is no implicit conversion from type 'object' to other reference types
5126 // => Conversion of 'null' to a reference type is better than conversion to 'object'
5128 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
5129 // null type. I think it used to be 'object' and thus needed a special
5130 // case to avoid the immediately following two checks.
5132 if (!p.IsValueType && q == TypeManager.object_type)
5133 return p;
5134 if (!q.IsValueType && p == TypeManager.object_type)
5135 return q;
5138 if (argument_type == p)
5139 return p;
5141 if (argument_type == q)
5142 return q;
5144 Expression p_tmp = new EmptyExpression (p);
5145 Expression q_tmp = new EmptyExpression (q);
5147 bool p_to_q = Convert.WideningConversionExists (ec, p_tmp, q);
5148 bool q_to_p = Convert.WideningConversionExists (ec, q_tmp, p);
5150 if (p_to_q && !q_to_p)
5151 return p;
5153 if (q_to_p && !p_to_q)
5154 return q;
5156 if (p == TypeManager.sbyte_type)
5157 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
5158 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
5159 return p;
5160 if (q == TypeManager.sbyte_type)
5161 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
5162 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
5163 return q;
5165 if (p == TypeManager.short_type)
5166 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
5167 q == TypeManager.uint64_type)
5168 return p;
5170 if (q == TypeManager.short_type)
5171 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
5172 p == TypeManager.uint64_type)
5173 return q;
5175 if (p == TypeManager.int32_type)
5176 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
5177 return p;
5179 if (q == TypeManager.int32_type)
5180 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
5181 return q;
5183 if (p == TypeManager.int64_type)
5184 if (q == TypeManager.uint64_type)
5185 return p;
5186 if (q == TypeManager.int64_type)
5187 if (p == TypeManager.uint64_type)
5188 return q;
5190 return null;
5193 /// <summary>
5194 /// Determines "Better function" between candidate
5195 /// and the current best match
5196 /// </summary>
5197 /// <remarks>
5198 /// Returns a boolean indicating :
5199 /// false if candidate ain't better
5200 /// true if candidate is better than the current best match
5201 /// </remarks>
5202 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
5203 MethodBase candidate, bool candidate_params,
5204 MethodBase best, bool best_params, Location loc)
5206 ParameterData candidate_pd = GetParameterData (candidate);
5207 ParameterData best_pd = GetParameterData (best);
5209 int cand_count = candidate_pd.Count;
5212 // If there is no best method, than this one
5213 // is better, however, if we already found a
5214 // best method, we cant tell. This happens
5215 // if we have:
5217 // interface IFoo {
5218 // void DoIt ();
5219 // }
5221 // interface IBar {
5222 // void DoIt ();
5223 // }
5225 // interface IFooBar : IFoo, IBar {}
5227 // We cant tell if IFoo.DoIt is better than IBar.DoIt
5229 // However, we have to consider that
5230 // Trim (); is better than Trim (params char[] chars);
5232 if (cand_count == 0 && argument_count == 0)
5233 return !candidate_params && best_params;
5235 if ((candidate_pd.ParameterModifier (cand_count - 1) != Parameter.Modifier.PARAMS) &&
5236 (candidate_pd.ParameterModifier (cand_count - 1) != Parameter.Modifier.ARGLIST))
5237 if (cand_count != argument_count)
5238 return false;
5240 bool better_at_least_one = false;
5241 bool is_equal = true;
5243 for (int j = 0; j < argument_count; ++j) {
5244 Argument a = (Argument) args [j];
5246 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
5247 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
5249 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
5250 if (candidate_params)
5251 ct = TypeManager.GetElementType (ct);
5253 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
5254 if (best_params)
5255 bt = TypeManager.GetElementType (bt);
5257 if (!ct.Equals (bt))
5258 is_equal = false;
5260 Type better = BetterConversion (ec, a, ct, bt, loc);
5261 // for each argument, the conversion to 'ct' should be no worse than
5262 // the conversion to 'bt'.
5263 if (better == bt)
5264 return false;
5266 // for at least one argument, the conversion to 'ct' should be better than
5267 // the conversion to 'bt'.
5268 if (better == ct)
5269 better_at_least_one = true;
5273 // If a method (in the normal form) with the
5274 // same signature as the expanded form of the
5275 // current best params method already exists,
5276 // the expanded form is not applicable so we
5277 // force it to select the candidate
5279 if (!candidate_params && best_params && cand_count == argument_count)
5280 return true;
5283 // If two methods have equal parameter types, but
5284 // only one of them is generic, the non-generic one wins.
5286 if (is_equal) {
5287 if (TypeManager.IsGenericMethod (best) && !TypeManager.IsGenericMethod (candidate))
5288 return true;
5289 else if (!TypeManager.IsGenericMethod (best) && TypeManager.IsGenericMethod (candidate))
5290 return false;
5293 return better_at_least_one;
5296 public static string FullMethodDesc (MethodBase mb)
5298 string ret_type = "";
5300 if (mb == null)
5301 return "";
5303 if (mb is MethodInfo)
5304 ret_type = TypeManager.CSharpName (((MethodInfo) mb).ReturnType);
5306 StringBuilder sb = new StringBuilder (ret_type);
5307 sb.Append (" ");
5308 sb.Append (mb.ReflectedType.ToString ());
5309 sb.Append (".");
5310 sb.Append (mb.Name);
5312 ParameterData pd = GetParameterData (mb);
5314 int count = pd.Count;
5315 sb.Append (" (");
5317 for (int i = count; i > 0; ) {
5318 i--;
5320 sb.Append (pd.ParameterDesc (count - i - 1));
5321 if (i != 0)
5322 sb.Append (", ");
5325 sb.Append (")");
5326 return sb.ToString ();
5329 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
5331 MemberInfo [] miset;
5332 MethodGroupExpr union;
5334 if (mg1 == null) {
5335 if (mg2 == null)
5336 return null;
5337 return (MethodGroupExpr) mg2;
5338 } else {
5339 if (mg2 == null)
5340 return (MethodGroupExpr) mg1;
5343 MethodGroupExpr left_set = null, right_set = null;
5344 int length1 = 0, length2 = 0;
5346 left_set = (MethodGroupExpr) mg1;
5347 length1 = left_set.Methods.Length;
5349 right_set = (MethodGroupExpr) mg2;
5350 length2 = right_set.Methods.Length;
5352 ArrayList common = new ArrayList ();
5354 foreach (MethodBase r in right_set.Methods){
5355 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
5356 common.Add (r);
5359 miset = new MemberInfo [length1 + length2 - common.Count];
5360 left_set.Methods.CopyTo (miset, 0);
5362 int k = length1;
5364 foreach (MethodBase r in right_set.Methods) {
5365 if (!common.Contains (r))
5366 miset [k++] = r;
5369 union = new MethodGroupExpr (miset, loc);
5371 return union;
5374 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
5375 ArrayList arguments, int arg_count,
5376 ref MethodBase candidate)
5378 return IsParamsMethodApplicable (
5379 ec, me, arguments, arg_count, false, ref candidate) ||
5380 IsParamsMethodApplicable (
5381 ec, me, arguments, arg_count, true, ref candidate);
5386 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
5387 ArrayList arguments, int arg_count,
5388 bool do_varargs, ref MethodBase candidate)
5390 if (!me.HasTypeArguments &&
5391 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
5392 return false;
5394 return IsParamsMethodApplicable (
5395 ec, arguments, arg_count, candidate, do_varargs);
5398 /// <summary>
5399 /// Determines if the candidate method, if a params method, is applicable
5400 /// in its expanded form to the given set of arguments
5401 /// </summary>
5402 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
5403 int arg_count, MethodBase candidate,
5404 bool do_varargs)
5406 ParameterData pd = GetParameterData (candidate);
5408 int pd_count = pd.Count;
5410 if (pd_count == 0)
5411 return false;
5413 int count = pd_count - 1;
5414 if (do_varargs) {
5415 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
5416 return false;
5417 if (pd_count != arg_count)
5418 return false;
5419 } else {
5420 if (pd.ParameterModifier (count) != Parameter.Modifier.PARAMS)
5421 return false;
5424 if (count > arg_count)
5425 return false;
5427 if (pd_count == 1 && arg_count == 0)
5428 return true;
5431 // If we have come this far, the case which
5432 // remains is when the number of parameters is
5433 // less than or equal to the argument count.
5435 for (int i = 0; i < count; ++i) {
5437 Argument a = (Argument) arguments [i];
5439 Parameter.Modifier a_mod = a.GetParameterModifier () &
5440 (unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF)));
5441 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
5442 (unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF)));
5444 if (a_mod == p_mod) {
5446 if (a_mod == Parameter.Modifier.NONE)
5447 if (!Convert.WideningConversionExists (ec,
5448 a.Expr,
5449 pd.ParameterType (i)))
5450 return false;
5452 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
5453 Type pt = pd.ParameterType (i);
5455 if (!pt.IsByRef)
5456 pt = TypeManager.GetReferenceType (pt);
5458 if (pt != a.Type)
5459 return false;
5461 } else
5462 return false;
5466 if (do_varargs) {
5467 Argument a = (Argument) arguments [count];
5468 if (!(a.Expr is Arglist))
5469 return false;
5471 return true;
5474 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
5476 for (int i = pd_count - 1; i < arg_count; i++) {
5477 Argument a = (Argument) arguments [i];
5479 if (!Convert.WideningConversionExists (ec, a.Expr, element_type))
5480 return false;
5483 return true;
5486 static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
5487 ArrayList arguments, int arg_count,
5488 ref MethodBase candidate)
5490 if (!me.HasTypeArguments &&
5491 !TypeManager.InferTypeArguments (ec, arguments, ref candidate))
5492 return false;
5494 return IsApplicable (ec, arguments, arg_count, candidate);
5497 /// <summary>
5498 /// Determines if the candidate method is applicable (section 14.4.2.1)
5499 /// to the given set of arguments
5500 /// </summary>
5501 static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
5502 MethodBase candidate)
5504 ParameterData pd = GetParameterData (candidate);
5506 if (arg_count != pd.Count)
5507 return false;
5509 for (int i = arg_count; i > 0; ) {
5510 i--;
5512 Argument a = (Argument) arguments [i];
5514 Parameter.Modifier a_mod = a.GetParameterModifier () &
5515 unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF));
5516 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
5517 unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF));
5520 if (a_mod == p_mod ||
5521 (a_mod == Parameter.Modifier.NONE && p_mod == Parameter.Modifier.PARAMS)) {
5522 if (a_mod == Parameter.Modifier.NONE) {
5523 if (!Convert.WideningConversionExists (ec,
5524 a.Expr,
5525 pd.ParameterType (i)))
5526 return false;
5529 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
5530 Type pt = pd.ParameterType (i);
5532 if (!pt.IsByRef)
5533 pt = TypeManager.GetReferenceType (pt);
5535 if (pt != a.Type)
5536 return false;
5538 } else
5539 return false;
5542 return true;
5545 static private bool IsAncestralType (Type first_type, Type second_type)
5547 return first_type != second_type &&
5548 (second_type.IsSubclassOf (first_type) ||
5549 TypeManager.ImplementsInterface (second_type, first_type));
5552 /// <summary>
5553 /// Find the Applicable Function Members (7.4.2.1)
5555 /// me: Method Group expression with the members to select.
5556 /// it might contain constructors or methods (or anything
5557 /// that maps to a method).
5559 /// Arguments: ArrayList containing resolved Argument objects.
5561 /// loc: The location if we want an error to be reported, or a Null
5562 /// location for "probing" purposes.
5564 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
5565 /// that is the best match of me on Arguments.
5567 /// </summary>
5568 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
5569 ArrayList Arguments, bool may_fail,
5570 Location loc)
5572 MethodBase method = null;
5573 bool method_params = false;
5574 Type applicable_type = null;
5575 int arg_count = 0;
5576 ArrayList candidates = new ArrayList ();
5579 // Used to keep a map between the candidate
5580 // and whether it is being considered in its
5581 // normal or expanded form
5583 // false is normal form, true is expanded form
5585 Hashtable candidate_to_form = null;
5587 if (Arguments != null)
5588 arg_count = Arguments.Count;
5590 if ((me.Name == "Invoke") &&
5591 TypeManager.IsDelegateType (me.DeclaringType)) {
5592 Error_InvokeOnDelegate (loc);
5593 return null;
5596 MethodBase[] methods = me.Methods;
5599 // First we construct the set of applicable methods
5601 bool is_sorted = true;
5602 for (int i = 0; i < methods.Length; i++){
5603 Type decl_type = methods [i].DeclaringType;
5606 // If we have already found an applicable method
5607 // we eliminate all base types (Section 14.5.5.1)
5609 if ((applicable_type != null) &&
5610 IsAncestralType (decl_type, applicable_type))
5611 continue;
5614 // Check if candidate is applicable (section 14.4.2.1)
5615 // Is candidate applicable in normal form?
5617 bool is_applicable = IsApplicable (
5618 ec, me, Arguments, arg_count, ref methods [i]);
5620 if (!is_applicable &&
5621 (IsParamsMethodApplicable (
5622 ec, me, Arguments, arg_count, ref methods [i]))) {
5623 MethodBase candidate = methods [i];
5624 if (candidate_to_form == null)
5625 candidate_to_form = new PtrHashtable ();
5626 candidate_to_form [candidate] = candidate;
5627 // Candidate is applicable in expanded form
5628 is_applicable = true;
5631 if (!is_applicable)
5632 continue;
5634 candidates.Add (methods [i]);
5636 if (applicable_type == null)
5637 applicable_type = decl_type;
5638 else if (applicable_type != decl_type) {
5639 is_sorted = false;
5640 if (IsAncestralType (applicable_type, decl_type))
5641 applicable_type = decl_type;
5645 int candidate_top = candidates.Count;
5647 if (candidate_top == 0) {
5649 // Okay so we have failed to find anything so we
5650 // return by providing info about the closest match
5652 for (int i = 0; i < methods.Length; ++i) {
5653 MethodBase c = (MethodBase) methods [i];
5654 ParameterData pd = GetParameterData (c);
5656 if (pd.Count != arg_count)
5657 continue;
5659 if (!TypeManager.InferTypeArguments (ec, Arguments, ref c))
5660 continue;
5662 VerifyArgumentsCompat (ec, Arguments, arg_count,
5663 c, false, null, may_fail, loc);
5664 break;
5667 if (!may_fail) {
5668 string report_name = me.Name;
5669 if (report_name == ".ctor")
5670 report_name = me.DeclaringType.ToString ();
5672 for (int i = 0; i < methods.Length; ++i) {
5673 MethodBase c = methods [i];
5674 ParameterData pd = GetParameterData (c);
5676 if (pd.Count != arg_count)
5677 continue;
5679 if (TypeManager.InferTypeArguments (ec, Arguments, ref c))
5680 continue;
5682 Report.Error (
5683 411, loc, "The type arguments for " +
5684 "method `{0}' cannot be infered from " +
5685 "the usage. Try specifying the type " +
5686 "arguments explicitly.", report_name);
5687 return null;
5690 Error_WrongNumArguments (
5691 loc, report_name, arg_count);
5692 return null;
5695 return null;
5698 if (!is_sorted) {
5700 // At this point, applicable_type is _one_ of the most derived types
5701 // in the set of types containing the methods in this MethodGroup.
5702 // Filter the candidates so that they only contain methods from the
5703 // most derived types.
5706 int finalized = 0; // Number of finalized candidates
5708 do {
5709 // Invariant: applicable_type is a most derived type
5711 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
5712 // eliminating all it's base types. At the same time, we'll also move
5713 // every unrelated type to the end of the array, and pick the next
5714 // 'applicable_type'.
5716 Type next_applicable_type = null;
5717 int j = finalized; // where to put the next finalized candidate
5718 int k = finalized; // where to put the next undiscarded candidate
5719 for (int i = finalized; i < candidate_top; ++i) {
5720 Type decl_type = ((MethodBase) candidates[i]).DeclaringType;
5722 if (decl_type == applicable_type) {
5723 candidates[k++] = candidates[j];
5724 candidates[j++] = candidates[i];
5725 continue;
5728 if (IsAncestralType (decl_type, applicable_type))
5729 continue;
5731 if (next_applicable_type != null &&
5732 IsAncestralType (decl_type, next_applicable_type))
5733 continue;
5735 candidates[k++] = candidates[i];
5737 if (next_applicable_type == null ||
5738 IsAncestralType (next_applicable_type, decl_type))
5739 next_applicable_type = decl_type;
5742 applicable_type = next_applicable_type;
5743 finalized = j;
5744 candidate_top = k;
5745 } while (applicable_type != null);
5749 // Now we actually find the best method
5752 method = (MethodBase) candidates[0];
5753 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
5754 for (int ix = 1; ix < candidate_top; ix++){
5755 MethodBase candidate = (MethodBase) candidates [ix];
5756 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
5758 if (BetterFunction (ec, Arguments, arg_count,
5759 candidate, cand_params,
5760 method, method_params, loc)) {
5761 method = candidate;
5762 method_params = cand_params;
5767 // Now check that there are no ambiguities i.e the selected method
5768 // should be better than all the others
5770 bool ambiguous = false;
5771 for (int ix = 0; ix < candidate_top; ix++){
5772 MethodBase candidate = (MethodBase) candidates [ix];
5774 if (candidate == method)
5775 continue;
5777 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
5778 if (!BetterFunction (ec, Arguments, arg_count,
5779 method, method_params,
5780 candidate, cand_params,
5781 loc)) {
5782 Report.SymbolRelatedToPreviousError (candidate);
5783 ambiguous = true;
5787 if (ambiguous) {
5788 Report.SymbolRelatedToPreviousError (method);
5789 Report.Error (121, loc, "Ambiguous call when selecting function due to implicit casts");
5790 return null;
5794 // And now check if the arguments are all
5795 // compatible, perform conversions if
5796 // necessary etc. and return if everything is
5797 // all right
5799 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
5800 method_params, null, may_fail, loc))
5801 return null;
5803 return method;
5806 static void Error_WrongNumArguments (Location loc, String name, int arg_count)
5808 Report.Error (1501, loc,
5809 "No overload for method `" + name + "' takes `" +
5810 arg_count + "' arguments");
5813 static void Error_InvokeOnDelegate (Location loc)
5815 Report.Error (1533, loc,
5816 "Invoke cannot be called directly on a delegate");
5819 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
5820 Type delegate_type, string arg_sig, string par_desc)
5822 if (delegate_type == null)
5823 Report.Error (1502, loc,
5824 "The best overloaded match for method '" +
5825 FullMethodDesc (method) +
5826 "' has some invalid arguments");
5827 else
5828 Report.Error (1594, loc,
5829 "Delegate '" + delegate_type.ToString () +
5830 "' has some invalid arguments.");
5831 Report.Error (1503, loc,
5832 String.Format ("Argument {0}: Cannot convert from '{1}' to '{2}'",
5833 idx, arg_sig, par_desc));
5836 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
5837 int arg_count, MethodBase method,
5838 bool chose_params_expanded,
5839 Type delegate_type, bool may_fail,
5840 Location loc)
5842 ParameterData pd = GetParameterData (method);
5843 int pd_count = pd.Count;
5845 for (int j = 0; j < arg_count; j++) {
5846 Argument a = (Argument) Arguments [j];
5847 Expression a_expr = a.Expr;
5848 Type parameter_type = pd.ParameterType (j);
5849 Parameter.Modifier pm = pd.ParameterModifier (j);
5851 if (pm == Parameter.Modifier.PARAMS){
5852 if ((pm & ~Parameter.Modifier.PARAMS) != a.GetParameterModifier ()) {
5853 if (!may_fail)
5854 Error_InvalidArguments (
5855 loc, j, method, delegate_type,
5856 Argument.FullDesc (a), pd.ParameterDesc (j));
5857 return false;
5860 if (chose_params_expanded)
5861 parameter_type = TypeManager.GetElementType (parameter_type);
5862 } else if (pm == Parameter.Modifier.ARGLIST){
5863 continue;
5864 } else {
5866 // Check modifiers
5868 if (pd.ParameterModifier (j) != a.GetParameterModifier ()){
5869 if (!may_fail)
5870 Error_InvalidArguments (
5871 loc, j, method, delegate_type,
5872 Argument.FullDesc (a), pd.ParameterDesc (j));
5873 return false;
5878 // Check Type
5880 if (!TypeManager.IsEqual (a.Type, parameter_type)){
5881 Expression conv;
5883 conv = Convert.WideningConversion (ec, a_expr, parameter_type, loc);
5885 if (conv == null) {
5886 if (!may_fail)
5887 Error_InvalidArguments (
5888 loc, j, method, delegate_type,
5889 Argument.FullDesc (a), pd.ParameterDesc (j));
5890 return false;
5894 // Update the argument with the implicit conversion
5896 if (a_expr != conv)
5897 a.Expr = conv;
5900 if (parameter_type.IsPointer){
5901 if (!ec.InUnsafe){
5902 UnsafeError (loc);
5903 return false;
5907 Parameter.Modifier a_mod = a.GetParameterModifier () &
5908 unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF));
5909 Parameter.Modifier p_mod = pd.ParameterModifier (j) &
5910 unchecked (~(Parameter.Modifier.OUT | Parameter.Modifier.REF));
5912 if (a_mod != p_mod &&
5913 pd.ParameterModifier (pd_count - 1) != Parameter.Modifier.PARAMS) {
5914 if (!may_fail) {
5915 Report.Error (1502, loc,
5916 "The best overloaded match for method '" + FullMethodDesc (method)+
5917 "' has some invalid arguments");
5918 Report.Error (1503, loc,
5919 "Argument " + (j+1) +
5920 ": Cannot convert from '" + Argument.FullDesc (a)
5921 + "' to '" + pd.ParameterDesc (j) + "'");
5924 return false;
5928 return true;
5931 public override Expression DoResolve (EmitContext ec)
5934 // First, resolve the expression that is used to
5935 // trigger the invocation
5937 if (expr is ConstructedType)
5938 expr = ((ConstructedType) expr).GetSimpleName (ec);
5940 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
5941 if (expr == null)
5942 return null;
5944 if (!(expr is MethodGroupExpr)) {
5945 Type expr_type = expr.Type;
5947 if (expr_type != null){
5948 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
5949 if (IsDelegate)
5950 return (new DelegateInvocation (
5951 this.expr, Arguments, loc)).Resolve (ec);
5955 if (!(expr is MethodGroupExpr)){
5956 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
5957 return null;
5961 // Next, evaluate all the expressions in the argument list
5963 if (Arguments != null){
5964 foreach (Argument a in Arguments){
5965 if (!a.Resolve (ec, loc))
5966 return null;
5970 MethodGroupExpr mg = (MethodGroupExpr) expr;
5971 method = OverloadResolve (ec, mg, Arguments, false, loc);
5973 if (method == null)
5974 return null;
5976 MethodInfo mi = method as MethodInfo;
5977 if (mi != null) {
5978 type = TypeManager.TypeToCoreType (mi.ReturnType);
5979 if (!mi.IsStatic && !mg.IsExplicitImpl && (mg.InstanceExpression == null)) {
5980 SimpleName.Error_ObjectRefRequired (ec, loc, mi.Name);
5981 return null;
5984 Expression iexpr = mg.InstanceExpression;
5985 if (mi.IsStatic && (iexpr != null) && !(iexpr is This)) {
5986 if (mg.IdenticalTypeName)
5987 mg.InstanceExpression = null;
5988 else {
5989 MemberAccess.error176 (loc, mi.Name);
5990 return null;
5995 if (type.IsPointer){
5996 if (!ec.InUnsafe){
5997 UnsafeError (loc);
5998 return null;
6003 // Only base will allow this invocation to happen.
6005 if (mg.IsBase && method.IsAbstract){
6006 Report.Error (205, loc, "Cannot call an abstract base member: " +
6007 FullMethodDesc (method));
6008 return null;
6011 if (method.Name == "Finalize" && Arguments == null) {
6012 if (mg.IsBase)
6013 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
6014 else
6015 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
6016 return null;
6019 if ((method.Attributes & MethodAttributes.SpecialName) != 0){
6020 if (TypeManager.LookupDeclSpace (method.DeclaringType) != null || TypeManager.IsSpecialMethod (method)) {
6021 Report.Error (571, loc, TypeManager.CSharpSignature (method) + ": can not call operator or accessor");
6022 return null;
6026 if (mg.InstanceExpression != null)
6027 mg.InstanceExpression.CheckMarshallByRefAccess (ec.ContainerType);
6029 eclass = ExprClass.Value;
6030 return this;
6033 // <summary>
6034 // Emits the list of arguments as an array
6035 // </summary>
6036 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
6038 ILGenerator ig = ec.ig;
6039 int count = arguments.Count - idx;
6040 Argument a = (Argument) arguments [idx];
6041 Type t = a.Expr.Type;
6043 IntConstant.EmitInt (ig, count);
6044 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
6046 int top = arguments.Count;
6047 for (int j = idx; j < top; j++){
6048 a = (Argument) arguments [j];
6050 ig.Emit (OpCodes.Dup);
6051 IntConstant.EmitInt (ig, j - idx);
6053 bool is_stobj, has_type_arg;
6054 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
6055 if (is_stobj)
6056 ig.Emit (OpCodes.Ldelema, t);
6058 a.Emit (ec);
6060 if (has_type_arg)
6061 ig.Emit (op, t);
6062 else
6063 ig.Emit (op);
6067 /// <summary>
6068 /// Emits a list of resolved Arguments that are in the arguments
6069 /// ArrayList.
6070 ///
6071 /// The MethodBase argument might be null if the
6072 /// emission of the arguments is known not to contain
6073 /// a `params' field (for example in constructors or other routines
6074 /// that keep their arguments in this structure)
6075 ///
6076 /// if `dup_args' is true, a copy of the arguments will be left
6077 /// on the stack. If `dup_args' is true, you can specify `this_arg'
6078 /// which will be duplicated before any other args. Only EmitCall
6079 /// should be using this interface.
6080 /// </summary>
6081 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
6083 ParameterData pd;
6084 if (mb != null)
6085 pd = GetParameterData (mb);
6086 else
6087 pd = null;
6089 LocalTemporary [] temps = null;
6091 if (dup_args)
6092 temps = new LocalTemporary [arguments.Count];
6095 // If we are calling a params method with no arguments, special case it
6097 if (arguments == null){
6098 if (pd != null && pd.Count > 0 &&
6099 pd.ParameterModifier (0) == Parameter.Modifier.PARAMS){
6100 ILGenerator ig = ec.ig;
6102 IntConstant.EmitInt (ig, 0);
6103 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (0)));
6106 return;
6109 int top = arguments.Count;
6111 for (int i = 0; i < top; i++){
6112 Argument a = (Argument) arguments [i];
6114 if (pd != null){
6115 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
6117 // Special case if we are passing the same data as the
6118 // params argument, do not put it in an array.
6120 if (pd.ParameterType (i) == a.Type)
6121 a.Emit (ec);
6122 else
6123 EmitParams (ec, i, arguments);
6124 return;
6128 a.Emit (ec);
6129 if (dup_args) {
6130 ec.ig.Emit (OpCodes.Dup);
6131 (temps [i] = new LocalTemporary (ec, a.Type)).Store (ec);
6135 if (dup_args) {
6136 if (this_arg != null)
6137 this_arg.Emit (ec);
6139 for (int i = 0; i < top; i ++)
6140 temps [i].Emit (ec);
6143 if (pd != null && pd.Count > top &&
6144 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
6145 ILGenerator ig = ec.ig;
6147 IntConstant.EmitInt (ig, 0);
6148 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
6152 static Type[] GetVarargsTypes (EmitContext ec, MethodBase mb,
6153 ArrayList arguments)
6155 ParameterData pd = GetParameterData (mb);
6157 if (arguments == null)
6158 return new Type [0];
6160 Argument a = (Argument) arguments [pd.Count - 1];
6161 Arglist list = (Arglist) a.Expr;
6163 return list.ArgumentTypes;
6166 /// <summary>
6167 /// This checks the ConditionalAttribute on the method
6168 /// </summary>
6169 static bool IsMethodExcluded (MethodBase method, EmitContext ec)
6171 if (method.IsConstructor)
6172 return false;
6174 IMethodData md = TypeManager.GetMethod (method);
6175 if (md != null)
6176 return md.IsExcluded (ec);
6178 // For some methods (generated by delegate class) GetMethod returns null
6179 // because they are not included in builder_to_method table
6180 if (method.DeclaringType is TypeBuilder)
6181 return false;
6183 return AttributeTester.IsConditionalMethodExcluded (method);
6186 /// <remarks>
6187 /// is_base tells whether we want to force the use of the `call'
6188 /// opcode instead of using callvirt. Call is required to call
6189 /// a specific method, while callvirt will always use the most
6190 /// recent method in the vtable.
6192 /// is_static tells whether this is an invocation on a static method
6194 /// instance_expr is an expression that represents the instance
6195 /// it must be non-null if is_static is false.
6197 /// method is the method to invoke.
6199 /// Arguments is the list of arguments to pass to the method or constructor.
6200 /// </remarks>
6201 public static void EmitCall (EmitContext ec, bool is_base,
6202 bool is_static, Expression instance_expr,
6203 MethodBase method, ArrayList Arguments, Location loc)
6205 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
6208 // `dup_args' leaves an extra copy of the arguments on the stack
6209 // `omit_args' does not leave any arguments at all.
6210 // So, basically, you could make one call with `dup_args' set to true,
6211 // and then another with `omit_args' set to true, and the two calls
6212 // would have the same set of arguments. However, each argument would
6213 // only have been evaluated once.
6214 public static void EmitCall (EmitContext ec, bool is_base,
6215 bool is_static, Expression instance_expr,
6216 MethodBase method, ArrayList Arguments, Location loc,
6217 bool dup_args, bool omit_args)
6219 ILGenerator ig = ec.ig;
6220 bool struct_call = false;
6221 bool this_call = false;
6222 LocalTemporary this_arg = null;
6224 Type decl_type = method.DeclaringType;
6226 if (!RootContext.StdLib) {
6227 // Replace any calls to the system's System.Array type with calls to
6228 // the newly created one.
6229 if (method == TypeManager.system_int_array_get_length)
6230 method = TypeManager.int_array_get_length;
6231 else if (method == TypeManager.system_int_array_get_rank)
6232 method = TypeManager.int_array_get_rank;
6233 else if (method == TypeManager.system_object_array_clone)
6234 method = TypeManager.object_array_clone;
6235 else if (method == TypeManager.system_int_array_get_length_int)
6236 method = TypeManager.int_array_get_length_int;
6237 else if (method == TypeManager.system_int_array_get_lower_bound_int)
6238 method = TypeManager.int_array_get_lower_bound_int;
6239 else if (method == TypeManager.system_int_array_get_upper_bound_int)
6240 method = TypeManager.int_array_get_upper_bound_int;
6241 else if (method == TypeManager.system_void_array_copyto_array_int)
6242 method = TypeManager.void_array_copyto_array_int;
6245 if (ec.TestObsoleteMethodUsage) {
6247 // This checks ObsoleteAttribute on the method and on the declaring type
6249 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
6250 if (oa != null)
6251 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
6253 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
6254 if (oa != null) {
6255 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
6259 if (IsMethodExcluded (method, ec))
6260 return;
6262 if (!is_static){
6263 this_call = instance_expr == null;
6264 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
6265 struct_call = true;
6268 // If this is ourselves, push "this"
6270 if (!omit_args) {
6271 Type t = null;
6272 if (this_call) {
6273 ig.Emit (OpCodes.Ldarg_0);
6274 t = decl_type;
6275 } else {
6276 Type iexpr_type = instance_expr.Type;
6279 // Push the instance expression
6281 if (TypeManager.IsValueType (iexpr_type)) {
6283 // Special case: calls to a function declared in a
6284 // reference-type with a value-type argument need
6285 // to have their value boxed.
6286 if (decl_type.IsValueType ||
6287 iexpr_type.IsGenericParameter) {
6289 // If the expression implements IMemoryLocation, then
6290 // we can optimize and use AddressOf on the
6291 // return.
6293 // If not we have to use some temporary storage for
6294 // it.
6295 if (instance_expr is IMemoryLocation) {
6296 ((IMemoryLocation)instance_expr).
6297 AddressOf (ec, AddressOp.LoadStore);
6298 } else {
6299 LocalTemporary temp = new LocalTemporary (ec, iexpr_type);
6300 instance_expr.Emit (ec);
6301 temp.Store (ec);
6302 temp.AddressOf (ec, AddressOp.Load);
6305 // avoid the overhead of doing this all the time.
6306 if (dup_args)
6307 t = TypeManager.GetReferenceType (iexpr_type);
6308 } else {
6309 instance_expr.Emit (ec);
6310 ig.Emit (OpCodes.Box, instance_expr.Type);
6311 t = TypeManager.object_type;
6313 } else {
6314 instance_expr.Emit (ec);
6315 t = instance_expr.Type;
6319 if (dup_args) {
6320 this_arg = new LocalTemporary (ec, t);
6321 ig.Emit (OpCodes.Dup);
6322 this_arg.Store (ec);
6327 if (!omit_args)
6328 EmitArguments (ec, method, Arguments, dup_args, this_arg);
6330 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
6331 ig.Emit (OpCodes.Constrained, instance_expr.Type);
6333 OpCode call_op;
6334 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
6335 call_op = OpCodes.Call;
6336 else
6337 call_op = OpCodes.Callvirt;
6339 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
6340 Type[] varargs_types = GetVarargsTypes (ec, method, Arguments);
6341 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
6342 return;
6346 // If you have:
6347 // this.DoFoo ();
6348 // and DoFoo is not virtual, you can omit the callvirt,
6349 // because you don't need the null checking behavior.
6351 if (method is MethodInfo)
6352 ig.Emit (call_op, (MethodInfo) method);
6353 else
6354 ig.Emit (call_op, (ConstructorInfo) method);
6357 public override void Emit (EmitContext ec)
6359 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
6361 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
6364 public override void EmitStatement (EmitContext ec)
6366 Emit (ec);
6369 // Pop the return value if there is one
6371 if (method is MethodInfo){
6372 Type ret = ((MethodInfo)method).ReturnType;
6373 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
6374 ec.ig.Emit (OpCodes.Pop);
6379 public class InvocationOrCast : ExpressionStatement
6381 Expression expr;
6382 Expression argument;
6384 public InvocationOrCast (Expression expr, Expression argument, Location loc)
6386 this.expr = expr;
6387 this.argument = argument;
6388 this.loc = loc;
6391 public override Expression DoResolve (EmitContext ec)
6394 // First try to resolve it as a cast.
6396 TypeExpr te = expr.ResolveAsTypeStep (ec) as TypeExpr;
6397 if ((te != null) && (te.eclass == ExprClass.Type)) {
6398 Cast cast = new Cast (te, argument, loc);
6399 return cast.Resolve (ec);
6403 // This can either be a type or a delegate invocation.
6404 // Let's just resolve it and see what we'll get.
6406 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
6407 if (expr == null)
6408 return null;
6411 // Ok, so it's a Cast.
6413 if (expr.eclass == ExprClass.Type) {
6414 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
6415 return cast.Resolve (ec);
6419 // It's a delegate invocation.
6421 if (!TypeManager.IsDelegateType (expr.Type)) {
6422 Error (149, "Method name expected");
6423 return null;
6426 ArrayList args = new ArrayList ();
6427 args.Add (new Argument (argument, Argument.AType.Expression));
6428 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
6429 return invocation.Resolve (ec);
6432 void error201 ()
6434 Error (201, "Only assignment, call, increment, decrement and new object " +
6435 "expressions can be used as a statement");
6438 public override ExpressionStatement ResolveStatement (EmitContext ec)
6441 // First try to resolve it as a cast.
6443 TypeExpr te = expr.ResolveAsTypeStep (ec) as TypeExpr;
6444 if ((te != null) && (te.eclass == ExprClass.Type)) {
6445 error201 ();
6446 return null;
6450 // This can either be a type or a delegate invocation.
6451 // Let's just resolve it and see what we'll get.
6453 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
6454 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
6455 error201 ();
6456 return null;
6460 // It's a delegate invocation.
6462 if (!TypeManager.IsDelegateType (expr.Type)) {
6463 Error (149, "Method name expected");
6464 return null;
6467 ArrayList args = new ArrayList ();
6468 args.Add (new Argument (argument, Argument.AType.Expression));
6469 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
6470 return invocation.ResolveStatement (ec);
6473 public override void Emit (EmitContext ec)
6475 throw new Exception ("Cannot happen");
6478 public override void EmitStatement (EmitContext ec)
6480 throw new Exception ("Cannot happen");
6485 // This class is used to "disable" the code generation for the
6486 // temporary variable when initializing value types.
6488 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
6489 public void AddressOf (EmitContext ec, AddressOp Mode)
6491 // nothing
6495 /// <summary>
6496 /// Implements the new expression
6497 /// </summary>
6498 public class New : ExpressionStatement, IMemoryLocation {
6499 public readonly ArrayList Arguments;
6502 // During bootstrap, it contains the RequestedType,
6503 // but if `type' is not null, it *might* contain a NewDelegate
6504 // (because of field multi-initialization)
6506 public Expression RequestedType;
6508 MethodBase method = null;
6511 // If set, the new expression is for a value_target, and
6512 // we will not leave anything on the stack.
6514 Expression value_target;
6515 bool value_target_set = false;
6516 bool is_type_parameter = false;
6518 public New (Expression requested_type, ArrayList arguments, Location l)
6520 RequestedType = requested_type;
6521 Arguments = arguments;
6522 loc = l;
6525 public bool SetValueTypeVariable (Expression value)
6527 value_target = value;
6528 value_target_set = true;
6529 if (!(value_target is IMemoryLocation)){
6530 Error_UnexpectedKind ("variable", loc);
6531 return false;
6533 return true;
6537 // This function is used to disable the following code sequence for
6538 // value type initialization:
6540 // AddressOf (temporary)
6541 // Construct/Init
6542 // LoadTemporary
6544 // Instead the provide will have provided us with the address on the
6545 // stack to store the results.
6547 static Expression MyEmptyExpression;
6549 public void DisableTemporaryValueType ()
6551 if (MyEmptyExpression == null)
6552 MyEmptyExpression = new EmptyAddressOf ();
6555 // To enable this, look into:
6556 // test-34 and test-89 and self bootstrapping.
6558 // For instance, we can avoid a copy by using `newobj'
6559 // instead of Call + Push-temp on value types.
6560 // value_target = MyEmptyExpression;
6563 public override Expression DoResolve (EmitContext ec)
6566 // The New DoResolve might be called twice when initializing field
6567 // expressions (see EmitFieldInitializers, the call to
6568 // GetInitializerExpression will perform a resolve on the expression,
6569 // and later the assign will trigger another resolution
6571 // This leads to bugs (#37014)
6573 if (type != null){
6574 if (RequestedType is NewDelegate)
6575 return RequestedType;
6576 return this;
6579 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec);
6580 if (texpr == null)
6581 return null;
6583 type = texpr.Type;
6584 if (type == null)
6585 return null;
6587 CheckObsoleteAttribute (type);
6589 bool IsDelegate = TypeManager.IsDelegateType (type);
6591 if (IsDelegate){
6592 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
6593 if (RequestedType != null)
6594 if (!(RequestedType is DelegateCreation))
6595 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
6596 return RequestedType;
6599 if (type.IsGenericParameter) {
6600 if (!TypeManager.HasConstructorConstraint (type)) {
6601 Error (304, String.Format (
6602 "Cannot create an instance of the " +
6603 "variable type '{0}' because it " +
6604 "doesn't have the new() constraint",
6605 type));
6606 return null;
6609 if ((Arguments != null) && (Arguments.Count != 0)) {
6610 Error (417, String.Format (
6611 "`{0}': cannot provide arguments " +
6612 "when creating an instance of a " +
6613 "variable type.", type));
6614 return null;
6617 is_type_parameter = true;
6618 eclass = ExprClass.Value;
6619 return this;
6622 if (type.IsInterface || type.IsAbstract){
6623 Error (144, "It is not possible to create instances of interfaces or abstract classes");
6624 return null;
6627 if (type.IsAbstract && type.IsSealed) {
6628 Report.Error (712, loc, "Cannot create an instance of the static class '{0}'", TypeManager.CSharpName (type));
6629 return null;
6632 bool is_struct = type.IsValueType;
6633 eclass = ExprClass.Value;
6636 // SRE returns a match for .ctor () on structs (the object constructor),
6637 // so we have to manually ignore it.
6639 if (is_struct && Arguments == null)
6640 return this;
6642 Expression ml;
6643 ml = MemberLookupFinal (ec, type, type, ".ctor",
6644 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
6645 MemberTypes.Constructor,
6646 AllBindingFlags | BindingFlags.DeclaredOnly, loc);
6648 if (ml == null)
6649 return null;
6651 if (! (ml is MethodGroupExpr)){
6652 if (!is_struct){
6653 ml.Error_UnexpectedKind ("method group", loc);
6654 return null;
6658 if (ml != null) {
6659 if (Arguments != null){
6660 foreach (Argument a in Arguments){
6661 if (!a.Resolve (ec, loc))
6662 return null;
6666 method = Invocation.OverloadResolve (
6667 ec, (MethodGroupExpr) ml, Arguments, true, loc);
6671 if (method == null) {
6672 if (almostMatchedMembers.Count != 0) {
6673 MemberLookupFailed (ec, type, type, ".ctor", null, loc);
6674 return null;
6677 if (!is_struct || Arguments.Count > 0) {
6678 Error (1501, String.Format (
6679 "New invocation: Can not find a constructor in `{0}' for this argument list",
6680 TypeManager.CSharpName (type)));
6681 return null;
6685 return this;
6688 bool DoEmitTypeParameter (EmitContext ec)
6690 ILGenerator ig = ec.ig;
6692 ig.Emit (OpCodes.Ldtoken, type);
6693 ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6694 ig.Emit (OpCodes.Call, TypeManager.activator_create_instance);
6695 ig.Emit (OpCodes.Unbox_Any, type);
6697 return true;
6701 // This DoEmit can be invoked in two contexts:
6702 // * As a mechanism that will leave a value on the stack (new object)
6703 // * As one that wont (init struct)
6705 // You can control whether a value is required on the stack by passing
6706 // need_value_on_stack. The code *might* leave a value on the stack
6707 // so it must be popped manually
6709 // If we are dealing with a ValueType, we have a few
6710 // situations to deal with:
6712 // * The target is a ValueType, and we have been provided
6713 // the instance (this is easy, we are being assigned).
6715 // * The target of New is being passed as an argument,
6716 // to a boxing operation or a function that takes a
6717 // ValueType.
6719 // In this case, we need to create a temporary variable
6720 // that is the argument of New.
6722 // Returns whether a value is left on the stack
6724 bool DoEmit (EmitContext ec, bool need_value_on_stack)
6726 bool is_value_type = TypeManager.IsValueType (type);
6727 ILGenerator ig = ec.ig;
6729 if (is_value_type){
6730 IMemoryLocation ml;
6732 // Allow DoEmit() to be called multiple times.
6733 // We need to create a new LocalTemporary each time since
6734 // you can't share LocalBuilders among ILGeneators.
6735 if (!value_target_set)
6736 value_target = new LocalTemporary (ec, type);
6738 ml = (IMemoryLocation) value_target;
6739 ml.AddressOf (ec, AddressOp.Store);
6742 if (method != null)
6743 Invocation.EmitArguments (ec, method, Arguments, false, null);
6745 if (is_value_type){
6746 if (method == null)
6747 ig.Emit (OpCodes.Initobj, type);
6748 else
6749 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
6750 if (need_value_on_stack){
6751 value_target.Emit (ec);
6752 return true;
6754 return false;
6755 } else {
6756 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
6757 return true;
6761 public override void Emit (EmitContext ec)
6763 if (is_type_parameter)
6764 DoEmitTypeParameter (ec);
6765 else
6766 DoEmit (ec, true);
6769 public override void EmitStatement (EmitContext ec)
6771 if (is_type_parameter)
6772 throw new InvalidOperationException ();
6774 if (DoEmit (ec, false))
6775 ec.ig.Emit (OpCodes.Pop);
6778 public void AddressOf (EmitContext ec, AddressOp Mode)
6780 if (is_type_parameter)
6781 throw new InvalidOperationException ();
6783 if (!type.IsValueType){
6785 // We throw an exception. So far, I believe we only need to support
6786 // value types:
6787 // foreach (int j in new StructType ())
6788 // see bug 42390
6790 throw new Exception ("AddressOf should not be used for classes");
6793 if (!value_target_set)
6794 value_target = new LocalTemporary (ec, type);
6796 IMemoryLocation ml = (IMemoryLocation) value_target;
6797 ml.AddressOf (ec, AddressOp.Store);
6798 if (method != null)
6799 Invocation.EmitArguments (ec, method, Arguments, false, null);
6801 if (method == null)
6802 ec.ig.Emit (OpCodes.Initobj, type);
6803 else
6804 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
6806 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
6810 /// <summary>
6811 /// 14.5.10.2: Represents an array creation expression.
6812 /// </summary>
6814 /// <remarks>
6815 /// There are two possible scenarios here: one is an array creation
6816 /// expression that specifies the dimensions and optionally the
6817 /// initialization data and the other which does not need dimensions
6818 /// specified but where initialization data is mandatory.
6819 /// </remarks>
6820 public class ArrayCreation : Expression {
6821 Expression requested_base_type;
6822 ArrayList initializers;
6825 // The list of Argument types.
6826 // This is used to construct the `newarray' or constructor signature
6828 ArrayList arguments;
6831 // Method used to create the array object.
6833 MethodBase new_method = null;
6835 Type array_element_type;
6836 Type underlying_type;
6837 bool is_one_dimensional = false;
6838 bool is_builtin_type = false;
6839 bool expect_initializers = false;
6840 int num_arguments = 0;
6841 int dimensions = 0;
6842 string rank;
6844 ArrayList array_data;
6846 Hashtable bounds;
6849 // The number of array initializers that we can handle
6850 // via the InitializeArray method - through EmitStaticInitializers
6852 int num_automatic_initializers;
6854 const int max_automatic_initializers = 6;
6856 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
6858 this.requested_base_type = requested_base_type;
6859 this.initializers = initializers;
6860 this.rank = rank;
6861 loc = l;
6863 arguments = new ArrayList ();
6865 foreach (Expression e in exprs) {
6866 arguments.Add (new Argument (e, Argument.AType.Expression));
6867 num_arguments++;
6871 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
6873 this.requested_base_type = requested_base_type;
6874 this.initializers = initializers;
6875 this.rank = rank;
6876 loc = l;
6878 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
6880 //string tmp = rank.Substring (rank.LastIndexOf ('['));
6882 //dimensions = tmp.Length - 1;
6883 expect_initializers = true;
6886 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
6888 StringBuilder sb = new StringBuilder (rank);
6890 sb.Append ("[");
6891 for (int i = 1; i < idx_count; i++)
6892 sb.Append (",");
6894 sb.Append ("]");
6896 return new ComposedCast (base_type, sb.ToString (), loc);
6899 void Error_IncorrectArrayInitializer ()
6901 Error (178, "Incorrectly structured array initializer");
6904 public bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
6906 if (specified_dims) {
6907 Argument a = (Argument) arguments [idx];
6909 if (!a.Resolve (ec, loc))
6910 return false;
6912 if (!(a.Expr is Constant)) {
6913 Error (150, "A constant value is expected");
6914 return false;
6917 int value = (int) ((Constant) a.Expr).GetValue ();
6919 if (value != probe.Count) {
6920 Error_IncorrectArrayInitializer ();
6921 return false;
6924 bounds [idx] = value;
6927 int child_bounds = -1;
6928 foreach (object o in probe) {
6929 if (o is ArrayList) {
6930 int current_bounds = ((ArrayList) o).Count;
6932 if (child_bounds == -1)
6933 child_bounds = current_bounds;
6935 else if (child_bounds != current_bounds){
6936 Error_IncorrectArrayInitializer ();
6937 return false;
6939 if (specified_dims && (idx + 1 >= arguments.Count)){
6940 Error (623, "Array initializers can only be used in a variable or field initializer, try using the new expression");
6941 return false;
6944 bool ret = CheckIndices (ec, (ArrayList) o, idx + 1, specified_dims);
6945 if (!ret)
6946 return false;
6947 } else {
6948 if (child_bounds != -1){
6949 Error_IncorrectArrayInitializer ();
6950 return false;
6953 Expression tmp = (Expression) o;
6954 tmp = tmp.Resolve (ec);
6955 if (tmp == null)
6956 return false;
6958 // Console.WriteLine ("I got: " + tmp);
6959 // Handle initialization from vars, fields etc.
6961 Expression conv = Convert.WideningConversionRequired (
6962 ec, tmp, underlying_type, loc);
6964 if (conv == null)
6965 return false;
6967 if (conv is StringConstant || conv is DecimalConstant || conv is NullCast) {
6968 // These are subclasses of Constant that can appear as elements of an
6969 // array that cannot be statically initialized (with num_automatic_initializers
6970 // > max_automatic_initializers), so num_automatic_initializers should be left as zero.
6971 array_data.Add (conv);
6972 } else if (conv is Constant) {
6973 // These are the types of Constant that can appear in arrays that can be
6974 // statically allocated.
6975 array_data.Add (conv);
6976 num_automatic_initializers++;
6977 } else
6978 array_data.Add (conv);
6982 return true;
6985 public void UpdateIndices (EmitContext ec)
6987 int i = 0;
6988 for (ArrayList probe = initializers; probe != null;) {
6989 if (probe.Count > 0 && probe [0] is ArrayList) {
6990 Expression e = new IntConstant (probe.Count);
6991 arguments.Add (new Argument (e, Argument.AType.Expression));
6993 bounds [i++] = probe.Count;
6995 probe = (ArrayList) probe [0];
6997 } else {
6998 Expression e = new IntConstant (probe.Count);
6999 arguments.Add (new Argument (e, Argument.AType.Expression));
7001 bounds [i++] = probe.Count;
7002 probe = null;
7008 public bool ValidateInitializers (EmitContext ec, Type array_type)
7010 if (initializers == null) {
7011 if (expect_initializers)
7012 return false;
7013 else
7014 return true;
7017 if (underlying_type == null)
7018 return false;
7021 // We use this to store all the date values in the order in which we
7022 // will need to store them in the byte blob later
7024 array_data = new ArrayList ();
7025 bounds = new Hashtable ();
7027 bool ret;
7029 if (arguments != null) {
7030 ret = CheckIndices (ec, initializers, 0, true);
7031 return ret;
7032 } else {
7033 arguments = new ArrayList ();
7035 ret = CheckIndices (ec, initializers, 0, false);
7037 if (!ret)
7038 return false;
7040 UpdateIndices (ec);
7042 if (arguments.Count != dimensions) {
7043 Error_IncorrectArrayInitializer ();
7044 return false;
7047 return ret;
7052 // Converts `source' to an int, uint, long or ulong.
7054 Expression ExpressionToArrayArgument (EmitContext ec, Expression source)
7056 Expression target;
7058 bool old_checked = ec.CheckState;
7059 ec.CheckState = true;
7061 target = Convert.WideningConversion (ec, source, TypeManager.int32_type, loc);
7062 if (target == null){
7063 target = Convert.WideningConversion (ec, source, TypeManager.uint32_type, loc);
7064 if (target == null){
7065 target = Convert.WideningConversion (ec, source, TypeManager.int64_type, loc);
7066 if (target == null){
7067 target = Convert.WideningConversion (ec, source, TypeManager.uint64_type, loc);
7068 if (target == null)
7069 Convert.Error_CannotWideningConversion (loc, source.Type, TypeManager.int32_type);
7073 ec.CheckState = old_checked;
7076 // Only positive constants are allowed at compile time
7078 if (target is Constant){
7079 if (target is IntConstant){
7080 if (((IntConstant) target).Value < 0){
7081 Expression.Error_NegativeArrayIndex (loc);
7082 return null;
7086 if (target is LongConstant){
7087 if (((LongConstant) target).Value < 0){
7088 Expression.Error_NegativeArrayIndex (loc);
7089 return null;
7095 return target;
7099 // Creates the type of the array
7101 bool LookupType (EmitContext ec)
7103 StringBuilder array_qualifier = new StringBuilder (rank);
7106 // `In the first form allocates an array instace of the type that results
7107 // from deleting each of the individual expression from the expression list'
7109 if (num_arguments > 0) {
7110 array_qualifier.Append ("[");
7111 for (int i = num_arguments-1; i > 0; i--)
7112 array_qualifier.Append (",");
7113 array_qualifier.Append ("]");
7117 // Lookup the type
7119 TypeExpr array_type_expr;
7120 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
7121 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec);
7122 if (array_type_expr == null)
7123 return false;
7125 type = array_type_expr.Type;
7127 if (!type.IsArray) {
7128 Error (622, "Can only use array initializer expressions to assign to array types. Try using a new expression instead.");
7129 return false;
7131 underlying_type = TypeManager.GetElementType (type);
7132 dimensions = type.GetArrayRank ();
7134 return true;
7137 public override Expression DoResolve (EmitContext ec)
7139 int arg_count;
7141 if (!LookupType (ec))
7142 return null;
7145 // First step is to validate the initializers and fill
7146 // in any missing bits
7148 if (!ValidateInitializers (ec, type))
7149 return null;
7151 if (arguments == null)
7152 arg_count = 0;
7153 else {
7154 arg_count = arguments.Count;
7155 foreach (Argument a in arguments){
7156 if (!a.Resolve (ec, loc))
7157 return null;
7159 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
7160 if (real_arg == null)
7161 return null;
7163 a.Expr = real_arg;
7167 array_element_type = TypeManager.GetElementType (type);
7169 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
7170 Report.Error (719, loc, "'{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
7171 return null;
7174 if (arg_count == 1) {
7175 is_one_dimensional = true;
7176 eclass = ExprClass.Value;
7177 return this;
7180 is_builtin_type = TypeManager.IsBuiltinType (type);
7182 if (is_builtin_type) {
7183 Expression ml;
7185 ml = MemberLookup (ec, type, ".ctor", MemberTypes.Constructor,
7186 AllBindingFlags, loc);
7188 if (!(ml is MethodGroupExpr)) {
7189 ml.Error_UnexpectedKind ("method group", loc);
7190 return null;
7193 if (ml == null) {
7194 Error (-6, "New invocation: Can not find a constructor for " +
7195 "this argument list");
7196 return null;
7199 new_method = Invocation.OverloadResolve (
7200 ec, (MethodGroupExpr) ml, arguments, false, loc);
7202 if (new_method == null) {
7203 Error (-6, "New invocation: Can not find a constructor for " +
7204 "this argument list");
7205 return null;
7208 eclass = ExprClass.Value;
7209 return this;
7210 } else {
7211 ModuleBuilder mb = CodeGen.Module.Builder;
7212 ArrayList args = new ArrayList ();
7214 if (arguments != null) {
7215 for (int i = 0; i < arg_count; i++)
7216 args.Add (TypeManager.int32_type);
7219 Type [] arg_types = null;
7221 if (args.Count > 0)
7222 arg_types = new Type [args.Count];
7224 args.CopyTo (arg_types, 0);
7226 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
7227 arg_types);
7229 if (new_method == null) {
7230 Error (-6, "New invocation: Can not find a constructor for " +
7231 "this argument list");
7232 return null;
7235 eclass = ExprClass.Value;
7236 return this;
7240 public static byte [] MakeByteBlob (ArrayList array_data, Type underlying_type, Location loc)
7242 int factor;
7243 byte [] data;
7244 byte [] element;
7245 int count = array_data.Count;
7247 if (underlying_type.IsEnum)
7248 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
7250 factor = GetTypeSize (underlying_type);
7251 if (factor == 0)
7252 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
7254 data = new byte [(count * factor + 4) & ~3];
7255 int idx = 0;
7257 for (int i = 0; i < count; ++i) {
7258 object v = array_data [i];
7260 if (v is EnumConstant)
7261 v = ((EnumConstant) v).Child;
7263 if (v is Constant && !(v is StringConstant))
7264 v = ((Constant) v).GetValue ();
7265 else {
7266 idx += factor;
7267 continue;
7270 if (underlying_type == TypeManager.int64_type){
7271 if (!(v is Expression)){
7272 long val = (long) v;
7274 for (int j = 0; j < factor; ++j) {
7275 data [idx + j] = (byte) (val & 0xFF);
7276 val = (val >> 8);
7279 } else if (underlying_type == TypeManager.uint64_type){
7280 if (!(v is Expression)){
7281 ulong val = (ulong) v;
7283 for (int j = 0; j < factor; ++j) {
7284 data [idx + j] = (byte) (val & 0xFF);
7285 val = (val >> 8);
7288 } else if (underlying_type == TypeManager.float_type) {
7289 if (!(v is Expression)){
7290 element = BitConverter.GetBytes ((float) v);
7292 for (int j = 0; j < factor; ++j)
7293 data [idx + j] = element [j];
7295 } else if (underlying_type == TypeManager.double_type) {
7296 if (!(v is Expression)){
7297 element = BitConverter.GetBytes ((double) v);
7299 for (int j = 0; j < factor; ++j)
7300 data [idx + j] = element [j];
7302 } else if (underlying_type == TypeManager.char_type){
7303 if (!(v is Expression)){
7304 int val = (int) ((char) v);
7306 data [idx] = (byte) (val & 0xff);
7307 data [idx+1] = (byte) (val >> 8);
7309 } else if (underlying_type == TypeManager.short_type){
7310 if (!(v is Expression)){
7311 int val = (int) ((short) v);
7313 data [idx] = (byte) (val & 0xff);
7314 data [idx+1] = (byte) (val >> 8);
7316 } else if (underlying_type == TypeManager.ushort_type){
7317 if (!(v is Expression)){
7318 int val = (int) ((ushort) v);
7320 data [idx] = (byte) (val & 0xff);
7321 data [idx+1] = (byte) (val >> 8);
7323 } else if (underlying_type == TypeManager.int32_type) {
7324 if (!(v is Expression)){
7325 int val = (int) v;
7327 data [idx] = (byte) (val & 0xff);
7328 data [idx+1] = (byte) ((val >> 8) & 0xff);
7329 data [idx+2] = (byte) ((val >> 16) & 0xff);
7330 data [idx+3] = (byte) (val >> 24);
7332 } else if (underlying_type == TypeManager.uint32_type) {
7333 if (!(v is Expression)){
7334 uint val = (uint) v;
7336 data [idx] = (byte) (val & 0xff);
7337 data [idx+1] = (byte) ((val >> 8) & 0xff);
7338 data [idx+2] = (byte) ((val >> 16) & 0xff);
7339 data [idx+3] = (byte) (val >> 24);
7341 } else if (underlying_type == TypeManager.sbyte_type) {
7342 if (!(v is Expression)){
7343 sbyte val = (sbyte) v;
7344 data [idx] = (byte) val;
7346 } else if (underlying_type == TypeManager.byte_type) {
7347 if (!(v is Expression)){
7348 byte val = (byte) v;
7349 data [idx] = (byte) val;
7351 } else if (underlying_type == TypeManager.bool_type) {
7352 if (!(v is Expression)){
7353 bool val = (bool) v;
7354 data [idx] = (byte) (val ? 1 : 0);
7356 } else if (underlying_type == TypeManager.decimal_type){
7357 if (!(v is Expression)){
7358 int [] bits = Decimal.GetBits ((decimal) v);
7359 int p = idx;
7361 // FIXME: For some reason, this doesn't work on the MS runtime.
7362 int [] nbits = new int [4];
7363 nbits [0] = bits [3];
7364 nbits [1] = bits [2];
7365 nbits [2] = bits [0];
7366 nbits [3] = bits [1];
7368 for (int j = 0; j < 4; j++){
7369 data [p++] = (byte) (nbits [j] & 0xff);
7370 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
7371 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
7372 data [p++] = (byte) (nbits [j] >> 24);
7375 } else
7376 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
7378 idx += factor;
7381 return data;
7385 // Emits the initializers for the array
7387 void EmitStaticInitializers (EmitContext ec)
7390 // First, the static data
7392 FieldBuilder fb;
7393 ILGenerator ig = ec.ig;
7395 byte [] data = MakeByteBlob (array_data, underlying_type, loc);
7397 fb = RootContext.MakeStaticData (data);
7399 ig.Emit (OpCodes.Dup);
7400 ig.Emit (OpCodes.Ldtoken, fb);
7401 ig.Emit (OpCodes.Call,
7402 TypeManager.void_initializearray_array_fieldhandle);
7406 // Emits pieces of the array that can not be computed at compile
7407 // time (variables and string locations).
7409 // This always expect the top value on the stack to be the array
7411 void EmitDynamicInitializers (EmitContext ec)
7413 ILGenerator ig = ec.ig;
7414 int dims = bounds.Count;
7415 int [] current_pos = new int [dims];
7416 int top = array_data.Count;
7418 MethodInfo set = null;
7420 if (dims != 1){
7421 Type [] args;
7422 ModuleBuilder mb = null;
7423 mb = CodeGen.Module.Builder;
7424 args = new Type [dims + 1];
7426 int j;
7427 for (j = 0; j < dims; j++)
7428 args [j] = TypeManager.int32_type;
7430 args [j] = array_element_type;
7432 set = mb.GetArrayMethod (
7433 type, "Set",
7434 CallingConventions.HasThis | CallingConventions.Standard,
7435 TypeManager.void_type, args);
7438 for (int i = 0; i < top; i++){
7440 Expression e = null;
7442 if (array_data [i] is Expression)
7443 e = (Expression) array_data [i];
7445 if (e != null) {
7447 // Basically we do this for string literals and
7448 // other non-literal expressions
7450 if (e is EnumConstant){
7451 e = ((EnumConstant) e).Child;
7454 if (e is StringConstant || e is DecimalConstant || !(e is Constant) ||
7455 num_automatic_initializers <= max_automatic_initializers) {
7456 Type etype = e.Type;
7458 ig.Emit (OpCodes.Dup);
7460 for (int idx = 0; idx < dims; idx++)
7461 IntConstant.EmitInt (ig, current_pos [idx]);
7464 // If we are dealing with a struct, get the
7465 // address of it, so we can store it.
7467 if ((dims == 1) && etype.IsValueType &&
7468 (!TypeManager.IsBuiltinOrEnum (etype) ||
7469 etype == TypeManager.decimal_type)) {
7470 if (e is New){
7471 New n = (New) e;
7474 // Let new know that we are providing
7475 // the address where to store the results
7477 n.DisableTemporaryValueType ();
7480 ig.Emit (OpCodes.Ldelema, etype);
7483 e.Emit (ec);
7485 if (dims == 1) {
7486 bool is_stobj, has_type_arg;
7487 OpCode op = ArrayAccess.GetStoreOpcode (
7488 etype, out is_stobj,
7489 out has_type_arg);
7490 if (is_stobj)
7491 ig.Emit (OpCodes.Stobj, etype);
7492 else if (has_type_arg)
7493 ig.Emit (op, etype);
7494 else
7495 ig.Emit (op);
7496 } else
7497 ig.Emit (OpCodes.Call, set);
7502 // Advance counter
7504 for (int j = dims - 1; j >= 0; j--){
7505 current_pos [j]++;
7506 if (current_pos [j] < (int) bounds [j])
7507 break;
7508 current_pos [j] = 0;
7513 void EmitArrayArguments (EmitContext ec)
7515 ILGenerator ig = ec.ig;
7517 foreach (Argument a in arguments) {
7518 Type atype = a.Type;
7519 a.Emit (ec);
7521 if (atype == TypeManager.uint64_type)
7522 ig.Emit (OpCodes.Conv_Ovf_U4);
7523 else if (atype == TypeManager.int64_type)
7524 ig.Emit (OpCodes.Conv_Ovf_I4);
7528 public override void Emit (EmitContext ec)
7530 ILGenerator ig = ec.ig;
7532 EmitArrayArguments (ec);
7533 if (is_one_dimensional)
7534 ig.Emit (OpCodes.Newarr, array_element_type);
7535 else {
7536 if (is_builtin_type)
7537 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
7538 else
7539 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
7542 if (initializers != null){
7544 // FIXME: Set this variable correctly.
7546 bool dynamic_initializers = true;
7548 // This will never be true for array types that cannot be statically
7549 // initialized. num_automatic_initializers will always be zero. See
7550 // CheckIndices.
7551 if (num_automatic_initializers > max_automatic_initializers)
7552 EmitStaticInitializers (ec);
7554 if (dynamic_initializers)
7555 EmitDynamicInitializers (ec);
7559 public object EncodeAsAttribute ()
7561 if (!is_one_dimensional){
7562 Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
7563 return null;
7566 if (array_data == null){
7567 Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
7568 return null;
7571 object [] ret = new object [array_data.Count];
7572 int i = 0;
7573 foreach (Expression e in array_data){
7574 object v;
7576 if (e is NullLiteral)
7577 v = null;
7578 else {
7579 if (!Attribute.GetAttributeArgumentExpression (e, Location, array_element_type, out v))
7580 return null;
7582 ret [i++] = v;
7584 return ret;
7588 /// <summary>
7589 /// Represents the `this' construct
7590 /// </summary>
7591 public class This : Expression, IAssignMethod, IMemoryLocation, IVariable {
7593 Block block;
7594 VariableInfo variable_info;
7596 public This (Block block, Location loc)
7598 this.loc = loc;
7599 this.block = block;
7602 public This (Location loc)
7604 this.loc = loc;
7607 public VariableInfo VariableInfo {
7608 get { return variable_info; }
7611 public bool VerifyFixed (bool is_expression)
7613 if ((variable_info == null) || (variable_info.LocalInfo == null))
7614 return false;
7615 else
7616 return variable_info.LocalInfo.IsFixed;
7619 public bool ResolveBase (EmitContext ec)
7621 eclass = ExprClass.Variable;
7623 if (ec.TypeContainer.CurrentType != null)
7624 type = ec.TypeContainer.CurrentType;
7625 else
7626 type = ec.ContainerType;
7628 if (ec.IsStatic) {
7629 Error (26, "Keyword this not valid in static code");
7630 return false;
7633 if ((block != null) && (block.ThisVariable != null))
7634 variable_info = block.ThisVariable.VariableInfo;
7636 return true;
7639 public override Expression DoResolve (EmitContext ec)
7641 if (!ResolveBase (ec))
7642 return null;
7644 if ((variable_info != null) && !variable_info.IsAssigned (ec)) {
7645 Error (188, "The this object cannot be used before all " +
7646 "of its fields are assigned to");
7647 variable_info.SetAssigned (ec);
7648 return this;
7651 if (ec.IsFieldInitializer) {
7652 Error (27, "Keyword `this' can't be used outside a constructor, " +
7653 "a method or a property.");
7654 return null;
7657 return this;
7660 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
7662 if (!ResolveBase (ec))
7663 return null;
7665 if (variable_info != null)
7666 variable_info.SetAssigned (ec);
7668 if (ec.TypeContainer is Class){
7669 Error (1604, "Cannot assign to `this'");
7670 return null;
7673 return this;
7676 public void Emit (EmitContext ec, bool leave_copy)
7678 Emit (ec);
7679 if (leave_copy)
7680 ec.ig.Emit (OpCodes.Dup);
7683 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7685 ILGenerator ig = ec.ig;
7687 if (ec.TypeContainer is Struct){
7688 ec.EmitThis ();
7689 source.Emit (ec);
7690 if (leave_copy)
7691 ec.ig.Emit (OpCodes.Dup);
7692 ig.Emit (OpCodes.Stobj, type);
7693 } else {
7694 throw new Exception ("how did you get here");
7698 public override void Emit (EmitContext ec)
7700 ILGenerator ig = ec.ig;
7702 ec.EmitThis ();
7703 if (ec.TypeContainer is Struct)
7704 ig.Emit (OpCodes.Ldobj, type);
7707 public void AddressOf (EmitContext ec, AddressOp mode)
7709 ec.EmitThis ();
7711 // FIMXE
7712 // FIGURE OUT WHY LDARG_S does not work
7714 // consider: struct X { int val; int P { set { val = value; }}}
7716 // Yes, this looks very bad. Look at `NOTAS' for
7717 // an explanation.
7718 // ec.ig.Emit (OpCodes.Ldarga_S, (byte) 0);
7722 /// <summary>
7723 /// Represents the `__arglist' construct
7724 /// </summary>
7725 public class ArglistAccess : Expression
7727 public ArglistAccess (Location loc)
7729 this.loc = loc;
7732 public bool ResolveBase (EmitContext ec)
7734 eclass = ExprClass.Variable;
7735 type = TypeManager.runtime_argument_handle_type;
7736 return true;
7739 public override Expression DoResolve (EmitContext ec)
7741 if (!ResolveBase (ec))
7742 return null;
7744 if (ec.IsFieldInitializer || !ec.CurrentBlock.HasVarargs) {
7745 Error (190, "The __arglist construct is valid only within " +
7746 "a variable argument method.");
7747 return null;
7750 return this;
7753 public override void Emit (EmitContext ec)
7755 ec.ig.Emit (OpCodes.Arglist);
7759 /// <summary>
7760 /// Represents the `__arglist (....)' construct
7761 /// </summary>
7762 public class Arglist : Expression
7764 public readonly Argument[] Arguments;
7766 public Arglist (Argument[] args, Location l)
7768 Arguments = args;
7769 loc = l;
7772 public Type[] ArgumentTypes {
7773 get {
7774 Type[] retval = new Type [Arguments.Length];
7775 for (int i = 0; i < Arguments.Length; i++)
7776 retval [i] = Arguments [i].Type;
7777 return retval;
7781 public override Expression DoResolve (EmitContext ec)
7783 eclass = ExprClass.Variable;
7784 type = TypeManager.runtime_argument_handle_type;
7786 foreach (Argument arg in Arguments) {
7787 if (!arg.Resolve (ec, loc))
7788 return null;
7791 return this;
7794 public override void Emit (EmitContext ec)
7796 foreach (Argument arg in Arguments)
7797 arg.Emit (ec);
7802 // This produces the value that renders an instance, used by the iterators code
7804 public class ProxyInstance : Expression, IMemoryLocation {
7805 public override Expression DoResolve (EmitContext ec)
7807 eclass = ExprClass.Variable;
7808 type = ec.ContainerType;
7809 return this;
7812 public override void Emit (EmitContext ec)
7814 ec.ig.Emit (OpCodes.Ldarg_0);
7818 public void AddressOf (EmitContext ec, AddressOp mode)
7820 ec.ig.Emit (OpCodes.Ldarg_0);
7824 /// <summary>
7825 /// Implements the typeof operator
7826 /// </summary>
7827 public class TypeOf : Expression {
7828 public Expression QueriedType;
7829 protected Type typearg;
7831 public TypeOf (Expression queried_type, Location l)
7833 QueriedType = queried_type;
7834 loc = l;
7837 public override Expression DoResolve (EmitContext ec)
7839 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec);
7840 if (texpr == null)
7841 return null;
7843 typearg = texpr.Type;
7845 if (typearg == TypeManager.void_type) {
7846 Error (673, "System.Void cannot be used from C# - " +
7847 "use typeof (void) to get the void type object");
7848 return null;
7851 if (typearg.IsPointer && !ec.InUnsafe){
7852 UnsafeError (loc);
7853 return null;
7855 CheckObsoleteAttribute (typearg);
7857 type = TypeManager.type_type;
7858 eclass = ExprClass.Type;
7859 return this;
7862 public override void Emit (EmitContext ec)
7864 ec.ig.Emit (OpCodes.Ldtoken, typearg);
7865 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
7868 public Type TypeArg {
7869 get { return typearg; }
7873 /// <summary>
7874 /// Implements the `typeof (void)' operator
7875 /// </summary>
7876 public class TypeOfVoid : TypeOf {
7877 public TypeOfVoid (Location l) : base (null, l)
7879 loc = l;
7882 public override Expression DoResolve (EmitContext ec)
7884 type = TypeManager.type_type;
7885 typearg = TypeManager.void_type;
7886 eclass = ExprClass.Type;
7887 return this;
7891 /// <summary>
7892 /// Implements the sizeof expression
7893 /// </summary>
7894 public class SizeOf : Expression {
7895 public Expression QueriedType;
7896 Type type_queried;
7898 public SizeOf (Expression queried_type, Location l)
7900 this.QueriedType = queried_type;
7901 loc = l;
7904 public override Expression DoResolve (EmitContext ec)
7906 if (!ec.InUnsafe) {
7907 Report.Error (
7908 233, loc, "Sizeof may only be used in an unsafe context " +
7909 "(consider using System.Runtime.InteropServices.Marshal.SizeOf");
7910 return null;
7913 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec);
7914 if (texpr == null)
7915 return null;
7917 if (texpr is TypeParameterExpr){
7918 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
7919 return null;
7922 type_queried = texpr.Type;
7924 CheckObsoleteAttribute (type_queried);
7926 if (!TypeManager.IsUnmanagedType (type_queried)){
7927 Report.Error (208, loc, "Cannot take the size of an unmanaged type (" + TypeManager.CSharpName (type_queried) + ")");
7928 return null;
7931 type = TypeManager.int32_type;
7932 eclass = ExprClass.Value;
7933 return this;
7936 public override void Emit (EmitContext ec)
7938 int size = GetTypeSize (type_queried);
7940 if (size == 0)
7941 ec.ig.Emit (OpCodes.Sizeof, type_queried);
7942 else
7943 IntConstant.EmitInt (ec.ig, size);
7947 /// <summary>
7948 /// Implements the member access expression
7949 /// </summary>
7950 public class MemberAccess : Expression {
7951 public string Identifier;
7952 protected Expression expr;
7953 protected TypeArguments args;
7955 public MemberAccess (Expression expr, string id, Location l)
7957 this.expr = expr;
7958 Identifier = id;
7959 loc = l;
7962 public MemberAccess (Expression expr, string id, TypeArguments args,
7963 Location l)
7964 : this (expr, id, l)
7966 this.args = args;
7969 public Expression Expr {
7970 get {
7971 return expr;
7975 public static void error176 (Location loc, string name)
7977 Report.Error (176, loc, "Static member `" +
7978 name + "' cannot be accessed " +
7979 "with an instance reference, qualify with a " +
7980 "type name instead");
7983 public static bool IdenticalNameAndTypeName (EmitContext ec, Expression left_original, Expression left, Location loc)
7985 SimpleName sn = left_original as SimpleName;
7986 if (sn == null || left == null || left.Type.Name != sn.Name)
7987 return false;
7989 return ec.DeclSpace.LookupType (sn.Name, true, loc) != null;
7992 // TODO: possible optimalization
7993 // Cache resolved constant result in FieldBuilder <-> expresion map
7994 public static Expression ResolveMemberAccess (EmitContext ec, Expression member_lookup,
7995 Expression left, Location loc,
7996 Expression left_original)
7998 bool left_is_type, left_is_explicit;
8000 // If `left' is null, then we're called from SimpleNameResolve and this is
8001 // a member in the currently defining class.
8002 if (left == null) {
8003 left_is_type = ec.IsStatic || ec.IsFieldInitializer;
8004 left_is_explicit = false;
8006 // Implicitly default to `this' unless we're static.
8007 if (!ec.IsStatic && !ec.IsFieldInitializer && !ec.InEnumContext)
8008 left = ec.GetThis (loc);
8009 } else {
8010 left_is_type = left is TypeExpr;
8011 left_is_explicit = true;
8014 if (member_lookup is FieldExpr){
8015 FieldExpr fe = (FieldExpr) member_lookup;
8016 FieldInfo fi = fe.FieldInfo.Mono_GetGenericFieldDefinition ();
8017 Type decl_type = fi.DeclaringType;
8019 bool is_emitted = fi is FieldBuilder;
8020 Type t = fi.FieldType;
8022 if (is_emitted) {
8023 Const c = TypeManager.LookupConstant ((FieldBuilder) fi);
8025 if (c != null) {
8026 object o;
8027 if (!c.LookupConstantValue (out o))
8028 return null;
8030 object real_value = ((Constant) c.Expr).GetValue ();
8032 Expression exp = Constantify (real_value, t);
8034 if (left_is_explicit && !left_is_type && !IdenticalNameAndTypeName (ec, left_original, left, loc)) {
8035 Report.SymbolRelatedToPreviousError (c);
8036 error176 (loc, c.GetSignatureForError ());
8037 return null;
8040 return exp;
8044 // IsInitOnly is because of MS compatibility, I don't know why but they emit decimal constant as InitOnly
8045 if (fi.IsInitOnly && !is_emitted && t == TypeManager.decimal_type) {
8046 object[] attrs = fi.GetCustomAttributes (TypeManager.decimal_constant_attribute_type, false);
8047 if (attrs.Length == 1)
8048 return new DecimalConstant (((System.Runtime.CompilerServices.DecimalConstantAttribute) attrs [0]).Value);
8051 if (fi.IsLiteral) {
8052 object o;
8054 if (is_emitted)
8055 o = TypeManager.GetValue ((FieldBuilder) fi);
8056 else
8057 o = fi.GetValue (fi);
8059 if (decl_type.IsSubclassOf (TypeManager.enum_type)) {
8060 if (left_is_explicit && !left_is_type &&
8061 !IdenticalNameAndTypeName (ec, left_original, member_lookup, loc)) {
8062 error176 (loc, fe.FieldInfo.Name);
8063 return null;
8066 Expression enum_member = MemberLookup (
8067 ec, decl_type, "value__", MemberTypes.Field,
8068 AllBindingFlags, loc);
8070 Enum en = TypeManager.LookupEnum (decl_type);
8072 Constant c;
8073 if (en != null)
8074 c = Constantify (o, en.UnderlyingType);
8075 else
8076 c = Constantify (o, enum_member.Type);
8078 return new EnumConstant (c, decl_type);
8081 Expression exp = Constantify (o, t);
8083 if (left_is_explicit && !left_is_type) {
8084 error176 (loc, fe.FieldInfo.Name);
8085 return null;
8088 return exp;
8091 if (t.IsPointer && !ec.InUnsafe){
8092 UnsafeError (loc);
8093 return null;
8097 if (member_lookup is EventExpr) {
8098 EventExpr ee = (EventExpr) member_lookup;
8101 // If the event is local to this class, we transform ourselves into
8102 // a FieldExpr
8105 if (ee.EventInfo.DeclaringType == ec.ContainerType ||
8106 TypeManager.IsNestedChildOf(ec.ContainerType, ee.EventInfo.DeclaringType)) {
8107 MemberInfo mi = GetFieldFromEvent (ee);
8109 if (mi == null) {
8111 // If this happens, then we have an event with its own
8112 // accessors and private field etc so there's no need
8113 // to transform ourselves.
8115 ee.InstanceExpression = left;
8116 return ee;
8119 Expression ml = ExprClassFromMemberInfo (ec, mi, loc);
8121 if (ml == null) {
8122 Report.Error (-200, loc, "Internal error!!");
8123 return null;
8126 if (!left_is_explicit)
8127 left = null;
8129 ee.InstanceExpression = left;
8131 return ResolveMemberAccess (ec, ml, left, loc, left_original);
8135 if (member_lookup is IMemberExpr) {
8136 IMemberExpr me = (IMemberExpr) member_lookup;
8137 MethodGroupExpr mg = me as MethodGroupExpr;
8139 if (left_is_type){
8140 if ((mg != null) && left_is_explicit && left.Type.IsInterface)
8141 mg.IsExplicitImpl = left_is_explicit;
8143 if (!me.IsStatic){
8144 if ((ec.IsFieldInitializer || ec.IsStatic) &&
8145 IdenticalNameAndTypeName (ec, left_original, member_lookup, loc))
8146 return member_lookup;
8148 SimpleName.Error_ObjectRefRequired (ec, loc, me.Name);
8149 return null;
8152 } else {
8153 if (!me.IsInstance){
8154 if (IdenticalNameAndTypeName (ec, left_original, left, loc))
8155 return member_lookup;
8157 if (left_is_explicit) {
8158 error176 (loc, me.Name);
8159 return null;
8164 // Since we can not check for instance objects in SimpleName,
8165 // becaue of the rule that allows types and variables to share
8166 // the name (as long as they can be de-ambiguated later, see
8167 // IdenticalNameAndTypeName), we have to check whether left
8168 // is an instance variable in a static context
8170 // However, if the left-hand value is explicitly given, then
8171 // it is already our instance expression, so we aren't in
8172 // static context.
8175 if (ec.IsStatic && !left_is_explicit && left is IMemberExpr){
8176 IMemberExpr mexp = (IMemberExpr) left;
8178 if (!mexp.IsStatic){
8179 SimpleName.Error_ObjectRefRequired (ec, loc, mexp.Name);
8180 return null;
8184 if ((mg != null) && IdenticalNameAndTypeName (ec, left_original, left, loc))
8185 mg.IdenticalTypeName = true;
8187 me.InstanceExpression = left;
8190 return member_lookup;
8193 Console.WriteLine ("Left is: " + left);
8194 Report.Error (-100, loc, "Support for [" + member_lookup + "] is not present yet");
8195 Environment.Exit (1);
8196 return null;
8199 public virtual Expression DoResolve (EmitContext ec, Expression right_side,
8200 ResolveFlags flags)
8202 if (type != null)
8203 throw new Exception ();
8206 // Resolve the expression with flow analysis turned off, we'll do the definite
8207 // assignment checks later. This is because we don't know yet what the expression
8208 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
8209 // definite assignment check on the actual field and not on the whole struct.
8212 Expression original = expr;
8213 expr = expr.Resolve (ec, flags | ResolveFlags.Intermediate | ResolveFlags.DisableFlowAnalysis);
8214 if (expr == null)
8215 return null;
8217 if (expr is Namespace) {
8218 Namespace ns = (Namespace) expr;
8219 string lookup_id = MemberName.MakeName (Identifier, args);
8220 FullNamedExpression retval = ns.Lookup (ec.DeclSpace, lookup_id, loc);
8221 if ((retval != null) && (args != null))
8222 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec);
8223 if (retval == null)
8224 Report.Error (234, loc, "The type or namespace name `{0}' could not be found in namespace `{1}'", Identifier, ns.FullName);
8225 return retval;
8229 // TODO: I mailed Ravi about this, and apparently we can get rid
8230 // of this and put it in the right place.
8232 // Handle enums here when they are in transit.
8233 // Note that we cannot afford to hit MemberLookup in this case because
8234 // it will fail to find any members at all
8237 Type expr_type;
8238 if (expr is TypeExpr){
8239 expr_type = expr.Type;
8241 if (!ec.DeclSpace.CheckAccessLevel (expr_type)){
8242 Report.Error (122, loc, "'{0}' is inaccessible due to its protection level", expr_type);
8243 return null;
8246 if (expr_type == TypeManager.enum_type || expr_type.IsSubclassOf (TypeManager.enum_type)){
8247 Enum en = TypeManager.LookupEnum (expr_type);
8249 if (en != null) {
8250 object value = en.LookupEnumValue (ec, Identifier, loc);
8252 if (value != null){
8253 MemberCore mc = en.GetDefinition (Identifier);
8254 ObsoleteAttribute oa = mc.GetObsoleteAttribute (en);
8255 if (oa != null) {
8256 AttributeTester.Report_ObsoleteMessage (oa, mc.GetSignatureForError (), Location);
8258 oa = en.GetObsoleteAttribute (en);
8259 if (oa != null) {
8260 AttributeTester.Report_ObsoleteMessage (oa, en.GetSignatureForError (), Location);
8263 Constant c = Constantify (value, en.UnderlyingType);
8264 return new EnumConstant (c, expr_type);
8266 } else {
8267 CheckObsoleteAttribute (expr_type);
8269 FieldInfo fi = expr_type.GetField (Identifier);
8270 if (fi != null) {
8271 ObsoleteAttribute oa = AttributeTester.GetMemberObsoleteAttribute (fi);
8272 if (oa != null)
8273 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.GetFullNameSignature (fi), Location);
8277 } else
8278 expr_type = expr.Type;
8280 if (expr_type.IsPointer){
8281 Error (23, "The `.' operator can not be applied to pointer operands (" +
8282 TypeManager.CSharpName (expr_type) + ")");
8283 return null;
8286 Expression member_lookup;
8287 member_lookup = MemberLookup (
8288 ec, expr_type, expr_type, Identifier, loc);
8289 if ((member_lookup == null) && (args != null)) {
8290 string lookup_id = MemberName.MakeName (Identifier, args);
8291 member_lookup = MemberLookup (
8292 ec, expr_type, expr_type, lookup_id, loc);
8294 if (member_lookup == null) {
8295 MemberLookupFailed (
8296 ec, expr_type, expr_type, Identifier, null, loc);
8297 return null;
8300 if (member_lookup is TypeExpr) {
8301 if (!(expr is TypeExpr) &&
8302 !IdenticalNameAndTypeName (ec, original, expr, loc)) {
8303 Error (572, "Can't reference type `" + Identifier + "' through an expression; try `" +
8304 member_lookup.Type + "' instead");
8305 return null;
8308 return member_lookup;
8311 if (args != null) {
8312 string full_name = expr_type + "." + Identifier;
8314 if (member_lookup is FieldExpr) {
8315 Report.Error (307, loc, "The field `{0}' cannot " +
8316 "be used with type arguments", full_name);
8317 return null;
8318 } else if (member_lookup is EventExpr) {
8319 Report.Error (307, loc, "The event `{0}' cannot " +
8320 "be used with type arguments", full_name);
8321 return null;
8322 } else if (member_lookup is PropertyExpr) {
8323 Report.Error (307, loc, "The property `{0}' cannot " +
8324 "be used with type arguments", full_name);
8325 return null;
8329 member_lookup = ResolveMemberAccess (ec, member_lookup, expr, loc, original);
8330 if (member_lookup == null)
8331 return null;
8333 if (args != null) {
8334 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
8335 if (mg == null)
8336 throw new InternalErrorException ();
8338 return mg.ResolveGeneric (ec, args);
8341 // The following DoResolve/DoResolveLValue will do the definite assignment
8342 // check.
8344 if (right_side != null)
8345 member_lookup = member_lookup.DoResolveLValue (ec, right_side);
8346 else
8347 member_lookup = member_lookup.DoResolve (ec);
8349 return member_lookup;
8352 public override Expression DoResolve (EmitContext ec)
8354 return DoResolve (ec, null, ResolveFlags.VariableOrValue | ResolveFlags.Type);
8357 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8359 return DoResolve (ec, right_side, ResolveFlags.VariableOrValue | ResolveFlags.Type);
8362 public override FullNamedExpression ResolveAsTypeStep (EmitContext ec)
8364 return ResolveNamespaceOrType (ec, false);
8367 public FullNamedExpression ResolveNamespaceOrType (EmitContext ec, bool silent)
8369 FullNamedExpression new_expr = expr.ResolveAsTypeStep (ec);
8371 if (new_expr == null)
8372 return null;
8374 string lookup_id = MemberName.MakeName (Identifier, args);
8376 if (new_expr is Namespace) {
8377 Namespace ns = (Namespace) new_expr;
8378 FullNamedExpression retval = ns.Lookup (ec.DeclSpace, lookup_id, loc);
8379 if ((retval != null) && (args != null))
8380 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec);
8381 if (!silent && retval == null)
8382 Report.Error (234, loc, "The type or namespace name `{0}' could not be found in namespace `{1}'", Identifier, ns.FullName);
8383 return retval;
8386 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (ec);
8387 if (tnew_expr == null)
8388 return null;
8390 Type expr_type = tnew_expr.Type;
8392 if (expr_type.IsPointer){
8393 Error (23, "The `.' operator can not be applied to pointer operands (" +
8394 TypeManager.CSharpName (expr_type) + ")");
8395 return null;
8398 Expression member_lookup;
8399 member_lookup = MemberLookupFinal (ec, expr_type, expr_type, lookup_id, loc);
8400 if (!silent && member_lookup == null) {
8401 Report.Error (234, loc, "The type name `{0}' could not be found in type `{1}'",
8402 Identifier, new_expr.FullName);
8403 return null;
8406 if (!(member_lookup is TypeExpr)) {
8407 Report.Error (118, loc, "'{0}.{1}' denotes a '{2}', where a type was expected",
8408 new_expr.FullName, Identifier, member_lookup.ExprClassName ());
8409 return null;
8412 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (ec);
8413 if (texpr == null)
8414 return null;
8416 TypeArguments the_args = args;
8417 if (TypeManager.HasGenericArguments (expr_type)) {
8418 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
8420 TypeArguments new_args = new TypeArguments (loc);
8421 foreach (Type decl in decl_args)
8422 new_args.Add (new TypeExpression (decl, loc));
8424 if (args != null)
8425 new_args.Add (args);
8427 the_args = new_args;
8430 if (the_args != null) {
8431 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
8432 return ctype.ResolveAsTypeStep (ec);
8435 return texpr;
8438 public override void Emit (EmitContext ec)
8440 throw new Exception ("Should not happen");
8443 public override string ToString ()
8445 return expr + "." + MemberName.MakeName (Identifier, args);
8449 /// <summary>
8450 /// Implements checked expressions
8451 /// </summary>
8452 public class CheckedExpr : Expression {
8454 public Expression Expr;
8456 public CheckedExpr (Expression e, Location l)
8458 Expr = e;
8459 loc = l;
8462 public override Expression DoResolve (EmitContext ec)
8464 bool last_check = ec.CheckState;
8465 bool last_const_check = ec.ConstantCheckState;
8467 ec.CheckState = true;
8468 ec.ConstantCheckState = true;
8469 Expr = Expr.Resolve (ec);
8470 ec.CheckState = last_check;
8471 ec.ConstantCheckState = last_const_check;
8473 if (Expr == null)
8474 return null;
8476 if (Expr is Constant)
8477 return Expr;
8479 eclass = Expr.eclass;
8480 type = Expr.Type;
8481 return this;
8484 public override void Emit (EmitContext ec)
8486 bool last_check = ec.CheckState;
8487 bool last_const_check = ec.ConstantCheckState;
8489 ec.CheckState = true;
8490 ec.ConstantCheckState = true;
8491 Expr.Emit (ec);
8492 ec.CheckState = last_check;
8493 ec.ConstantCheckState = last_const_check;
8498 /// <summary>
8499 /// Implements the unchecked expression
8500 /// </summary>
8501 public class UnCheckedExpr : Expression {
8503 public Expression Expr;
8505 public UnCheckedExpr (Expression e, Location l)
8507 Expr = e;
8508 loc = l;
8511 public override Expression DoResolve (EmitContext ec)
8513 bool last_check = ec.CheckState;
8514 bool last_const_check = ec.ConstantCheckState;
8516 ec.CheckState = false;
8517 ec.ConstantCheckState = false;
8518 Expr = Expr.Resolve (ec);
8519 ec.CheckState = last_check;
8520 ec.ConstantCheckState = last_const_check;
8522 if (Expr == null)
8523 return null;
8525 if (Expr is Constant)
8526 return Expr;
8528 eclass = Expr.eclass;
8529 type = Expr.Type;
8530 return this;
8533 public override void Emit (EmitContext ec)
8535 bool last_check = ec.CheckState;
8536 bool last_const_check = ec.ConstantCheckState;
8538 ec.CheckState = false;
8539 ec.ConstantCheckState = false;
8540 Expr.Emit (ec);
8541 ec.CheckState = last_check;
8542 ec.ConstantCheckState = last_const_check;
8547 /// <summary>
8548 /// An Element Access expression.
8550 /// During semantic analysis these are transformed into
8551 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
8552 /// </summary>
8553 public class ElementAccess : Expression {
8554 public ArrayList Arguments;
8555 public Expression Expr;
8557 public ElementAccess (Expression e, ArrayList e_list, Location l)
8559 Expr = e;
8561 loc = l;
8563 if (e_list == null)
8564 return;
8566 Arguments = new ArrayList ();
8567 foreach (Expression tmp in e_list)
8568 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
8572 bool CommonResolve (EmitContext ec)
8574 Expr = Expr.Resolve (ec);
8576 if (Expr == null)
8577 return false;
8579 if (Arguments == null)
8580 return false;
8582 foreach (Argument a in Arguments){
8583 if (!a.Resolve (ec, loc))
8584 return false;
8587 return true;
8590 Expression MakePointerAccess (EmitContext ec)
8592 Type t = Expr.Type;
8594 if (t == TypeManager.void_ptr_type){
8595 Error (242, "The array index operation is not valid for void pointers");
8596 return null;
8598 if (Arguments.Count != 1){
8599 Error (196, "A pointer must be indexed by a single value");
8600 return null;
8602 Expression p;
8604 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
8605 if (p == null)
8606 return null;
8607 return new Indirection (p, loc).Resolve (ec);
8610 public override Expression DoResolve (EmitContext ec)
8612 if (!CommonResolve (ec))
8613 return null;
8616 // We perform some simple tests, and then to "split" the emit and store
8617 // code we create an instance of a different class, and return that.
8619 // I am experimenting with this pattern.
8621 Type t = Expr.Type;
8623 if (t == TypeManager.array_type){
8624 Report.Error (21, loc, "Cannot use indexer on System.Array");
8625 return null;
8628 if (t.IsArray)
8629 return (new ArrayAccess (this, loc)).Resolve (ec);
8630 else if (t.IsPointer)
8631 return MakePointerAccess (ec);
8632 else
8633 return (new IndexerAccess (this, loc)).Resolve (ec);
8636 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8638 if (!CommonResolve (ec))
8639 return null;
8641 Type t = Expr.Type;
8642 if (t.IsArray)
8643 return (new ArrayAccess (this, loc)).ResolveLValue (ec, right_side);
8644 else if (t.IsPointer)
8645 return MakePointerAccess (ec);
8646 else
8647 return (new IndexerAccess (this, loc)).ResolveLValue (ec, right_side);
8650 public override void Emit (EmitContext ec)
8652 throw new Exception ("Should never be reached");
8656 /// <summary>
8657 /// Implements array access
8658 /// </summary>
8659 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
8661 // Points to our "data" repository
8663 ElementAccess ea;
8665 LocalTemporary temp;
8666 bool prepared;
8668 public ArrayAccess (ElementAccess ea_data, Location l)
8670 ea = ea_data;
8671 eclass = ExprClass.Variable;
8672 loc = l;
8675 public override Expression DoResolve (EmitContext ec)
8677 #if false
8678 ExprClass eclass = ea.Expr.eclass;
8680 // As long as the type is valid
8681 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
8682 eclass == ExprClass.Value)) {
8683 ea.Expr.Error_UnexpectedKind ("variable or value");
8684 return null;
8686 #endif
8688 Type t = ea.Expr.Type;
8689 if (t.GetArrayRank () != ea.Arguments.Count){
8690 ea.Error (22,
8691 "Incorrect number of indexes for array " +
8692 " expected: " + t.GetArrayRank () + " got: " +
8693 ea.Arguments.Count);
8694 return null;
8697 type = TypeManager.GetElementType (t);
8698 if (type.IsPointer && !ec.InUnsafe){
8699 UnsafeError (ea.Location);
8700 return null;
8703 foreach (Argument a in ea.Arguments){
8704 Type argtype = a.Type;
8706 if (argtype == TypeManager.int32_type ||
8707 argtype == TypeManager.uint32_type ||
8708 argtype == TypeManager.int64_type ||
8709 argtype == TypeManager.uint64_type) {
8710 Constant c = a.Expr as Constant;
8711 if (c != null && c.IsNegative) {
8712 Report.Warning (251, 2, a.Expr.Location, "Indexing an array with a negative index (array indices always start at zero)");
8714 continue;
8718 // Mhm. This is strage, because the Argument.Type is not the same as
8719 // Argument.Expr.Type: the value changes depending on the ref/out setting.
8721 // Wonder if I will run into trouble for this.
8723 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
8724 if (a.Expr == null)
8725 return null;
8728 eclass = ExprClass.Variable;
8730 return this;
8733 /// <summary>
8734 /// Emits the right opcode to load an object of Type `t'
8735 /// from an array of T
8736 /// </summary>
8737 static public void EmitLoadOpcode (ILGenerator ig, Type type)
8739 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
8740 ig.Emit (OpCodes.Ldelem_U1);
8741 else if (type == TypeManager.sbyte_type)
8742 ig.Emit (OpCodes.Ldelem_I1);
8743 else if (type == TypeManager.short_type)
8744 ig.Emit (OpCodes.Ldelem_I2);
8745 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
8746 ig.Emit (OpCodes.Ldelem_U2);
8747 else if (type == TypeManager.int32_type)
8748 ig.Emit (OpCodes.Ldelem_I4);
8749 else if (type == TypeManager.uint32_type)
8750 ig.Emit (OpCodes.Ldelem_U4);
8751 else if (type == TypeManager.uint64_type)
8752 ig.Emit (OpCodes.Ldelem_I8);
8753 else if (type == TypeManager.int64_type)
8754 ig.Emit (OpCodes.Ldelem_I8);
8755 else if (type == TypeManager.float_type)
8756 ig.Emit (OpCodes.Ldelem_R4);
8757 else if (type == TypeManager.double_type)
8758 ig.Emit (OpCodes.Ldelem_R8);
8759 else if (type == TypeManager.intptr_type)
8760 ig.Emit (OpCodes.Ldelem_I);
8761 else if (TypeManager.IsEnumType (type)){
8762 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
8763 } else if (type.IsValueType){
8764 ig.Emit (OpCodes.Ldelema, type);
8765 ig.Emit (OpCodes.Ldobj, type);
8766 } else if (type.IsGenericParameter)
8767 ig.Emit (OpCodes.Ldelem_Any, type);
8768 else
8769 ig.Emit (OpCodes.Ldelem_Ref);
8772 /// <summary>
8773 /// Returns the right opcode to store an object of Type `t'
8774 /// from an array of T.
8775 /// </summary>
8776 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
8778 //Console.WriteLine (new System.Diagnostics.StackTrace ());
8779 has_type_arg = false; is_stobj = false;
8780 t = TypeManager.TypeToCoreType (t);
8781 if (TypeManager.IsEnumType (t))
8782 t = TypeManager.EnumToUnderlying (t);
8783 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
8784 t == TypeManager.bool_type)
8785 return OpCodes.Stelem_I1;
8786 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
8787 t == TypeManager.char_type)
8788 return OpCodes.Stelem_I2;
8789 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
8790 return OpCodes.Stelem_I4;
8791 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
8792 return OpCodes.Stelem_I8;
8793 else if (t == TypeManager.float_type)
8794 return OpCodes.Stelem_R4;
8795 else if (t == TypeManager.double_type)
8796 return OpCodes.Stelem_R8;
8797 else if (t == TypeManager.intptr_type) {
8798 has_type_arg = true;
8799 is_stobj = true;
8800 return OpCodes.Stobj;
8801 } else if (t.IsValueType) {
8802 has_type_arg = true;
8803 is_stobj = true;
8804 return OpCodes.Stobj;
8805 } else if (t.IsGenericParameter) {
8806 has_type_arg = true;
8807 return OpCodes.Stelem_Any;
8808 } else
8809 return OpCodes.Stelem_Ref;
8812 MethodInfo FetchGetMethod ()
8814 ModuleBuilder mb = CodeGen.Module.Builder;
8815 int arg_count = ea.Arguments.Count;
8816 Type [] args = new Type [arg_count];
8817 MethodInfo get;
8819 for (int i = 0; i < arg_count; i++){
8820 //args [i++] = a.Type;
8821 args [i] = TypeManager.int32_type;
8824 get = mb.GetArrayMethod (
8825 ea.Expr.Type, "Get",
8826 CallingConventions.HasThis |
8827 CallingConventions.Standard,
8828 type, args);
8829 return get;
8833 MethodInfo FetchAddressMethod ()
8835 ModuleBuilder mb = CodeGen.Module.Builder;
8836 int arg_count = ea.Arguments.Count;
8837 Type [] args = new Type [arg_count];
8838 MethodInfo address;
8839 Type ret_type;
8841 ret_type = TypeManager.GetReferenceType (type);
8843 for (int i = 0; i < arg_count; i++){
8844 //args [i++] = a.Type;
8845 args [i] = TypeManager.int32_type;
8848 address = mb.GetArrayMethod (
8849 ea.Expr.Type, "Address",
8850 CallingConventions.HasThis |
8851 CallingConventions.Standard,
8852 ret_type, args);
8854 return address;
8858 // Load the array arguments into the stack.
8860 // If we have been requested to cache the values (cached_locations array
8861 // initialized), then load the arguments the first time and store them
8862 // in locals. otherwise load from local variables.
8864 void LoadArrayAndArguments (EmitContext ec)
8866 ILGenerator ig = ec.ig;
8868 ea.Expr.Emit (ec);
8869 foreach (Argument a in ea.Arguments){
8870 Type argtype = a.Expr.Type;
8872 a.Expr.Emit (ec);
8874 if (argtype == TypeManager.int64_type)
8875 ig.Emit (OpCodes.Conv_Ovf_I);
8876 else if (argtype == TypeManager.uint64_type)
8877 ig.Emit (OpCodes.Conv_Ovf_I_Un);
8881 public void Emit (EmitContext ec, bool leave_copy)
8883 int rank = ea.Expr.Type.GetArrayRank ();
8884 ILGenerator ig = ec.ig;
8886 if (!prepared) {
8887 LoadArrayAndArguments (ec);
8889 if (rank == 1)
8890 EmitLoadOpcode (ig, type);
8891 else {
8892 MethodInfo method;
8894 method = FetchGetMethod ();
8895 ig.Emit (OpCodes.Call, method);
8897 } else
8898 LoadFromPtr (ec.ig, this.type);
8900 if (leave_copy) {
8901 ec.ig.Emit (OpCodes.Dup);
8902 temp = new LocalTemporary (ec, this.type);
8903 temp.Store (ec);
8907 public override void Emit (EmitContext ec)
8909 Emit (ec, false);
8912 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8914 int rank = ea.Expr.Type.GetArrayRank ();
8915 ILGenerator ig = ec.ig;
8916 Type t = source.Type;
8917 prepared = prepare_for_load;
8919 if (prepare_for_load) {
8920 AddressOf (ec, AddressOp.LoadStore);
8921 ec.ig.Emit (OpCodes.Dup);
8922 source.Emit (ec);
8923 if (leave_copy) {
8924 ec.ig.Emit (OpCodes.Dup);
8925 temp = new LocalTemporary (ec, this.type);
8926 temp.Store (ec);
8928 StoreFromPtr (ec.ig, t);
8930 if (temp != null)
8931 temp.Emit (ec);
8933 return;
8936 LoadArrayAndArguments (ec);
8938 if (rank == 1) {
8939 bool is_stobj, has_type_arg;
8940 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
8943 // The stobj opcode used by value types will need
8944 // an address on the stack, not really an array/array
8945 // pair
8947 if (is_stobj)
8948 ig.Emit (OpCodes.Ldelema, t);
8950 source.Emit (ec);
8951 if (leave_copy) {
8952 ec.ig.Emit (OpCodes.Dup);
8953 temp = new LocalTemporary (ec, this.type);
8954 temp.Store (ec);
8957 if (is_stobj)
8958 ig.Emit (OpCodes.Stobj, t);
8959 else if (has_type_arg)
8960 ig.Emit (op, t);
8961 else
8962 ig.Emit (op);
8963 } else {
8964 ModuleBuilder mb = CodeGen.Module.Builder;
8965 int arg_count = ea.Arguments.Count;
8966 Type [] args = new Type [arg_count + 1];
8967 MethodInfo set;
8969 source.Emit (ec);
8970 if (leave_copy) {
8971 ec.ig.Emit (OpCodes.Dup);
8972 temp = new LocalTemporary (ec, this.type);
8973 temp.Store (ec);
8976 for (int i = 0; i < arg_count; i++){
8977 //args [i++] = a.Type;
8978 args [i] = TypeManager.int32_type;
8981 args [arg_count] = type;
8983 set = mb.GetArrayMethod (
8984 ea.Expr.Type, "Set",
8985 CallingConventions.HasThis |
8986 CallingConventions.Standard,
8987 TypeManager.void_type, args);
8989 ig.Emit (OpCodes.Call, set);
8992 if (temp != null)
8993 temp.Emit (ec);
8996 public void AddressOf (EmitContext ec, AddressOp mode)
8998 int rank = ea.Expr.Type.GetArrayRank ();
8999 ILGenerator ig = ec.ig;
9001 LoadArrayAndArguments (ec);
9003 if (rank == 1){
9004 ig.Emit (OpCodes.Ldelema, type);
9005 } else {
9006 MethodInfo address = FetchAddressMethod ();
9007 ig.Emit (OpCodes.Call, address);
9013 class Indexers {
9014 public ArrayList Properties;
9015 static Hashtable map;
9017 public struct Indexer {
9018 public readonly Type Type;
9019 public readonly MethodInfo Getter, Setter;
9021 public Indexer (Type type, MethodInfo get, MethodInfo set)
9023 this.Type = type;
9024 this.Getter = get;
9025 this.Setter = set;
9029 static Indexers ()
9031 map = new Hashtable ();
9034 Indexers ()
9036 Properties = new ArrayList ();
9039 void Append (MemberInfo [] mi)
9041 foreach (PropertyInfo property in mi){
9042 MethodInfo get, set;
9044 get = property.GetGetMethod (true);
9045 set = property.GetSetMethod (true);
9046 Properties.Add (new Indexer (property.PropertyType, get, set));
9050 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
9052 string p_name = TypeManager.IndexerPropertyName (lookup_type);
9054 MemberInfo [] mi = TypeManager.MemberLookup (
9055 caller_type, caller_type, lookup_type, MemberTypes.Property,
9056 BindingFlags.Public | BindingFlags.Instance |
9057 BindingFlags.DeclaredOnly, p_name, null);
9059 if (mi == null || mi.Length == 0)
9060 return null;
9062 return mi;
9065 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type, Location loc)
9067 Indexers ix = (Indexers) map [lookup_type];
9069 if (ix != null)
9070 return ix;
9072 Type copy = lookup_type;
9073 while (copy != TypeManager.object_type && copy != null){
9074 MemberInfo [] mi = GetIndexersForTypeOrInterface (caller_type, copy);
9076 if (mi != null){
9077 if (ix == null)
9078 ix = new Indexers ();
9080 ix.Append (mi);
9083 copy = copy.BaseType;
9086 if (!lookup_type.IsInterface)
9087 return ix;
9089 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
9090 if (ifaces != null) {
9091 foreach (Type itype in ifaces) {
9092 MemberInfo [] mi = GetIndexersForTypeOrInterface (caller_type, itype);
9093 if (mi != null){
9094 if (ix == null)
9095 ix = new Indexers ();
9097 ix.Append (mi);
9102 return ix;
9106 /// <summary>
9107 /// Expressions that represent an indexer call.
9108 /// </summary>
9109 public class IndexerAccess : Expression, IAssignMethod {
9111 // Points to our "data" repository
9113 MethodInfo get, set;
9114 ArrayList set_arguments;
9115 bool is_base_indexer;
9117 protected Type indexer_type;
9118 protected Type current_type;
9119 protected Expression instance_expr;
9120 protected ArrayList arguments;
9122 public IndexerAccess (ElementAccess ea, Location loc)
9123 : this (ea.Expr, false, loc)
9125 this.arguments = ea.Arguments;
9128 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
9129 Location loc)
9131 this.instance_expr = instance_expr;
9132 this.is_base_indexer = is_base_indexer;
9133 this.eclass = ExprClass.Value;
9134 this.loc = loc;
9137 protected virtual bool CommonResolve (EmitContext ec)
9139 indexer_type = instance_expr.Type;
9140 current_type = ec.ContainerType;
9142 return true;
9145 public override Expression DoResolve (EmitContext ec)
9147 ArrayList AllGetters = new ArrayList();
9148 if (!CommonResolve (ec))
9149 return null;
9152 // Step 1: Query for all `Item' *properties*. Notice
9153 // that the actual methods are pointed from here.
9155 // This is a group of properties, piles of them.
9157 bool found_any = false, found_any_getters = false;
9158 Type lookup_type = indexer_type;
9160 Indexers ilist;
9161 ilist = Indexers.GetIndexersForType (current_type, lookup_type, loc);
9162 if (ilist != null) {
9163 found_any = true;
9164 if (ilist.Properties != null) {
9165 foreach (Indexers.Indexer ix in ilist.Properties) {
9166 if (ix.Getter != null)
9167 AllGetters.Add(ix.Getter);
9172 if (AllGetters.Count > 0) {
9173 found_any_getters = true;
9174 get = (MethodInfo) Invocation.OverloadResolve (
9175 ec, new MethodGroupExpr (AllGetters, loc),
9176 arguments, false, loc);
9179 if (!found_any) {
9180 Report.Error (21, loc,
9181 "Type `" + TypeManager.CSharpName (indexer_type) +
9182 "' does not have any indexers defined");
9183 return null;
9186 if (!found_any_getters) {
9187 Error (154, "indexer can not be used in this context, because " +
9188 "it lacks a `get' accessor");
9189 return null;
9192 if (get == null) {
9193 Error (1501, "No Overload for method `this' takes `" +
9194 arguments.Count + "' arguments");
9195 return null;
9199 // Only base will allow this invocation to happen.
9201 if (get.IsAbstract && this is BaseIndexerAccess){
9202 Report.Error (205, loc, "Cannot call an abstract base indexer: " + Invocation.FullMethodDesc (get));
9203 return null;
9206 type = get.ReturnType;
9207 if (type.IsPointer && !ec.InUnsafe){
9208 UnsafeError (loc);
9209 return null;
9212 instance_expr.CheckMarshallByRefAccess (ec.ContainerType);
9214 eclass = ExprClass.IndexerAccess;
9215 return this;
9218 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
9220 ArrayList AllSetters = new ArrayList();
9221 if (!CommonResolve (ec))
9222 return null;
9224 bool found_any = false, found_any_setters = false;
9226 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type, loc);
9227 if (ilist != null) {
9228 found_any = true;
9229 if (ilist.Properties != null) {
9230 foreach (Indexers.Indexer ix in ilist.Properties) {
9231 if (ix.Setter != null)
9232 AllSetters.Add(ix.Setter);
9236 if (AllSetters.Count > 0) {
9237 found_any_setters = true;
9238 set_arguments = (ArrayList) arguments.Clone ();
9239 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
9240 set = (MethodInfo) Invocation.OverloadResolve (
9241 ec, new MethodGroupExpr (AllSetters, loc),
9242 set_arguments, false, loc);
9245 if (!found_any) {
9246 Report.Error (21, loc,
9247 "Type `" + TypeManager.CSharpName (indexer_type) +
9248 "' does not have any indexers defined");
9249 return null;
9252 if (!found_any_setters) {
9253 Error (154, "indexer can not be used in this context, because " +
9254 "it lacks a `set' accessor");
9255 return null;
9258 if (set == null) {
9259 Error (1501, "No Overload for method `this' takes `" +
9260 arguments.Count + "' arguments");
9261 return null;
9265 // Only base will allow this invocation to happen.
9267 if (set.IsAbstract && this is BaseIndexerAccess){
9268 Report.Error (205, loc, "Cannot call an abstract base indexer: " + Invocation.FullMethodDesc (set));
9269 return null;
9273 // Now look for the actual match in the list of indexers to set our "return" type
9275 type = TypeManager.void_type; // default value
9276 foreach (Indexers.Indexer ix in ilist.Properties){
9277 if (ix.Setter == set){
9278 type = ix.Type;
9279 break;
9283 instance_expr.CheckMarshallByRefAccess (ec.ContainerType);
9285 eclass = ExprClass.IndexerAccess;
9286 return this;
9289 bool prepared = false;
9290 LocalTemporary temp;
9292 public void Emit (EmitContext ec, bool leave_copy)
9294 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
9295 if (leave_copy) {
9296 ec.ig.Emit (OpCodes.Dup);
9297 temp = new LocalTemporary (ec, Type);
9298 temp.Store (ec);
9303 // source is ignored, because we already have a copy of it from the
9304 // LValue resolution and we have already constructed a pre-cached
9305 // version of the arguments (ea.set_arguments);
9307 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
9309 prepared = prepare_for_load;
9310 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
9312 if (prepared) {
9313 source.Emit (ec);
9314 if (leave_copy) {
9315 ec.ig.Emit (OpCodes.Dup);
9316 temp = new LocalTemporary (ec, Type);
9317 temp.Store (ec);
9319 } else if (leave_copy) {
9320 temp = new LocalTemporary (ec, Type);
9321 source.Emit (ec);
9322 temp.Store (ec);
9323 a.Expr = temp;
9326 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
9328 if (temp != null)
9329 temp.Emit (ec);
9333 public override void Emit (EmitContext ec)
9335 Emit (ec, false);
9339 /// <summary>
9340 /// The base operator for method names
9341 /// </summary>
9342 public class BaseAccess : Expression {
9343 public string member;
9345 public BaseAccess (string member, Location l)
9347 this.member = member;
9348 loc = l;
9351 public override Expression DoResolve (EmitContext ec)
9353 Expression c = CommonResolve (ec);
9355 if (c == null)
9356 return null;
9359 // MethodGroups use this opportunity to flag an error on lacking ()
9361 if (!(c is MethodGroupExpr))
9362 return c.Resolve (ec);
9363 return c;
9366 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
9368 Expression c = CommonResolve (ec);
9370 if (c == null)
9371 return null;
9374 // MethodGroups use this opportunity to flag an error on lacking ()
9376 if (! (c is MethodGroupExpr))
9377 return c.DoResolveLValue (ec, right_side);
9379 return c;
9382 Expression CommonResolve (EmitContext ec)
9384 Expression member_lookup;
9385 Type current_type = ec.ContainerType;
9386 Type base_type = current_type.BaseType;
9387 Expression e;
9389 if (ec.IsStatic){
9390 Error (1511, "Keyword base is not allowed in static method");
9391 return null;
9394 if (ec.IsFieldInitializer){
9395 Error (1512, "Keyword base is not available in the current context");
9396 return null;
9399 member_lookup = MemberLookup (ec, ec.ContainerType, null, base_type,
9400 member, AllMemberTypes, AllBindingFlags,
9401 loc);
9402 if (member_lookup == null) {
9403 MemberLookupFailed (
9404 ec, base_type, base_type, member, null, loc);
9405 return null;
9408 Expression left;
9410 if (ec.IsStatic)
9411 left = new TypeExpression (base_type, loc);
9412 else
9413 left = ec.GetThis (loc);
9415 e = MemberAccess.ResolveMemberAccess (ec, member_lookup, left, loc, null);
9417 if (e is PropertyExpr){
9418 PropertyExpr pe = (PropertyExpr) e;
9420 pe.IsBase = true;
9423 if (e is MethodGroupExpr)
9424 ((MethodGroupExpr) e).IsBase = true;
9426 return e;
9429 public override void Emit (EmitContext ec)
9431 throw new Exception ("Should never be called");
9435 /// <summary>
9436 /// The base indexer operator
9437 /// </summary>
9438 public class BaseIndexerAccess : IndexerAccess {
9439 public BaseIndexerAccess (ArrayList args, Location loc)
9440 : base (null, true, loc)
9442 arguments = new ArrayList ();
9443 foreach (Expression tmp in args)
9444 arguments.Add (new Argument (tmp, Argument.AType.Expression));
9447 protected override bool CommonResolve (EmitContext ec)
9449 instance_expr = ec.GetThis (loc);
9451 current_type = ec.ContainerType.BaseType;
9452 indexer_type = current_type;
9454 foreach (Argument a in arguments){
9455 if (!a.Resolve (ec, loc))
9456 return false;
9459 return true;
9463 /// <summary>
9464 /// This class exists solely to pass the Type around and to be a dummy
9465 /// that can be passed to the conversion functions (this is used by
9466 /// foreach implementation to typecast the object return value from
9467 /// get_Current into the proper type. All code has been generated and
9468 /// we only care about the side effect conversions to be performed
9470 /// This is also now used as a placeholder where a no-action expression
9471 /// is needed (the `New' class).
9472 /// </summary>
9473 public class EmptyExpression : Expression {
9474 public static readonly EmptyExpression Null = new EmptyExpression ();
9476 // TODO: should be protected
9477 public EmptyExpression ()
9479 type = TypeManager.object_type;
9480 eclass = ExprClass.Value;
9481 loc = Location.Null;
9484 public EmptyExpression (Type t)
9486 type = t;
9487 eclass = ExprClass.Value;
9488 loc = Location.Null;
9491 public override Expression DoResolve (EmitContext ec)
9493 return this;
9496 public override void Emit (EmitContext ec)
9498 // nothing, as we only exist to not do anything.
9502 // This is just because we might want to reuse this bad boy
9503 // instead of creating gazillions of EmptyExpressions.
9504 // (CanImplicitConversion uses it)
9506 public void SetType (Type t)
9508 type = t;
9512 public class UserCast : Expression {
9513 MethodBase method;
9514 Expression source;
9516 public UserCast (MethodInfo method, Expression source, Location l)
9518 this.method = method;
9519 this.source = source;
9520 type = method.ReturnType;
9521 eclass = ExprClass.Value;
9522 loc = l;
9525 public Expression Source {
9526 get {
9527 return source;
9531 public override Expression DoResolve (EmitContext ec)
9534 // We are born fully resolved
9536 return this;
9539 public override void Emit (EmitContext ec)
9541 ILGenerator ig = ec.ig;
9543 source.Emit (ec);
9545 if (method is MethodInfo)
9546 ig.Emit (OpCodes.Call, (MethodInfo) method);
9547 else
9548 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
9553 // <summary>
9554 // This class is used to "construct" the type during a typecast
9555 // operation. Since the Type.GetType class in .NET can parse
9556 // the type specification, we just use this to construct the type
9557 // one bit at a time.
9558 // </summary>
9559 public class ComposedCast : TypeExpr {
9560 Expression left;
9561 string dim;
9563 public ComposedCast (Expression left, string dim, Location l)
9565 this.left = left;
9566 this.dim = dim;
9567 loc = l;
9570 protected override TypeExpr DoResolveAsTypeStep (EmitContext ec)
9572 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec);
9573 if (lexpr == null)
9574 return null;
9576 Type ltype = lexpr.Type;
9578 if ((ltype == TypeManager.void_type) && (dim != "*")) {
9579 Report.Error (1547, Location,
9580 "Keyword 'void' cannot be used in this context");
9581 return null;
9584 if ((dim.Length > 0) && (dim [0] == '?')) {
9585 TypeExpr nullable = new NullableType (left, loc);
9586 if (dim.Length > 1)
9587 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
9588 return nullable.ResolveAsTypeTerminal (ec);
9591 int pos = 0;
9592 while ((pos < dim.Length) && (dim [pos] == '[')) {
9593 pos++;
9595 if (dim [pos] == ']') {
9596 ltype = ltype.MakeArrayType ();
9597 pos++;
9599 if (pos < dim.Length)
9600 continue;
9602 type = ltype;
9603 eclass = ExprClass.Type;
9604 return this;
9607 int rank = 0;
9608 while (dim [pos] == ',') {
9609 pos++; rank++;
9612 if ((dim [pos] != ']') || (pos != dim.Length-1))
9613 return null;
9615 type = ltype.MakeArrayType (rank + 1);
9616 eclass = ExprClass.Type;
9617 return this;
9620 if (dim != "") {
9622 // ltype.Fullname is already fully qualified, so we can skip
9623 // a lot of probes, and go directly to TypeManager.LookupType
9625 string fname = ltype.FullName != null ? ltype.FullName : ltype.Name;
9626 string cname = fname + dim;
9627 type = TypeManager.LookupTypeDirect (cname);
9628 if (type == null){
9630 // For arrays of enumerations we are having a problem
9631 // with the direct lookup. Need to investigate.
9633 // For now, fall back to the full lookup in that case.
9635 FullNamedExpression e = ec.DeclSpace.LookupType (cname, false, loc);
9636 if (e is TypeExpr)
9637 type = ((TypeExpr) e).ResolveType (ec);
9638 if (type == null)
9639 return null;
9641 } else {
9642 type = ltype;
9645 if (!ec.InUnsafe && type.IsPointer){
9646 UnsafeError (loc);
9647 return null;
9650 if (type.IsArray && (type.GetElementType () == TypeManager.arg_iterator_type ||
9651 type.GetElementType () == TypeManager.typed_reference_type)) {
9652 Report.Error (611, loc, "Array elements cannot be of type '{0}'", TypeManager.CSharpName (type.GetElementType ()));
9653 return null;
9656 eclass = ExprClass.Type;
9657 return this;
9660 public override string Name {
9661 get {
9662 return left + dim;
9666 public override string FullName {
9667 get {
9668 return type.FullName;
9674 // This class is used to represent the address of an array, used
9675 // only by the Fixed statement, this is like the C "&a [0]" construct.
9677 public class ArrayPtr : Expression {
9678 Expression array;
9680 public ArrayPtr (Expression array, Location l)
9682 Type array_type = TypeManager.GetElementType (array.Type);
9684 this.array = array;
9686 type = TypeManager.GetPointerType (array_type);
9687 eclass = ExprClass.Value;
9688 loc = l;
9691 public override void Emit (EmitContext ec)
9693 ILGenerator ig = ec.ig;
9695 array.Emit (ec);
9696 IntLiteral.EmitInt (ig, 0);
9697 ig.Emit (OpCodes.Ldelema, TypeManager.GetElementType (array.Type));
9700 public override Expression DoResolve (EmitContext ec)
9703 // We are born fully resolved
9705 return this;
9710 // Used by the fixed statement
9712 public class StringPtr : Expression {
9713 LocalBuilder b;
9715 public StringPtr (LocalBuilder b, Location l)
9717 this.b = b;
9718 eclass = ExprClass.Value;
9719 type = TypeManager.char_ptr_type;
9720 loc = l;
9723 public override Expression DoResolve (EmitContext ec)
9725 // This should never be invoked, we are born in fully
9726 // initialized state.
9728 return this;
9731 public override void Emit (EmitContext ec)
9733 ILGenerator ig = ec.ig;
9735 ig.Emit (OpCodes.Ldloc, b);
9736 ig.Emit (OpCodes.Conv_I);
9737 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
9738 ig.Emit (OpCodes.Add);
9743 // Implements the `stackalloc' keyword
9745 public class StackAlloc : Expression {
9746 Type otype;
9747 Expression t;
9748 Expression count;
9750 public StackAlloc (Expression type, Expression count, Location l)
9752 t = type;
9753 this.count = count;
9754 loc = l;
9757 public override Expression DoResolve (EmitContext ec)
9759 count = count.Resolve (ec);
9760 if (count == null)
9761 return null;
9763 if (count.Type != TypeManager.int32_type){
9764 count = Convert.WideningConversionRequired (ec, count, TypeManager.int32_type, loc);
9765 if (count == null)
9766 return null;
9769 Constant c = count as Constant;
9770 if (c != null && c.IsNegative) {
9771 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
9772 return null;
9775 if (ec.CurrentBranching.InCatch () ||
9776 ec.CurrentBranching.InFinally (true)) {
9777 Error (255,
9778 "stackalloc can not be used in a catch or finally block");
9779 return null;
9782 TypeExpr texpr = t.ResolveAsTypeTerminal (ec);
9783 if (texpr == null)
9784 return null;
9786 otype = texpr.Type;
9788 if (!TypeManager.VerifyUnManaged (otype, loc))
9789 return null;
9791 type = TypeManager.GetPointerType (otype);
9792 eclass = ExprClass.Value;
9794 return this;
9797 public override void Emit (EmitContext ec)
9799 int size = GetTypeSize (otype);
9800 ILGenerator ig = ec.ig;
9802 if (size == 0)
9803 ig.Emit (OpCodes.Sizeof, otype);
9804 else
9805 IntConstant.EmitInt (ig, size);
9806 count.Emit (ec);
9807 ig.Emit (OpCodes.Mul);
9808 ig.Emit (OpCodes.Localloc);