2 // expression.cs: Expression representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@seznam.cz)
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2003, 2004 Novell, Inc.
13 namespace Mono.CSharp {
15 using System.Collections;
16 using System.Reflection;
17 using System.Reflection.Emit;
21 /// This is just a helper class, it is generated by Unary, UnaryMutator
22 /// when an overloaded method has been found. It just emits the code for a
25 public class StaticCallExpr : ExpressionStatement {
29 public StaticCallExpr (MethodInfo m, ArrayList a, Location l)
35 eclass = ExprClass.Value;
39 public override Expression DoResolve (EmitContext ec)
42 // We are born fully resolved
47 public override void Emit (EmitContext ec)
50 Invocation.EmitArguments (ec, mi, args, false, null);
52 ec.ig.Emit (OpCodes.Call, mi);
56 static public StaticCallExpr MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
57 Expression e, Location loc)
61 args = new ArrayList (1);
62 Argument a = new Argument (e, Argument.AType.Expression);
64 // We need to resolve the arguments before sending them in !
65 if (!a.Resolve (ec, loc))
69 mg = mg.OverloadResolve (ec, args, false, loc);
74 return new StaticCallExpr ((MethodInfo) mg, args, loc);
77 public override void EmitStatement (EmitContext ec)
80 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
81 ec.ig.Emit (OpCodes.Pop);
84 public MethodInfo Method {
89 public class ParenthesizedExpression : Expression
91 public Expression Expr;
93 public ParenthesizedExpression (Expression expr)
98 public override Expression DoResolve (EmitContext ec)
100 Expr = Expr.Resolve (ec);
104 public override void Emit (EmitContext ec)
106 throw new Exception ("Should not happen");
109 public override Location Location
112 return Expr.Location;
116 protected override void CloneTo (CloneContext clonectx, Expression t)
118 ParenthesizedExpression target = (ParenthesizedExpression) t;
120 target.Expr = Expr.Clone (clonectx);
125 /// Unary expressions.
129 /// Unary implements unary expressions. It derives from
130 /// ExpressionStatement becuase the pre/post increment/decrement
131 /// operators can be used in a statement context.
133 public class Unary : Expression {
134 public enum Operator : byte {
135 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
136 Indirection, AddressOf, TOP
139 public readonly Operator Oper;
140 public Expression Expr;
142 public Unary (Operator op, Expression expr, Location loc)
150 /// Returns a stringified representation of the Operator
152 static public string OperName (Operator oper)
155 case Operator.UnaryPlus:
157 case Operator.UnaryNegation:
159 case Operator.LogicalNot:
161 case Operator.OnesComplement:
163 case Operator.AddressOf:
165 case Operator.Indirection:
169 return oper.ToString ();
172 public static readonly string [] oper_names;
176 oper_names = new string [(int)Operator.TOP];
178 oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus";
179 oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation";
180 oper_names [(int) Operator.LogicalNot] = "op_LogicalNot";
181 oper_names [(int) Operator.OnesComplement] = "op_OnesComplement";
182 oper_names [(int) Operator.Indirection] = "op_Indirection";
183 oper_names [(int) Operator.AddressOf] = "op_AddressOf";
186 public static void Error_OperatorCannotBeApplied (Location loc, string oper, Type t)
188 Error_OperatorCannotBeApplied (loc, oper, TypeManager.CSharpName (t));
191 public static void Error_OperatorCannotBeApplied (Location loc, string oper, string type)
193 Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'",
197 void Error23 (Type t)
199 Error_OperatorCannotBeApplied (loc, OperName (Oper), t);
203 // This routine will attempt to simplify the unary expression when the
204 // argument is a constant.
206 Constant TryReduceConstant (EmitContext ec, Constant e)
208 Type expr_type = e.Type;
211 case Operator.UnaryPlus:
212 // Unary numeric promotions
213 if (expr_type == TypeManager.byte_type)
214 return new IntConstant (((ByteConstant)e).Value, e.Location);
215 if (expr_type == TypeManager.sbyte_type)
216 return new IntConstant (((SByteConstant)e).Value, e.Location);
217 if (expr_type == TypeManager.short_type)
218 return new IntConstant (((ShortConstant)e).Value, e.Location);
219 if (expr_type == TypeManager.ushort_type)
220 return new IntConstant (((UShortConstant)e).Value, e.Location);
221 if (expr_type == TypeManager.char_type)
222 return new IntConstant (((CharConstant)e).Value, e.Location);
224 // Predefined operators
225 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
226 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
227 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
228 expr_type == TypeManager.decimal_type)
235 case Operator.UnaryNegation:
236 // Unary numeric promotions
237 if (expr_type == TypeManager.byte_type)
238 return new IntConstant (-((ByteConstant)e).Value, e.Location);
239 if (expr_type == TypeManager.sbyte_type)
240 return new IntConstant (-((SByteConstant)e).Value, e.Location);
241 if (expr_type == TypeManager.short_type)
242 return new IntConstant (-((ShortConstant)e).Value, e.Location);
243 if (expr_type == TypeManager.ushort_type)
244 return new IntConstant (-((UShortConstant)e).Value, e.Location);
245 if (expr_type == TypeManager.char_type)
246 return new IntConstant (-((CharConstant)e).Value, e.Location);
248 // Predefined operators
249 if (expr_type == TypeManager.int32_type) {
250 int value = ((IntConstant)e).Value;
251 if (value == int.MinValue) {
252 if (ec.ConstantCheckState) {
253 ConstantFold.Error_CompileTimeOverflow (loc);
258 return new IntConstant (-value, e.Location);
260 if (expr_type == TypeManager.int64_type) {
261 long value = ((LongConstant)e).Value;
262 if (value == long.MinValue) {
263 if (ec.ConstantCheckState) {
264 ConstantFold.Error_CompileTimeOverflow (loc);
269 return new LongConstant (-value, e.Location);
272 if (expr_type == TypeManager.uint32_type) {
273 UIntLiteral uil = e as UIntLiteral;
275 if (uil.Value == 2147483648)
276 return new IntLiteral (int.MinValue, e.Location);
277 return new LongLiteral (-uil.Value, e.Location);
279 return new LongConstant (-((UIntConstant)e).Value, e.Location);
282 if (expr_type == TypeManager.uint64_type) {
283 ULongLiteral ull = e as ULongLiteral;
284 if (ull != null && ull.Value == 9223372036854775808)
285 return new LongLiteral (long.MinValue, e.Location);
289 if (expr_type == TypeManager.float_type) {
290 FloatLiteral fl = e as FloatLiteral;
291 // For better error reporting
293 fl.Value = -fl.Value;
296 return new FloatConstant (-((FloatConstant)e).Value, e.Location);
298 if (expr_type == TypeManager.double_type) {
299 DoubleLiteral dl = e as DoubleLiteral;
300 // For better error reporting
302 dl.Value = -dl.Value;
306 return new DoubleConstant (-((DoubleConstant)e).Value, e.Location);
308 if (expr_type == TypeManager.decimal_type)
309 return new DecimalConstant (-((DecimalConstant)e).Value, e.Location);
313 case Operator.LogicalNot:
314 if (expr_type != TypeManager.bool_type)
317 BoolConstant b = (BoolConstant) e;
318 return new BoolConstant (!(b.Value), b.Location);
320 case Operator.OnesComplement:
321 // Unary numeric promotions
322 if (expr_type == TypeManager.byte_type)
323 return new IntConstant (~((ByteConstant)e).Value, e.Location);
324 if (expr_type == TypeManager.sbyte_type)
325 return new IntConstant (~((SByteConstant)e).Value, e.Location);
326 if (expr_type == TypeManager.short_type)
327 return new IntConstant (~((ShortConstant)e).Value, e.Location);
328 if (expr_type == TypeManager.ushort_type)
329 return new IntConstant (~((UShortConstant)e).Value, e.Location);
330 if (expr_type == TypeManager.char_type)
331 return new IntConstant (~((CharConstant)e).Value, e.Location);
333 // Predefined operators
334 if (expr_type == TypeManager.int32_type)
335 return new IntConstant (~((IntConstant)e).Value, e.Location);
336 if (expr_type == TypeManager.uint32_type)
337 return new UIntConstant (~((UIntConstant)e).Value, e.Location);
338 if (expr_type == TypeManager.int64_type)
339 return new LongConstant (~((LongConstant)e).Value, e.Location);
340 if (expr_type == TypeManager.uint64_type){
341 return new ULongConstant (~((ULongConstant)e).Value, e.Location);
343 if (e is EnumConstant) {
344 e = TryReduceConstant (ec, ((EnumConstant)e).Child);
346 e = new EnumConstant (e, expr_type);
351 case Operator.AddressOf:
354 case Operator.Indirection:
357 throw new Exception ("Can not constant fold: " + Oper.ToString());
360 Expression ResolveOperator (EmitContext ec)
363 // Step 1: Default operations on CLI native types.
366 // Attempt to use a constant folding operation.
367 Constant cexpr = Expr as Constant;
369 cexpr = TryReduceConstant (ec, cexpr);
376 // Step 2: Perform Operator Overload location
378 Type expr_type = Expr.Type;
379 string op_name = oper_names [(int) Oper];
381 Expression mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
383 Expression e = StaticCallExpr.MakeSimpleCall (
384 ec, (MethodGroupExpr) mg, Expr, loc);
395 case Operator.LogicalNot:
396 if (expr_type != TypeManager.bool_type) {
397 Expr = ResolveBoolean (ec, Expr, loc);
404 type = TypeManager.bool_type;
407 case Operator.OnesComplement:
408 // Unary numeric promotions
409 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
410 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
411 expr_type == TypeManager.char_type)
413 type = TypeManager.int32_type;
414 return new EmptyCast (this, type);
417 // Predefined operators
418 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
419 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
420 TypeManager.IsEnumType (expr_type))
426 type = TypeManager.int32_type;
427 Expr = Convert.ImplicitUserConversion(ec, Expr, type, loc);
434 case Operator.AddressOf:
440 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){
444 IVariable variable = Expr as IVariable;
445 bool is_fixed = variable != null && variable.VerifyFixed ();
447 if (!ec.InFixedInitializer && !is_fixed) {
448 Error (212, "You can only take the address of unfixed expression inside " +
449 "of a fixed statement initializer");
453 if (ec.InFixedInitializer && is_fixed) {
454 Error (213, "You cannot use the fixed statement to take the address of an already fixed expression");
458 LocalVariableReference lr = Expr as LocalVariableReference;
460 if (lr.local_info.IsCaptured){
461 AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc);
464 lr.local_info.AddressTaken = true;
465 lr.local_info.Used = true;
468 ParameterReference pr = Expr as ParameterReference;
469 if ((pr != null) && pr.Parameter.IsCaptured) {
470 AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc);
474 // According to the specs, a variable is considered definitely assigned if you take
476 if ((variable != null) && (variable.VariableInfo != null)){
477 variable.VariableInfo.SetAssigned (ec);
480 type = TypeManager.GetPointerType (Expr.Type);
483 case Operator.Indirection:
489 if (!expr_type.IsPointer){
490 Error (193, "The * or -> operator must be applied to a pointer");
495 // We create an Indirection expression, because
496 // it can implement the IMemoryLocation.
498 return new Indirection (Expr, loc);
500 case Operator.UnaryPlus:
501 // Unary numeric promotions
502 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
503 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
504 expr_type == TypeManager.char_type)
506 return new EmptyCast (Expr, TypeManager.int32_type);
509 // Predefined operators
510 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
511 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
512 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
513 expr_type == TypeManager.decimal_type)
518 Expr = Convert.ImplicitUserConversion(ec, Expr, TypeManager.int32_type, loc);
520 // Because we can completely ignore unary +
527 case Operator.UnaryNegation:
529 // transform - - expr into expr
531 Unary u = Expr as Unary;
532 if (u != null && u.Oper == Operator.UnaryNegation) {
536 // Unary numeric promotions
537 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
538 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
539 expr_type == TypeManager.char_type)
541 type = TypeManager.int32_type;
542 return new EmptyCast (this, type);
546 // Predefined operators
548 if (expr_type == TypeManager.uint32_type) {
549 type = TypeManager.int64_type;
550 Expr = Convert.ImplicitNumericConversion (Expr, type);
554 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.int64_type ||
555 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
556 expr_type == TypeManager.decimal_type)
565 type = TypeManager.int32_type;
566 Expr = Convert.ImplicitUserConversion(ec, Expr, type, loc);
574 Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" +
575 TypeManager.CSharpName (expr_type) + "'");
579 public override Expression DoResolve (EmitContext ec)
581 if (Oper == Operator.AddressOf) {
582 Expr = Expr.DoResolveLValue (ec, new EmptyExpression ());
584 if (Expr == null || Expr.eclass != ExprClass.Variable){
585 Error (211, "Cannot take the address of the given expression");
590 Expr = Expr.Resolve (ec);
596 if (TypeManager.IsNullableValueType (Expr.Type))
597 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
600 eclass = ExprClass.Value;
601 return ResolveOperator (ec);
604 public override Expression DoResolveLValue (EmitContext ec, Expression right)
606 if (Oper == Operator.Indirection)
607 return DoResolve (ec);
612 public override void Emit (EmitContext ec)
614 ILGenerator ig = ec.ig;
617 case Operator.UnaryPlus:
618 throw new Exception ("This should be caught by Resolve");
620 case Operator.UnaryNegation:
621 if (ec.CheckState && type != TypeManager.float_type && type != TypeManager.double_type) {
622 ig.Emit (OpCodes.Ldc_I4_0);
623 if (type == TypeManager.int64_type)
624 ig.Emit (OpCodes.Conv_U8);
626 ig.Emit (OpCodes.Sub_Ovf);
629 ig.Emit (OpCodes.Neg);
634 case Operator.LogicalNot:
636 ig.Emit (OpCodes.Ldc_I4_0);
637 ig.Emit (OpCodes.Ceq);
640 case Operator.OnesComplement:
642 ig.Emit (OpCodes.Not);
645 case Operator.AddressOf:
646 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
650 throw new Exception ("This should not happen: Operator = "
655 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
657 if (Oper == Operator.LogicalNot)
658 Expr.EmitBranchable (ec, target, !onTrue);
660 base.EmitBranchable (ec, target, onTrue);
663 public override string ToString ()
665 return "Unary (" + Oper + ", " + Expr + ")";
668 protected override void CloneTo (CloneContext clonectx, Expression t)
670 Unary target = (Unary) t;
672 target.Expr = Expr.Clone (clonectx);
677 // Unary operators are turned into Indirection expressions
678 // after semantic analysis (this is so we can take the address
679 // of an indirection).
681 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
683 LocalTemporary temporary;
686 public Indirection (Expression expr, Location l)
689 type = TypeManager.HasElementType (expr.Type) ? TypeManager.GetElementType (expr.Type) : expr.Type;
690 eclass = ExprClass.Variable;
694 public override void Emit (EmitContext ec)
699 LoadFromPtr (ec.ig, Type);
702 public void Emit (EmitContext ec, bool leave_copy)
706 ec.ig.Emit (OpCodes.Dup);
707 temporary = new LocalTemporary (expr.Type);
708 temporary.Store (ec);
712 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
714 prepared = prepare_for_load;
718 if (prepare_for_load)
719 ec.ig.Emit (OpCodes.Dup);
723 ec.ig.Emit (OpCodes.Dup);
724 temporary = new LocalTemporary (expr.Type);
725 temporary.Store (ec);
728 StoreFromPtr (ec.ig, type);
730 if (temporary != null) {
732 temporary.Release (ec);
736 public void AddressOf (EmitContext ec, AddressOp Mode)
741 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
743 return DoResolve (ec);
746 public override Expression DoResolve (EmitContext ec)
749 // Born fully resolved
754 public override string ToString ()
756 return "*(" + expr + ")";
759 #region IVariable Members
761 public VariableInfo VariableInfo {
765 public bool VerifyFixed ()
767 // A pointer-indirection is always fixed.
775 /// Unary Mutator expressions (pre and post ++ and --)
779 /// UnaryMutator implements ++ and -- expressions. It derives from
780 /// ExpressionStatement becuase the pre/post increment/decrement
781 /// operators can be used in a statement context.
783 /// FIXME: Idea, we could split this up in two classes, one simpler
784 /// for the common case, and one with the extra fields for more complex
785 /// classes (indexers require temporary access; overloaded require method)
788 public class UnaryMutator : ExpressionStatement {
790 public enum Mode : byte {
797 PreDecrement = IsDecrement,
798 PostIncrement = IsPost,
799 PostDecrement = IsPost | IsDecrement
803 bool is_expr = false;
804 bool recurse = false;
809 // This is expensive for the simplest case.
811 StaticCallExpr method;
813 public UnaryMutator (Mode m, Expression e, Location l)
820 static string OperName (Mode mode)
822 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
827 /// Returns whether an object of type `t' can be incremented
828 /// or decremented with add/sub (ie, basically whether we can
829 /// use pre-post incr-decr operations on it, but it is not a
830 /// System.Decimal, which we require operator overloading to catch)
832 static bool IsIncrementableNumber (Type t)
834 return (t == TypeManager.sbyte_type) ||
835 (t == TypeManager.byte_type) ||
836 (t == TypeManager.short_type) ||
837 (t == TypeManager.ushort_type) ||
838 (t == TypeManager.int32_type) ||
839 (t == TypeManager.uint32_type) ||
840 (t == TypeManager.int64_type) ||
841 (t == TypeManager.uint64_type) ||
842 (t == TypeManager.char_type) ||
843 (t.IsSubclassOf (TypeManager.enum_type)) ||
844 (t == TypeManager.float_type) ||
845 (t == TypeManager.double_type) ||
846 (t.IsPointer && t != TypeManager.void_ptr_type);
849 Expression ResolveOperator (EmitContext ec)
851 Type expr_type = expr.Type;
854 // Step 1: Perform Operator Overload location
859 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
860 op_name = "op_Increment";
862 op_name = "op_Decrement";
864 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
867 method = StaticCallExpr.MakeSimpleCall (
868 ec, (MethodGroupExpr) mg, expr, loc);
871 } else if (!IsIncrementableNumber (expr_type)) {
872 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
873 TypeManager.CSharpName (expr_type) + "'");
878 // The operand of the prefix/postfix increment decrement operators
879 // should be an expression that is classified as a variable,
880 // a property access or an indexer access
883 if (expr.eclass == ExprClass.Variable){
884 LocalVariableReference var = expr as LocalVariableReference;
885 if ((var != null) && var.IsReadOnly) {
886 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
889 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
890 expr = expr.ResolveLValue (ec, this, Location);
894 if (expr.eclass == ExprClass.Value) {
895 Error_ValueAssignment (loc);
897 expr.Error_UnexpectedKind (ec.DeclContainer, "variable, indexer or property access", loc);
905 public override Expression DoResolve (EmitContext ec)
907 expr = expr.Resolve (ec);
912 eclass = ExprClass.Value;
915 if (TypeManager.IsNullableValueType (expr.Type))
916 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
919 return ResolveOperator (ec);
922 static int PtrTypeSize (Type t)
924 return GetTypeSize (TypeManager.GetElementType (t));
928 // Loads the proper "1" into the stack based on the type, then it emits the
929 // opcode for the operation requested
931 void LoadOneAndEmitOp (EmitContext ec, Type t)
934 // Measure if getting the typecode and using that is more/less efficient
935 // that comparing types. t.GetTypeCode() is an internal call.
937 ILGenerator ig = ec.ig;
939 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
940 LongConstant.EmitLong (ig, 1);
941 else if (t == TypeManager.double_type)
942 ig.Emit (OpCodes.Ldc_R8, 1.0);
943 else if (t == TypeManager.float_type)
944 ig.Emit (OpCodes.Ldc_R4, 1.0F);
945 else if (t.IsPointer){
946 int n = PtrTypeSize (t);
949 ig.Emit (OpCodes.Sizeof, t);
951 IntConstant.EmitInt (ig, n);
953 ig.Emit (OpCodes.Ldc_I4_1);
956 // Now emit the operation
959 if (t == TypeManager.int32_type ||
960 t == TypeManager.int64_type){
961 if ((mode & Mode.IsDecrement) != 0)
962 ig.Emit (OpCodes.Sub_Ovf);
964 ig.Emit (OpCodes.Add_Ovf);
965 } else if (t == TypeManager.uint32_type ||
966 t == TypeManager.uint64_type){
967 if ((mode & Mode.IsDecrement) != 0)
968 ig.Emit (OpCodes.Sub_Ovf_Un);
970 ig.Emit (OpCodes.Add_Ovf_Un);
972 if ((mode & Mode.IsDecrement) != 0)
973 ig.Emit (OpCodes.Sub_Ovf);
975 ig.Emit (OpCodes.Add_Ovf);
978 if ((mode & Mode.IsDecrement) != 0)
979 ig.Emit (OpCodes.Sub);
981 ig.Emit (OpCodes.Add);
984 if (t == TypeManager.sbyte_type){
986 ig.Emit (OpCodes.Conv_Ovf_I1);
988 ig.Emit (OpCodes.Conv_I1);
989 } else if (t == TypeManager.byte_type){
991 ig.Emit (OpCodes.Conv_Ovf_U1);
993 ig.Emit (OpCodes.Conv_U1);
994 } else if (t == TypeManager.short_type){
996 ig.Emit (OpCodes.Conv_Ovf_I2);
998 ig.Emit (OpCodes.Conv_I2);
999 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
1001 ig.Emit (OpCodes.Conv_Ovf_U2);
1003 ig.Emit (OpCodes.Conv_U2);
1008 void EmitCode (EmitContext ec, bool is_expr)
1011 this.is_expr = is_expr;
1012 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1015 public override void Emit (EmitContext ec)
1018 // We use recurse to allow ourselfs to be the source
1019 // of an assignment. This little hack prevents us from
1020 // having to allocate another expression
1023 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1025 LoadOneAndEmitOp (ec, expr.Type);
1027 ec.ig.Emit (OpCodes.Call, method.Method);
1032 EmitCode (ec, true);
1035 public override void EmitStatement (EmitContext ec)
1037 EmitCode (ec, false);
1040 protected override void CloneTo (CloneContext clonectx, Expression t)
1042 UnaryMutator target = (UnaryMutator) t;
1044 target.expr = expr.Clone (clonectx);
1049 /// Base class for the `Is' and `As' classes.
1053 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1056 public abstract class Probe : Expression {
1057 public Expression ProbeType;
1058 protected Expression expr;
1059 protected TypeExpr probe_type_expr;
1061 public Probe (Expression expr, Expression probe_type, Location l)
1063 ProbeType = probe_type;
1068 public Expression Expr {
1074 public override Expression DoResolve (EmitContext ec)
1076 probe_type_expr = ProbeType.ResolveAsTypeTerminal (ec, false);
1077 if (probe_type_expr == null)
1080 expr = expr.Resolve (ec);
1084 if (expr.Type.IsPointer) {
1085 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1091 protected override void CloneTo (CloneContext clonectx, Expression t)
1093 Probe target = (Probe) t;
1095 target.expr = expr.Clone (clonectx);
1096 target.ProbeType = ProbeType.Clone (clonectx);
1102 /// Implementation of the `is' operator.
1104 public class Is : Probe {
1105 public Is (Expression expr, Expression probe_type, Location l)
1106 : base (expr, probe_type, l)
1111 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1116 public override void Emit (EmitContext ec)
1118 ILGenerator ig = ec.ig;
1123 case Action.AlwaysFalse:
1124 ig.Emit (OpCodes.Pop);
1125 IntConstant.EmitInt (ig, 0);
1127 case Action.AlwaysTrue:
1128 ig.Emit (OpCodes.Pop);
1129 IntConstant.EmitInt (ig, 1);
1131 case Action.LeaveOnStack:
1132 // the `e != null' rule.
1133 ig.Emit (OpCodes.Ldnull);
1134 ig.Emit (OpCodes.Ceq);
1135 ig.Emit (OpCodes.Ldc_I4_0);
1136 ig.Emit (OpCodes.Ceq);
1139 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1140 ig.Emit (OpCodes.Ldnull);
1141 ig.Emit (OpCodes.Cgt_Un);
1144 throw new Exception ("never reached");
1147 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1149 ILGenerator ig = ec.ig;
1152 case Action.AlwaysFalse:
1154 ig.Emit (OpCodes.Br, target);
1157 case Action.AlwaysTrue:
1159 ig.Emit (OpCodes.Br, target);
1162 case Action.LeaveOnStack:
1163 // the `e != null' rule.
1165 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1169 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1170 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1173 throw new Exception ("never reached");
1176 public override Expression DoResolve (EmitContext ec)
1178 Expression e = base.DoResolve (ec);
1180 if ((e == null) || (expr == null))
1183 Type etype = expr.Type;
1184 type = TypeManager.bool_type;
1185 eclass = ExprClass.Value;
1188 // First case, if at compile time, there is an implicit conversion
1189 // then e != null (objects) or true (value types)
1191 Type probe_type = probe_type_expr.Type;
1192 e = Convert.ImplicitConversionStandard (ec, expr, probe_type, loc);
1195 if (etype.IsValueType)
1196 action = Action.AlwaysTrue;
1198 action = Action.LeaveOnStack;
1200 Constant c = e as Constant;
1201 if (c != null && c.GetValue () == null) {
1202 action = Action.AlwaysFalse;
1203 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
1204 TypeManager.CSharpName (probe_type));
1205 } else if (etype.IsValueType) {
1206 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
1207 TypeManager.CSharpName (probe_type));
1212 if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1213 if (TypeManager.IsGenericParameter (etype))
1214 expr = new BoxedCast (expr, etype);
1217 // Second case: explicit reference convresion
1219 if (expr is NullLiteral)
1220 action = Action.AlwaysFalse;
1222 action = Action.Probe;
1223 } else if (TypeManager.ContainsGenericParameters (etype) ||
1224 TypeManager.ContainsGenericParameters (probe_type)) {
1225 expr = new BoxedCast (expr, etype);
1226 action = Action.Probe;
1228 action = Action.AlwaysFalse;
1229 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1230 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1238 /// Implementation of the `as' operator.
1240 public class As : Probe {
1241 public As (Expression expr, Expression probe_type, Location l)
1242 : base (expr, probe_type, l)
1246 bool do_isinst = false;
1247 Expression resolved_type;
1249 public override void Emit (EmitContext ec)
1251 ILGenerator ig = ec.ig;
1256 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1259 if (TypeManager.IsNullableType (type))
1260 ig.Emit (OpCodes.Unbox_Any, type);
1264 static void Error_CannotConvertType (Type source, Type target, Location loc)
1266 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1267 TypeManager.CSharpName (source),
1268 TypeManager.CSharpName (target));
1271 public override Expression DoResolve (EmitContext ec)
1273 if (resolved_type == null) {
1274 resolved_type = base.DoResolve (ec);
1276 if (resolved_type == null)
1280 type = probe_type_expr.Type;
1281 eclass = ExprClass.Value;
1282 Type etype = expr.Type;
1284 if (type.IsValueType && !TypeManager.IsNullableType (type)) {
1285 Report.Error (77, loc, "The as operator must be used with a reference type (`" +
1286 TypeManager.CSharpName (type) + "' is a value type)");
1293 // If the type is a type parameter, ensure
1294 // that it is constrained by a class
1296 TypeParameterExpr tpe = probe_type_expr as TypeParameterExpr;
1298 GenericConstraints constraints = tpe.TypeParameter.GenericConstraints;
1301 if (constraints == null)
1304 if (!constraints.HasClassConstraint)
1305 if ((constraints.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == 0)
1309 Report.Error (413, loc,
1310 "The as operator requires that the `{0}' type parameter be constrained by a class",
1311 probe_type_expr.GetSignatureForError ());
1317 Expression e = Convert.ImplicitConversion (ec, expr, type, loc);
1324 if (Convert.ExplicitReferenceConversionExists (etype, type)){
1325 if (TypeManager.IsGenericParameter (etype))
1326 expr = new BoxedCast (expr, etype);
1332 if (TypeManager.ContainsGenericParameters (etype) ||
1333 TypeManager.ContainsGenericParameters (type)) {
1334 expr = new BoxedCast (expr, etype);
1339 Error_CannotConvertType (etype, type, loc);
1343 public override bool GetAttributableValue (Type valueType, out object value)
1345 return expr.GetAttributableValue (valueType, out value);
1350 /// This represents a typecast in the source language.
1352 /// FIXME: Cast expressions have an unusual set of parsing
1353 /// rules, we need to figure those out.
1355 public class Cast : Expression {
1356 Expression target_type;
1359 public Cast (Expression cast_type, Expression expr)
1360 : this (cast_type, expr, cast_type.Location)
1364 public Cast (Expression cast_type, Expression expr, Location loc)
1366 this.target_type = cast_type;
1370 if (target_type == TypeManager.system_void_expr)
1371 Error_VoidInvalidInTheContext (loc);
1374 public Expression TargetType {
1375 get { return target_type; }
1378 public Expression Expr {
1379 get { return expr; }
1382 public override Expression DoResolve (EmitContext ec)
1384 expr = expr.Resolve (ec);
1388 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1394 if (type.IsAbstract && type.IsSealed) {
1395 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1399 eclass = ExprClass.Value;
1401 Constant c = expr as Constant;
1403 c = c.TryReduce (ec, type, loc);
1408 if (type.IsPointer && !ec.InUnsafe) {
1412 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1416 public override void Emit (EmitContext ec)
1418 throw new Exception ("Should not happen");
1421 protected override void CloneTo (CloneContext clonectx, Expression t)
1423 Cast target = (Cast) t;
1425 target.target_type = target_type.Clone (clonectx);
1426 target.expr = expr.Clone (clonectx);
1431 /// Binary operators
1433 public class Binary : Expression {
1434 public enum Operator : byte {
1435 Multiply, Division, Modulus,
1436 Addition, Subtraction,
1437 LeftShift, RightShift,
1438 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1439 Equality, Inequality,
1448 readonly Operator oper;
1449 Expression left, right;
1451 // This must be kept in sync with Operator!!!
1452 public static readonly string [] oper_names;
1456 oper_names = new string [(int) Operator.TOP];
1458 oper_names [(int) Operator.Multiply] = "op_Multiply";
1459 oper_names [(int) Operator.Division] = "op_Division";
1460 oper_names [(int) Operator.Modulus] = "op_Modulus";
1461 oper_names [(int) Operator.Addition] = "op_Addition";
1462 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1463 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1464 oper_names [(int) Operator.RightShift] = "op_RightShift";
1465 oper_names [(int) Operator.LessThan] = "op_LessThan";
1466 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1467 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1468 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1469 oper_names [(int) Operator.Equality] = "op_Equality";
1470 oper_names [(int) Operator.Inequality] = "op_Inequality";
1471 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1472 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1473 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1474 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1475 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1478 public Binary (Operator oper, Expression left, Expression right)
1483 this.loc = left.Location;
1486 public Operator Oper {
1493 /// Returns a stringified representation of the Operator
1495 public static string OperName (Operator oper)
1498 case Operator.Multiply:
1500 case Operator.Division:
1502 case Operator.Modulus:
1504 case Operator.Addition:
1506 case Operator.Subtraction:
1508 case Operator.LeftShift:
1510 case Operator.RightShift:
1512 case Operator.LessThan:
1514 case Operator.GreaterThan:
1516 case Operator.LessThanOrEqual:
1518 case Operator.GreaterThanOrEqual:
1520 case Operator.Equality:
1522 case Operator.Inequality:
1524 case Operator.BitwiseAnd:
1526 case Operator.BitwiseOr:
1528 case Operator.ExclusiveOr:
1530 case Operator.LogicalOr:
1532 case Operator.LogicalAnd:
1536 return oper.ToString ();
1539 public override string ToString ()
1541 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1542 right.ToString () + ")";
1545 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1547 if (expr.Type == target_type)
1550 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1553 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1556 34, loc, "Operator `" + OperName (oper)
1557 + "' is ambiguous on operands of type `"
1558 + TypeManager.CSharpName (l) + "' "
1559 + "and `" + TypeManager.CSharpName (r)
1563 bool IsConvertible (EmitContext ec, Expression le, Expression re, Type t)
1565 return Convert.ImplicitConversionExists (ec, le, t) && Convert.ImplicitConversionExists (ec, re, t);
1568 bool VerifyApplicable_Predefined (EmitContext ec, Type t)
1570 if (!IsConvertible (ec, left, right, t))
1572 left = ForceConversion (ec, left, t);
1573 right = ForceConversion (ec, right, t);
1578 bool IsApplicable_String (EmitContext ec, Expression le, Expression re, Operator oper)
1580 bool l = Convert.ImplicitConversionExists (ec, le, TypeManager.string_type);
1581 bool r = Convert.ImplicitConversionExists (ec, re, TypeManager.string_type);
1583 if (oper == Operator.Equality || oper == Operator.Inequality)
1585 if (oper == Operator.Addition)
1590 bool OverloadResolve_PredefinedString (EmitContext ec, Operator oper)
1592 if (!IsApplicable_String (ec, left, right, oper))
1594 Type t = TypeManager.string_type;
1595 if (Convert.ImplicitConversionExists (ec, left, t))
1596 left = ForceConversion (ec, left, t);
1597 if (Convert.ImplicitConversionExists (ec, right, t))
1598 right = ForceConversion (ec, right, t);
1603 bool OverloadResolve_PredefinedIntegral (EmitContext ec)
1605 return VerifyApplicable_Predefined (ec, TypeManager.int32_type) ||
1606 VerifyApplicable_Predefined (ec, TypeManager.uint32_type) ||
1607 VerifyApplicable_Predefined (ec, TypeManager.int64_type) ||
1608 VerifyApplicable_Predefined (ec, TypeManager.uint64_type) ||
1612 bool OverloadResolve_PredefinedFloating (EmitContext ec)
1614 return VerifyApplicable_Predefined (ec, TypeManager.float_type) ||
1615 VerifyApplicable_Predefined (ec, TypeManager.double_type) ||
1619 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1621 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1624 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1626 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1630 void Error_OperatorCannotBeApplied ()
1632 Error_OperatorCannotBeApplied (Location, OperName (oper), TypeManager.CSharpName (left.Type),
1633 TypeManager.CSharpName(right.Type));
1636 static bool is_unsigned (Type t)
1638 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1639 t == TypeManager.short_type || t == TypeManager.byte_type);
1642 Expression Make32or64 (EmitContext ec, Expression e)
1646 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1647 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1649 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1652 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1655 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1658 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1664 Expression CheckShiftArguments (EmitContext ec)
1666 Expression new_left = Make32or64 (ec, left);
1667 Expression new_right = ForceConversion (ec, right, TypeManager.int32_type);
1668 if (new_left == null || new_right == null) {
1669 Error_OperatorCannotBeApplied ();
1672 type = new_left.Type;
1673 int shiftmask = (type == TypeManager.int32_type || type == TypeManager.uint32_type) ? 31 : 63;
1675 right = new Binary (Binary.Operator.BitwiseAnd, new_right, new IntConstant (shiftmask, loc)).DoResolve (ec);
1680 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1681 // i.e., not invoke op_Equality.
1683 static bool EqualsNullIsReferenceEquals (Type t)
1685 return t == TypeManager.object_type || t == TypeManager.string_type ||
1686 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1689 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1691 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1692 "Possible unintended reference comparison; to get a value comparison, " +
1693 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1696 Expression ResolveOperator (EmitContext ec)
1699 Type r = right.Type;
1701 if (oper == Operator.Equality || oper == Operator.Inequality){
1702 if (TypeManager.IsGenericParameter (l) && (right is NullLiteral)) {
1703 if (l.BaseType == TypeManager.value_type) {
1704 Error_OperatorCannotBeApplied ();
1708 left = new BoxedCast (left, TypeManager.object_type);
1709 Type = TypeManager.bool_type;
1713 if (TypeManager.IsGenericParameter (r) && (left is NullLiteral)) {
1714 if (r.BaseType == TypeManager.value_type) {
1715 Error_OperatorCannotBeApplied ();
1719 right = new BoxedCast (right, TypeManager.object_type);
1720 Type = TypeManager.bool_type;
1725 // Optimize out call to op_Equality in a few cases.
1727 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1728 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1729 Type = TypeManager.bool_type;
1734 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1735 Type = TypeManager.bool_type;
1741 // Delegate equality
1743 MethodGroupExpr mg = null;
1744 Type delegate_type = null;
1745 if (left.eclass == ExprClass.MethodGroup) {
1746 if (!TypeManager.IsDelegateType(r)) {
1747 Error_OperatorCannotBeApplied(Location, OperName(oper),
1748 left.ExprClassName, right.ExprClassName);
1751 mg = (MethodGroupExpr)left;
1753 } else if (right.eclass == ExprClass.MethodGroup) {
1754 if (!TypeManager.IsDelegateType(l)) {
1755 Error_OperatorCannotBeApplied(Location, OperName(oper),
1756 left.ExprClassName, right.ExprClassName);
1759 mg = (MethodGroupExpr)right;
1764 Expression e = ImplicitDelegateCreation.Create (ec, mg, delegate_type, loc);
1768 // Find operator method
1769 string op = oper_names[(int)oper];
1770 MemberInfo[] mi = TypeManager.MemberLookup(ec.ContainerType, null,
1771 TypeManager.delegate_type, MemberTypes.Method, AllBindingFlags, op, null);
1773 ArrayList args = new ArrayList(2);
1774 args.Add(new Argument(e, Argument.AType.Expression));
1775 if (delegate_type == l)
1776 args.Insert(0, new Argument(left, Argument.AType.Expression));
1778 args.Add(new Argument(right, Argument.AType.Expression));
1780 return new BinaryMethod (TypeManager.bool_type, (MethodInfo)mi [0], args);
1783 if (l == TypeManager.anonymous_method_type || r == TypeManager.anonymous_method_type) {
1784 Error_OperatorCannotBeApplied(Location, OperName(oper),
1785 left.ExprClassName, right.ExprClassName);
1792 // Do not perform operator overload resolution when both sides are
1795 MethodGroupExpr left_operators = null, right_operators = null;
1796 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))) {
1798 // Step 1: Perform Operator Overload location
1800 string op = oper_names [(int) oper];
1802 MethodGroupExpr union;
1803 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc) as MethodGroupExpr;
1805 right_operators = MemberLookup (
1806 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc) as MethodGroupExpr;
1807 union = MethodGroupExpr.MakeUnionSet (left_operators, right_operators, loc);
1809 union = left_operators;
1811 if (union != null) {
1812 ArrayList args = new ArrayList (2);
1813 args.Add (new Argument (left, Argument.AType.Expression));
1814 args.Add (new Argument (right, Argument.AType.Expression));
1816 union = union.OverloadResolve (ec, args, true, Location.Null);
1818 if (union != null) {
1819 MethodInfo mi = (MethodInfo) union;
1820 return new BinaryMethod (mi.ReturnType, mi, args);
1826 // Step 0: String concatenation (because overloading will get this wrong)
1828 if (oper == Operator.Addition){
1830 // If any of the arguments is a string, cast to string
1833 // Simple constant folding
1834 if (left is StringConstant && right is StringConstant)
1835 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1837 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1839 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1840 Error_OperatorCannotBeApplied ();
1844 // try to fold it in on the left
1845 if (left is StringConcat) {
1848 // We have to test here for not-null, since we can be doubly-resolved
1849 // take care of not appending twice
1852 type = TypeManager.string_type;
1853 ((StringConcat) left).Append (ec, right);
1854 return left.Resolve (ec);
1860 // Otherwise, start a new concat expression
1861 return new StringConcat (ec, loc, left, right).Resolve (ec);
1865 // Transform a + ( - b) into a - b
1867 if (right is Unary){
1868 Unary right_unary = (Unary) right;
1870 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1871 return new Binary (Operator.Subtraction, left, right_unary.Expr).Resolve (ec);
1876 if (oper == Operator.Equality || oper == Operator.Inequality){
1877 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1878 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1879 Error_OperatorCannotBeApplied ();
1883 type = TypeManager.bool_type;
1887 if (l.IsPointer || r.IsPointer) {
1888 if (l.IsPointer && r.IsPointer) {
1889 type = TypeManager.bool_type;
1893 if (l.IsPointer && r == TypeManager.null_type) {
1894 right = new EmptyCast (NullPointer.Null, l);
1895 type = TypeManager.bool_type;
1899 if (r.IsPointer && l == TypeManager.null_type) {
1900 left = new EmptyCast (NullPointer.Null, r);
1901 type = TypeManager.bool_type;
1907 if (l.IsGenericParameter && r.IsGenericParameter) {
1908 GenericConstraints l_gc, r_gc;
1910 l_gc = TypeManager.GetTypeParameterConstraints (l);
1911 r_gc = TypeManager.GetTypeParameterConstraints (r);
1913 if ((l_gc == null) || (r_gc == null) ||
1914 !(l_gc.HasReferenceTypeConstraint || l_gc.HasClassConstraint) ||
1915 !(r_gc.HasReferenceTypeConstraint || r_gc.HasClassConstraint)) {
1916 Error_OperatorCannotBeApplied ();
1924 // operator != (object a, object b)
1925 // operator == (object a, object b)
1927 // For this to be used, both arguments have to be reference-types.
1928 // Read the rationale on the spec (14.9.6)
1930 if (!(l.IsValueType || r.IsValueType)){
1931 type = TypeManager.bool_type;
1937 // Also, a standard conversion must exist from either one
1939 bool left_to_right =
1940 Convert.ImplicitStandardConversionExists (left, r);
1941 bool right_to_left = !left_to_right &&
1942 Convert.ImplicitStandardConversionExists (right, l);
1944 if (!left_to_right && !right_to_left) {
1945 Error_OperatorCannotBeApplied ();
1949 if (left_to_right && left_operators != null &&
1950 Report.WarningLevel >= 2) {
1951 ArrayList args = new ArrayList (2);
1952 args.Add (new Argument (left, Argument.AType.Expression));
1953 args.Add (new Argument (left, Argument.AType.Expression));
1954 if (left_operators.OverloadResolve (ec, args, true, Location.Null) != null)
1955 Warning_UnintendedReferenceComparison (loc, "right", l);
1958 if (right_to_left && right_operators != null &&
1959 Report.WarningLevel >= 2) {
1960 ArrayList args = new ArrayList (2);
1961 args.Add (new Argument (right, Argument.AType.Expression));
1962 args.Add (new Argument (right, Argument.AType.Expression));
1963 if (right_operators.OverloadResolve (ec, args, true, Location.Null) != null)
1964 Warning_UnintendedReferenceComparison (loc, "left", r);
1968 // We are going to have to convert to an object to compare
1970 if (l != TypeManager.object_type)
1971 left = new EmptyCast (left, TypeManager.object_type);
1972 if (r != TypeManager.object_type)
1973 right = new EmptyCast (right, TypeManager.object_type);
1979 // Only perform numeric promotions on:
1980 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
1982 if (oper == Operator.Addition || oper == Operator.Subtraction) {
1983 if (TypeManager.IsDelegateType (l)){
1984 if (((right.eclass == ExprClass.MethodGroup) ||
1985 (r == TypeManager.anonymous_method_type))){
1986 if ((RootContext.Version != LanguageVersion.ISO_1)){
1987 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
1995 if (TypeManager.IsDelegateType (r) || right is NullLiteral){
1997 ArrayList args = new ArrayList (2);
1999 args = new ArrayList (2);
2000 args.Add (new Argument (left, Argument.AType.Expression));
2001 args.Add (new Argument (right, Argument.AType.Expression));
2003 if (oper == Operator.Addition)
2004 method = TypeManager.delegate_combine_delegate_delegate;
2006 method = TypeManager.delegate_remove_delegate_delegate;
2008 if (!TypeManager.IsEqual (l, r) && !(right is NullLiteral)) {
2009 Error_OperatorCannotBeApplied ();
2013 return new BinaryDelegate (l, method, args);
2018 // Pointer arithmetic:
2020 // T* operator + (T* x, int y);
2021 // T* operator + (T* x, uint y);
2022 // T* operator + (T* x, long y);
2023 // T* operator + (T* x, ulong y);
2025 // T* operator + (int y, T* x);
2026 // T* operator + (uint y, T *x);
2027 // T* operator + (long y, T *x);
2028 // T* operator + (ulong y, T *x);
2030 // T* operator - (T* x, int y);
2031 // T* operator - (T* x, uint y);
2032 // T* operator - (T* x, long y);
2033 // T* operator - (T* x, ulong y);
2035 // long operator - (T* x, T *y)
2038 if (r.IsPointer && oper == Operator.Subtraction){
2040 return new PointerArithmetic (
2041 false, left, right, TypeManager.int64_type,
2044 Expression t = Make32or64 (ec, right);
2046 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
2048 } else if (r.IsPointer && oper == Operator.Addition){
2049 Expression t = Make32or64 (ec, left);
2051 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
2056 // Enumeration operators
2058 bool lie = TypeManager.IsEnumType (l);
2059 bool rie = TypeManager.IsEnumType (r);
2063 // U operator - (E e, E f)
2065 if (oper == Operator.Subtraction){
2067 type = TypeManager.EnumToUnderlying (l);
2070 Error_OperatorCannotBeApplied ();
2076 // operator + (E e, U x)
2077 // operator - (E e, U x)
2079 if (oper == Operator.Addition || oper == Operator.Subtraction){
2080 Type enum_type = lie ? l : r;
2081 Type other_type = lie ? r : l;
2082 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2084 if (underlying_type != other_type){
2085 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2095 Error_OperatorCannotBeApplied ();
2104 temp = Convert.ImplicitConversion (ec, right, l, loc);
2108 Error_OperatorCannotBeApplied ();
2112 temp = Convert.ImplicitConversion (ec, left, r, loc);
2117 Error_OperatorCannotBeApplied ();
2122 if (oper == Operator.Equality || oper == Operator.Inequality ||
2123 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2124 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2125 if (left.Type != right.Type){
2126 Error_OperatorCannotBeApplied ();
2129 type = TypeManager.bool_type;
2133 if (oper == Operator.BitwiseAnd ||
2134 oper == Operator.BitwiseOr ||
2135 oper == Operator.ExclusiveOr){
2136 if (left.Type != right.Type){
2137 Error_OperatorCannotBeApplied ();
2143 Error_OperatorCannotBeApplied ();
2147 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2148 return CheckShiftArguments (ec);
2150 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2151 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2152 type = TypeManager.bool_type;
2156 Expression left_operators_e = l == TypeManager.bool_type ?
2157 left : Convert.ImplicitUserConversion (ec, left, TypeManager.bool_type, loc);
2158 Expression right_operators_e = r == TypeManager.bool_type ?
2159 right : Convert.ImplicitUserConversion (ec, right, TypeManager.bool_type, loc);
2161 if (left_operators_e != null && right_operators_e != null) {
2162 left = left_operators_e;
2163 right = right_operators_e;
2164 type = TypeManager.bool_type;
2168 Expression e = new ConditionalLogicalOperator (
2169 oper == Operator.LogicalAnd, left, right, l, loc);
2170 return e.Resolve (ec);
2173 Expression orig_left = left;
2174 Expression orig_right = right;
2177 // operator & (bool x, bool y)
2178 // operator | (bool x, bool y)
2179 // operator ^ (bool x, bool y)
2181 if (oper == Operator.BitwiseAnd ||
2182 oper == Operator.BitwiseOr ||
2183 oper == Operator.ExclusiveOr) {
2184 if (OverloadResolve_PredefinedIntegral (ec)) {
2185 if (IsConvertible (ec, orig_left, orig_right, TypeManager.bool_type)) {
2186 Error_OperatorAmbiguous (loc, oper, l, r);
2190 if (oper == Operator.BitwiseOr && l != r && !(orig_right is Constant) && right is OpcodeCast &&
2191 (r == TypeManager.sbyte_type || r == TypeManager.short_type ||
2192 r == TypeManager.int32_type || r == TypeManager.int64_type)) {
2193 Report.Warning (675, 3, loc, "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first",
2194 TypeManager.CSharpName (r));
2197 } else if (!VerifyApplicable_Predefined (ec, TypeManager.bool_type)) {
2198 Error_OperatorCannotBeApplied ();
2205 // Pointer comparison
2207 if (l.IsPointer && r.IsPointer){
2208 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2209 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2210 type = TypeManager.bool_type;
2215 if (OverloadResolve_PredefinedIntegral (ec)) {
2216 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2217 Error_OperatorAmbiguous (loc, oper, l, r);
2220 } else if (OverloadResolve_PredefinedFloating (ec)) {
2221 if (IsConvertible (ec, orig_left, orig_right, TypeManager.decimal_type) ||
2222 IsApplicable_String (ec, orig_left, orig_right, oper)) {
2223 Error_OperatorAmbiguous (loc, oper, l, r);
2226 } else if (VerifyApplicable_Predefined (ec, TypeManager.decimal_type)) {
2227 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2228 Error_OperatorAmbiguous (loc, oper, l, r);
2231 } else if (!OverloadResolve_PredefinedString (ec, oper)) {
2232 Error_OperatorCannotBeApplied ();
2236 if (oper == Operator.Equality ||
2237 oper == Operator.Inequality ||
2238 oper == Operator.LessThanOrEqual ||
2239 oper == Operator.LessThan ||
2240 oper == Operator.GreaterThanOrEqual ||
2241 oper == Operator.GreaterThan)
2242 type = TypeManager.bool_type;
2247 if (l == TypeManager.decimal_type || l == TypeManager.string_type || r == TypeManager.string_type) {
2249 if (r == TypeManager.string_type)
2251 MethodGroupExpr ops = (MethodGroupExpr) MemberLookup (
2252 ec.ContainerType, lookup, oper_names [(int) oper],
2253 MemberTypes.Method, AllBindingFlags, loc);
2254 ArrayList args = new ArrayList (2);
2255 args.Add (new Argument (left, Argument.AType.Expression));
2256 args.Add (new Argument (right, Argument.AType.Expression));
2257 ops = ops.OverloadResolve (ec, args, true, Location.Null);
2258 return new BinaryMethod (type, (MethodInfo)ops, args);
2264 Constant EnumLiftUp (Constant left, Constant right)
2267 case Operator.BitwiseOr:
2268 case Operator.BitwiseAnd:
2269 case Operator.ExclusiveOr:
2270 case Operator.Equality:
2271 case Operator.Inequality:
2272 case Operator.LessThan:
2273 case Operator.LessThanOrEqual:
2274 case Operator.GreaterThan:
2275 case Operator.GreaterThanOrEqual:
2276 if (left is EnumConstant)
2279 if (left.IsZeroInteger)
2280 return new EnumConstant (left, right.Type);
2284 case Operator.Addition:
2285 case Operator.Subtraction:
2288 case Operator.Multiply:
2289 case Operator.Division:
2290 case Operator.Modulus:
2291 case Operator.LeftShift:
2292 case Operator.RightShift:
2293 if (right is EnumConstant || left is EnumConstant)
2297 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2301 public override Expression DoResolve (EmitContext ec)
2306 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2307 left = ((ParenthesizedExpression) left).Expr;
2308 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2312 if (left.eclass == ExprClass.Type) {
2313 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2317 left = left.Resolve (ec);
2322 Constant lc = left as Constant;
2323 if (lc != null && lc.Type == TypeManager.bool_type &&
2324 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2325 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2327 // TODO: make a sense to resolve unreachable expression as we do for statement
2328 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2332 right = right.Resolve (ec);
2336 eclass = ExprClass.Value;
2337 Constant rc = right as Constant;
2339 // The conversion rules are ignored in enum context but why
2340 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2341 left = lc = EnumLiftUp (lc, rc);
2345 right = rc = EnumLiftUp (rc, lc);
2350 if (oper == Operator.BitwiseAnd) {
2351 if (rc != null && rc.IsZeroInteger) {
2352 return lc is EnumConstant ?
2353 new EnumConstant (rc, lc.Type):
2357 if (lc != null && lc.IsZeroInteger) {
2358 return rc is EnumConstant ?
2359 new EnumConstant (lc, rc.Type):
2363 else if (oper == Operator.BitwiseOr) {
2364 if (lc is EnumConstant &&
2365 rc != null && rc.IsZeroInteger)
2367 if (rc is EnumConstant &&
2368 lc != null && lc.IsZeroInteger)
2370 } else if (oper == Operator.LogicalAnd) {
2371 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2373 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2377 if (rc != null && lc != null){
2378 int prev_e = Report.Errors;
2379 Expression e = ConstantFold.BinaryFold (
2380 ec, oper, lc, rc, loc);
2381 if (e != null || Report.Errors != prev_e)
2386 if ((left is NullLiteral || left.Type.IsValueType) &&
2387 (right is NullLiteral || right.Type.IsValueType) &&
2388 !(left is NullLiteral && right is NullLiteral) &&
2389 (TypeManager.IsNullableType (left.Type) || TypeManager.IsNullableType (right.Type)))
2390 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2393 // Comparison warnings
2394 if (oper == Operator.Equality || oper == Operator.Inequality ||
2395 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2396 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2397 if (left.Equals (right)) {
2398 Report.Warning (1718, 3, loc, "A comparison made to same variable. Did you mean to compare something else?");
2400 CheckUselessComparison (lc, right.Type);
2401 CheckUselessComparison (rc, left.Type);
2404 return ResolveOperator (ec);
2407 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2412 private void CheckUselessComparison (Constant c, Type type)
2414 if (c == null || !IsTypeIntegral (type)
2415 || c is StringConstant
2416 || c is BoolConstant
2417 || c is FloatConstant
2418 || c is DoubleConstant
2419 || c is DecimalConstant
2425 if (c is ULongConstant) {
2426 ulong uvalue = ((ULongConstant) c).Value;
2427 if (uvalue > long.MaxValue) {
2428 if (type == TypeManager.byte_type ||
2429 type == TypeManager.sbyte_type ||
2430 type == TypeManager.short_type ||
2431 type == TypeManager.ushort_type ||
2432 type == TypeManager.int32_type ||
2433 type == TypeManager.uint32_type ||
2434 type == TypeManager.int64_type ||
2435 type == TypeManager.char_type)
2436 WarnUselessComparison (type);
2439 value = (long) uvalue;
2441 else if (c is ByteConstant)
2442 value = ((ByteConstant) c).Value;
2443 else if (c is SByteConstant)
2444 value = ((SByteConstant) c).Value;
2445 else if (c is ShortConstant)
2446 value = ((ShortConstant) c).Value;
2447 else if (c is UShortConstant)
2448 value = ((UShortConstant) c).Value;
2449 else if (c is IntConstant)
2450 value = ((IntConstant) c).Value;
2451 else if (c is UIntConstant)
2452 value = ((UIntConstant) c).Value;
2453 else if (c is LongConstant)
2454 value = ((LongConstant) c).Value;
2455 else if (c is CharConstant)
2456 value = ((CharConstant)c).Value;
2461 if (IsValueOutOfRange (value, type))
2462 WarnUselessComparison (type);
2465 private bool IsValueOutOfRange (long value, Type type)
2467 if (IsTypeUnsigned (type) && value < 0)
2469 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2470 type == TypeManager.byte_type && value >= 0x100 ||
2471 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2472 type == TypeManager.ushort_type && value >= 0x10000 ||
2473 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2474 type == TypeManager.uint32_type && value >= 0x100000000;
2477 private static bool IsTypeIntegral (Type type)
2479 return type == TypeManager.uint64_type ||
2480 type == TypeManager.int64_type ||
2481 type == TypeManager.uint32_type ||
2482 type == TypeManager.int32_type ||
2483 type == TypeManager.ushort_type ||
2484 type == TypeManager.short_type ||
2485 type == TypeManager.sbyte_type ||
2486 type == TypeManager.byte_type ||
2487 type == TypeManager.char_type;
2490 private static bool IsTypeUnsigned (Type type)
2492 return type == TypeManager.uint64_type ||
2493 type == TypeManager.uint32_type ||
2494 type == TypeManager.ushort_type ||
2495 type == TypeManager.byte_type ||
2496 type == TypeManager.char_type;
2499 private void WarnUselessComparison (Type type)
2501 Report.Warning (652, 2, loc, "A comparison between a constant and a variable is useless. The constant is out of the range of the variable type `{0}'",
2502 TypeManager.CSharpName (type));
2506 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2507 /// context of a conditional bool expression. This function will return
2508 /// false if it is was possible to use EmitBranchable, or true if it was.
2510 /// The expression's code is generated, and we will generate a branch to `target'
2511 /// if the resulting expression value is equal to isTrue
2513 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2515 ILGenerator ig = ec.ig;
2518 // This is more complicated than it looks, but its just to avoid
2519 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2520 // but on top of that we want for == and != to use a special path
2521 // if we are comparing against null
2523 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2524 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2527 // put the constant on the rhs, for simplicity
2529 if (left is Constant) {
2530 Expression swap = right;
2535 if (((Constant) right).IsZeroInteger) {
2538 ig.Emit (OpCodes.Brtrue, target);
2540 ig.Emit (OpCodes.Brfalse, target);
2543 } else if (right is BoolConstant) {
2545 if (my_on_true != ((BoolConstant) right).Value)
2546 ig.Emit (OpCodes.Brtrue, target);
2548 ig.Emit (OpCodes.Brfalse, target);
2553 } else if (oper == Operator.LogicalAnd) {
2556 Label tests_end = ig.DefineLabel ();
2558 left.EmitBranchable (ec, tests_end, false);
2559 right.EmitBranchable (ec, target, true);
2560 ig.MarkLabel (tests_end);
2563 // This optimizes code like this
2564 // if (true && i > 4)
2566 if (!(left is Constant))
2567 left.EmitBranchable (ec, target, false);
2569 if (!(right is Constant))
2570 right.EmitBranchable (ec, target, false);
2575 } else if (oper == Operator.LogicalOr){
2577 left.EmitBranchable (ec, target, true);
2578 right.EmitBranchable (ec, target, true);
2581 Label tests_end = ig.DefineLabel ();
2582 left.EmitBranchable (ec, tests_end, true);
2583 right.EmitBranchable (ec, target, false);
2584 ig.MarkLabel (tests_end);
2589 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2590 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2591 oper == Operator.Equality || oper == Operator.Inequality)) {
2592 base.EmitBranchable (ec, target, onTrue);
2600 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2603 case Operator.Equality:
2605 ig.Emit (OpCodes.Beq, target);
2607 ig.Emit (OpCodes.Bne_Un, target);
2610 case Operator.Inequality:
2612 ig.Emit (OpCodes.Bne_Un, target);
2614 ig.Emit (OpCodes.Beq, target);
2617 case Operator.LessThan:
2620 ig.Emit (OpCodes.Blt_Un, target);
2622 ig.Emit (OpCodes.Blt, target);
2625 ig.Emit (OpCodes.Bge_Un, target);
2627 ig.Emit (OpCodes.Bge, target);
2630 case Operator.GreaterThan:
2633 ig.Emit (OpCodes.Bgt_Un, target);
2635 ig.Emit (OpCodes.Bgt, target);
2638 ig.Emit (OpCodes.Ble_Un, target);
2640 ig.Emit (OpCodes.Ble, target);
2643 case Operator.LessThanOrEqual:
2646 ig.Emit (OpCodes.Ble_Un, target);
2648 ig.Emit (OpCodes.Ble, target);
2651 ig.Emit (OpCodes.Bgt_Un, target);
2653 ig.Emit (OpCodes.Bgt, target);
2657 case Operator.GreaterThanOrEqual:
2660 ig.Emit (OpCodes.Bge_Un, target);
2662 ig.Emit (OpCodes.Bge, target);
2665 ig.Emit (OpCodes.Blt_Un, target);
2667 ig.Emit (OpCodes.Blt, target);
2670 Console.WriteLine (oper);
2671 throw new Exception ("what is THAT");
2675 public override void Emit (EmitContext ec)
2677 ILGenerator ig = ec.ig;
2682 // Handle short-circuit operators differently
2685 if (oper == Operator.LogicalAnd) {
2686 Label load_zero = ig.DefineLabel ();
2687 Label end = ig.DefineLabel ();
2689 left.EmitBranchable (ec, load_zero, false);
2691 ig.Emit (OpCodes.Br, end);
2693 ig.MarkLabel (load_zero);
2694 ig.Emit (OpCodes.Ldc_I4_0);
2697 } else if (oper == Operator.LogicalOr) {
2698 Label load_one = ig.DefineLabel ();
2699 Label end = ig.DefineLabel ();
2701 left.EmitBranchable (ec, load_one, true);
2703 ig.Emit (OpCodes.Br, end);
2705 ig.MarkLabel (load_one);
2706 ig.Emit (OpCodes.Ldc_I4_1);
2714 bool isUnsigned = is_unsigned (left.Type);
2717 case Operator.Multiply:
2719 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2720 opcode = OpCodes.Mul_Ovf;
2721 else if (isUnsigned)
2722 opcode = OpCodes.Mul_Ovf_Un;
2724 opcode = OpCodes.Mul;
2726 opcode = OpCodes.Mul;
2730 case Operator.Division:
2732 opcode = OpCodes.Div_Un;
2734 opcode = OpCodes.Div;
2737 case Operator.Modulus:
2739 opcode = OpCodes.Rem_Un;
2741 opcode = OpCodes.Rem;
2744 case Operator.Addition:
2746 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2747 opcode = OpCodes.Add_Ovf;
2748 else if (isUnsigned)
2749 opcode = OpCodes.Add_Ovf_Un;
2751 opcode = OpCodes.Add;
2753 opcode = OpCodes.Add;
2756 case Operator.Subtraction:
2758 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2759 opcode = OpCodes.Sub_Ovf;
2760 else if (isUnsigned)
2761 opcode = OpCodes.Sub_Ovf_Un;
2763 opcode = OpCodes.Sub;
2765 opcode = OpCodes.Sub;
2768 case Operator.RightShift:
2770 opcode = OpCodes.Shr_Un;
2772 opcode = OpCodes.Shr;
2775 case Operator.LeftShift:
2776 opcode = OpCodes.Shl;
2779 case Operator.Equality:
2780 opcode = OpCodes.Ceq;
2783 case Operator.Inequality:
2784 ig.Emit (OpCodes.Ceq);
2785 ig.Emit (OpCodes.Ldc_I4_0);
2787 opcode = OpCodes.Ceq;
2790 case Operator.LessThan:
2792 opcode = OpCodes.Clt_Un;
2794 opcode = OpCodes.Clt;
2797 case Operator.GreaterThan:
2799 opcode = OpCodes.Cgt_Un;
2801 opcode = OpCodes.Cgt;
2804 case Operator.LessThanOrEqual:
2805 Type lt = left.Type;
2807 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2808 ig.Emit (OpCodes.Cgt_Un);
2810 ig.Emit (OpCodes.Cgt);
2811 ig.Emit (OpCodes.Ldc_I4_0);
2813 opcode = OpCodes.Ceq;
2816 case Operator.GreaterThanOrEqual:
2817 Type le = left.Type;
2819 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2820 ig.Emit (OpCodes.Clt_Un);
2822 ig.Emit (OpCodes.Clt);
2824 ig.Emit (OpCodes.Ldc_I4_0);
2826 opcode = OpCodes.Ceq;
2829 case Operator.BitwiseOr:
2830 opcode = OpCodes.Or;
2833 case Operator.BitwiseAnd:
2834 opcode = OpCodes.And;
2837 case Operator.ExclusiveOr:
2838 opcode = OpCodes.Xor;
2842 throw new Exception ("This should not happen: Operator = "
2843 + oper.ToString ());
2849 protected override void CloneTo (CloneContext clonectx, Expression t)
2851 Binary target = (Binary) t;
2853 target.left = left.Clone (clonectx);
2854 target.right = right.Clone (clonectx);
2859 // Object created by Binary when the binary operator uses an method instead of being
2860 // a binary operation that maps to a CIL binary operation.
2862 public class BinaryMethod : Expression {
2863 public MethodBase method;
2864 public ArrayList Arguments;
2866 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2871 eclass = ExprClass.Value;
2874 public override Expression DoResolve (EmitContext ec)
2879 public override void Emit (EmitContext ec)
2881 ILGenerator ig = ec.ig;
2883 if (Arguments != null)
2884 Invocation.EmitArguments (ec, method, Arguments, false, null);
2886 if (method is MethodInfo)
2887 ig.Emit (OpCodes.Call, (MethodInfo) method);
2889 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2894 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2895 // b, c, d... may be strings or objects.
2897 public class StringConcat : Expression {
2899 bool invalid = false;
2900 bool emit_conv_done = false;
2902 // Are we also concating objects?
2904 bool is_strings_only = true;
2906 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2909 type = TypeManager.string_type;
2910 eclass = ExprClass.Value;
2912 operands = new ArrayList (2);
2917 public override Expression DoResolve (EmitContext ec)
2925 public void Append (EmitContext ec, Expression operand)
2930 StringConstant sc = operand as StringConstant;
2932 // TODO: it will be better to do this silently as an optimalization
2934 // string s = "" + i;
2935 // because this code has poor performace
2936 // if (sc.Value.Length == 0)
2937 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2939 if (operands.Count != 0) {
2940 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2941 if (last_operand != null) {
2942 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2949 // Conversion to object
2951 if (operand.Type != TypeManager.string_type) {
2952 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2955 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2961 operands.Add (operand);
2964 public override void Emit (EmitContext ec)
2966 MethodInfo concat_method = null;
2969 // Do conversion to arguments; check for strings only
2972 // This can get called multiple times, so we have to deal with that.
2973 if (!emit_conv_done) {
2974 emit_conv_done = true;
2975 for (int i = 0; i < operands.Count; i ++) {
2976 Expression e = (Expression) operands [i];
2977 is_strings_only &= e.Type == TypeManager.string_type;
2980 for (int i = 0; i < operands.Count; i ++) {
2981 Expression e = (Expression) operands [i];
2983 if (! is_strings_only && e.Type == TypeManager.string_type) {
2984 // need to make sure this is an object, because the EmitParams
2985 // method might look at the type of this expression, see it is a
2986 // string and emit a string [] when we want an object [];
2988 e = new EmptyCast (e, TypeManager.object_type);
2990 operands [i] = new Argument (e, Argument.AType.Expression);
2995 // Find the right method
2997 switch (operands.Count) {
3000 // This should not be possible, because simple constant folding
3001 // is taken care of in the Binary code.
3003 throw new Exception ("how did you get here?");
3006 concat_method = is_strings_only ?
3007 TypeManager.string_concat_string_string :
3008 TypeManager.string_concat_object_object ;
3011 concat_method = is_strings_only ?
3012 TypeManager.string_concat_string_string_string :
3013 TypeManager.string_concat_object_object_object ;
3017 // There is not a 4 param overlaod for object (the one that there is
3018 // is actually a varargs methods, and is only in corlib because it was
3019 // introduced there before.).
3021 if (!is_strings_only)
3024 concat_method = TypeManager.string_concat_string_string_string_string;
3027 concat_method = is_strings_only ?
3028 TypeManager.string_concat_string_dot_dot_dot :
3029 TypeManager.string_concat_object_dot_dot_dot ;
3033 Invocation.EmitArguments (ec, concat_method, operands, false, null);
3034 ec.ig.Emit (OpCodes.Call, concat_method);
3039 // Object created with +/= on delegates
3041 public class BinaryDelegate : Expression {
3045 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
3050 eclass = ExprClass.Value;
3053 public override Expression DoResolve (EmitContext ec)
3058 public override void Emit (EmitContext ec)
3060 ILGenerator ig = ec.ig;
3062 Invocation.EmitArguments (ec, method, args, false, null);
3064 ig.Emit (OpCodes.Call, (MethodInfo) method);
3065 ig.Emit (OpCodes.Castclass, type);
3068 public Expression Right {
3070 Argument arg = (Argument) args [1];
3075 public bool IsAddition {
3077 return method == TypeManager.delegate_combine_delegate_delegate;
3083 // User-defined conditional logical operator
3084 public class ConditionalLogicalOperator : Expression {
3085 Expression left, right;
3088 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
3091 eclass = ExprClass.Value;
3095 this.is_and = is_and;
3098 protected void Error19 ()
3100 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3103 protected void Error218 ()
3105 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3106 "declarations of operator true and operator false");
3109 Expression op_true, op_false, op;
3110 LocalTemporary left_temp;
3112 public override Expression DoResolve (EmitContext ec)
3114 MethodGroupExpr operator_group;
3116 operator_group = MethodLookup (ec.ContainerType, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc) as MethodGroupExpr;
3117 if (operator_group == null) {
3122 left_temp = new LocalTemporary (type);
3124 ArrayList arguments = new ArrayList (2);
3125 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3126 arguments.Add (new Argument (right, Argument.AType.Expression));
3127 operator_group = operator_group.OverloadResolve (ec, arguments, false, loc);
3128 if (operator_group == null) {
3133 MethodInfo method = (MethodInfo)operator_group;
3134 if (method.ReturnType != type) {
3135 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3136 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3140 op = new StaticCallExpr (method, arguments, loc);
3142 op_true = GetOperatorTrue (ec, left_temp, loc);
3143 op_false = GetOperatorFalse (ec, left_temp, loc);
3144 if ((op_true == null) || (op_false == null)) {
3152 public override void Emit (EmitContext ec)
3154 ILGenerator ig = ec.ig;
3155 Label false_target = ig.DefineLabel ();
3156 Label end_target = ig.DefineLabel ();
3159 left_temp.Store (ec);
3161 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3162 left_temp.Emit (ec);
3163 ig.Emit (OpCodes.Br, end_target);
3164 ig.MarkLabel (false_target);
3166 ig.MarkLabel (end_target);
3168 // We release 'left_temp' here since 'op' may refer to it too
3169 left_temp.Release (ec);
3173 public class PointerArithmetic : Expression {
3174 Expression left, right;
3178 // We assume that `l' is always a pointer
3180 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3186 is_add = is_addition;
3189 public override Expression DoResolve (EmitContext ec)
3191 eclass = ExprClass.Variable;
3193 if (left.Type == TypeManager.void_ptr_type) {
3194 Error (242, "The operation in question is undefined on void pointers");
3201 public override void Emit (EmitContext ec)
3203 Type op_type = left.Type;
3204 ILGenerator ig = ec.ig;
3206 // It must be either array or fixed buffer
3207 Type element = TypeManager.HasElementType (op_type) ?
3208 element = TypeManager.GetElementType (op_type) :
3209 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3211 int size = GetTypeSize (element);
3212 Type rtype = right.Type;
3214 if (rtype.IsPointer){
3216 // handle (pointer - pointer)
3220 ig.Emit (OpCodes.Sub);
3224 ig.Emit (OpCodes.Sizeof, element);
3226 IntLiteral.EmitInt (ig, size);
3227 ig.Emit (OpCodes.Div);
3229 ig.Emit (OpCodes.Conv_I8);
3232 // handle + and - on (pointer op int)
3235 ig.Emit (OpCodes.Conv_I);
3237 Constant right_const = right as Constant;
3238 if (right_const != null && size != 0) {
3239 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3247 ig.Emit (OpCodes.Sizeof, element);
3249 IntLiteral.EmitInt (ig, size);
3250 if (rtype == TypeManager.int64_type)
3251 ig.Emit (OpCodes.Conv_I8);
3252 else if (rtype == TypeManager.uint64_type)
3253 ig.Emit (OpCodes.Conv_U8);
3254 ig.Emit (OpCodes.Mul);
3258 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3259 ig.Emit (OpCodes.Conv_I);
3262 ig.Emit (OpCodes.Add);
3264 ig.Emit (OpCodes.Sub);
3270 /// Implements the ternary conditional operator (?:)
3272 public class Conditional : Expression {
3273 Expression expr, trueExpr, falseExpr;
3275 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3278 this.trueExpr = trueExpr;
3279 this.falseExpr = falseExpr;
3280 this.loc = expr.Location;
3283 public Expression Expr {
3289 public Expression TrueExpr {
3295 public Expression FalseExpr {
3301 public override Expression DoResolve (EmitContext ec)
3303 expr = expr.Resolve (ec);
3309 if (TypeManager.IsNullableValueType (expr.Type))
3310 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
3313 if (expr.Type != TypeManager.bool_type){
3314 expr = Expression.ResolveBoolean (
3321 Assign ass = expr as Assign;
3322 if (ass != null && ass.Source is Constant) {
3323 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3326 trueExpr = trueExpr.Resolve (ec);
3327 falseExpr = falseExpr.Resolve (ec);
3329 if (trueExpr == null || falseExpr == null)
3332 eclass = ExprClass.Value;
3333 if (trueExpr.Type == falseExpr.Type) {
3334 type = trueExpr.Type;
3335 if (type == TypeManager.null_type) {
3336 // TODO: probably will have to implement ConditionalConstant
3337 // to call method without return constant as well
3338 Report.Warning (-101, 1, loc, "Conditional expression will always return same value");
3343 Type true_type = trueExpr.Type;
3344 Type false_type = falseExpr.Type;
3347 // First, if an implicit conversion exists from trueExpr
3348 // to falseExpr, then the result type is of type falseExpr.Type
3350 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3353 // Check if both can convert implicitl to each other's type
3355 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3357 "Can not compute type of conditional expression " +
3358 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3359 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3360 "' convert implicitly to each other");
3365 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3369 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3370 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3375 // Dead code optimalization
3376 if (expr is BoolConstant){
3377 BoolConstant bc = (BoolConstant) expr;
3379 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3380 return bc.Value ? trueExpr : falseExpr;
3386 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3391 public override void Emit (EmitContext ec)
3393 ILGenerator ig = ec.ig;
3394 Label false_target = ig.DefineLabel ();
3395 Label end_target = ig.DefineLabel ();
3397 expr.EmitBranchable (ec, false_target, false);
3399 ig.Emit (OpCodes.Br, end_target);
3400 ig.MarkLabel (false_target);
3401 falseExpr.Emit (ec);
3402 ig.MarkLabel (end_target);
3405 protected override void CloneTo (CloneContext clonectx, Expression t)
3407 Conditional target = (Conditional) t;
3409 target.expr = expr.Clone (clonectx);
3410 target.trueExpr = trueExpr.Clone (clonectx);
3411 target.falseExpr = falseExpr.Clone (clonectx);
3415 public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation {
3417 LocalTemporary temp;
3419 public abstract Variable Variable {
3423 public abstract bool IsRef {
3427 public override void Emit (EmitContext ec)
3433 // This method is used by parameters that are references, that are
3434 // being passed as references: we only want to pass the pointer (that
3435 // is already stored in the parameter, not the address of the pointer,
3436 // and not the value of the variable).
3438 public void EmitLoad (EmitContext ec)
3440 Report.Debug (64, "VARIABLE EMIT LOAD", this, Variable, type, loc);
3442 Variable.EmitInstance (ec);
3446 public void Emit (EmitContext ec, bool leave_copy)
3448 Report.Debug (64, "VARIABLE EMIT", this, Variable, type, IsRef, loc);
3454 ec.ig.Emit (OpCodes.Dup);
3457 // If we are a reference, we loaded on the stack a pointer
3458 // Now lets load the real value
3460 LoadFromPtr (ec.ig, type);
3464 ec.ig.Emit (OpCodes.Dup);
3466 if (IsRef || Variable.NeedsTemporary) {
3467 temp = new LocalTemporary (Type);
3473 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
3474 bool prepare_for_load)
3476 Report.Debug (64, "VARIABLE EMIT ASSIGN", this, Variable, type, IsRef,
3479 ILGenerator ig = ec.ig;
3480 prepared = prepare_for_load;
3482 Variable.EmitInstance (ec);
3483 if (prepare_for_load && Variable.HasInstance)
3484 ig.Emit (OpCodes.Dup);
3485 else if (IsRef && !prepared)
3491 ig.Emit (OpCodes.Dup);
3492 if (IsRef || Variable.NeedsTemporary) {
3493 temp = new LocalTemporary (Type);
3499 StoreFromPtr (ig, type);
3501 Variable.EmitAssign (ec);
3509 public void AddressOf (EmitContext ec, AddressOp mode)
3511 Variable.EmitInstance (ec);
3512 Variable.EmitAddressOf (ec);
3519 public class LocalVariableReference : VariableReference, IVariable {
3520 public readonly string Name;
3522 public LocalInfo local_info;
3526 public LocalVariableReference (Block block, string name, Location l)
3531 eclass = ExprClass.Variable;
3535 // Setting `is_readonly' to false will allow you to create a writable
3536 // reference to a read-only variable. This is used by foreach and using.
3538 public LocalVariableReference (Block block, string name, Location l,
3539 LocalInfo local_info, bool is_readonly)
3540 : this (block, name, l)
3542 this.local_info = local_info;
3543 this.is_readonly = is_readonly;
3546 public VariableInfo VariableInfo {
3547 get { return local_info.VariableInfo; }
3550 public override bool IsRef {
3551 get { return false; }
3554 public bool IsReadOnly {
3555 get { return is_readonly; }
3558 public bool VerifyAssigned (EmitContext ec)
3560 VariableInfo variable_info = local_info.VariableInfo;
3561 return variable_info == null || variable_info.IsAssigned (ec, loc);
3564 void ResolveLocalInfo ()
3566 if (local_info == null) {
3567 local_info = Block.GetLocalInfo (Name);
3568 type = local_info.VariableType;
3569 is_readonly = local_info.ReadOnly;
3573 protected Expression DoResolveBase (EmitContext ec)
3575 type = local_info.VariableType;
3577 Expression e = Block.GetConstantExpression (Name);
3579 return e.Resolve (ec);
3581 if (!VerifyAssigned (ec))
3585 // If we are referencing a variable from the external block
3586 // flag it for capturing
3588 if (ec.MustCaptureVariable (local_info)) {
3589 if (local_info.AddressTaken){
3590 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3594 ScopeInfo scope = local_info.Block.CreateScopeInfo ();
3595 variable = scope.AddLocal (local_info);
3596 type = variable.Type;
3602 public override Expression DoResolve (EmitContext ec)
3604 ResolveLocalInfo ();
3605 local_info.Used = true;
3607 if (type == null && local_info.Type is VarExpr) {
3608 local_info.VariableType = TypeManager.object_type;
3609 Error_VariableIsUsedBeforeItIsDeclared (Name);
3613 return DoResolveBase (ec);
3616 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3618 ResolveLocalInfo ();
3621 if (right_side == EmptyExpression.OutAccess)
3622 local_info.Used = true;
3624 // Infer implicitly typed local variable
3626 VarExpr ve = local_info.Type as VarExpr;
3628 ve.DoResolveLValue (ec, right_side);
3629 type = local_info.VariableType = ve.Type;
3636 if (right_side == EmptyExpression.OutAccess) {
3637 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3638 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3639 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3640 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3641 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3643 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
3645 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3649 if (VariableInfo != null)
3650 VariableInfo.SetAssigned (ec);
3652 return DoResolveBase (ec);
3655 public bool VerifyFixed ()
3657 // A local Variable is always fixed.
3661 public override int GetHashCode ()
3663 return Name.GetHashCode ();
3666 public override bool Equals (object obj)
3668 LocalVariableReference lvr = obj as LocalVariableReference;
3672 return Name == lvr.Name && Block == lvr.Block;
3675 public override Variable Variable {
3676 get { return variable != null ? variable : local_info.Variable; }
3679 public override string ToString ()
3681 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3684 protected override void CloneTo (CloneContext clonectx, Expression t)
3686 LocalVariableReference target = (LocalVariableReference) t;
3688 target.Block = clonectx.LookupBlock (Block);
3693 /// This represents a reference to a parameter in the intermediate
3696 public class ParameterReference : VariableReference, IVariable {
3697 readonly ToplevelParameterInfo pi;
3698 readonly ToplevelBlock referenced;
3701 public bool is_ref, is_out;
3704 get { return is_out; }
3707 public override bool IsRef {
3708 get { return is_ref; }
3711 public string Name {
3712 get { return Parameter.Name; }
3715 public Parameter Parameter {
3716 get { return pi.Parameter; }
3719 public ParameterReference (ToplevelBlock referenced, ToplevelParameterInfo pi, Location loc)
3722 this.referenced = referenced;
3724 eclass = ExprClass.Variable;
3727 public VariableInfo VariableInfo {
3728 get { return pi.VariableInfo; }
3731 public override Variable Variable {
3732 get { return variable != null ? variable : Parameter.Variable; }
3735 public bool VerifyFixed ()
3737 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3738 return Parameter.ModFlags == Parameter.Modifier.NONE;
3741 public bool IsAssigned (EmitContext ec, Location loc)
3743 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (VariableInfo))
3746 Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
3750 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3752 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (VariableInfo, field_name))
3755 Report.Error (170, loc, "Use of possibly unassigned field `{0}'", field_name);
3759 public void SetAssigned (EmitContext ec)
3761 if (is_out && ec.DoFlowAnalysis)
3762 ec.CurrentBranching.SetAssigned (VariableInfo);
3765 public void SetFieldAssigned (EmitContext ec, string field_name)
3767 if (is_out && ec.DoFlowAnalysis)
3768 ec.CurrentBranching.SetFieldAssigned (VariableInfo, field_name);
3771 protected bool DoResolveBase (EmitContext ec)
3773 Parameter par = Parameter;
3774 if (!par.Resolve (ec)) {
3778 type = par.ParameterType;
3779 Parameter.Modifier mod = par.ModFlags;
3780 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3781 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3782 eclass = ExprClass.Variable;
3784 AnonymousContainer am = ec.CurrentAnonymousMethod;
3788 ToplevelBlock declared = pi.Block;
3789 if (is_ref && declared != referenced) {
3790 Report.Error (1628, Location,
3791 "Cannot use ref or out parameter `{0}' inside an " +
3792 "anonymous method block", par.Name);
3796 if (!am.IsIterator && declared == referenced)
3799 // Don't capture aruments when the probing is on
3800 if (!ec.IsInProbingMode) {
3801 ScopeInfo scope = declared.CreateScopeInfo ();
3802 variable = scope.AddParameter (par, pi.Index);
3803 type = variable.Type;
3808 public override int GetHashCode ()
3810 return Name.GetHashCode ();
3813 public override bool Equals (object obj)
3815 ParameterReference pr = obj as ParameterReference;
3819 return Name == pr.Name && referenced == pr.referenced;
3823 // Notice that for ref/out parameters, the type exposed is not the
3824 // same type exposed externally.
3827 // externally we expose "int&"
3828 // here we expose "int".
3830 // We record this in "is_ref". This means that the type system can treat
3831 // the type as it is expected, but when we generate the code, we generate
3832 // the alternate kind of code.
3834 public override Expression DoResolve (EmitContext ec)
3836 if (!DoResolveBase (ec))
3839 if (is_out && ec.DoFlowAnalysis &&
3840 (!ec.OmitStructFlowAnalysis || !VariableInfo.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3846 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3848 if (!DoResolveBase (ec))
3856 static public void EmitLdArg (ILGenerator ig, int x)
3860 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3861 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3862 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3863 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3864 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3867 ig.Emit (OpCodes.Ldarg, x);
3870 public override string ToString ()
3872 return "ParameterReference[" + Name + "]";
3877 /// Used for arguments to New(), Invocation()
3879 public class Argument {
3880 public enum AType : byte {
3887 public static readonly Argument[] Empty = new Argument [0];
3889 public readonly AType ArgType;
3890 public Expression Expr;
3892 public Argument (Expression expr, AType type)
3895 this.ArgType = type;
3898 public Argument (Expression expr)
3901 this.ArgType = AType.Expression;
3906 if (ArgType == AType.Ref || ArgType == AType.Out)
3907 return TypeManager.GetReferenceType (Expr.Type);
3913 public Parameter.Modifier Modifier
3918 return Parameter.Modifier.OUT;
3921 return Parameter.Modifier.REF;
3924 return Parameter.Modifier.NONE;
3929 public static string FullDesc (Argument a)
3931 if (a.ArgType == AType.ArgList)
3934 return (a.ArgType == AType.Ref ? "ref " :
3935 (a.ArgType == AType.Out ? "out " : "")) +
3936 TypeManager.CSharpName (a.Expr.Type);
3939 public bool ResolveMethodGroup (EmitContext ec)
3941 SimpleName sn = Expr as SimpleName;
3943 Expr = sn.GetMethodGroup ();
3945 // FIXME: csc doesn't report any error if you try to use `ref' or
3946 // `out' in a delegate creation expression.
3947 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3954 public bool Resolve (EmitContext ec, Location loc)
3956 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3957 // Verify that the argument is readable
3958 if (ArgType != AType.Out)
3959 Expr = Expr.Resolve (ec);
3961 // Verify that the argument is writeable
3962 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3963 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3965 return Expr != null;
3969 public void Emit (EmitContext ec)
3971 if (ArgType != AType.Ref && ArgType != AType.Out) {
3976 AddressOp mode = AddressOp.Store;
3977 if (ArgType == AType.Ref)
3978 mode |= AddressOp.Load;
3980 IMemoryLocation ml = (IMemoryLocation) Expr;
3981 ParameterReference pr = ml as ParameterReference;
3984 // ParameterReferences might already be references, so we want
3985 // to pass just the value
3987 if (pr != null && pr.IsRef)
3990 ml.AddressOf (ec, mode);
3993 public Argument Clone (CloneContext clonectx)
3995 return new Argument (Expr.Clone (clonectx), ArgType);
4000 /// Invocation of methods or delegates.
4002 public class Invocation : ExpressionStatement {
4003 ArrayList Arguments;
4008 // arguments is an ArrayList, but we do not want to typecast,
4009 // as it might be null.
4011 public Invocation (Expression expr, ArrayList arguments)
4013 SimpleName sn = expr as SimpleName;
4015 this.expr = sn.GetMethodGroup ();
4019 Arguments = arguments;
4020 loc = expr.Location;
4023 public static string FullMethodDesc (MethodBase mb)
4029 if (mb is MethodInfo) {
4030 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4034 sb = new StringBuilder ();
4036 sb.Append (TypeManager.CSharpSignature (mb));
4037 return sb.ToString ();
4040 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4041 ArrayList arguments, int arg_count,
4042 ref MethodBase candidate)
4044 return IsParamsMethodApplicable (
4045 ec, me, arguments, arg_count, false, ref candidate) ||
4046 IsParamsMethodApplicable (
4047 ec, me, arguments, arg_count, true, ref candidate);
4052 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4053 ArrayList arguments, int arg_count,
4054 bool do_varargs, ref MethodBase candidate)
4057 if (!me.HasTypeArguments &&
4058 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4061 if (TypeManager.IsGenericMethodDefinition (candidate))
4062 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4065 return IsParamsMethodApplicable (
4066 ec, arguments, arg_count, candidate, do_varargs);
4070 /// Determines if the candidate method, if a params method, is applicable
4071 /// in its expanded form to the given set of arguments
4073 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4074 int arg_count, MethodBase candidate,
4077 ParameterData pd = TypeManager.GetParameterData (candidate);
4079 int pd_count = pd.Count;
4083 int count = pd_count - 1;
4085 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4087 if (pd_count != arg_count)
4090 if (!(((Argument) arguments [count]).Expr is Arglist))
4098 if (count > arg_count)
4101 if (pd_count == 1 && arg_count == 0)
4105 // If we have come this far, the case which
4106 // remains is when the number of parameters is
4107 // less than or equal to the argument count.
4109 int argument_index = 0;
4111 for (int i = 0; i < pd_count; ++i) {
4113 if ((pd.ParameterModifier (i) & Parameter.Modifier.PARAMS) != 0) {
4114 Type element_type = TypeManager.GetElementType (pd.ParameterType (i));
4115 int params_args_count = arg_count - pd_count;
4116 if (params_args_count < 0)
4120 a = (Argument) arguments [argument_index++];
4122 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4124 } while (params_args_count-- > 0);
4128 a = (Argument) arguments [argument_index++];
4130 Parameter.Modifier a_mod = a.Modifier &
4131 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4132 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4133 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4135 if (a_mod == p_mod) {
4137 if (a_mod == Parameter.Modifier.NONE)
4138 if (!Convert.ImplicitConversionExists (ec,
4140 pd.ParameterType (i)))
4143 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4144 Type pt = pd.ParameterType (i);
4147 pt = TypeManager.GetReferenceType (pt);
4160 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4161 ArrayList arguments, int arg_count,
4162 ref MethodBase method)
4164 MethodBase candidate = method;
4167 if (!me.HasTypeArguments &&
4168 !TypeManager.InferTypeArguments (ec, arguments, ref candidate))
4171 if (TypeManager.IsGenericMethodDefinition (candidate))
4172 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4175 if (IsApplicable (ec, arguments, arg_count, candidate)) {
4184 /// Determines if the candidate method is applicable (section 14.4.2.1)
4185 /// to the given set of arguments
4187 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4188 MethodBase candidate)
4190 ParameterData pd = TypeManager.GetParameterData (candidate);
4192 if (arg_count != pd.Count)
4195 for (int i = arg_count; i > 0; ) {
4198 Argument a = (Argument) arguments [i];
4200 Parameter.Modifier a_mod = a.Modifier &
4201 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4203 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4204 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4209 Type pt = pd.ParameterType (i);
4211 if (TypeManager.IsEqual (pt, a.Type))
4214 if (a_mod != Parameter.Modifier.NONE)
4217 // FIXME: Kill this abomination (EmitContext.TempEc)
4218 EmitContext prevec = EmitContext.TempEc;
4219 EmitContext.TempEc = ec;
4221 if (!Convert.ImplicitConversionExists (ec, a.Expr, pt))
4224 EmitContext.TempEc = prevec;
4231 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4233 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4234 name, arg_count.ToString ());
4237 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4238 Type delegate_type, Argument a, ParameterData expected_par)
4240 if (delegate_type == null)
4241 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4242 TypeManager.CSharpSignature (method));
4244 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4245 TypeManager.CSharpName (delegate_type));
4247 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4249 string index = (idx + 1).ToString ();
4250 if ((a.Modifier & Parameter.Modifier.ISBYREF) != 0 && mod != a.Modifier) {
4251 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4252 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4253 index, Parameter.GetModifierSignature (a.Modifier));
4255 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4256 index, Parameter.GetModifierSignature (mod));
4258 string p1 = Argument.FullDesc (a);
4259 string p2 = TypeManager.CSharpName (expected_par.ParameterType (idx));
4262 Report.ExtraInformation (loc, "(equally named types possibly from different assemblies in previous ");
4263 Report.SymbolRelatedToPreviousError (a.Expr.Type);
4264 Report.SymbolRelatedToPreviousError (expected_par.ParameterType (idx));
4266 Report.Error (1503, loc, "Argument {0}: Cannot convert type `{1}' to `{2}'", index, p1, p2);
4270 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4271 int arg_count, MethodBase method,
4272 bool chose_params_expanded,
4273 Type delegate_type, bool may_fail,
4276 ParameterData pd = TypeManager.GetParameterData (method);
4280 for (j = 0; j < pd.Count; j++) {
4281 Type parameter_type = pd.ParameterType (j);
4282 Parameter.Modifier pm = pd.ParameterModifier (j);
4284 if (pm == Parameter.Modifier.ARGLIST) {
4285 a = (Argument) Arguments [a_idx];
4286 if (!(a.Expr is Arglist))
4292 int params_arg_count = 1;
4293 if (pm == Parameter.Modifier.PARAMS) {
4294 pm = Parameter.Modifier.NONE;
4295 params_arg_count = arg_count - pd.Count + 1;
4296 if (chose_params_expanded)
4297 parameter_type = TypeManager.GetElementType (parameter_type);
4300 while (params_arg_count > 0) {
4301 a = (Argument) Arguments [a_idx];
4302 if (pm != a.Modifier)
4305 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4306 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4309 Expression conv = Convert.ImplicitConversion (ec, a.Expr, parameter_type, loc);
4313 // Update the argument with the implicit conversion
4321 if (params_arg_count > 0)
4324 if (parameter_type.IsPointer && !ec.InUnsafe) {
4331 if (a_idx == arg_count)
4335 Error_InvalidArguments (loc, a_idx, method, delegate_type, a, pd);
4339 public override Expression DoResolve (EmitContext ec)
4341 Expression expr_resolved = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4342 if (expr_resolved == null)
4345 mg = expr_resolved as MethodGroupExpr;
4347 Type expr_type = expr_resolved.Type;
4349 if (expr_type != null && TypeManager.IsDelegateType (expr_type)){
4350 return (new DelegateInvocation (
4351 expr_resolved, Arguments, loc)).Resolve (ec);
4353 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4358 // Next, evaluate all the expressions in the argument list
4360 if (Arguments != null){
4361 foreach (Argument a in Arguments){
4362 if (!a.Resolve (ec, loc))
4367 mg = mg.OverloadResolve (ec, Arguments, false, loc);
4371 MethodInfo method = (MethodInfo)mg;
4372 if (method != null) {
4373 type = TypeManager.TypeToCoreType (method.ReturnType);
4374 Expression iexpr = mg.InstanceExpression;
4375 if (method.IsStatic) {
4376 if (iexpr == null ||
4377 iexpr is This || iexpr is EmptyExpression ||
4378 mg.IdenticalTypeName) {
4379 mg.InstanceExpression = null;
4381 MemberExpr.error176 (loc, mg.GetSignatureForError ());
4385 if (iexpr == null || iexpr is EmptyExpression) {
4386 SimpleName.Error_ObjectRefRequired (ec, loc, mg.GetSignatureForError ());
4392 if (type.IsPointer){
4400 // Only base will allow this invocation to happen.
4402 if (mg.IsBase && method.IsAbstract){
4403 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
4407 if (Arguments == null && method.Name == "Finalize") {
4409 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
4411 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
4415 if (IsSpecialMethodInvocation (method)) {
4419 if (mg.InstanceExpression != null){
4420 mg.InstanceExpression.CheckMarshalByRefAccess ();
4423 // This is used to check that no methods are called in struct
4424 // constructors before all the fields on the struct have been
4427 if (!method.IsStatic){
4428 This mgthis = mg.InstanceExpression as This;
4429 if (mgthis != null){
4430 if (!mgthis.CheckThisUsage (ec))
4436 eclass = ExprClass.Value;
4440 bool IsSpecialMethodInvocation (MethodBase method)
4442 if (!TypeManager.IsSpecialMethod (method))
4445 Report.SymbolRelatedToPreviousError (method);
4446 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
4447 TypeManager.CSharpSignature (method, true));
4453 // Emits the list of arguments as an array
4455 static void EmitParams (EmitContext ec, ArrayList arguments, int idx, int count)
4457 ILGenerator ig = ec.ig;
4459 for (int j = 0; j < count; j++){
4460 Argument a = (Argument) arguments [j + idx];
4463 IntConstant.EmitInt (ig, count);
4464 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
4467 ig.Emit (OpCodes.Dup);
4468 IntConstant.EmitInt (ig, j);
4470 bool is_stobj, has_type_arg;
4471 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
4473 ig.Emit (OpCodes.Ldelema, t);
4485 /// Emits a list of resolved Arguments that are in the arguments
4488 /// The MethodBase argument might be null if the
4489 /// emission of the arguments is known not to contain
4490 /// a `params' field (for example in constructors or other routines
4491 /// that keep their arguments in this structure)
4493 /// if `dup_args' is true, a copy of the arguments will be left
4494 /// on the stack. If `dup_args' is true, you can specify `this_arg'
4495 /// which will be duplicated before any other args. Only EmitCall
4496 /// should be using this interface.
4498 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
4500 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
4502 LocalTemporary [] temps = null;
4504 if (dup_args && top != 0)
4505 temps = new LocalTemporary [top];
4507 int argument_index = 0;
4509 for (int i = 0; i < top; i++){
4511 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
4512 //Type element_type = TypeManager.GetElementType (pd.ParameterType (i));
4513 int params_args_count = arguments == null ?
4514 0 : arguments.Count - top + 1;
4516 // Fill not provided argument
4517 if (params_args_count <= 0) {
4518 ILGenerator ig = ec.ig;
4519 IntConstant.EmitInt (ig, 0);
4520 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (i)));
4525 // Special case if we are passing the same data as the
4526 // params argument, we do not need to recreate an array.
4528 a = (Argument) arguments [argument_index];
4529 if (params_args_count == 1 && pd.ParameterType (i) == a.Type) {
4535 EmitParams (ec, arguments, i, params_args_count);
4536 argument_index += params_args_count;
4541 a = (Argument) arguments [argument_index++];
4544 ec.ig.Emit (OpCodes.Dup);
4545 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
4550 if (this_arg != null)
4553 for (int i = 0; i < top; i ++) {
4554 temps [i].Emit (ec);
4555 temps [i].Release (ec);
4560 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
4562 ParameterData pd = TypeManager.GetParameterData (mb);
4564 if (arguments == null)
4565 return new Type [0];
4567 Argument a = (Argument) arguments [pd.Count - 1];
4568 Arglist list = (Arglist) a.Expr;
4570 return list.ArgumentTypes;
4574 /// This checks the ConditionalAttribute on the method
4576 static bool IsMethodExcluded (MethodBase method)
4578 if (method.IsConstructor)
4581 IMethodData md = TypeManager.GetMethod (method);
4583 return md.IsExcluded ();
4585 // For some methods (generated by delegate class) GetMethod returns null
4586 // because they are not included in builder_to_method table
4587 if (method.DeclaringType is TypeBuilder)
4590 return AttributeTester.IsConditionalMethodExcluded (method);
4594 /// is_base tells whether we want to force the use of the `call'
4595 /// opcode instead of using callvirt. Call is required to call
4596 /// a specific method, while callvirt will always use the most
4597 /// recent method in the vtable.
4599 /// is_static tells whether this is an invocation on a static method
4601 /// instance_expr is an expression that represents the instance
4602 /// it must be non-null if is_static is false.
4604 /// method is the method to invoke.
4606 /// Arguments is the list of arguments to pass to the method or constructor.
4608 public static void EmitCall (EmitContext ec, bool is_base,
4609 Expression instance_expr,
4610 MethodBase method, ArrayList Arguments, Location loc)
4612 EmitCall (ec, is_base, instance_expr, method, Arguments, loc, false, false);
4615 // `dup_args' leaves an extra copy of the arguments on the stack
4616 // `omit_args' does not leave any arguments at all.
4617 // So, basically, you could make one call with `dup_args' set to true,
4618 // and then another with `omit_args' set to true, and the two calls
4619 // would have the same set of arguments. However, each argument would
4620 // only have been evaluated once.
4621 public static void EmitCall (EmitContext ec, bool is_base,
4622 Expression instance_expr,
4623 MethodBase method, ArrayList Arguments, Location loc,
4624 bool dup_args, bool omit_args)
4626 ILGenerator ig = ec.ig;
4627 bool struct_call = false;
4628 bool this_call = false;
4629 LocalTemporary this_arg = null;
4631 Type decl_type = method.DeclaringType;
4633 if (!RootContext.StdLib) {
4634 // Replace any calls to the system's System.Array type with calls to
4635 // the newly created one.
4636 if (method == TypeManager.system_int_array_get_length)
4637 method = TypeManager.int_array_get_length;
4638 else if (method == TypeManager.system_int_array_get_rank)
4639 method = TypeManager.int_array_get_rank;
4640 else if (method == TypeManager.system_object_array_clone)
4641 method = TypeManager.object_array_clone;
4642 else if (method == TypeManager.system_int_array_get_length_int)
4643 method = TypeManager.int_array_get_length_int;
4644 else if (method == TypeManager.system_int_array_get_lower_bound_int)
4645 method = TypeManager.int_array_get_lower_bound_int;
4646 else if (method == TypeManager.system_int_array_get_upper_bound_int)
4647 method = TypeManager.int_array_get_upper_bound_int;
4648 else if (method == TypeManager.system_void_array_copyto_array_int)
4649 method = TypeManager.void_array_copyto_array_int;
4652 if (!ec.IsInObsoleteScope) {
4654 // This checks ObsoleteAttribute on the method and on the declaring type
4656 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
4658 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
4660 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
4662 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
4666 if (IsMethodExcluded (method))
4669 bool is_static = method.IsStatic;
4671 if (instance_expr == EmptyExpression.Null) {
4672 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
4676 this_call = instance_expr is This;
4677 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
4681 // If this is ourselves, push "this"
4685 Type iexpr_type = instance_expr.Type;
4688 // Push the instance expression
4690 if (TypeManager.IsValueType (iexpr_type)) {
4692 // Special case: calls to a function declared in a
4693 // reference-type with a value-type argument need
4694 // to have their value boxed.
4695 if (decl_type.IsValueType ||
4696 TypeManager.IsGenericParameter (iexpr_type)) {
4698 // If the expression implements IMemoryLocation, then
4699 // we can optimize and use AddressOf on the
4702 // If not we have to use some temporary storage for
4704 if (instance_expr is IMemoryLocation) {
4705 ((IMemoryLocation)instance_expr).
4706 AddressOf (ec, AddressOp.LoadStore);
4708 LocalTemporary temp = new LocalTemporary (iexpr_type);
4709 instance_expr.Emit (ec);
4711 temp.AddressOf (ec, AddressOp.Load);
4714 // avoid the overhead of doing this all the time.
4716 t = TypeManager.GetReferenceType (iexpr_type);
4718 instance_expr.Emit (ec);
4719 ig.Emit (OpCodes.Box, instance_expr.Type);
4720 t = TypeManager.object_type;
4723 instance_expr.Emit (ec);
4724 t = instance_expr.Type;
4728 ig.Emit (OpCodes.Dup);
4729 if (Arguments != null && Arguments.Count != 0) {
4730 this_arg = new LocalTemporary (t);
4731 this_arg.Store (ec);
4738 EmitArguments (ec, method, Arguments, dup_args, this_arg);
4741 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
4742 ig.Emit (OpCodes.Constrained, instance_expr.Type);
4746 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
4747 call_op = OpCodes.Call;
4749 call_op = OpCodes.Callvirt;
4751 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
4752 Type[] varargs_types = GetVarargsTypes (method, Arguments);
4753 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
4760 // and DoFoo is not virtual, you can omit the callvirt,
4761 // because you don't need the null checking behavior.
4763 if (method is MethodInfo)
4764 ig.Emit (call_op, (MethodInfo) method);
4766 ig.Emit (call_op, (ConstructorInfo) method);
4769 public override void Emit (EmitContext ec)
4771 mg.EmitCall (ec, Arguments);
4774 public override void EmitStatement (EmitContext ec)
4779 // Pop the return value if there is one
4781 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
4782 ec.ig.Emit (OpCodes.Pop);
4785 protected override void CloneTo (CloneContext clonectx, Expression t)
4787 Invocation target = (Invocation) t;
4789 if (Arguments != null) {
4790 target.Arguments = new ArrayList (Arguments.Count);
4791 foreach (Argument a in Arguments)
4792 target.Arguments.Add (a.Clone (clonectx));
4795 target.expr = expr.Clone (clonectx);
4799 public class InvocationOrCast : ExpressionStatement
4802 Expression argument;
4804 public InvocationOrCast (Expression expr, Expression argument)
4807 this.argument = argument;
4808 this.loc = expr.Location;
4811 public override Expression DoResolve (EmitContext ec)
4814 // First try to resolve it as a cast.
4816 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
4817 if ((te != null) && (te.eclass == ExprClass.Type)) {
4818 Cast cast = new Cast (te, argument, loc);
4819 return cast.Resolve (ec);
4823 // This can either be a type or a delegate invocation.
4824 // Let's just resolve it and see what we'll get.
4826 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
4831 // Ok, so it's a Cast.
4833 if (expr.eclass == ExprClass.Type) {
4834 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
4835 return cast.Resolve (ec);
4839 // It's a delegate invocation.
4841 if (!TypeManager.IsDelegateType (expr.Type)) {
4842 Error (149, "Method name expected");
4846 ArrayList args = new ArrayList ();
4847 args.Add (new Argument (argument, Argument.AType.Expression));
4848 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
4849 return invocation.Resolve (ec);
4852 public override ExpressionStatement ResolveStatement (EmitContext ec)
4855 // First try to resolve it as a cast.
4857 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
4858 if ((te != null) && (te.eclass == ExprClass.Type)) {
4859 Error_InvalidExpressionStatement ();
4864 // This can either be a type or a delegate invocation.
4865 // Let's just resolve it and see what we'll get.
4867 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
4868 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
4869 Error_InvalidExpressionStatement ();
4874 // It's a delegate invocation.
4876 if (!TypeManager.IsDelegateType (expr.Type)) {
4877 Error (149, "Method name expected");
4881 ArrayList args = new ArrayList ();
4882 args.Add (new Argument (argument, Argument.AType.Expression));
4883 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
4884 return invocation.ResolveStatement (ec);
4887 public override void Emit (EmitContext ec)
4889 throw new Exception ("Cannot happen");
4892 public override void EmitStatement (EmitContext ec)
4894 throw new Exception ("Cannot happen");
4897 protected override void CloneTo (CloneContext clonectx, Expression t)
4899 InvocationOrCast target = (InvocationOrCast) t;
4901 target.expr = expr.Clone (clonectx);
4902 target.argument = argument.Clone (clonectx);
4907 // This class is used to "disable" the code generation for the
4908 // temporary variable when initializing value types.
4910 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
4911 public void AddressOf (EmitContext ec, AddressOp Mode)
4918 /// Implements the new expression
4920 public class New : ExpressionStatement, IMemoryLocation {
4921 ArrayList Arguments;
4924 // During bootstrap, it contains the RequestedType,
4925 // but if `type' is not null, it *might* contain a NewDelegate
4926 // (because of field multi-initialization)
4928 public Expression RequestedType;
4930 MethodGroupExpr method;
4933 // If set, the new expression is for a value_target, and
4934 // we will not leave anything on the stack.
4936 Expression value_target;
4937 bool value_target_set = false;
4938 bool is_type_parameter = false;
4940 public New (Expression requested_type, ArrayList arguments, Location l)
4942 RequestedType = requested_type;
4943 Arguments = arguments;
4947 public bool SetValueTypeVariable (Expression value)
4949 value_target = value;
4950 value_target_set = true;
4951 if (!(value_target is IMemoryLocation)){
4952 Error_UnexpectedKind (null, "variable", loc);
4959 // This function is used to disable the following code sequence for
4960 // value type initialization:
4962 // AddressOf (temporary)
4966 // Instead the provide will have provided us with the address on the
4967 // stack to store the results.
4969 static Expression MyEmptyExpression;
4971 public void DisableTemporaryValueType ()
4973 if (MyEmptyExpression == null)
4974 MyEmptyExpression = new EmptyAddressOf ();
4977 // To enable this, look into:
4978 // test-34 and test-89 and self bootstrapping.
4980 // For instance, we can avoid a copy by using `newobj'
4981 // instead of Call + Push-temp on value types.
4982 // value_target = MyEmptyExpression;
4987 /// Converts complex core type syntax like 'new int ()' to simple constant
4989 public static Constant Constantify (Type t)
4991 if (t == TypeManager.int32_type)
4992 return new IntConstant (0, Location.Null);
4993 if (t == TypeManager.uint32_type)
4994 return new UIntConstant (0, Location.Null);
4995 if (t == TypeManager.int64_type)
4996 return new LongConstant (0, Location.Null);
4997 if (t == TypeManager.uint64_type)
4998 return new ULongConstant (0, Location.Null);
4999 if (t == TypeManager.float_type)
5000 return new FloatConstant (0, Location.Null);
5001 if (t == TypeManager.double_type)
5002 return new DoubleConstant (0, Location.Null);
5003 if (t == TypeManager.short_type)
5004 return new ShortConstant (0, Location.Null);
5005 if (t == TypeManager.ushort_type)
5006 return new UShortConstant (0, Location.Null);
5007 if (t == TypeManager.sbyte_type)
5008 return new SByteConstant (0, Location.Null);
5009 if (t == TypeManager.byte_type)
5010 return new ByteConstant (0, Location.Null);
5011 if (t == TypeManager.char_type)
5012 return new CharConstant ('\0', Location.Null);
5013 if (t == TypeManager.bool_type)
5014 return new BoolConstant (false, Location.Null);
5015 if (t == TypeManager.decimal_type)
5016 return new DecimalConstant (0, Location.Null);
5017 if (TypeManager.IsEnumType (t))
5018 return new EnumConstant (Constantify (TypeManager.EnumToUnderlying (t)), t);
5024 // Checks whether the type is an interface that has the
5025 // [ComImport, CoClass] attributes and must be treated
5028 public Expression CheckComImport (EmitContext ec)
5030 if (!type.IsInterface)
5034 // Turn the call into:
5035 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5037 Type real_class = AttributeTester.GetCoClassAttribute (type);
5038 if (real_class == null)
5041 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5042 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5043 return cast.Resolve (ec);
5046 public override Expression DoResolve (EmitContext ec)
5049 // The New DoResolve might be called twice when initializing field
5050 // expressions (see EmitFieldInitializers, the call to
5051 // GetInitializerExpression will perform a resolve on the expression,
5052 // and later the assign will trigger another resolution
5054 // This leads to bugs (#37014)
5057 if (RequestedType is NewDelegate)
5058 return RequestedType;
5062 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5068 if (type == TypeManager.void_type) {
5069 Error_VoidInvalidInTheContext (loc);
5073 if (Arguments == null) {
5074 Expression c = Constantify (type);
5079 if (TypeManager.IsDelegateType (type)) {
5080 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5081 if (RequestedType != null)
5082 if (!(RequestedType is DelegateCreation))
5083 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5084 return RequestedType;
5088 if (type.IsGenericParameter) {
5089 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5091 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5092 Error (304, String.Format (
5093 "Cannot create an instance of the " +
5094 "variable type '{0}' because it " +
5095 "doesn't have the new() constraint",
5100 if ((Arguments != null) && (Arguments.Count != 0)) {
5101 Error (417, String.Format (
5102 "`{0}': cannot provide arguments " +
5103 "when creating an instance of a " +
5104 "variable type.", type));
5108 is_type_parameter = true;
5109 eclass = ExprClass.Value;
5114 if (type.IsAbstract && type.IsSealed) {
5115 Report.SymbolRelatedToPreviousError (type);
5116 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5120 if (type.IsInterface || type.IsAbstract){
5121 if (!TypeManager.IsGenericType (type)) {
5122 RequestedType = CheckComImport (ec);
5123 if (RequestedType != null)
5124 return RequestedType;
5127 Report.SymbolRelatedToPreviousError (type);
5128 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5132 bool is_struct = type.IsValueType;
5133 eclass = ExprClass.Value;
5136 // SRE returns a match for .ctor () on structs (the object constructor),
5137 // so we have to manually ignore it.
5139 if (is_struct && Arguments == null)
5142 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5143 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5144 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5149 method = ml as MethodGroupExpr;
5151 if (method == null) {
5152 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5156 if (Arguments != null){
5157 foreach (Argument a in Arguments){
5158 if (!a.Resolve (ec, loc))
5163 method = method.OverloadResolve (ec, Arguments, false, loc);
5164 if (method == null) {
5165 if (almostMatchedMembers.Count != 0)
5166 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5173 bool DoEmitTypeParameter (EmitContext ec)
5176 ILGenerator ig = ec.ig;
5177 // IMemoryLocation ml;
5179 MethodInfo ci = TypeManager.activator_create_instance.MakeGenericMethod (
5180 new Type [] { type });
5182 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5183 if (gc.HasReferenceTypeConstraint || gc.HasClassConstraint) {
5184 ig.Emit (OpCodes.Call, ci);
5188 // Allow DoEmit() to be called multiple times.
5189 // We need to create a new LocalTemporary each time since
5190 // you can't share LocalBuilders among ILGeneators.
5191 LocalTemporary temp = new LocalTemporary (type);
5193 Label label_activator = ig.DefineLabel ();
5194 Label label_end = ig.DefineLabel ();
5196 temp.AddressOf (ec, AddressOp.Store);
5197 ig.Emit (OpCodes.Initobj, type);
5200 ig.Emit (OpCodes.Box, type);
5201 ig.Emit (OpCodes.Brfalse, label_activator);
5203 temp.AddressOf (ec, AddressOp.Store);
5204 ig.Emit (OpCodes.Initobj, type);
5206 ig.Emit (OpCodes.Br, label_end);
5208 ig.MarkLabel (label_activator);
5210 ig.Emit (OpCodes.Call, ci);
5211 ig.MarkLabel (label_end);
5214 throw new InternalErrorException ();
5219 // This DoEmit can be invoked in two contexts:
5220 // * As a mechanism that will leave a value on the stack (new object)
5221 // * As one that wont (init struct)
5223 // You can control whether a value is required on the stack by passing
5224 // need_value_on_stack. The code *might* leave a value on the stack
5225 // so it must be popped manually
5227 // If we are dealing with a ValueType, we have a few
5228 // situations to deal with:
5230 // * The target is a ValueType, and we have been provided
5231 // the instance (this is easy, we are being assigned).
5233 // * The target of New is being passed as an argument,
5234 // to a boxing operation or a function that takes a
5237 // In this case, we need to create a temporary variable
5238 // that is the argument of New.
5240 // Returns whether a value is left on the stack
5242 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5244 bool is_value_type = TypeManager.IsValueType (type);
5245 ILGenerator ig = ec.ig;
5250 // Allow DoEmit() to be called multiple times.
5251 // We need to create a new LocalTemporary each time since
5252 // you can't share LocalBuilders among ILGeneators.
5253 if (!value_target_set)
5254 value_target = new LocalTemporary (type);
5256 ml = (IMemoryLocation) value_target;
5257 ml.AddressOf (ec, AddressOp.Store);
5261 method.EmitArguments (ec, Arguments);
5265 ig.Emit (OpCodes.Initobj, type);
5267 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5268 if (need_value_on_stack){
5269 value_target.Emit (ec);
5274 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5279 public override void Emit (EmitContext ec)
5281 if (is_type_parameter)
5282 DoEmitTypeParameter (ec);
5287 public override void EmitStatement (EmitContext ec)
5289 bool value_on_stack;
5291 if (is_type_parameter)
5292 value_on_stack = DoEmitTypeParameter (ec);
5294 value_on_stack = DoEmit (ec, false);
5297 ec.ig.Emit (OpCodes.Pop);
5301 public void AddressOf (EmitContext ec, AddressOp Mode)
5303 if (is_type_parameter) {
5304 LocalTemporary temp = new LocalTemporary (type);
5305 DoEmitTypeParameter (ec);
5307 temp.AddressOf (ec, Mode);
5311 if (!type.IsValueType){
5313 // We throw an exception. So far, I believe we only need to support
5315 // foreach (int j in new StructType ())
5318 throw new Exception ("AddressOf should not be used for classes");
5321 if (!value_target_set)
5322 value_target = new LocalTemporary (type);
5323 IMemoryLocation ml = (IMemoryLocation) value_target;
5325 ml.AddressOf (ec, AddressOp.Store);
5326 if (method == null) {
5327 ec.ig.Emit (OpCodes.Initobj, type);
5329 method.EmitArguments (ec, Arguments);
5330 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5333 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5336 protected override void CloneTo (CloneContext clonectx, Expression t)
5338 New target = (New) t;
5340 target.RequestedType = RequestedType.Clone (clonectx);
5341 if (Arguments != null){
5342 target.Arguments = new ArrayList ();
5343 foreach (Argument a in Arguments){
5344 target.Arguments.Add (a.Clone (clonectx));
5351 /// 14.5.10.2: Represents an array creation expression.
5355 /// There are two possible scenarios here: one is an array creation
5356 /// expression that specifies the dimensions and optionally the
5357 /// initialization data and the other which does not need dimensions
5358 /// specified but where initialization data is mandatory.
5360 public class ArrayCreation : Expression {
5361 Expression requested_base_type;
5362 ArrayList initializers;
5365 // The list of Argument types.
5366 // This is used to construct the `newarray' or constructor signature
5368 protected ArrayList arguments;
5370 protected Type array_element_type;
5371 bool expect_initializers = false;
5372 int num_arguments = 0;
5373 protected int dimensions;
5374 protected readonly string rank;
5376 protected ArrayList array_data;
5380 // The number of constants in array initializers
5381 int const_initializers_count;
5382 bool only_constant_initializers;
5384 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5386 this.requested_base_type = requested_base_type;
5387 this.initializers = initializers;
5391 arguments = new ArrayList ();
5393 foreach (Expression e in exprs) {
5394 arguments.Add (new Argument (e, Argument.AType.Expression));
5399 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5401 this.requested_base_type = requested_base_type;
5402 this.initializers = initializers;
5406 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5408 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5410 //dimensions = tmp.Length - 1;
5411 expect_initializers = true;
5414 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5416 StringBuilder sb = new StringBuilder (rank);
5419 for (int i = 1; i < idx_count; i++)
5424 return new ComposedCast (base_type, sb.ToString (), loc);
5427 void Error_IncorrectArrayInitializer ()
5429 Error (178, "Invalid rank specifier: expected `,' or `]'");
5432 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5434 if (specified_dims) {
5435 Argument a = (Argument) arguments [idx];
5437 if (!a.Resolve (ec, loc))
5440 Constant c = a.Expr as Constant;
5442 c = c.ImplicitConversionRequired (TypeManager.int32_type, a.Expr.Location);
5446 Report.Error (150, a.Expr.Location, "A constant value is expected");
5450 int value = (int) c.GetValue ();
5452 if (value != probe.Count) {
5453 Error_IncorrectArrayInitializer ();
5457 bounds [idx] = value;
5460 int child_bounds = -1;
5461 only_constant_initializers = true;
5462 for (int i = 0; i < probe.Count; ++i) {
5463 object o = probe [i];
5464 if (o is ArrayList) {
5465 ArrayList sub_probe = o as ArrayList;
5466 int current_bounds = sub_probe.Count;
5468 if (child_bounds == -1)
5469 child_bounds = current_bounds;
5471 else if (child_bounds != current_bounds){
5472 Error_IncorrectArrayInitializer ();
5475 if (idx + 1 >= dimensions){
5476 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
5480 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
5484 if (child_bounds != -1){
5485 Error_IncorrectArrayInitializer ();
5489 Expression element = ResolveArrayElement (ec, (Expression) o);
5490 if (element == null)
5493 // Initializers with the default values can be ignored
5494 Constant c = element as Constant;
5496 if (c.IsDefaultInitializer (array_element_type)) {
5500 ++const_initializers_count;
5503 only_constant_initializers = false;
5506 array_data.Add (element);
5513 public void UpdateIndices ()
5516 for (ArrayList probe = initializers; probe != null;) {
5517 if (probe.Count > 0 && probe [0] is ArrayList) {
5518 Expression e = new IntConstant (probe.Count, Location.Null);
5519 arguments.Add (new Argument (e, Argument.AType.Expression));
5521 bounds [i++] = probe.Count;
5523 probe = (ArrayList) probe [0];
5526 Expression e = new IntConstant (probe.Count, Location.Null);
5527 arguments.Add (new Argument (e, Argument.AType.Expression));
5529 bounds [i++] = probe.Count;
5536 protected virtual Expression ResolveArrayElement (EmitContext ec, Expression element)
5538 element = element.Resolve (ec);
5539 if (element == null)
5542 return Convert.ImplicitConversionRequired (
5543 ec, element, array_element_type, loc);
5546 protected bool ResolveInitializers (EmitContext ec)
5548 if (initializers == null) {
5549 return !expect_initializers;
5553 // We use this to store all the date values in the order in which we
5554 // will need to store them in the byte blob later
5556 array_data = new ArrayList ();
5557 bounds = new System.Collections.Specialized.HybridDictionary ();
5559 if (arguments != null)
5560 return CheckIndices (ec, initializers, 0, true);
5562 arguments = new ArrayList ();
5564 if (!CheckIndices (ec, initializers, 0, false))
5573 // Resolved the type of the array
5575 bool ResolveArrayType (EmitContext ec)
5577 if (requested_base_type == null) {
5578 Report.Error (622, loc, "Can only use array initializer expressions to assign to array types. Try using a new expression instead");
5582 StringBuilder array_qualifier = new StringBuilder (rank);
5585 // `In the first form allocates an array instace of the type that results
5586 // from deleting each of the individual expression from the expression list'
5588 if (num_arguments > 0) {
5589 array_qualifier.Append ("[");
5590 for (int i = num_arguments-1; i > 0; i--)
5591 array_qualifier.Append (",");
5592 array_qualifier.Append ("]");
5598 TypeExpr array_type_expr;
5599 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
5600 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
5601 if (array_type_expr == null)
5604 type = array_type_expr.Type;
5605 array_element_type = TypeManager.GetElementType (type);
5606 dimensions = type.GetArrayRank ();
5611 public override Expression DoResolve (EmitContext ec)
5616 if (!ResolveArrayType (ec))
5619 if ((array_element_type.Attributes & Class.StaticClassAttribute) == Class.StaticClassAttribute) {
5620 Report.Error (719, loc, "`{0}': array elements cannot be of static type",
5621 TypeManager.CSharpName (array_element_type));
5625 // First step is to validate the initializers and fill
5626 // in any missing bits
5628 if (!ResolveInitializers (ec))
5631 if (arguments.Count != dimensions) {
5632 Error_IncorrectArrayInitializer ();
5635 foreach (Argument a in arguments){
5636 if (!a.Resolve (ec, loc))
5639 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
5640 if (real_arg == null)
5646 eclass = ExprClass.Value;
5650 MethodInfo GetArrayMethod (int arguments)
5652 ModuleBuilder mb = CodeGen.Module.Builder;
5654 Type[] arg_types = new Type[arguments];
5655 for (int i = 0; i < arguments; i++)
5656 arg_types[i] = TypeManager.int32_type;
5658 MethodInfo mi = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
5662 Report.Error (-6, "New invocation: Can not find a constructor for " +
5663 "this argument list");
5670 byte [] MakeByteBlob ()
5675 int count = array_data.Count;
5677 if (array_element_type.IsEnum)
5678 array_element_type = TypeManager.EnumToUnderlying (array_element_type);
5680 factor = GetTypeSize (array_element_type);
5682 throw new Exception ("unrecognized type in MakeByteBlob: " + array_element_type);
5684 data = new byte [(count * factor + 4) & ~3];
5687 for (int i = 0; i < count; ++i) {
5688 object v = array_data [i];
5690 if (v is EnumConstant)
5691 v = ((EnumConstant) v).Child;
5693 if (v is Constant && !(v is StringConstant))
5694 v = ((Constant) v).GetValue ();
5700 if (array_element_type == TypeManager.int64_type){
5701 if (!(v is Expression)){
5702 long val = (long) v;
5704 for (int j = 0; j < factor; ++j) {
5705 data [idx + j] = (byte) (val & 0xFF);
5709 } else if (array_element_type == TypeManager.uint64_type){
5710 if (!(v is Expression)){
5711 ulong val = (ulong) v;
5713 for (int j = 0; j < factor; ++j) {
5714 data [idx + j] = (byte) (val & 0xFF);
5718 } else if (array_element_type == TypeManager.float_type) {
5719 if (!(v is Expression)){
5720 element = BitConverter.GetBytes ((float) v);
5722 for (int j = 0; j < factor; ++j)
5723 data [idx + j] = element [j];
5724 if (!BitConverter.IsLittleEndian)
5725 System.Array.Reverse (data, idx, 4);
5727 } else if (array_element_type == TypeManager.double_type) {
5728 if (!(v is Expression)){
5729 element = BitConverter.GetBytes ((double) v);
5731 for (int j = 0; j < factor; ++j)
5732 data [idx + j] = element [j];
5734 // FIXME: Handle the ARM float format.
5735 if (!BitConverter.IsLittleEndian)
5736 System.Array.Reverse (data, idx, 8);
5738 } else if (array_element_type == TypeManager.char_type){
5739 if (!(v is Expression)){
5740 int val = (int) ((char) v);
5742 data [idx] = (byte) (val & 0xff);
5743 data [idx+1] = (byte) (val >> 8);
5745 } else if (array_element_type == TypeManager.short_type){
5746 if (!(v is Expression)){
5747 int val = (int) ((short) v);
5749 data [idx] = (byte) (val & 0xff);
5750 data [idx+1] = (byte) (val >> 8);
5752 } else if (array_element_type == TypeManager.ushort_type){
5753 if (!(v is Expression)){
5754 int val = (int) ((ushort) v);
5756 data [idx] = (byte) (val & 0xff);
5757 data [idx+1] = (byte) (val >> 8);
5759 } else if (array_element_type == TypeManager.int32_type) {
5760 if (!(v is Expression)){
5763 data [idx] = (byte) (val & 0xff);
5764 data [idx+1] = (byte) ((val >> 8) & 0xff);
5765 data [idx+2] = (byte) ((val >> 16) & 0xff);
5766 data [idx+3] = (byte) (val >> 24);
5768 } else if (array_element_type == TypeManager.uint32_type) {
5769 if (!(v is Expression)){
5770 uint val = (uint) v;
5772 data [idx] = (byte) (val & 0xff);
5773 data [idx+1] = (byte) ((val >> 8) & 0xff);
5774 data [idx+2] = (byte) ((val >> 16) & 0xff);
5775 data [idx+3] = (byte) (val >> 24);
5777 } else if (array_element_type == TypeManager.sbyte_type) {
5778 if (!(v is Expression)){
5779 sbyte val = (sbyte) v;
5780 data [idx] = (byte) val;
5782 } else if (array_element_type == TypeManager.byte_type) {
5783 if (!(v is Expression)){
5784 byte val = (byte) v;
5785 data [idx] = (byte) val;
5787 } else if (array_element_type == TypeManager.bool_type) {
5788 if (!(v is Expression)){
5789 bool val = (bool) v;
5790 data [idx] = (byte) (val ? 1 : 0);
5792 } else if (array_element_type == TypeManager.decimal_type){
5793 if (!(v is Expression)){
5794 int [] bits = Decimal.GetBits ((decimal) v);
5797 // FIXME: For some reason, this doesn't work on the MS runtime.
5798 int [] nbits = new int [4];
5799 nbits [0] = bits [3];
5800 nbits [1] = bits [2];
5801 nbits [2] = bits [0];
5802 nbits [3] = bits [1];
5804 for (int j = 0; j < 4; j++){
5805 data [p++] = (byte) (nbits [j] & 0xff);
5806 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
5807 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
5808 data [p++] = (byte) (nbits [j] >> 24);
5812 throw new Exception ("Unrecognized type in MakeByteBlob: " + array_element_type);
5821 // Emits the initializers for the array
5823 void EmitStaticInitializers (EmitContext ec)
5826 // First, the static data
5829 ILGenerator ig = ec.ig;
5831 byte [] data = MakeByteBlob ();
5833 fb = RootContext.MakeStaticData (data);
5835 ig.Emit (OpCodes.Dup);
5836 ig.Emit (OpCodes.Ldtoken, fb);
5837 ig.Emit (OpCodes.Call,
5838 TypeManager.void_initializearray_array_fieldhandle);
5842 // Emits pieces of the array that can not be computed at compile
5843 // time (variables and string locations).
5845 // This always expect the top value on the stack to be the array
5847 void EmitDynamicInitializers (EmitContext ec, bool emitConstants)
5849 ILGenerator ig = ec.ig;
5850 int dims = bounds.Count;
5851 int [] current_pos = new int [dims];
5853 MethodInfo set = null;
5856 Type [] args = new Type [dims + 1];
5858 for (int j = 0; j < dims; j++)
5859 args [j] = TypeManager.int32_type;
5860 args [dims] = array_element_type;
5862 set = CodeGen.Module.Builder.GetArrayMethod (
5864 CallingConventions.HasThis | CallingConventions.Standard,
5865 TypeManager.void_type, args);
5868 for (int i = 0; i < array_data.Count; i++){
5870 Expression e = (Expression)array_data [i];
5872 // Constant can be initialized via StaticInitializer
5873 if (e != null && !(!emitConstants && e is Constant)) {
5874 Type etype = e.Type;
5876 ig.Emit (OpCodes.Dup);
5878 for (int idx = 0; idx < dims; idx++)
5879 IntConstant.EmitInt (ig, current_pos [idx]);
5882 // If we are dealing with a struct, get the
5883 // address of it, so we can store it.
5885 if ((dims == 1) && etype.IsValueType &&
5886 (!TypeManager.IsBuiltinOrEnum (etype) ||
5887 etype == TypeManager.decimal_type)) {
5892 // Let new know that we are providing
5893 // the address where to store the results
5895 n.DisableTemporaryValueType ();
5898 ig.Emit (OpCodes.Ldelema, etype);
5904 bool is_stobj, has_type_arg;
5905 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
5907 ig.Emit (OpCodes.Stobj, etype);
5908 else if (has_type_arg)
5909 ig.Emit (op, etype);
5913 ig.Emit (OpCodes.Call, set);
5920 for (int j = dims - 1; j >= 0; j--){
5922 if (current_pos [j] < (int) bounds [j])
5924 current_pos [j] = 0;
5929 void EmitArrayArguments (EmitContext ec)
5931 ILGenerator ig = ec.ig;
5933 foreach (Argument a in arguments) {
5934 Type atype = a.Type;
5937 if (atype == TypeManager.uint64_type)
5938 ig.Emit (OpCodes.Conv_Ovf_U4);
5939 else if (atype == TypeManager.int64_type)
5940 ig.Emit (OpCodes.Conv_Ovf_I4);
5944 public override void Emit (EmitContext ec)
5946 ILGenerator ig = ec.ig;
5948 EmitArrayArguments (ec);
5949 if (arguments.Count == 1)
5950 ig.Emit (OpCodes.Newarr, array_element_type);
5952 ig.Emit (OpCodes.Newobj, GetArrayMethod (arguments.Count));
5955 if (initializers == null)
5958 // Emit static initializer for arrays which have contain more than 4 items and
5959 // the static initializer will initialize at least 25% of array values.
5960 // NOTE: const_initializers_count does not contain default constant values.
5961 if (const_initializers_count >= 4 && const_initializers_count * 4 > (array_data.Count) &&
5962 TypeManager.IsPrimitiveType (array_element_type)) {
5963 EmitStaticInitializers (ec);
5965 if (!only_constant_initializers)
5966 EmitDynamicInitializers (ec, false);
5968 EmitDynamicInitializers (ec, true);
5972 public override bool GetAttributableValue (Type valueType, out object value)
5974 if (arguments.Count != 1) {
5975 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
5976 return base.GetAttributableValue (null, out value);
5979 if (array_data == null) {
5980 Constant c = (Constant)((Argument)arguments [0]).Expr;
5981 if (c.IsDefaultValue) {
5982 value = Array.CreateInstance (array_element_type, 0);
5985 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
5986 return base.GetAttributableValue (null, out value);
5989 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
5990 object element_value;
5991 for (int i = 0; i < ret.Length; ++i)
5993 Expression e = (Expression)array_data [i];
5995 // Is null when an initializer is optimized (value == predefined value)
5999 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6003 ret.SetValue (element_value, i);
6009 protected override void CloneTo (CloneContext clonectx, Expression t)
6011 ArrayCreation target = (ArrayCreation) t;
6013 target.requested_base_type = requested_base_type.Clone (clonectx);
6014 target.arguments = new ArrayList ();
6015 foreach (Argument a in arguments)
6016 target.arguments.Add (a.Clone (clonectx));
6018 if (initializers != null){
6019 target.initializers = new ArrayList ();
6020 foreach (Expression initializer in initializers)
6021 target.initializers.Add (initializer.Clone (clonectx));
6027 // Represents an implicitly typed array epxression
6029 public class ImplicitlyTypedArrayCreation : ArrayCreation
6031 public ImplicitlyTypedArrayCreation (string rank, ArrayList initializers, Location loc)
6032 : base (null, rank, initializers, loc)
6034 if (rank.Length > 2) {
6035 while (rank [++dimensions] == ',');
6041 public override Expression DoResolve (EmitContext ec)
6046 if (!ResolveInitializers (ec))
6049 if (array_element_type == null || array_element_type == TypeManager.null_type ||
6050 array_element_type == TypeManager.void_type || array_element_type == TypeManager.anonymous_method_type ||
6051 arguments.Count != dimensions) {
6052 Report.Error (826, loc, "The type of an implicitly typed array cannot be inferred from the initializer. Try specifying array type explicitly");
6057 // At this point we found common base type for all initializer elements
6058 // but we have to be sure that all static initializer elements are of
6061 UnifyInitializerElement (ec);
6063 type = TypeManager.GetConstructedType (array_element_type, rank);
6064 eclass = ExprClass.Value;
6069 // Converts static initializer only
6071 void UnifyInitializerElement (EmitContext ec)
6073 for (int i = 0; i < array_data.Count; ++i) {
6074 Expression e = (Expression)array_data[i];
6076 array_data [i] = Convert.ImplicitConversionStandard (ec, e, array_element_type, Location.Null);
6080 protected override Expression ResolveArrayElement (EmitContext ec, Expression element)
6082 element = element.Resolve (ec);
6083 if (element == null)
6086 if (array_element_type == null) {
6087 array_element_type = element.Type;
6091 if (Convert.ImplicitStandardConversionExists (element, array_element_type)) {
6095 if (Convert.ImplicitStandardConversionExists (new TypeExpression (array_element_type, loc), element.Type)) {
6096 array_element_type = element.Type;
6100 element.Error_ValueCannotBeConverted (ec, element.Location, array_element_type, false);
6105 public sealed class CompilerGeneratedThis : This
6107 public static This Instance = new CompilerGeneratedThis ();
6109 private CompilerGeneratedThis ()
6110 : base (Location.Null)
6114 public override Expression DoResolve (EmitContext ec)
6116 eclass = ExprClass.Variable;
6117 type = ec.ContainerType;
6118 variable = new SimpleThis (type);
6124 /// Represents the `this' construct
6127 public class This : VariableReference, IVariable
6130 VariableInfo variable_info;
6131 protected Variable variable;
6134 public This (Block block, Location loc)
6140 public This (Location loc)
6145 public VariableInfo VariableInfo {
6146 get { return variable_info; }
6149 public bool VerifyFixed ()
6151 return !TypeManager.IsValueType (Type);
6154 public override bool IsRef {
6155 get { return is_struct; }
6158 public override Variable Variable {
6159 get { return variable; }
6162 public bool ResolveBase (EmitContext ec)
6164 eclass = ExprClass.Variable;
6166 if (ec.TypeContainer.CurrentType != null)
6167 type = ec.TypeContainer.CurrentType;
6169 type = ec.ContainerType;
6171 is_struct = ec.TypeContainer is Struct;
6174 Error (26, "Keyword `this' is not valid in a static property, " +
6175 "static method, or static field initializer");
6179 if (block != null) {
6180 if (block.Toplevel.ThisVariable != null)
6181 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6183 AnonymousContainer am = ec.CurrentAnonymousMethod;
6184 if (is_struct && (am != null) && !am.IsIterator) {
6185 Report.Error (1673, loc, "Anonymous methods inside structs " +
6186 "cannot access instance members of `this'. " +
6187 "Consider copying `this' to a local variable " +
6188 "outside the anonymous method and using the " +
6193 RootScopeInfo host = block.Toplevel.RootScope;
6194 if ((host != null) && !ec.IsConstructor &&
6195 (!is_struct || host.IsIterator)) {
6196 variable = host.CaptureThis ();
6197 type = variable.Type;
6202 if (variable == null)
6203 variable = new SimpleThis (type);
6209 // Called from Invocation to check if the invocation is correct
6211 public bool CheckThisUsage (EmitContext ec)
6213 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) &&
6214 !variable_info.IsAssigned (ec)) {
6215 Error (188, "The `this' object cannot be used before all of its " +
6216 "fields are assigned to");
6217 variable_info.SetAssigned (ec);
6224 public override Expression DoResolve (EmitContext ec)
6226 if (!ResolveBase (ec))
6230 if (ec.IsFieldInitializer) {
6231 Error (27, "Keyword `this' is not available in the current context");
6238 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6240 if (!ResolveBase (ec))
6243 if (variable_info != null)
6244 variable_info.SetAssigned (ec);
6246 if (ec.TypeContainer is Class){
6247 Error (1604, "Cannot assign to 'this' because it is read-only");
6253 public override int GetHashCode()
6255 return block.GetHashCode ();
6258 public override bool Equals (object obj)
6260 This t = obj as This;
6264 return block == t.block;
6267 protected class SimpleThis : Variable
6271 public SimpleThis (Type type)
6276 public override Type Type {
6277 get { return type; }
6280 public override bool HasInstance {
6281 get { return false; }
6284 public override bool NeedsTemporary {
6285 get { return false; }
6288 public override void EmitInstance (EmitContext ec)
6293 public override void Emit (EmitContext ec)
6295 ec.ig.Emit (OpCodes.Ldarg_0);
6298 public override void EmitAssign (EmitContext ec)
6300 throw new InvalidOperationException ();
6303 public override void EmitAddressOf (EmitContext ec)
6305 ec.ig.Emit (OpCodes.Ldarg_0);
6309 protected override void CloneTo (CloneContext clonectx, Expression t)
6311 This target = (This) t;
6313 target.block = clonectx.LookupBlock (block);
6318 /// Represents the `__arglist' construct
6320 public class ArglistAccess : Expression
6322 public ArglistAccess (Location loc)
6327 public override Expression DoResolve (EmitContext ec)
6329 eclass = ExprClass.Variable;
6330 type = TypeManager.runtime_argument_handle_type;
6332 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6334 Error (190, "The __arglist construct is valid only within " +
6335 "a variable argument method");
6342 public override void Emit (EmitContext ec)
6344 ec.ig.Emit (OpCodes.Arglist);
6347 protected override void CloneTo (CloneContext clonectx, Expression target)
6354 /// Represents the `__arglist (....)' construct
6356 public class Arglist : Expression
6358 Argument[] Arguments;
6360 public Arglist (Location loc)
6361 : this (Argument.Empty, loc)
6365 public Arglist (Argument[] args, Location l)
6371 public Type[] ArgumentTypes {
6373 Type[] retval = new Type [Arguments.Length];
6374 for (int i = 0; i < Arguments.Length; i++)
6375 retval [i] = Arguments [i].Type;
6380 public override Expression DoResolve (EmitContext ec)
6382 eclass = ExprClass.Variable;
6383 type = TypeManager.runtime_argument_handle_type;
6385 foreach (Argument arg in Arguments) {
6386 if (!arg.Resolve (ec, loc))
6393 public override void Emit (EmitContext ec)
6395 foreach (Argument arg in Arguments)
6399 protected override void CloneTo (CloneContext clonectx, Expression t)
6401 Arglist target = (Arglist) t;
6403 target.Arguments = new Argument [Arguments.Length];
6404 for (int i = 0; i < Arguments.Length; i++)
6405 target.Arguments [i] = Arguments [i].Clone (clonectx);
6410 // This produces the value that renders an instance, used by the iterators code
6412 public class ProxyInstance : Expression, IMemoryLocation {
6413 public override Expression DoResolve (EmitContext ec)
6415 eclass = ExprClass.Variable;
6416 type = ec.ContainerType;
6420 public override void Emit (EmitContext ec)
6422 ec.ig.Emit (OpCodes.Ldarg_0);
6426 public void AddressOf (EmitContext ec, AddressOp mode)
6428 ec.ig.Emit (OpCodes.Ldarg_0);
6433 /// Implements the typeof operator
6435 public class TypeOf : Expression {
6436 Expression QueriedType;
6437 protected Type typearg;
6439 public TypeOf (Expression queried_type, Location l)
6441 QueriedType = queried_type;
6445 public override Expression DoResolve (EmitContext ec)
6447 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6451 typearg = texpr.Type;
6453 if (typearg == TypeManager.void_type) {
6454 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6458 if (typearg.IsPointer && !ec.InUnsafe){
6463 type = TypeManager.type_type;
6464 // Even though what is returned is a type object, it's treated as a value by the compiler.
6465 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6466 eclass = ExprClass.Value;
6470 public override void Emit (EmitContext ec)
6472 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6473 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6476 public override bool GetAttributableValue (Type valueType, out object value)
6478 if (TypeManager.ContainsGenericParameters (typearg)) {
6479 Report.SymbolRelatedToPreviousError(typearg);
6480 Report.Error(416, loc, "`{0}': an attribute argument cannot use type parameters",
6481 TypeManager.CSharpName(typearg));
6486 if (valueType == TypeManager.object_type) {
6487 value = (object)typearg;
6494 public Type TypeArgument
6502 protected override void CloneTo (CloneContext clonectx, Expression t)
6504 TypeOf target = (TypeOf) t;
6506 target.QueriedType = QueriedType.Clone (clonectx);
6511 /// Implements the `typeof (void)' operator
6513 public class TypeOfVoid : TypeOf {
6514 public TypeOfVoid (Location l) : base (null, l)
6519 public override Expression DoResolve (EmitContext ec)
6521 type = TypeManager.type_type;
6522 typearg = TypeManager.void_type;
6523 // See description in TypeOf.
6524 eclass = ExprClass.Value;
6530 /// Implements the sizeof expression
6532 public class SizeOf : Expression {
6533 readonly Expression QueriedType;
6536 public SizeOf (Expression queried_type, Location l)
6538 this.QueriedType = queried_type;
6542 public override Expression DoResolve (EmitContext ec)
6544 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6549 if (texpr is TypeParameterExpr){
6550 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
6555 type_queried = texpr.Type;
6556 if (type_queried.IsEnum)
6557 type_queried = TypeManager.EnumToUnderlying (type_queried);
6559 if (type_queried == TypeManager.void_type) {
6560 Expression.Error_VoidInvalidInTheContext (loc);
6564 int size_of = GetTypeSize (type_queried);
6566 return new IntConstant (size_of, loc);
6570 Report.Error (233, loc, "`{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf)",
6571 TypeManager.CSharpName (type_queried));
6575 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
6579 type = TypeManager.int32_type;
6580 eclass = ExprClass.Value;
6584 public override void Emit (EmitContext ec)
6586 int size = GetTypeSize (type_queried);
6589 ec.ig.Emit (OpCodes.Sizeof, type_queried);
6591 IntConstant.EmitInt (ec.ig, size);
6594 protected override void CloneTo (CloneContext clonectx, Expression t)
6600 /// Implements the qualified-alias-member (::) expression.
6602 public class QualifiedAliasMember : Expression
6604 string alias, identifier;
6606 public QualifiedAliasMember (string alias, string identifier, Location l)
6608 if (RootContext.Version == LanguageVersion.ISO_1)
6609 Report.FeatureIsNotISO1 (l, "namespace alias qualifier");
6612 this.identifier = identifier;
6616 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
6618 if (alias == "global")
6619 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
6621 int errors = Report.Errors;
6622 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6624 if (errors == Report.Errors)
6625 Report.Error (432, loc, "Alias `{0}' not found", alias);
6628 if (fne.eclass != ExprClass.Namespace) {
6630 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6633 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
6636 public override Expression DoResolve (EmitContext ec)
6638 FullNamedExpression fne;
6639 if (alias == "global") {
6640 fne = RootNamespace.Global;
6642 int errors = Report.Errors;
6643 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6645 if (errors == Report.Errors)
6646 Report.Error (432, loc, "Alias `{0}' not found", alias);
6651 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
6655 if (!(retval is FullNamedExpression)) {
6656 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
6660 // We defer this check till the end to match the behaviour of CSC
6661 if (fne.eclass != ExprClass.Namespace) {
6662 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6668 public override void Emit (EmitContext ec)
6670 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
6674 public override string ToString ()
6676 return alias + "::" + identifier;
6679 public override string GetSignatureForError ()
6684 protected override void CloneTo (CloneContext clonectx, Expression t)
6691 /// Implements the member access expression
6693 public class MemberAccess : Expression {
6694 public readonly string Identifier;
6696 readonly TypeArguments args;
6698 public MemberAccess (Expression expr, string id)
6699 : this (expr, id, expr.Location)
6703 public MemberAccess (Expression expr, string identifier, Location loc)
6706 Identifier = identifier;
6710 public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
6711 : this (expr, identifier, loc)
6716 protected string LookupIdentifier {
6717 get { return MemberName.MakeName (Identifier, args); }
6720 // TODO: this method has very poor performace for Enum fields and
6721 // probably for other constants as well
6722 Expression DoResolve (EmitContext ec, Expression right_side)
6725 throw new Exception ();
6728 // Resolve the expression with flow analysis turned off, we'll do the definite
6729 // assignment checks later. This is because we don't know yet what the expression
6730 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
6731 // definite assignment check on the actual field and not on the whole struct.
6734 SimpleName original = expr as SimpleName;
6735 Expression expr_resolved = expr.Resolve (ec,
6736 ResolveFlags.VariableOrValue | ResolveFlags.Type |
6737 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
6739 if (expr_resolved == null)
6742 if (expr_resolved is Namespace) {
6743 Namespace ns = (Namespace) expr_resolved;
6744 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, LookupIdentifier, loc);
6746 if ((retval != null) && (args != null))
6747 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
6751 ns.Error_NamespaceDoesNotExist (ec.DeclContainer, loc, Identifier);
6755 Type expr_type = expr_resolved.Type;
6756 if (expr_type.IsPointer || expr_type == TypeManager.void_type || expr_resolved is NullLiteral){
6757 Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type);
6760 if (expr_type == TypeManager.anonymous_method_type){
6761 Unary.Error_OperatorCannotBeApplied (loc, ".", "anonymous method");
6765 Constant c = expr_resolved as Constant;
6766 if (c != null && c.GetValue () == null) {
6767 Report.Warning (1720, 1, loc, "Expression will always cause a `{0}'",
6768 "System.NullReferenceException");
6771 Expression member_lookup;
6772 member_lookup = MemberLookup (
6773 ec.ContainerType, expr_type, expr_type, Identifier, loc);
6775 if ((member_lookup == null) && (args != null)) {
6776 member_lookup = MemberLookup (
6777 ec.ContainerType, expr_type, expr_type, LookupIdentifier, loc);
6780 if (member_lookup == null) {
6781 ExtensionMethodGroupExpr ex_method_lookup = ec.DeclContainer.LookupExtensionMethod (expr_type, Identifier);
6782 if (ex_method_lookup != null) {
6783 ex_method_lookup.ExtensionExpression = expr_resolved;
6784 return ex_method_lookup.DoResolve (ec);
6787 if (!ec.IsInProbingMode)
6788 MemberLookupFailed (
6789 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
6793 TypeExpr texpr = member_lookup as TypeExpr;
6794 if (texpr != null) {
6795 if (!(expr_resolved is TypeExpr) &&
6796 (original == null || !original.IdenticalNameAndTypeName (ec, expr_resolved, loc))) {
6797 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
6798 Identifier, member_lookup.GetSignatureForError ());
6802 if (!texpr.CheckAccessLevel (ec.DeclContainer)) {
6803 Report.SymbolRelatedToPreviousError (member_lookup.Type);
6804 ErrorIsInaccesible (loc, TypeManager.CSharpName (member_lookup.Type));
6809 ConstructedType ct = expr_resolved as ConstructedType;
6812 // When looking up a nested type in a generic instance
6813 // via reflection, we always get a generic type definition
6814 // and not a generic instance - so we have to do this here.
6816 // See gtest-172-lib.cs and gtest-172.cs for an example.
6818 ct = new ConstructedType (
6819 member_lookup.Type, ct.TypeArguments, loc);
6821 return ct.ResolveAsTypeStep (ec, false);
6824 return member_lookup;
6827 MemberExpr me = (MemberExpr) member_lookup;
6828 member_lookup = me.ResolveMemberAccess (ec, expr_resolved, loc, original);
6829 if (member_lookup == null)
6833 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
6835 throw new InternalErrorException ();
6837 return mg.ResolveGeneric (ec, args);
6840 if (original != null && !TypeManager.IsValueType (expr_type)) {
6841 me = member_lookup as MemberExpr;
6842 if (me != null && me.IsInstance) {
6843 LocalVariableReference var = expr_resolved as LocalVariableReference;
6844 if (var != null && !var.VerifyAssigned (ec))
6849 // The following DoResolve/DoResolveLValue will do the definite assignment
6852 if (right_side != null)
6853 return member_lookup.DoResolveLValue (ec, right_side);
6855 return member_lookup.DoResolve (ec);
6858 public override Expression DoResolve (EmitContext ec)
6860 return DoResolve (ec, null);
6863 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
6865 return DoResolve (ec, right_side);
6868 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
6870 return ResolveNamespaceOrType (ec, silent);
6873 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
6875 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
6877 if (new_expr == null)
6880 if (new_expr is Namespace) {
6881 Namespace ns = (Namespace) new_expr;
6882 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, LookupIdentifier, loc);
6884 if ((retval != null) && (args != null))
6885 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
6887 if (!silent && retval == null)
6888 ns.Error_NamespaceDoesNotExist (rc.DeclContainer, loc, LookupIdentifier);
6892 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
6893 if (tnew_expr == null)
6896 Type expr_type = tnew_expr.Type;
6898 if (expr_type.IsPointer){
6899 Error (23, "The `.' operator can not be applied to pointer operands (" +
6900 TypeManager.CSharpName (expr_type) + ")");
6904 Expression member_lookup = MemberLookup (
6905 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
6906 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
6907 if (member_lookup == null) {
6911 member_lookup = MemberLookup(
6912 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
6913 MemberTypes.All, BindingFlags.Public | BindingFlags.NonPublic, loc);
6915 if (member_lookup == null) {
6916 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
6917 Identifier, new_expr.GetSignatureForError ());
6919 // TODO: Report.SymbolRelatedToPreviousError
6920 member_lookup.Error_UnexpectedKind (null, "type", loc);
6925 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
6930 TypeArguments the_args = args;
6931 if (TypeManager.HasGenericArguments (expr_type)) {
6932 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
6934 TypeArguments new_args = new TypeArguments (loc);
6935 foreach (Type decl in decl_args)
6936 new_args.Add (new TypeExpression (decl, loc));
6939 new_args.Add (args);
6941 the_args = new_args;
6944 if (the_args != null) {
6945 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
6946 return ctype.ResolveAsTypeStep (rc, false);
6953 public override void Emit (EmitContext ec)
6955 throw new Exception ("Should not happen");
6958 public override string ToString ()
6960 return expr + "." + MemberName.MakeName (Identifier, args);
6963 public override string GetSignatureForError ()
6965 return expr.GetSignatureForError () + "." + Identifier;
6968 protected override void CloneTo (CloneContext clonectx, Expression t)
6970 MemberAccess target = (MemberAccess) t;
6972 target.expr = expr.Clone (clonectx);
6977 /// Implements checked expressions
6979 public class CheckedExpr : Expression {
6981 public Expression Expr;
6983 public CheckedExpr (Expression e, Location l)
6989 public override Expression DoResolve (EmitContext ec)
6991 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
6992 Expr = Expr.Resolve (ec);
6997 if (Expr is Constant)
7000 eclass = Expr.eclass;
7005 public override void Emit (EmitContext ec)
7007 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7011 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7013 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7014 Expr.EmitBranchable (ec, target, onTrue);
7017 protected override void CloneTo (CloneContext clonectx, Expression t)
7019 CheckedExpr target = (CheckedExpr) t;
7021 target.Expr = Expr.Clone (clonectx);
7026 /// Implements the unchecked expression
7028 public class UnCheckedExpr : Expression {
7030 public Expression Expr;
7032 public UnCheckedExpr (Expression e, Location l)
7038 public override Expression DoResolve (EmitContext ec)
7040 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7041 Expr = Expr.Resolve (ec);
7046 if (Expr is Constant)
7049 eclass = Expr.eclass;
7054 public override void Emit (EmitContext ec)
7056 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7060 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7062 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7063 Expr.EmitBranchable (ec, target, onTrue);
7066 protected override void CloneTo (CloneContext clonectx, Expression t)
7068 UnCheckedExpr target = (UnCheckedExpr) t;
7070 target.Expr = Expr.Clone (clonectx);
7075 /// An Element Access expression.
7077 /// During semantic analysis these are transformed into
7078 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7080 public class ElementAccess : Expression {
7081 public ArrayList Arguments;
7082 public Expression Expr;
7084 public ElementAccess (Expression e, ArrayList e_list)
7093 Arguments = new ArrayList ();
7094 foreach (Expression tmp in e_list)
7095 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7099 bool CommonResolve (EmitContext ec)
7101 Expr = Expr.Resolve (ec);
7103 if (Arguments == null)
7106 foreach (Argument a in Arguments){
7107 if (!a.Resolve (ec, loc))
7111 return Expr != null;
7114 Expression MakePointerAccess (EmitContext ec, Type t)
7116 if (t == TypeManager.void_ptr_type){
7117 Error (242, "The array index operation is not valid on void pointers");
7120 if (Arguments.Count != 1){
7121 Error (196, "A pointer must be indexed by only one value");
7126 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7129 return new Indirection (p, loc).Resolve (ec);
7132 public override Expression DoResolve (EmitContext ec)
7134 if (!CommonResolve (ec))
7138 // We perform some simple tests, and then to "split" the emit and store
7139 // code we create an instance of a different class, and return that.
7141 // I am experimenting with this pattern.
7145 if (t == TypeManager.array_type){
7146 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7151 return (new ArrayAccess (this, loc)).Resolve (ec);
7153 return MakePointerAccess (ec, t);
7155 FieldExpr fe = Expr as FieldExpr;
7157 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7159 return MakePointerAccess (ec, ff.ElementType);
7162 return (new IndexerAccess (this, loc)).Resolve (ec);
7165 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7167 if (!CommonResolve (ec))
7172 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7175 return MakePointerAccess (ec, t);
7177 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7180 public override void Emit (EmitContext ec)
7182 throw new Exception ("Should never be reached");
7185 protected override void CloneTo (CloneContext clonectx, Expression t)
7187 ElementAccess target = (ElementAccess) t;
7189 target.Expr = Expr.Clone (clonectx);
7190 target.Arguments = new ArrayList (Arguments.Count);
7191 foreach (Argument a in Arguments)
7192 target.Arguments.Add (a.Clone (clonectx));
7197 /// Implements array access
7199 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7201 // Points to our "data" repository
7205 LocalTemporary temp;
7208 public ArrayAccess (ElementAccess ea_data, Location l)
7211 eclass = ExprClass.Variable;
7215 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7217 return DoResolve (ec);
7220 public override Expression DoResolve (EmitContext ec)
7223 ExprClass eclass = ea.Expr.eclass;
7225 // As long as the type is valid
7226 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7227 eclass == ExprClass.Value)) {
7228 ea.Expr.Error_UnexpectedKind ("variable or value");
7233 Type t = ea.Expr.Type;
7234 if (t.GetArrayRank () != ea.Arguments.Count){
7235 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7236 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7240 type = TypeManager.GetElementType (t);
7241 if (type.IsPointer && !ec.InUnsafe){
7242 UnsafeError (ea.Location);
7246 foreach (Argument a in ea.Arguments){
7247 Type argtype = a.Type;
7249 if (argtype == TypeManager.int32_type ||
7250 argtype == TypeManager.uint32_type ||
7251 argtype == TypeManager.int64_type ||
7252 argtype == TypeManager.uint64_type) {
7253 Constant c = a.Expr as Constant;
7254 if (c != null && c.IsNegative) {
7255 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7261 // Mhm. This is strage, because the Argument.Type is not the same as
7262 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7264 // Wonder if I will run into trouble for this.
7266 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7271 eclass = ExprClass.Variable;
7277 /// Emits the right opcode to load an object of Type `t'
7278 /// from an array of T
7280 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7282 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7283 ig.Emit (OpCodes.Ldelem_U1);
7284 else if (type == TypeManager.sbyte_type)
7285 ig.Emit (OpCodes.Ldelem_I1);
7286 else if (type == TypeManager.short_type)
7287 ig.Emit (OpCodes.Ldelem_I2);
7288 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7289 ig.Emit (OpCodes.Ldelem_U2);
7290 else if (type == TypeManager.int32_type)
7291 ig.Emit (OpCodes.Ldelem_I4);
7292 else if (type == TypeManager.uint32_type)
7293 ig.Emit (OpCodes.Ldelem_U4);
7294 else if (type == TypeManager.uint64_type)
7295 ig.Emit (OpCodes.Ldelem_I8);
7296 else if (type == TypeManager.int64_type)
7297 ig.Emit (OpCodes.Ldelem_I8);
7298 else if (type == TypeManager.float_type)
7299 ig.Emit (OpCodes.Ldelem_R4);
7300 else if (type == TypeManager.double_type)
7301 ig.Emit (OpCodes.Ldelem_R8);
7302 else if (type == TypeManager.intptr_type)
7303 ig.Emit (OpCodes.Ldelem_I);
7304 else if (TypeManager.IsEnumType (type)){
7305 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7306 } else if (type.IsValueType){
7307 ig.Emit (OpCodes.Ldelema, type);
7308 ig.Emit (OpCodes.Ldobj, type);
7310 } else if (type.IsGenericParameter) {
7311 ig.Emit (OpCodes.Ldelem, type);
7313 } else if (type.IsPointer)
7314 ig.Emit (OpCodes.Ldelem_I);
7316 ig.Emit (OpCodes.Ldelem_Ref);
7320 /// Returns the right opcode to store an object of Type `t'
7321 /// from an array of T.
7323 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7325 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7326 has_type_arg = false; is_stobj = false;
7327 t = TypeManager.TypeToCoreType (t);
7328 if (TypeManager.IsEnumType (t))
7329 t = TypeManager.EnumToUnderlying (t);
7330 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7331 t == TypeManager.bool_type)
7332 return OpCodes.Stelem_I1;
7333 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7334 t == TypeManager.char_type)
7335 return OpCodes.Stelem_I2;
7336 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7337 return OpCodes.Stelem_I4;
7338 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7339 return OpCodes.Stelem_I8;
7340 else if (t == TypeManager.float_type)
7341 return OpCodes.Stelem_R4;
7342 else if (t == TypeManager.double_type)
7343 return OpCodes.Stelem_R8;
7344 else if (t == TypeManager.intptr_type) {
7345 has_type_arg = true;
7347 return OpCodes.Stobj;
7348 } else if (t.IsValueType) {
7349 has_type_arg = true;
7351 return OpCodes.Stobj;
7353 } else if (t.IsGenericParameter) {
7354 has_type_arg = true;
7355 return OpCodes.Stelem;
7358 } else if (t.IsPointer)
7359 return OpCodes.Stelem_I;
7361 return OpCodes.Stelem_Ref;
7364 MethodInfo FetchGetMethod ()
7366 ModuleBuilder mb = CodeGen.Module.Builder;
7367 int arg_count = ea.Arguments.Count;
7368 Type [] args = new Type [arg_count];
7371 for (int i = 0; i < arg_count; i++){
7372 //args [i++] = a.Type;
7373 args [i] = TypeManager.int32_type;
7376 get = mb.GetArrayMethod (
7377 ea.Expr.Type, "Get",
7378 CallingConventions.HasThis |
7379 CallingConventions.Standard,
7385 MethodInfo FetchAddressMethod ()
7387 ModuleBuilder mb = CodeGen.Module.Builder;
7388 int arg_count = ea.Arguments.Count;
7389 Type [] args = new Type [arg_count];
7393 ret_type = TypeManager.GetReferenceType (type);
7395 for (int i = 0; i < arg_count; i++){
7396 //args [i++] = a.Type;
7397 args [i] = TypeManager.int32_type;
7400 address = mb.GetArrayMethod (
7401 ea.Expr.Type, "Address",
7402 CallingConventions.HasThis |
7403 CallingConventions.Standard,
7410 // Load the array arguments into the stack.
7412 // If we have been requested to cache the values (cached_locations array
7413 // initialized), then load the arguments the first time and store them
7414 // in locals. otherwise load from local variables.
7416 void LoadArrayAndArguments (EmitContext ec)
7418 ILGenerator ig = ec.ig;
7421 foreach (Argument a in ea.Arguments){
7422 Type argtype = a.Expr.Type;
7426 if (argtype == TypeManager.int64_type)
7427 ig.Emit (OpCodes.Conv_Ovf_I);
7428 else if (argtype == TypeManager.uint64_type)
7429 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7433 public void Emit (EmitContext ec, bool leave_copy)
7435 int rank = ea.Expr.Type.GetArrayRank ();
7436 ILGenerator ig = ec.ig;
7439 LoadArrayAndArguments (ec);
7442 EmitLoadOpcode (ig, type);
7446 method = FetchGetMethod ();
7447 ig.Emit (OpCodes.Call, method);
7450 LoadFromPtr (ec.ig, this.type);
7453 ec.ig.Emit (OpCodes.Dup);
7454 temp = new LocalTemporary (this.type);
7459 public override void Emit (EmitContext ec)
7464 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7466 int rank = ea.Expr.Type.GetArrayRank ();
7467 ILGenerator ig = ec.ig;
7468 Type t = source.Type;
7469 prepared = prepare_for_load;
7471 if (prepare_for_load) {
7472 AddressOf (ec, AddressOp.LoadStore);
7473 ec.ig.Emit (OpCodes.Dup);
7476 ec.ig.Emit (OpCodes.Dup);
7477 temp = new LocalTemporary (this.type);
7480 StoreFromPtr (ec.ig, t);
7490 LoadArrayAndArguments (ec);
7493 bool is_stobj, has_type_arg;
7494 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7496 // The stobj opcode used by value types will need
7497 // an address on the stack, not really an array/array
7501 ig.Emit (OpCodes.Ldelema, t);
7505 ec.ig.Emit (OpCodes.Dup);
7506 temp = new LocalTemporary (this.type);
7511 ig.Emit (OpCodes.Stobj, t);
7512 else if (has_type_arg)
7517 ModuleBuilder mb = CodeGen.Module.Builder;
7518 int arg_count = ea.Arguments.Count;
7519 Type [] args = new Type [arg_count + 1];
7524 ec.ig.Emit (OpCodes.Dup);
7525 temp = new LocalTemporary (this.type);
7529 for (int i = 0; i < arg_count; i++){
7530 //args [i++] = a.Type;
7531 args [i] = TypeManager.int32_type;
7534 args [arg_count] = type;
7536 set = mb.GetArrayMethod (
7537 ea.Expr.Type, "Set",
7538 CallingConventions.HasThis |
7539 CallingConventions.Standard,
7540 TypeManager.void_type, args);
7542 ig.Emit (OpCodes.Call, set);
7551 public void AddressOf (EmitContext ec, AddressOp mode)
7553 int rank = ea.Expr.Type.GetArrayRank ();
7554 ILGenerator ig = ec.ig;
7556 LoadArrayAndArguments (ec);
7559 ig.Emit (OpCodes.Ldelema, type);
7561 MethodInfo address = FetchAddressMethod ();
7562 ig.Emit (OpCodes.Call, address);
7566 public void EmitGetLength (EmitContext ec, int dim)
7568 int rank = ea.Expr.Type.GetArrayRank ();
7569 ILGenerator ig = ec.ig;
7573 ig.Emit (OpCodes.Ldlen);
7574 ig.Emit (OpCodes.Conv_I4);
7576 IntLiteral.EmitInt (ig, dim);
7577 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7583 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7584 public readonly ArrayList Properties;
7585 static Indexers empty;
7587 public struct Indexer {
7588 public readonly PropertyInfo PropertyInfo;
7589 public readonly MethodInfo Getter, Setter;
7591 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7593 this.PropertyInfo = property_info;
7601 empty = new Indexers (null);
7604 Indexers (ArrayList array)
7609 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
7614 foreach (PropertyInfo property in mi){
7615 MethodInfo get, set;
7617 get = property.GetGetMethod (true);
7618 set = property.GetSetMethod (true);
7619 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
7621 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
7623 if (get != null || set != null) {
7625 ix = new Indexers (new ArrayList ());
7626 ix.Properties.Add (new Indexer (property, get, set));
7631 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
7633 string p_name = TypeManager.IndexerPropertyName (lookup_type);
7635 return TypeManager.MemberLookup (
7636 caller_type, caller_type, lookup_type, MemberTypes.Property,
7637 BindingFlags.Public | BindingFlags.Instance |
7638 BindingFlags.DeclaredOnly, p_name, null);
7641 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
7643 Indexers ix = empty;
7646 if (lookup_type.IsGenericParameter) {
7647 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
7651 if (gc.HasClassConstraint)
7652 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
7654 Type[] ifaces = gc.InterfaceConstraints;
7655 foreach (Type itype in ifaces)
7656 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
7662 Type copy = lookup_type;
7663 while (copy != TypeManager.object_type && copy != null){
7664 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
7665 copy = copy.BaseType;
7668 if (lookup_type.IsInterface) {
7669 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
7670 if (ifaces != null) {
7671 foreach (Type itype in ifaces)
7672 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
7681 /// Expressions that represent an indexer call.
7683 public class IndexerAccess : Expression, IAssignMethod {
7685 // Points to our "data" repository
7687 MethodInfo get, set;
7688 ArrayList set_arguments;
7689 bool is_base_indexer;
7691 protected Type indexer_type;
7692 protected Type current_type;
7693 protected Expression instance_expr;
7694 protected ArrayList arguments;
7696 public IndexerAccess (ElementAccess ea, Location loc)
7697 : this (ea.Expr, false, loc)
7699 this.arguments = ea.Arguments;
7702 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
7705 this.instance_expr = instance_expr;
7706 this.is_base_indexer = is_base_indexer;
7707 this.eclass = ExprClass.Value;
7711 protected virtual bool CommonResolve (EmitContext ec)
7713 indexer_type = instance_expr.Type;
7714 current_type = ec.ContainerType;
7719 public override Expression DoResolve (EmitContext ec)
7721 if (!CommonResolve (ec))
7725 // Step 1: Query for all `Item' *properties*. Notice
7726 // that the actual methods are pointed from here.
7728 // This is a group of properties, piles of them.
7730 ArrayList AllGetters = null;
7732 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
7733 if (ilist.Properties != null) {
7734 AllGetters = new ArrayList(ilist.Properties.Count);
7735 foreach (Indexers.Indexer ix in ilist.Properties) {
7736 if (ix.Getter != null)
7737 AllGetters.Add (ix.Getter);
7741 if (AllGetters == null) {
7742 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7743 TypeManager.CSharpName (indexer_type));
7747 if (AllGetters.Count == 0) {
7748 // FIXME: we cannot simply select first one as the error message is missleading when
7749 // multiple indexers exist
7750 Indexers.Indexer first_indexer = (Indexers.Indexer)ilist.Properties[ilist.Properties.Count - 1];
7751 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
7752 TypeManager.GetFullNameSignature (first_indexer.PropertyInfo));
7756 get = (MethodInfo)new MethodGroupExpr (AllGetters, loc).OverloadResolve (ec,
7757 arguments, false, loc);
7760 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7765 // Only base will allow this invocation to happen.
7767 if (get.IsAbstract && this is BaseIndexerAccess){
7768 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
7772 type = get.ReturnType;
7773 if (type.IsPointer && !ec.InUnsafe){
7778 instance_expr.CheckMarshalByRefAccess ();
7780 eclass = ExprClass.IndexerAccess;
7784 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7786 if (right_side == EmptyExpression.OutAccess) {
7787 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
7788 GetSignatureForError ());
7792 // if the indexer returns a value type, and we try to set a field in it
7793 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
7794 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
7795 GetSignatureForError ());
7799 ArrayList AllSetters = new ArrayList();
7800 if (!CommonResolve (ec))
7803 bool found_any = false, found_any_setters = false;
7805 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
7806 if (ilist.Properties != null) {
7808 foreach (Indexers.Indexer ix in ilist.Properties) {
7809 if (ix.Setter != null)
7810 AllSetters.Add (ix.Setter);
7813 if (AllSetters.Count > 0) {
7814 found_any_setters = true;
7815 set_arguments = (ArrayList) arguments.Clone ();
7816 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
7817 set = (MethodInfo)(new MethodGroupExpr (AllSetters, loc)).OverloadResolve (
7819 set_arguments, false, loc);
7823 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7824 TypeManager.CSharpName (indexer_type));
7828 if (!found_any_setters) {
7829 Error (154, "indexer can not be used in this context, because " +
7830 "it lacks a `set' accessor");
7835 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7840 // Only base will allow this invocation to happen.
7842 if (set.IsAbstract && this is BaseIndexerAccess){
7843 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
7848 // Now look for the actual match in the list of indexers to set our "return" type
7850 type = TypeManager.void_type; // default value
7851 foreach (Indexers.Indexer ix in ilist.Properties){
7852 if (ix.Setter == set){
7853 type = ix.PropertyInfo.PropertyType;
7858 instance_expr.CheckMarshalByRefAccess ();
7860 eclass = ExprClass.IndexerAccess;
7864 bool prepared = false;
7865 LocalTemporary temp;
7867 public void Emit (EmitContext ec, bool leave_copy)
7869 Invocation.EmitCall (ec, is_base_indexer, instance_expr, get, arguments, loc, prepared, false);
7871 ec.ig.Emit (OpCodes.Dup);
7872 temp = new LocalTemporary (Type);
7878 // source is ignored, because we already have a copy of it from the
7879 // LValue resolution and we have already constructed a pre-cached
7880 // version of the arguments (ea.set_arguments);
7882 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7884 prepared = prepare_for_load;
7885 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
7890 ec.ig.Emit (OpCodes.Dup);
7891 temp = new LocalTemporary (Type);
7894 } else if (leave_copy) {
7895 temp = new LocalTemporary (Type);
7901 Invocation.EmitCall (ec, is_base_indexer, instance_expr, set, set_arguments, loc, false, prepared);
7910 public override void Emit (EmitContext ec)
7915 public override string GetSignatureForError ()
7917 // FIXME: print the argument list of the indexer
7918 return instance_expr.GetSignatureForError () + ".this[...]";
7921 protected override void CloneTo (CloneContext clonectx, Expression t)
7923 IndexerAccess target = (IndexerAccess) t;
7925 if (arguments != null){
7926 target.arguments = new ArrayList ();
7927 foreach (Argument a in arguments)
7928 target.arguments.Add (a.Clone (clonectx));
7930 if (instance_expr != null)
7931 target.instance_expr = instance_expr.Clone (clonectx);
7936 /// The base operator for method names
7938 public class BaseAccess : Expression {
7939 public readonly string Identifier;
7942 public BaseAccess (string member, Location l)
7944 this.Identifier = member;
7948 public BaseAccess (string member, TypeArguments args, Location l)
7954 public override Expression DoResolve (EmitContext ec)
7956 Expression c = CommonResolve (ec);
7962 // MethodGroups use this opportunity to flag an error on lacking ()
7964 if (!(c is MethodGroupExpr))
7965 return c.Resolve (ec);
7969 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7971 Expression c = CommonResolve (ec);
7977 // MethodGroups use this opportunity to flag an error on lacking ()
7979 if (! (c is MethodGroupExpr))
7980 return c.DoResolveLValue (ec, right_side);
7985 Expression CommonResolve (EmitContext ec)
7987 Expression member_lookup;
7988 Type current_type = ec.ContainerType;
7989 Type base_type = current_type.BaseType;
7992 Error (1511, "Keyword `base' is not available in a static method");
7996 if (ec.IsFieldInitializer){
7997 Error (1512, "Keyword `base' is not available in the current context");
8001 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8002 AllMemberTypes, AllBindingFlags, loc);
8003 if (member_lookup == null) {
8004 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8011 left = new TypeExpression (base_type, loc);
8013 left = ec.GetThis (loc);
8015 MemberExpr me = (MemberExpr) member_lookup;
8017 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8019 if (e is PropertyExpr) {
8020 PropertyExpr pe = (PropertyExpr) e;
8025 MethodGroupExpr mg = e as MethodGroupExpr;
8031 return mg.ResolveGeneric (ec, args);
8033 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8041 public override void Emit (EmitContext ec)
8043 throw new Exception ("Should never be called");
8046 protected override void CloneTo (CloneContext clonectx, Expression t)
8048 BaseAccess target = (BaseAccess) t;
8050 target.args = args.Clone ();
8055 /// The base indexer operator
8057 public class BaseIndexerAccess : IndexerAccess {
8058 public BaseIndexerAccess (ArrayList args, Location loc)
8059 : base (null, true, loc)
8061 arguments = new ArrayList ();
8062 foreach (Expression tmp in args)
8063 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8066 protected override bool CommonResolve (EmitContext ec)
8068 instance_expr = ec.GetThis (loc);
8070 current_type = ec.ContainerType.BaseType;
8071 indexer_type = current_type;
8073 foreach (Argument a in arguments){
8074 if (!a.Resolve (ec, loc))
8083 /// This class exists solely to pass the Type around and to be a dummy
8084 /// that can be passed to the conversion functions (this is used by
8085 /// foreach implementation to typecast the object return value from
8086 /// get_Current into the proper type. All code has been generated and
8087 /// we only care about the side effect conversions to be performed
8089 /// This is also now used as a placeholder where a no-action expression
8090 /// is needed (the `New' class).
8092 public class EmptyExpression : Expression {
8093 public static readonly EmptyExpression Null = new EmptyExpression ();
8095 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8096 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8097 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8099 static EmptyExpression temp = new EmptyExpression ();
8100 public static EmptyExpression Grab ()
8102 EmptyExpression retval = temp == null ? new EmptyExpression () : temp;
8107 public static void Release (EmptyExpression e)
8112 // TODO: should be protected
8113 public EmptyExpression ()
8115 type = TypeManager.object_type;
8116 eclass = ExprClass.Value;
8117 loc = Location.Null;
8120 public EmptyExpression (Type t)
8123 eclass = ExprClass.Value;
8124 loc = Location.Null;
8127 public override Expression DoResolve (EmitContext ec)
8132 public override void Emit (EmitContext ec)
8134 // nothing, as we only exist to not do anything.
8138 // This is just because we might want to reuse this bad boy
8139 // instead of creating gazillions of EmptyExpressions.
8140 // (CanImplicitConversion uses it)
8142 public void SetType (Type t)
8148 public class UserCast : Expression {
8152 public UserCast (MethodInfo method, Expression source, Location l)
8154 this.method = method;
8155 this.source = source;
8156 type = method.ReturnType;
8157 eclass = ExprClass.Value;
8161 public Expression Source {
8167 public override Expression DoResolve (EmitContext ec)
8170 // We are born fully resolved
8175 public override void Emit (EmitContext ec)
8177 ILGenerator ig = ec.ig;
8181 if (method is MethodInfo)
8182 ig.Emit (OpCodes.Call, (MethodInfo) method);
8184 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8190 // This class is used to "construct" the type during a typecast
8191 // operation. Since the Type.GetType class in .NET can parse
8192 // the type specification, we just use this to construct the type
8193 // one bit at a time.
8195 public class ComposedCast : TypeExpr {
8199 public ComposedCast (Expression left, string dim)
8200 : this (left, dim, left.Location)
8204 public ComposedCast (Expression left, string dim, Location l)
8212 public Expression RemoveNullable ()
8214 if (dim.EndsWith ("?")) {
8215 dim = dim.Substring (0, dim.Length - 1);
8224 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8226 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8230 Type ltype = lexpr.Type;
8231 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8232 Error_VoidInvalidInTheContext (loc);
8237 if ((dim.Length > 0) && (dim [0] == '?')) {
8238 TypeExpr nullable = new NullableType (left, loc);
8240 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8241 return nullable.ResolveAsTypeTerminal (ec, false);
8245 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc))
8248 if (dim != "" && dim [0] == '[' &&
8249 (ltype == TypeManager.arg_iterator_type || ltype == TypeManager.typed_reference_type)) {
8250 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (ltype));
8255 type = TypeManager.GetConstructedType (ltype, dim);
8260 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8262 if (type.IsPointer && !ec.IsInUnsafeScope){
8267 eclass = ExprClass.Type;
8271 public override string Name {
8272 get { return left + dim; }
8275 public override string FullName {
8276 get { return type.FullName; }
8279 public override string GetSignatureForError ()
8281 return left.GetSignatureForError () + dim;
8284 protected override void CloneTo (CloneContext clonectx, Expression t)
8286 ComposedCast target = (ComposedCast) t;
8288 target.left = left.Clone (clonectx);
8292 public class FixedBufferPtr : Expression {
8295 public FixedBufferPtr (Expression array, Type array_type, Location l)
8300 type = TypeManager.GetPointerType (array_type);
8301 eclass = ExprClass.Value;
8304 public override void Emit(EmitContext ec)
8309 public override Expression DoResolve (EmitContext ec)
8312 // We are born fully resolved
8320 // This class is used to represent the address of an array, used
8321 // only by the Fixed statement, this generates "&a [0]" construct
8322 // for fixed (char *pa = a)
8324 public class ArrayPtr : FixedBufferPtr {
8327 public ArrayPtr (Expression array, Type array_type, Location l):
8328 base (array, array_type, l)
8330 this.array_type = array_type;
8333 public override void Emit (EmitContext ec)
8337 ILGenerator ig = ec.ig;
8338 IntLiteral.EmitInt (ig, 0);
8339 ig.Emit (OpCodes.Ldelema, array_type);
8344 // Used by the fixed statement
8346 public class StringPtr : Expression {
8349 public StringPtr (LocalBuilder b, Location l)
8352 eclass = ExprClass.Value;
8353 type = TypeManager.char_ptr_type;
8357 public override Expression DoResolve (EmitContext ec)
8359 // This should never be invoked, we are born in fully
8360 // initialized state.
8365 public override void Emit (EmitContext ec)
8367 ILGenerator ig = ec.ig;
8369 ig.Emit (OpCodes.Ldloc, b);
8370 ig.Emit (OpCodes.Conv_I);
8371 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8372 ig.Emit (OpCodes.Add);
8377 // Implements the `stackalloc' keyword
8379 public class StackAlloc : Expression {
8384 public StackAlloc (Expression type, Expression count, Location l)
8391 public override Expression DoResolve (EmitContext ec)
8393 count = count.Resolve (ec);
8397 if (count.Type != TypeManager.int32_type){
8398 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8403 Constant c = count as Constant;
8404 if (c != null && c.IsNegative) {
8405 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8409 if (ec.InCatch || ec.InFinally) {
8410 Error (255, "Cannot use stackalloc in finally or catch");
8414 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8420 if (!TypeManager.VerifyUnManaged (otype, loc))
8423 type = TypeManager.GetPointerType (otype);
8424 eclass = ExprClass.Value;
8429 public override void Emit (EmitContext ec)
8431 int size = GetTypeSize (otype);
8432 ILGenerator ig = ec.ig;
8435 ig.Emit (OpCodes.Sizeof, otype);
8437 IntConstant.EmitInt (ig, size);
8439 ig.Emit (OpCodes.Mul);
8440 ig.Emit (OpCodes.Localloc);
8443 protected override void CloneTo (CloneContext clonectx, Expression t)
8445 StackAlloc target = (StackAlloc) t;
8446 target.count = count.Clone (clonectx);
8447 target.t = t.Clone (clonectx);
8451 public interface IInitializable
8453 bool Initialize (EmitContext ec, Expression target);
8456 public class Initializer
8458 public readonly string Name;
8459 public readonly object Value;
8461 public Initializer (string name, Expression value)
8467 public Initializer (string name, IInitializable value)
8474 public class ObjectInitializer : IInitializable
8476 readonly ArrayList initializers;
8477 public ObjectInitializer (ArrayList initializers)
8479 this.initializers = initializers;
8482 public bool Initialize (EmitContext ec, Expression target)
8484 ArrayList initialized = new ArrayList (initializers.Count);
8485 for (int i = initializers.Count - 1; i >= 0; i--) {
8486 Initializer initializer = initializers[i] as Initializer;
8487 if (initialized.Contains (initializer.Name)) {
8488 //FIXME proper error
8489 Console.WriteLine ("Object member can only be initialized once");
8493 MemberAccess ma = new MemberAccess (target, initializer.Name);
8494 Expression expr = initializer.Value as Expression;
8495 // If it's an expresison, append the assign.
8497 Assign a = new Assign (ma, expr);
8498 ec.CurrentBlock.InsertStatementAfterCurrent (new StatementExpression (a));
8500 // If it's another initializer (object or collection), initialize it.
8501 else if (!((IInitializable)initializer.Value).Initialize (ec, ma))
8504 initialized.Add (initializer.Name);
8510 public class CollectionInitializer : IInitializable
8512 readonly ArrayList items;
8513 public CollectionInitializer (ArrayList items)
8518 bool CheckCollection (EmitContext ec, Expression e)
8520 if (e == null || e.Type == null)
8522 bool is_ienumerable = false;
8523 foreach (Type t in TypeManager.GetInterfaces (e.Type))
8524 if (t == typeof (IEnumerable)) {
8525 is_ienumerable = true;
8529 if (!is_ienumerable)
8532 MethodGroupExpr mg = Expression.MemberLookup (
8533 ec.ContainerType, e.Type, "Add", MemberTypes.Method,
8534 Expression.AllBindingFlags, Location.Null) as MethodGroupExpr;
8539 foreach (MethodInfo mi in mg.Methods) {
8540 if (TypeManager.GetParameterData (mi).Count != 1)
8542 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
8549 public bool Initialize (EmitContext ec, Expression target)
8551 if (!CheckCollection (ec, target.Resolve (ec))) {
8552 // FIXME throw proper error
8553 Console.WriteLine ("Error: This is not a collection");
8557 for (int i = items.Count - 1; i >= 0; i--) {
8558 MemberAccess ma = new MemberAccess (target, "Add");
8559 ArrayList array = new ArrayList ();
8560 array.Add (new Argument ((Expression)items[i]));
8561 Invocation invoke = new Invocation (ma, array);
8562 ec.CurrentBlock.InsertStatementAfterCurrent (new StatementExpression (invoke));
8568 public class AnonymousTypeInitializer : IInitializable
8570 readonly ArrayList initializers;
8571 public AnonymousTypeInitializer (ArrayList initializers)
8573 this.initializers = initializers;
8576 public bool Initialize (EmitContext ec, Expression target)
8578 foreach (AnonymousTypeParameter p in initializers) {
8579 MemberAccess ma = new MemberAccess (target, p.Name);
8580 Assign a = p.Expression as Assign;
8581 Assign assign = new Assign (ma, (a != null) ? a.Source : p.Expression);
8582 ec.CurrentBlock.InsertStatementAfterCurrent (new StatementExpression (assign));
8588 public class NewInitialize : New, IInitializable
8590 IInitializable initializer;
8592 public bool Initialize (EmitContext ec, Expression target)
8594 return initializer.Initialize (ec, target);
8597 public NewInitialize (Expression requested_type, ArrayList arguments, IInitializable initializer, Location l)
8598 : base (requested_type, arguments, l)
8600 this.initializer = initializer;
8604 public class AnonymousType : Expression
8606 ArrayList parameters;
8607 TypeContainer parent;
8608 TypeContainer anonymous_type;
8610 public AnonymousType (ArrayList parameters, TypeContainer parent, Location loc)
8612 this.parameters = parameters;
8613 this.parent = parent;
8617 public override Expression DoResolve (EmitContext ec)
8619 foreach (AnonymousTypeParameter p in parameters)
8622 anonymous_type = GetAnonymousType (ec);
8624 TypeExpression te = new TypeExpression (anonymous_type.TypeBuilder, loc);
8625 AnonymousTypeInitializer ati = new AnonymousTypeInitializer (parameters);
8626 return new NewInitialize (te, null, ati, loc).Resolve (ec);
8629 TypeContainer GetAnonymousType (EmitContext ec)
8631 // See if we already have an anonymous type with the right fields.
8632 // If not, create one.
8634 // Look through all availible pre-existing anonymous types:
8635 foreach (DictionaryEntry d in parent.AnonymousTypes) {
8636 ArrayList p = d.Key as ArrayList;
8637 if (p.Count != parameters.Count)
8640 // And for each of the fields we need...
8641 foreach (AnonymousTypeParameter atp in parameters) {
8642 // ... check each of the pre-existing A-type's fields.
8644 foreach (AnonymousTypeParameter a in p)
8645 if (atp.Equals(a)) {
8649 // If the pre-existing A-type doesn't have one of our fields, try the next one
8655 // If it's a match, return it.
8657 return d.Value as TypeContainer;
8659 // Otherwise, create a new type.
8660 return CreateAnonymousType (ec);
8663 TypeContainer CreateAnonymousType (EmitContext ec)
8665 TypeContainer type = new AnonymousClass (parent, loc);
8666 foreach (AnonymousTypeParameter p in parameters) {
8667 TypeExpression te = new TypeExpression (p.Type, loc);
8668 Field field = new Field (type, te, Modifiers.PUBLIC, p.Name, null, loc);
8669 type.AddField (field);
8672 type.DefineMembers ();
8673 parent.AnonymousTypes.Add (parameters, type);
8677 public override void Emit (EmitContext ec)
8679 TypeExpression te = new TypeExpression (anonymous_type.TypeBuilder, loc);
8680 new New (te, null, loc).Emit(ec);
8684 public class AnonymousTypeParameter : Expression
8688 Expression expression;
8690 public LocatedToken Token {
8691 get { return token; }
8694 public string Name {
8695 get { return name; }
8698 public Expression Expression {
8699 get { return expression; }
8702 public override bool Equals (object o)
8704 AnonymousTypeParameter other = o as AnonymousTypeParameter;
8705 return other != null && Name == other.Name && Type == other.Type;
8708 public override int GetHashCode ()
8710 return name.GetHashCode ();
8713 public override Expression DoResolve (EmitContext ec)
8715 Expression e = expression.Resolve(ec);
8720 public override void Emit (EmitContext ec)
8722 expression.Emit(ec);
8725 public AnonymousTypeParameter (Expression expression, string name)
8728 this.expression = expression;
8729 type = expression.Type;