2 // expression.cs: Expression representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@seznam.cz)
8 // Copyright 2001, 2002, 2003 Ximian, Inc.
9 // Copyright 2003-2008 Novell, Inc.
13 namespace Mono.CSharp {
15 using System.Collections;
16 using System.Reflection;
17 using System.Reflection.Emit;
21 // This is an user operator expression, automatically created during
24 public class UserOperatorCall : Expression {
25 public delegate Expression ExpressionTreeExpression (EmitContext ec, MethodGroupExpr mg);
27 protected readonly ArrayList arguments;
28 protected readonly MethodGroupExpr mg;
29 readonly ExpressionTreeExpression expr_tree;
31 public UserOperatorCall (MethodGroupExpr mg, ArrayList args, ExpressionTreeExpression expr_tree, Location loc)
34 this.arguments = args;
35 this.expr_tree = expr_tree;
37 type = TypeManager.TypeToCoreType (((MethodInfo) mg).ReturnType);
38 eclass = ExprClass.Value;
42 public override Expression CreateExpressionTree (EmitContext ec)
44 if (expr_tree != null)
45 return expr_tree (ec, mg);
47 ArrayList args = new ArrayList (arguments.Count + 1);
48 args.Add (new Argument (new NullLiteral (loc)));
49 args.Add (new Argument (mg.CreateExpressionTree (ec)));
50 foreach (Argument a in arguments) {
51 args.Add (new Argument (a.Expr.CreateExpressionTree (ec)));
54 return CreateExpressionFactoryCall ("Call", args);
57 public override Expression DoResolve (EmitContext ec)
60 // We are born fully resolved
65 public override void Emit (EmitContext ec)
67 mg.EmitCall (ec, arguments);
70 [Obsolete ("It may not be compatible with expression trees")]
71 static public UserOperatorCall MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
72 Expression e, Location loc)
76 args = new ArrayList (1);
77 Argument a = new Argument (e, Argument.AType.Expression);
79 // We need to resolve the arguments before sending them in !
80 if (!a.Resolve (ec, loc))
84 mg = mg.OverloadResolve (ec, ref args, false, loc);
89 return new UserOperatorCall (mg, args, null, loc);
92 public MethodGroupExpr Method {
97 public class ParenthesizedExpression : Expression
99 public Expression Expr;
101 public ParenthesizedExpression (Expression expr)
106 public override Expression DoResolve (EmitContext ec)
108 Expr = Expr.Resolve (ec);
112 public override void Emit (EmitContext ec)
114 throw new Exception ("Should not happen");
117 public override Location Location
120 return Expr.Location;
124 protected override void CloneTo (CloneContext clonectx, Expression t)
126 ParenthesizedExpression target = (ParenthesizedExpression) t;
128 target.Expr = Expr.Clone (clonectx);
133 // Unary implements unary expressions.
135 public class Unary : Expression {
136 public enum Operator : byte {
137 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
141 public static readonly string [] oper_names;
142 static Type [] [] predefined_operators;
144 public readonly Operator Oper;
145 public Expression Expr;
147 public Unary (Operator op, Expression expr, Location loc)
156 oper_names = new string [(int)Operator.TOP];
158 oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus";
159 oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation";
160 oper_names [(int) Operator.LogicalNot] = "op_LogicalNot";
161 oper_names [(int) Operator.OnesComplement] = "op_OnesComplement";
162 oper_names [(int) Operator.AddressOf] = "op_AddressOf";
166 // This routine will attempt to simplify the unary expression when the
167 // argument is a constant.
169 Constant TryReduceConstant (EmitContext ec, Constant e)
171 if (e is SideEffectConstant) {
172 Constant r = TryReduceConstant (ec, ((SideEffectConstant) e).value);
173 return r == null ? null : new SideEffectConstant (r, e, r.Location);
176 Type expr_type = e.Type;
179 case Operator.UnaryPlus:
180 // Unary numeric promotions
181 if (expr_type == TypeManager.byte_type)
182 return new IntConstant (((ByteConstant)e).Value, e.Location);
183 if (expr_type == TypeManager.sbyte_type)
184 return new IntConstant (((SByteConstant)e).Value, e.Location);
185 if (expr_type == TypeManager.short_type)
186 return new IntConstant (((ShortConstant)e).Value, e.Location);
187 if (expr_type == TypeManager.ushort_type)
188 return new IntConstant (((UShortConstant)e).Value, e.Location);
189 if (expr_type == TypeManager.char_type)
190 return new IntConstant (((CharConstant)e).Value, e.Location);
192 // Predefined operators
193 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
194 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
195 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
196 expr_type == TypeManager.decimal_type)
203 case Operator.UnaryNegation:
204 // Unary numeric promotions
205 if (expr_type == TypeManager.byte_type)
206 return new IntConstant (-((ByteConstant)e).Value, e.Location);
207 if (expr_type == TypeManager.sbyte_type)
208 return new IntConstant (-((SByteConstant)e).Value, e.Location);
209 if (expr_type == TypeManager.short_type)
210 return new IntConstant (-((ShortConstant)e).Value, e.Location);
211 if (expr_type == TypeManager.ushort_type)
212 return new IntConstant (-((UShortConstant)e).Value, e.Location);
213 if (expr_type == TypeManager.char_type)
214 return new IntConstant (-((CharConstant)e).Value, e.Location);
216 // Predefined operators
217 if (expr_type == TypeManager.int32_type) {
218 int value = ((IntConstant)e).Value;
219 if (value == int.MinValue) {
220 if (ec.ConstantCheckState) {
221 ConstantFold.Error_CompileTimeOverflow (loc);
226 return new IntConstant (-value, e.Location);
228 if (expr_type == TypeManager.int64_type) {
229 long value = ((LongConstant)e).Value;
230 if (value == long.MinValue) {
231 if (ec.ConstantCheckState) {
232 ConstantFold.Error_CompileTimeOverflow (loc);
237 return new LongConstant (-value, e.Location);
240 if (expr_type == TypeManager.uint32_type) {
241 UIntLiteral uil = e as UIntLiteral;
243 if (uil.Value == 2147483648)
244 return new IntLiteral (int.MinValue, e.Location);
245 return new LongLiteral (-uil.Value, e.Location);
247 return new LongConstant (-((UIntConstant)e).Value, e.Location);
250 if (expr_type == TypeManager.uint64_type) {
251 ULongLiteral ull = e as ULongLiteral;
252 if (ull != null && ull.Value == 9223372036854775808)
253 return new LongLiteral (long.MinValue, e.Location);
257 if (expr_type == TypeManager.float_type) {
258 FloatLiteral fl = e as FloatLiteral;
259 // For better error reporting
261 fl.Value = -fl.Value;
264 return new FloatConstant (-((FloatConstant)e).Value, e.Location);
266 if (expr_type == TypeManager.double_type) {
267 DoubleLiteral dl = e as DoubleLiteral;
268 // For better error reporting
270 dl.Value = -dl.Value;
274 return new DoubleConstant (-((DoubleConstant)e).Value, e.Location);
276 if (expr_type == TypeManager.decimal_type)
277 return new DecimalConstant (-((DecimalConstant)e).Value, e.Location);
281 case Operator.LogicalNot:
282 if (expr_type != TypeManager.bool_type)
285 bool b = (bool)e.GetValue ();
286 return new BoolConstant (!b, e.Location);
288 case Operator.OnesComplement:
289 // Unary numeric promotions
290 if (expr_type == TypeManager.byte_type)
291 return new IntConstant (~((ByteConstant)e).Value, e.Location);
292 if (expr_type == TypeManager.sbyte_type)
293 return new IntConstant (~((SByteConstant)e).Value, e.Location);
294 if (expr_type == TypeManager.short_type)
295 return new IntConstant (~((ShortConstant)e).Value, e.Location);
296 if (expr_type == TypeManager.ushort_type)
297 return new IntConstant (~((UShortConstant)e).Value, e.Location);
298 if (expr_type == TypeManager.char_type)
299 return new IntConstant (~((CharConstant)e).Value, e.Location);
301 // Predefined operators
302 if (expr_type == TypeManager.int32_type)
303 return new IntConstant (~((IntConstant)e).Value, e.Location);
304 if (expr_type == TypeManager.uint32_type)
305 return new UIntConstant (~((UIntConstant)e).Value, e.Location);
306 if (expr_type == TypeManager.int64_type)
307 return new LongConstant (~((LongConstant)e).Value, e.Location);
308 if (expr_type == TypeManager.uint64_type){
309 return new ULongConstant (~((ULongConstant)e).Value, e.Location);
311 if (e is EnumConstant) {
312 e = TryReduceConstant (ec, ((EnumConstant)e).Child);
314 e = new EnumConstant (e, expr_type);
319 throw new Exception ("Can not constant fold: " + Oper.ToString());
322 protected Expression ResolveOperator (EmitContext ec, Expression expr)
324 if (predefined_operators == null)
325 CreatePredefinedOperatorsTable ();
327 Type expr_type = expr.Type;
328 Expression best_expr;
331 // Primitive types first
333 if (TypeManager.IsPrimitiveType (expr_type)) {
334 best_expr = ResolvePrimitivePredefinedType (expr);
335 if (best_expr == null)
338 type = best_expr.Type;
344 // E operator ~(E x);
346 if (Oper == Operator.OnesComplement && TypeManager.IsEnumType (expr_type)) {
347 best_expr = ResolvePrimitivePredefinedType (EmptyCast.Create (expr, TypeManager.GetEnumUnderlyingType (expr_type)));
348 if (best_expr == null)
351 Expr = EmptyCast.Create (best_expr, expr_type);
356 return ResolveUserType (ec, expr);
359 public override Expression CreateExpressionTree (EmitContext ec)
361 return CreateExpressionTree (ec, null);
364 Expression CreateExpressionTree (EmitContext ec, MethodGroupExpr user_op)
368 case Operator.UnaryNegation:
369 if (ec.CheckState && user_op == null && !IsFloat (type))
370 method_name = "NegateChecked";
372 method_name = "Negate";
374 case Operator.OnesComplement:
375 case Operator.LogicalNot:
378 case Operator.UnaryPlus:
379 method_name = "UnaryPlus";
382 throw new InternalErrorException ("Unknown unary operator " + Oper.ToString ());
385 ArrayList args = new ArrayList (2);
386 args.Add (new Argument (Expr.CreateExpressionTree (ec)));
388 args.Add (new Argument (user_op.CreateExpressionTree (ec)));
389 return CreateExpressionFactoryCall (method_name, args);
392 static void CreatePredefinedOperatorsTable ()
394 predefined_operators = new Type [(int) Operator.TOP] [];
397 // 7.6.1 Unary plus operator
399 predefined_operators [(int) Operator.UnaryPlus] = new Type [] {
400 TypeManager.int32_type, TypeManager.uint32_type,
401 TypeManager.int64_type, TypeManager.uint64_type,
402 TypeManager.float_type, TypeManager.double_type,
403 TypeManager.decimal_type
407 // 7.6.2 Unary minus operator
409 predefined_operators [(int) Operator.UnaryNegation] = new Type [] {
410 TypeManager.int32_type,
411 TypeManager.int64_type,
412 TypeManager.float_type, TypeManager.double_type,
413 TypeManager.decimal_type
417 // 7.6.3 Logical negation operator
419 predefined_operators [(int) Operator.LogicalNot] = new Type [] {
420 TypeManager.bool_type
424 // 7.6.4 Bitwise complement operator
426 predefined_operators [(int) Operator.OnesComplement] = new Type [] {
427 TypeManager.int32_type, TypeManager.uint32_type,
428 TypeManager.int64_type, TypeManager.uint64_type
433 // Unary numeric promotions
435 static Expression DoNumericPromotion (Operator op, Expression expr)
437 Type expr_type = expr.Type;
438 if ((op == Operator.UnaryPlus || op == Operator.UnaryNegation || op == Operator.OnesComplement) &&
439 expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
440 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
441 expr_type == TypeManager.char_type)
442 return Convert.ImplicitNumericConversion (expr, TypeManager.int32_type);
444 if (op == Operator.UnaryNegation && expr_type == TypeManager.uint32_type)
445 return Convert.ImplicitNumericConversion (expr, TypeManager.int64_type);
450 public override Expression DoResolve (EmitContext ec)
452 eclass = ExprClass.Value;
454 if (Oper == Operator.AddressOf) {
455 Expr = Expr.DoResolveLValue (ec, new EmptyExpression ());
457 if (Expr == null || Expr.eclass != ExprClass.Variable){
458 Error (211, "Cannot take the address of the given expression");
462 return ResolveAddressOf (ec);
465 Expr = Expr.Resolve (ec);
469 if (TypeManager.IsNullableValueType (Expr.Type))
470 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
473 // Attempt to use a constant folding operation.
475 Constant cexpr = Expr as Constant;
477 cexpr = TryReduceConstant (ec, cexpr);
482 Expression expr = ResolveOperator (ec, Expr);
484 Error_OperatorCannotBeApplied (loc, OperName (Oper), Expr.Type);
487 // Reduce unary operator on predefined types
489 if (expr == this && Oper == Operator.UnaryPlus)
495 public override Expression DoResolveLValue (EmitContext ec, Expression right)
500 public override void Emit (EmitContext ec)
502 EmitOperator (ec, type);
505 protected void EmitOperator (EmitContext ec, Type type)
507 ILGenerator ig = ec.ig;
510 case Operator.UnaryPlus:
514 case Operator.UnaryNegation:
515 if (ec.CheckState && !IsFloat (type)) {
516 ig.Emit (OpCodes.Ldc_I4_0);
517 if (type == TypeManager.int64_type)
518 ig.Emit (OpCodes.Conv_U8);
520 ig.Emit (OpCodes.Sub_Ovf);
523 ig.Emit (OpCodes.Neg);
528 case Operator.LogicalNot:
530 ig.Emit (OpCodes.Ldc_I4_0);
531 ig.Emit (OpCodes.Ceq);
534 case Operator.OnesComplement:
536 ig.Emit (OpCodes.Not);
539 case Operator.AddressOf:
540 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
544 throw new Exception ("This should not happen: Operator = "
549 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
551 if (Oper == Operator.LogicalNot)
552 Expr.EmitBranchable (ec, target, !on_true);
554 base.EmitBranchable (ec, target, on_true);
557 public override void EmitSideEffect (EmitContext ec)
559 Expr.EmitSideEffect (ec);
562 public static void Error_OperatorCannotBeApplied (Location loc, string oper, Type t)
564 Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'",
565 oper, TypeManager.CSharpName (t));
568 static bool IsFloat (Type t)
570 return t == TypeManager.float_type || t == TypeManager.double_type;
574 // Returns a stringified representation of the Operator
576 public static string OperName (Operator oper)
579 case Operator.UnaryPlus:
581 case Operator.UnaryNegation:
583 case Operator.LogicalNot:
585 case Operator.OnesComplement:
587 case Operator.AddressOf:
591 throw new NotImplementedException (oper.ToString ());
594 Expression ResolveAddressOf (EmitContext ec)
601 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)) {
605 IVariable variable = Expr as IVariable;
606 bool is_fixed = variable != null && variable.VerifyFixed ();
608 if (!ec.InFixedInitializer && !is_fixed) {
609 Error (212, "You can only take the address of unfixed expression inside " +
610 "of a fixed statement initializer");
614 if (ec.InFixedInitializer && is_fixed) {
615 Error (213, "You cannot use the fixed statement to take the address of an already fixed expression");
619 LocalVariableReference lr = Expr as LocalVariableReference;
621 if (lr.local_info.IsCaptured) {
622 AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc);
625 lr.local_info.AddressTaken = true;
626 lr.local_info.Used = true;
629 ParameterReference pr = Expr as ParameterReference;
630 if ((pr != null) && pr.Parameter.IsCaptured) {
631 AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc);
635 // According to the specs, a variable is considered definitely assigned if you take
637 if ((variable != null) && (variable.VariableInfo != null)) {
638 variable.VariableInfo.SetAssigned (ec);
641 type = TypeManager.GetPointerType (Expr.Type);
645 Expression ResolvePrimitivePredefinedType (Expression expr)
647 expr = DoNumericPromotion (Oper, expr);
648 Type expr_type = expr.Type;
649 Type[] predefined = predefined_operators [(int) Oper];
650 foreach (Type t in predefined) {
658 // Perform user-operator overload resolution
660 protected virtual Expression ResolveUserOperator (EmitContext ec, Expression expr)
662 string op_name = oper_names [(int) Oper];
663 MethodGroupExpr user_op = MemberLookup (ec.ContainerType, expr.Type, op_name, MemberTypes.Method, AllBindingFlags, expr.Location) as MethodGroupExpr;
667 ArrayList args = new ArrayList (1);
668 args.Add (new Argument (expr));
669 user_op = user_op.OverloadResolve (ec, ref args, false, expr.Location);
674 Expr = ((Argument) args [0]).Expr;
675 return new UserOperatorCall (user_op, args, CreateExpressionTree, expr.Location);
679 // Unary user type overload resolution
681 Expression ResolveUserType (EmitContext ec, Expression expr)
683 Expression best_expr = ResolveUserOperator (ec, expr);
684 if (best_expr != null)
687 Type[] predefined = predefined_operators [(int) Oper];
688 foreach (Type t in predefined) {
689 Expression oper_expr = Convert.UserDefinedConversion (ec, expr, t, expr.Location, false);
690 if (oper_expr == null)
694 // decimal type is predefined but has user-operators
696 if (oper_expr.Type == TypeManager.decimal_type)
697 oper_expr = ResolveUserType (ec, oper_expr);
699 oper_expr = ResolvePrimitivePredefinedType (oper_expr);
701 if (oper_expr == null)
704 if (best_expr == null) {
705 best_expr = oper_expr;
709 int result = MethodGroupExpr.BetterTypeConversion (ec, best_expr.Type, t);
711 Report.Error (35, loc, "Operator `{0}' is ambiguous on an operand of type `{1}'",
712 OperName (Oper), TypeManager.CSharpName (expr.Type));
717 best_expr = oper_expr;
720 if (best_expr == null)
724 // HACK: Decimal user-operator is included in standard operators
726 if (best_expr.Type == TypeManager.decimal_type)
730 type = best_expr.Type;
734 protected override void CloneTo (CloneContext clonectx, Expression t)
736 Unary target = (Unary) t;
738 target.Expr = Expr.Clone (clonectx);
743 // Unary operators are turned into Indirection expressions
744 // after semantic analysis (this is so we can take the address
745 // of an indirection).
747 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
749 LocalTemporary temporary;
752 public Indirection (Expression expr, Location l)
758 public override void Emit (EmitContext ec)
763 LoadFromPtr (ec.ig, Type);
766 public void Emit (EmitContext ec, bool leave_copy)
770 ec.ig.Emit (OpCodes.Dup);
771 temporary = new LocalTemporary (expr.Type);
772 temporary.Store (ec);
776 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
778 prepared = prepare_for_load;
782 if (prepare_for_load)
783 ec.ig.Emit (OpCodes.Dup);
787 ec.ig.Emit (OpCodes.Dup);
788 temporary = new LocalTemporary (expr.Type);
789 temporary.Store (ec);
792 StoreFromPtr (ec.ig, type);
794 if (temporary != null) {
796 temporary.Release (ec);
800 public void AddressOf (EmitContext ec, AddressOp Mode)
805 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
807 return DoResolve (ec);
810 public override Expression DoResolve (EmitContext ec)
812 expr = expr.Resolve (ec);
819 if (!expr.Type.IsPointer) {
820 Error (193, "The * or -> operator must be applied to a pointer");
824 type = TypeManager.GetElementType (expr.Type);
825 eclass = ExprClass.Variable;
829 public override string ToString ()
831 return "*(" + expr + ")";
834 #region IVariable Members
836 public VariableInfo VariableInfo {
840 public bool VerifyFixed ()
842 // A pointer-indirection is always fixed.
850 /// Unary Mutator expressions (pre and post ++ and --)
854 /// UnaryMutator implements ++ and -- expressions. It derives from
855 /// ExpressionStatement becuase the pre/post increment/decrement
856 /// operators can be used in a statement context.
858 /// FIXME: Idea, we could split this up in two classes, one simpler
859 /// for the common case, and one with the extra fields for more complex
860 /// classes (indexers require temporary access; overloaded require method)
863 public class UnaryMutator : ExpressionStatement {
865 public enum Mode : byte {
872 PreDecrement = IsDecrement,
873 PostIncrement = IsPost,
874 PostDecrement = IsPost | IsDecrement
878 bool is_expr = false;
879 bool recurse = false;
884 // This is expensive for the simplest case.
886 UserOperatorCall method;
888 public UnaryMutator (Mode m, Expression e, Location l)
895 static string OperName (Mode mode)
897 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
902 /// Returns whether an object of type `t' can be incremented
903 /// or decremented with add/sub (ie, basically whether we can
904 /// use pre-post incr-decr operations on it, but it is not a
905 /// System.Decimal, which we require operator overloading to catch)
907 static bool IsIncrementableNumber (Type t)
909 return (t == TypeManager.sbyte_type) ||
910 (t == TypeManager.byte_type) ||
911 (t == TypeManager.short_type) ||
912 (t == TypeManager.ushort_type) ||
913 (t == TypeManager.int32_type) ||
914 (t == TypeManager.uint32_type) ||
915 (t == TypeManager.int64_type) ||
916 (t == TypeManager.uint64_type) ||
917 (t == TypeManager.char_type) ||
918 (TypeManager.IsSubclassOf (t, TypeManager.enum_type)) ||
919 (t == TypeManager.float_type) ||
920 (t == TypeManager.double_type) ||
921 (t.IsPointer && t != TypeManager.void_ptr_type);
924 Expression ResolveOperator (EmitContext ec)
926 Type expr_type = expr.Type;
929 // Step 1: Perform Operator Overload location
934 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
935 op_name = "op_Increment";
937 op_name = "op_Decrement";
939 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
942 method = UserOperatorCall.MakeSimpleCall (
943 ec, (MethodGroupExpr) mg, expr, loc);
946 } else if (!IsIncrementableNumber (expr_type)) {
947 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
948 TypeManager.CSharpName (expr_type) + "'");
953 // The operand of the prefix/postfix increment decrement operators
954 // should be an expression that is classified as a variable,
955 // a property access or an indexer access
958 if (expr.eclass == ExprClass.Variable){
959 LocalVariableReference var = expr as LocalVariableReference;
960 if ((var != null) && var.IsReadOnly) {
961 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
964 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
965 expr = expr.ResolveLValue (ec, this, Location);
969 Report.Error (1059, loc, "The operand of an increment or decrement operator must be a variable, property or indexer");
976 public override Expression CreateExpressionTree (EmitContext ec)
978 return new SimpleAssign (this, this).CreateExpressionTree (ec);
981 public override Expression DoResolve (EmitContext ec)
983 expr = expr.Resolve (ec);
988 eclass = ExprClass.Value;
991 if (TypeManager.IsNullableValueType (expr.Type))
992 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
995 return ResolveOperator (ec);
999 // Loads the proper "1" into the stack based on the type, then it emits the
1000 // opcode for the operation requested
1002 void LoadOneAndEmitOp (EmitContext ec, Type t)
1005 // Measure if getting the typecode and using that is more/less efficient
1006 // that comparing types. t.GetTypeCode() is an internal call.
1008 ILGenerator ig = ec.ig;
1010 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
1011 LongConstant.EmitLong (ig, 1);
1012 else if (t == TypeManager.double_type)
1013 ig.Emit (OpCodes.Ldc_R8, 1.0);
1014 else if (t == TypeManager.float_type)
1015 ig.Emit (OpCodes.Ldc_R4, 1.0F);
1016 else if (t.IsPointer){
1017 Type et = TypeManager.GetElementType (t);
1018 int n = GetTypeSize (et);
1021 ig.Emit (OpCodes.Sizeof, et);
1023 IntConstant.EmitInt (ig, n);
1025 ig.Emit (OpCodes.Ldc_I4_1);
1028 // Now emit the operation
1031 if (t == TypeManager.int32_type ||
1032 t == TypeManager.int64_type){
1033 if ((mode & Mode.IsDecrement) != 0)
1034 ig.Emit (OpCodes.Sub_Ovf);
1036 ig.Emit (OpCodes.Add_Ovf);
1037 } else if (t == TypeManager.uint32_type ||
1038 t == TypeManager.uint64_type){
1039 if ((mode & Mode.IsDecrement) != 0)
1040 ig.Emit (OpCodes.Sub_Ovf_Un);
1042 ig.Emit (OpCodes.Add_Ovf_Un);
1044 if ((mode & Mode.IsDecrement) != 0)
1045 ig.Emit (OpCodes.Sub_Ovf);
1047 ig.Emit (OpCodes.Add_Ovf);
1050 if ((mode & Mode.IsDecrement) != 0)
1051 ig.Emit (OpCodes.Sub);
1053 ig.Emit (OpCodes.Add);
1056 if (t == TypeManager.sbyte_type){
1058 ig.Emit (OpCodes.Conv_Ovf_I1);
1060 ig.Emit (OpCodes.Conv_I1);
1061 } else if (t == TypeManager.byte_type){
1063 ig.Emit (OpCodes.Conv_Ovf_U1);
1065 ig.Emit (OpCodes.Conv_U1);
1066 } else if (t == TypeManager.short_type){
1068 ig.Emit (OpCodes.Conv_Ovf_I2);
1070 ig.Emit (OpCodes.Conv_I2);
1071 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
1073 ig.Emit (OpCodes.Conv_Ovf_U2);
1075 ig.Emit (OpCodes.Conv_U2);
1080 void EmitCode (EmitContext ec, bool is_expr)
1083 this.is_expr = is_expr;
1084 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1087 public override void Emit (EmitContext ec)
1090 // We use recurse to allow ourselfs to be the source
1091 // of an assignment. This little hack prevents us from
1092 // having to allocate another expression
1095 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1097 LoadOneAndEmitOp (ec, expr.Type);
1099 ec.ig.Emit (OpCodes.Call, (MethodInfo)method.Method);
1104 EmitCode (ec, true);
1107 public override void EmitStatement (EmitContext ec)
1109 EmitCode (ec, false);
1112 protected override void CloneTo (CloneContext clonectx, Expression t)
1114 UnaryMutator target = (UnaryMutator) t;
1116 target.expr = expr.Clone (clonectx);
1121 /// Base class for the `Is' and `As' classes.
1125 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1128 public abstract class Probe : Expression {
1129 public Expression ProbeType;
1130 protected Expression expr;
1131 protected TypeExpr probe_type_expr;
1133 public Probe (Expression expr, Expression probe_type, Location l)
1135 ProbeType = probe_type;
1140 public Expression Expr {
1146 public override Expression DoResolve (EmitContext ec)
1148 probe_type_expr = ProbeType.ResolveAsTypeTerminal (ec, false);
1149 if (probe_type_expr == null)
1152 expr = expr.Resolve (ec);
1156 if (expr.Type.IsPointer || probe_type_expr.Type.IsPointer) {
1157 Report.Error (244, loc, "The `{0}' operator cannot be applied to an operand of pointer type",
1162 if (expr.Type == TypeManager.anonymous_method_type) {
1163 Report.Error (837, loc, "The `{0}' operator cannot be applied to a lambda expression or anonymous method",
1171 protected abstract string OperatorName { get; }
1173 protected override void CloneTo (CloneContext clonectx, Expression t)
1175 Probe target = (Probe) t;
1177 target.expr = expr.Clone (clonectx);
1178 target.ProbeType = ProbeType.Clone (clonectx);
1184 /// Implementation of the `is' operator.
1186 public class Is : Probe {
1187 public Is (Expression expr, Expression probe_type, Location l)
1188 : base (expr, probe_type, l)
1192 public override Expression CreateExpressionTree (EmitContext ec)
1194 ArrayList args = new ArrayList (2);
1195 args.Add (new Argument (expr.CreateExpressionTree (ec)));
1196 args.Add (new Argument (new TypeOf (probe_type_expr, loc)));
1197 return CreateExpressionFactoryCall ("TypeIs", args);
1200 public override void Emit (EmitContext ec)
1202 ILGenerator ig = ec.ig;
1205 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1206 ig.Emit (OpCodes.Ldnull);
1207 ig.Emit (OpCodes.Cgt_Un);
1210 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
1212 ILGenerator ig = ec.ig;
1215 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1216 ig.Emit (on_true ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1219 Expression CreateConstantResult (bool result)
1222 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
1223 TypeManager.CSharpName (probe_type_expr.Type));
1225 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
1226 TypeManager.CSharpName (probe_type_expr.Type));
1228 return ReducedExpression.Create (new BoolConstant (result, loc), this);
1231 public override Expression DoResolve (EmitContext ec)
1233 if (base.DoResolve (ec) == null)
1237 bool d_is_nullable = false;
1239 if (expr is Constant) {
1241 // If E is a method group or the null literal, of if the type of E is a reference
1242 // type or a nullable type and the value of E is null, the result is false
1245 return CreateConstantResult (false);
1246 } else if (TypeManager.IsNullableType (d) && !TypeManager.ContainsGenericParameters (d)) {
1247 d = TypeManager.GetTypeArguments (d) [0];
1248 d_is_nullable = true;
1251 type = TypeManager.bool_type;
1252 eclass = ExprClass.Value;
1253 Type t = probe_type_expr.Type;
1254 bool t_is_nullable = false;
1255 if (TypeManager.IsNullableType (t) && !TypeManager.ContainsGenericParameters (t)) {
1256 t = TypeManager.GetTypeArguments (t) [0];
1257 t_is_nullable = true;
1260 if (t.IsValueType) {
1263 // D and T are the same value types but D can be null
1265 if (d_is_nullable && !t_is_nullable)
1266 return Nullable.HasValue.Create (expr, ec);
1269 // The result is true if D and T are the same value types
1271 return CreateConstantResult (true);
1274 if (TypeManager.IsGenericParameter (d))
1275 return ResolveGenericParameter (t, d);
1278 // An unboxing conversion exists
1280 if (Convert.ExplicitReferenceConversionExists (d, t))
1283 if (TypeManager.IsGenericParameter (t))
1284 return ResolveGenericParameter (d, t);
1286 if (d.IsValueType) {
1288 if (Convert.ImplicitBoxingConversionExists (expr, t, out temp))
1289 return CreateConstantResult (true);
1291 if (TypeManager.IsGenericParameter (d))
1292 return ResolveGenericParameter (t, d);
1294 if (TypeManager.ContainsGenericParameters (d))
1297 if (Convert.ImplicitReferenceConversionExists (expr, t) ||
1298 Convert.ExplicitReferenceConversionExists (d, t)) {
1304 return CreateConstantResult (false);
1307 Expression ResolveGenericParameter (Type d, Type t)
1310 GenericConstraints constraints = TypeManager.GetTypeParameterConstraints (t);
1311 if (constraints != null) {
1312 if (constraints.IsReferenceType && d.IsValueType)
1313 return CreateConstantResult (false);
1315 if (constraints.IsValueType && !d.IsValueType)
1316 return CreateConstantResult (false);
1319 expr = new BoxedCast (expr, d);
1326 protected override string OperatorName {
1327 get { return "is"; }
1332 /// Implementation of the `as' operator.
1334 public class As : Probe {
1336 Expression resolved_type;
1338 public As (Expression expr, Expression probe_type, Location l)
1339 : base (expr, probe_type, l)
1343 public override Expression CreateExpressionTree (EmitContext ec)
1345 ArrayList args = new ArrayList (2);
1346 args.Add (new Argument (expr.CreateExpressionTree (ec)));
1347 args.Add (new Argument (new TypeOf (probe_type_expr, loc)));
1348 return CreateExpressionFactoryCall ("TypeAs", args);
1351 public override void Emit (EmitContext ec)
1353 ILGenerator ig = ec.ig;
1358 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1361 if (TypeManager.IsNullableType (type))
1362 ig.Emit (OpCodes.Unbox_Any, type);
1366 static void Error_CannotConvertType (Type source, Type target, Location loc)
1368 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1369 TypeManager.CSharpName (source),
1370 TypeManager.CSharpName (target));
1373 public override Expression DoResolve (EmitContext ec)
1375 if (resolved_type == null) {
1376 resolved_type = base.DoResolve (ec);
1378 if (resolved_type == null)
1382 type = probe_type_expr.Type;
1383 eclass = ExprClass.Value;
1384 Type etype = expr.Type;
1386 if (type.IsValueType && !TypeManager.IsNullableType (type)) {
1387 Report.Error (77, loc, "The `as' operator cannot be used with a non-nullable value type `{0}'",
1388 TypeManager.CSharpName (type));
1395 // If the type is a type parameter, ensure
1396 // that it is constrained by a class
1398 TypeParameterExpr tpe = probe_type_expr as TypeParameterExpr;
1400 GenericConstraints constraints = tpe.TypeParameter.GenericConstraints;
1403 if (constraints == null)
1406 if (!constraints.HasClassConstraint)
1407 if ((constraints.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == 0)
1411 Report.Error (413, loc,
1412 "The as operator requires that the `{0}' type parameter be constrained by a class",
1413 probe_type_expr.GetSignatureForError ());
1418 if (expr.IsNull && TypeManager.IsNullableType (type)) {
1419 return Nullable.LiftedNull.CreateFromExpression (this);
1422 Expression e = Convert.ImplicitConversion (ec, expr, type, loc);
1429 if (Convert.ExplicitReferenceConversionExists (etype, type)){
1430 if (TypeManager.IsGenericParameter (etype))
1431 expr = new BoxedCast (expr, etype);
1437 if (TypeManager.ContainsGenericParameters (etype) ||
1438 TypeManager.ContainsGenericParameters (type)) {
1439 expr = new BoxedCast (expr, etype);
1444 Error_CannotConvertType (etype, type, loc);
1448 protected override string OperatorName {
1449 get { return "as"; }
1452 public override bool GetAttributableValue (Type value_type, out object value)
1454 return expr.GetAttributableValue (value_type, out value);
1459 /// This represents a typecast in the source language.
1461 /// FIXME: Cast expressions have an unusual set of parsing
1462 /// rules, we need to figure those out.
1464 public class Cast : Expression {
1465 Expression target_type;
1468 public Cast (Expression cast_type, Expression expr)
1469 : this (cast_type, expr, cast_type.Location)
1473 public Cast (Expression cast_type, Expression expr, Location loc)
1475 this.target_type = cast_type;
1479 if (target_type == TypeManager.system_void_expr)
1480 Error_VoidInvalidInTheContext (loc);
1483 public Expression TargetType {
1484 get { return target_type; }
1487 public Expression Expr {
1488 get { return expr; }
1491 public override Expression DoResolve (EmitContext ec)
1493 expr = expr.Resolve (ec);
1497 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1503 if (type.IsAbstract && type.IsSealed) {
1504 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1508 eclass = ExprClass.Value;
1510 Constant c = expr as Constant;
1512 c = c.TryReduce (ec, type, loc);
1517 if (type.IsPointer && !ec.InUnsafe) {
1521 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1525 public override void Emit (EmitContext ec)
1527 throw new Exception ("Should not happen");
1530 protected override void CloneTo (CloneContext clonectx, Expression t)
1532 Cast target = (Cast) t;
1534 target.target_type = target_type.Clone (clonectx);
1535 target.expr = expr.Clone (clonectx);
1540 // C# 2.0 Default value expression
1542 public class DefaultValueExpression : Expression
1546 public DefaultValueExpression (Expression expr, Location loc)
1552 public override Expression CreateExpressionTree (EmitContext ec)
1554 ArrayList args = new ArrayList (2);
1555 args.Add (new Argument (this));
1556 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
1557 return CreateExpressionFactoryCall ("Constant", args);
1560 public override Expression DoResolve (EmitContext ec)
1562 TypeExpr texpr = expr.ResolveAsTypeTerminal (ec, false);
1568 if (type == TypeManager.void_type) {
1569 Error_VoidInvalidInTheContext (loc);
1573 if (TypeManager.IsGenericParameter (type)) {
1574 GenericConstraints constraints = TypeManager.GetTypeParameterConstraints(type);
1575 if (constraints != null && constraints.IsReferenceType)
1576 return new EmptyConstantCast (new NullLiteral (Location), type);
1578 Constant c = New.Constantify (type);
1580 return new EmptyConstantCast (c, type);
1582 if (!TypeManager.IsValueType (type))
1583 return new EmptyConstantCast (new NullLiteral (Location), type);
1585 eclass = ExprClass.Variable;
1589 public override void Emit (EmitContext ec)
1591 LocalTemporary temp_storage = new LocalTemporary(type);
1593 temp_storage.AddressOf(ec, AddressOp.LoadStore);
1594 ec.ig.Emit(OpCodes.Initobj, type);
1595 temp_storage.Emit(ec);
1598 protected override void CloneTo (CloneContext clonectx, Expression t)
1600 DefaultValueExpression target = (DefaultValueExpression) t;
1602 target.expr = expr.Clone (clonectx);
1607 /// Binary operators
1609 public class Binary : Expression {
1611 protected class PredefinedOperator {
1612 protected readonly Type left;
1613 protected readonly Type right;
1614 public readonly Operator OperatorsMask;
1615 public Type ReturnType;
1617 public PredefinedOperator (Type ltype, Type rtype, Operator op_mask)
1618 : this (ltype, rtype, op_mask, ltype)
1622 public PredefinedOperator (Type type, Operator op_mask, Type return_type)
1623 : this (type, type, op_mask, return_type)
1627 public PredefinedOperator (Type type, Operator op_mask)
1628 : this (type, type, op_mask, type)
1632 public PredefinedOperator (Type ltype, Type rtype, Operator op_mask, Type return_type)
1634 if ((op_mask & Operator.ValuesOnlyMask) != 0)
1635 throw new InternalErrorException ("Only masked values can be used");
1639 this.OperatorsMask = op_mask;
1640 this.ReturnType = return_type;
1643 public virtual Expression ConvertResult (EmitContext ec, Binary b)
1645 b.type = ReturnType;
1648 b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
1651 b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
1656 public bool IsPrimitiveApplicable (Type type)
1659 // We are dealing with primitive types only
1661 return left == type;
1664 public virtual bool IsApplicable (EmitContext ec, Expression lexpr, Expression rexpr)
1666 if (TypeManager.IsEqual (left, lexpr.Type) &&
1667 TypeManager.IsEqual (right, rexpr.Type))
1670 return Convert.ImplicitConversionExists (ec, lexpr, left) &&
1671 Convert.ImplicitConversionExists (ec, rexpr, right);
1674 public PredefinedOperator ResolveBetterOperator (EmitContext ec, Expression lexpr, Expression rexpr, PredefinedOperator best_operator)
1677 if (left != null && best_operator.left != null) {
1678 result = MethodGroupExpr.BetterTypeConversion (ec, best_operator.left, left);
1682 // When second arguments are same as the first one, the result is same
1684 if (left != right || best_operator.left != best_operator.right) {
1685 result |= MethodGroupExpr.BetterTypeConversion (ec, best_operator.right, right);
1688 if (result == 0 || result > 2)
1691 return result == 1 ? best_operator : this;
1695 class PredefinedStringOperator : PredefinedOperator {
1696 public PredefinedStringOperator (Type type, Operator op_mask)
1697 : base (type, op_mask, type)
1699 ReturnType = TypeManager.string_type;
1702 public PredefinedStringOperator (Type ltype, Type rtype, Operator op_mask)
1703 : base (ltype, rtype, op_mask)
1705 ReturnType = TypeManager.string_type;
1708 public override Expression ConvertResult (EmitContext ec, Binary b)
1711 // Use original expression for nullable arguments
1713 Nullable.Unwrap unwrap = b.left as Nullable.Unwrap;
1715 b.left = unwrap.Original;
1717 unwrap = b.right as Nullable.Unwrap;
1719 b.right = unwrap.Original;
1721 b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
1722 b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
1725 // Start a new concat expression using converted expression
1727 return new StringConcat (ec, b.loc, b.left, b.right).Resolve (ec);
1731 class PredefinedShiftOperator : PredefinedOperator {
1732 public PredefinedShiftOperator (Type ltype, Operator op_mask) :
1733 base (ltype, TypeManager.int32_type, op_mask)
1737 public override Expression ConvertResult (EmitContext ec, Binary b)
1739 b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
1741 Expression expr_tree_expr = EmptyCast.Create (b.right, TypeManager.int32_type);
1743 int right_mask = left == TypeManager.int32_type || left == TypeManager.uint32_type ? 0x1f : 0x3f;
1746 // b = b.left >> b.right & (0x1f|0x3f)
1748 b.right = new Binary (Operator.BitwiseAnd,
1749 b.right, new IntConstant (right_mask, b.right.Location)).Resolve (ec);
1752 // Expression tree representation does not use & mask
1754 b.right = ReducedExpression.Create (b.right, expr_tree_expr).Resolve (ec);
1755 b.type = ReturnType;
1760 class PredefinedPointerOperator : PredefinedOperator {
1761 public PredefinedPointerOperator (Type ltype, Type rtype, Operator op_mask)
1762 : base (ltype, rtype, op_mask)
1766 public PredefinedPointerOperator (Type type, Operator op_mask, Type return_type)
1767 : base (type, op_mask, return_type)
1771 public override bool IsApplicable (EmitContext ec, Expression lexpr, Expression rexpr)
1774 if (!lexpr.Type.IsPointer)
1777 if (!Convert.ImplicitConversionExists (ec, lexpr, left))
1781 if (right == null) {
1782 if (!rexpr.Type.IsPointer)
1785 if (!Convert.ImplicitConversionExists (ec, rexpr, right))
1792 public override Expression ConvertResult (EmitContext ec, Binary b)
1794 base.ConvertResult (ec, b);
1796 Type r_type = ReturnType;
1797 if (r_type == null) {
1798 r_type = b.left.Type;
1800 r_type = b.right.Type;
1803 return new PointerArithmetic (b.oper == Operator.Addition,
1804 b.left, b.right, r_type, b.loc).Resolve (ec);
1809 public enum Operator {
1810 Multiply = 0 | ArithmeticMask,
1811 Division = 1 | ArithmeticMask,
1812 Modulus = 2 | ArithmeticMask,
1813 Addition = 3 | ArithmeticMask | AdditionMask,
1814 Subtraction = 4 | ArithmeticMask | SubtractionMask,
1816 LeftShift = 5 | ShiftMask,
1817 RightShift = 6 | ShiftMask,
1819 LessThan = 7 | ComparisonMask | RelationalMask,
1820 GreaterThan = 8 | ComparisonMask | RelationalMask,
1821 LessThanOrEqual = 9 | ComparisonMask | RelationalMask,
1822 GreaterThanOrEqual = 10 | ComparisonMask | RelationalMask,
1823 Equality = 11 | ComparisonMask | EqualityMask,
1824 Inequality = 12 | ComparisonMask | EqualityMask,
1826 BitwiseAnd = 13 | BitwiseMask,
1827 ExclusiveOr = 14 | BitwiseMask,
1828 BitwiseOr = 15 | BitwiseMask,
1830 LogicalAnd = 16 | LogicalMask,
1831 LogicalOr = 17 | LogicalMask,
1836 ValuesOnlyMask = ArithmeticMask - 1,
1837 ArithmeticMask = 1 << 5,
1839 ComparisonMask = 1 << 7,
1840 EqualityMask = 1 << 8,
1841 BitwiseMask = 1 << 9,
1842 LogicalMask = 1 << 10,
1843 AdditionMask = 1 << 11,
1844 SubtractionMask = 1 << 12,
1845 RelationalMask = 1 << 13
1848 readonly Operator oper;
1849 protected Expression left, right;
1850 readonly bool is_compound;
1851 Expression enum_conversion;
1853 // This must be kept in sync with Operator!!!
1854 public static readonly string [] oper_names;
1856 static PredefinedOperator [] standard_operators;
1857 static PredefinedOperator [] pointer_operators;
1861 oper_names = new string [18];
1863 oper_names [(int) (Operator.Multiply & Operator.ValuesOnlyMask)] = "op_Multiply";
1864 oper_names [(int) (Operator.Division & Operator.ValuesOnlyMask)] = "op_Division";
1865 oper_names [(int) (Operator.Modulus & Operator.ValuesOnlyMask)] = "op_Modulus";
1866 oper_names [(int) (Operator.Addition & Operator.ValuesOnlyMask)] = "op_Addition";
1867 oper_names [(int) (Operator.Subtraction & Operator.ValuesOnlyMask)] = "op_Subtraction";
1868 oper_names [(int) (Operator.LeftShift & Operator.ValuesOnlyMask)] = "op_LeftShift";
1869 oper_names [(int) (Operator.RightShift & Operator.ValuesOnlyMask)] = "op_RightShift";
1870 oper_names [(int) (Operator.LessThan & Operator.ValuesOnlyMask)] = "op_LessThan";
1871 oper_names [(int) (Operator.GreaterThan & Operator.ValuesOnlyMask)] = "op_GreaterThan";
1872 oper_names [(int) (Operator.LessThanOrEqual & Operator.ValuesOnlyMask)] = "op_LessThanOrEqual";
1873 oper_names [(int) (Operator.GreaterThanOrEqual & Operator.ValuesOnlyMask)] = "op_GreaterThanOrEqual";
1874 oper_names [(int) (Operator.Equality & Operator.ValuesOnlyMask)] = "op_Equality";
1875 oper_names [(int) (Operator.Inequality & Operator.ValuesOnlyMask)] = "op_Inequality";
1876 oper_names [(int) (Operator.BitwiseAnd & Operator.ValuesOnlyMask)] = "op_BitwiseAnd";
1877 oper_names [(int) (Operator.BitwiseOr & Operator.ValuesOnlyMask)] = "op_BitwiseOr";
1878 oper_names [(int) (Operator.ExclusiveOr & Operator.ValuesOnlyMask)] = "op_ExclusiveOr";
1879 oper_names [(int) (Operator.LogicalOr & Operator.ValuesOnlyMask)] = "op_LogicalOr";
1880 oper_names [(int) (Operator.LogicalAnd & Operator.ValuesOnlyMask)] = "op_LogicalAnd";
1883 public Binary (Operator oper, Expression left, Expression right, bool isCompound)
1884 : this (oper, left, right)
1886 this.is_compound = isCompound;
1889 public Binary (Operator oper, Expression left, Expression right)
1894 this.loc = left.Location;
1897 public Operator Oper {
1904 /// Returns a stringified representation of the Operator
1906 string OperName (Operator oper)
1910 case Operator.Multiply:
1913 case Operator.Division:
1916 case Operator.Modulus:
1919 case Operator.Addition:
1922 case Operator.Subtraction:
1925 case Operator.LeftShift:
1928 case Operator.RightShift:
1931 case Operator.LessThan:
1934 case Operator.GreaterThan:
1937 case Operator.LessThanOrEqual:
1940 case Operator.GreaterThanOrEqual:
1943 case Operator.Equality:
1946 case Operator.Inequality:
1949 case Operator.BitwiseAnd:
1952 case Operator.BitwiseOr:
1955 case Operator.ExclusiveOr:
1958 case Operator.LogicalOr:
1961 case Operator.LogicalAnd:
1965 s = oper.ToString ();
1975 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1977 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1980 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1982 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1986 protected void Error_OperatorCannotBeApplied (Expression left, Expression right)
1989 // TODO: This should be handled as Type of method group in CSharpName
1990 if (left.eclass == ExprClass.MethodGroup)
1991 l = left.ExprClassName;
1993 l = TypeManager.CSharpName (left.Type);
1995 if (right.eclass == ExprClass.MethodGroup)
1996 r = right.ExprClassName;
1998 r = TypeManager.CSharpName (right.Type);
2000 Error_OperatorCannotBeApplied (Location, OperName (oper), l, r);
2003 public static string GetOperatorMetadataName (Operator op)
2005 return oper_names [(int)(op & Operator.ValuesOnlyMask)];
2008 static bool IsUnsigned (Type t)
2011 t = TypeManager.GetElementType (t);
2013 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
2014 t == TypeManager.ushort_type || t == TypeManager.byte_type);
2017 static bool IsFloat (Type t)
2019 return t == TypeManager.float_type || t == TypeManager.double_type;
2022 Expression ResolveOperator (EmitContext ec)
2025 Type r = right.Type;
2027 bool primitives_only = false;
2029 if (standard_operators == null)
2030 CreateStandardOperatorsTable ();
2033 // Handles predefined primitive types
2035 if (TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r)) {
2036 if ((oper & Operator.ShiftMask) == 0) {
2037 if (l != TypeManager.bool_type && !DoBinaryOperatorPromotion (ec))
2040 primitives_only = true;
2044 if (l.IsPointer || r.IsPointer)
2045 return ResolveOperatorPointer (ec, l, r);
2048 bool lenum = TypeManager.IsEnumType (l);
2049 bool renum = TypeManager.IsEnumType (r);
2050 if (lenum || renum) {
2051 expr = ResolveOperatorEnum (ec, lenum, renum, l, r);
2053 // TODO: Can this be ambiguous
2059 if (oper == Operator.Addition || oper == Operator.Subtraction) {
2060 if (TypeManager.IsDelegateType (l))
2061 return ResolveOperatorDelegateBinary (ec, l, r);
2065 expr = ResolveUserOperator (ec, l, r);
2069 // Predefined reference types equality
2070 if ((oper & Operator.EqualityMask) != 0) {
2071 expr = ResolveOperatorEqualityRerefence (ec, l, r);
2077 return ResolveOperatorPredefined (ec, standard_operators, primitives_only, null);
2080 // at least one of 'left' or 'right' is an enumeration constant (EnumConstant or SideEffectConstant or ...)
2081 // if 'left' is not an enumeration constant, create one from the type of 'right'
2082 Constant EnumLiftUp (EmitContext ec, Constant left, Constant right, Location loc)
2085 case Operator.BitwiseOr:
2086 case Operator.BitwiseAnd:
2087 case Operator.ExclusiveOr:
2088 case Operator.Equality:
2089 case Operator.Inequality:
2090 case Operator.LessThan:
2091 case Operator.LessThanOrEqual:
2092 case Operator.GreaterThan:
2093 case Operator.GreaterThanOrEqual:
2094 if (TypeManager.IsEnumType (left.Type))
2097 if (left.IsZeroInteger)
2098 return left.TryReduce (ec, right.Type, loc);
2102 case Operator.Addition:
2103 case Operator.Subtraction:
2106 case Operator.Multiply:
2107 case Operator.Division:
2108 case Operator.Modulus:
2109 case Operator.LeftShift:
2110 case Operator.RightShift:
2111 if (TypeManager.IsEnumType (right.Type) || TypeManager.IsEnumType (left.Type))
2115 Error_OperatorCannotBeApplied (this.left, this.right);
2120 // The `|' operator used on types which were extended is dangerous
2122 void CheckBitwiseOrOnSignExtended ()
2124 OpcodeCast lcast = left as OpcodeCast;
2125 if (lcast != null) {
2126 if (IsUnsigned (lcast.UnderlyingType))
2130 OpcodeCast rcast = right as OpcodeCast;
2131 if (rcast != null) {
2132 if (IsUnsigned (rcast.UnderlyingType))
2136 if (lcast == null && rcast == null)
2139 // FIXME: consider constants
2141 Report.Warning (675, 3, loc,
2142 "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first",
2143 TypeManager.CSharpName (lcast != null ? lcast.UnderlyingType : rcast.UnderlyingType));
2146 static void CreatePointerOperatorsTable ()
2148 ArrayList temp = new ArrayList ();
2151 // Pointer arithmetic:
2153 // T* operator + (T* x, int y); T* operator - (T* x, int y);
2154 // T* operator + (T* x, uint y); T* operator - (T* x, uint y);
2155 // T* operator + (T* x, long y); T* operator - (T* x, long y);
2156 // T* operator + (T* x, ulong y); T* operator - (T* x, ulong y);
2158 temp.Add (new PredefinedPointerOperator (null, TypeManager.int32_type, Operator.AdditionMask | Operator.SubtractionMask));
2159 temp.Add (new PredefinedPointerOperator (null, TypeManager.uint32_type, Operator.AdditionMask | Operator.SubtractionMask));
2160 temp.Add (new PredefinedPointerOperator (null, TypeManager.int64_type, Operator.AdditionMask | Operator.SubtractionMask));
2161 temp.Add (new PredefinedPointerOperator (null, TypeManager.uint64_type, Operator.AdditionMask | Operator.SubtractionMask));
2164 // T* operator + (int y, T* x);
2165 // T* operator + (uint y, T *x);
2166 // T* operator + (long y, T *x);
2167 // T* operator + (ulong y, T *x);
2169 temp.Add (new PredefinedPointerOperator (TypeManager.int32_type, null, Operator.AdditionMask));
2170 temp.Add (new PredefinedPointerOperator (TypeManager.uint32_type, null, Operator.AdditionMask));
2171 temp.Add (new PredefinedPointerOperator (TypeManager.int64_type, null, Operator.AdditionMask));
2172 temp.Add (new PredefinedPointerOperator (TypeManager.uint64_type, null, Operator.AdditionMask));
2175 // long operator - (T* x, T *y)
2177 temp.Add (new PredefinedPointerOperator (null, Operator.SubtractionMask, TypeManager.int64_type));
2179 pointer_operators = (PredefinedOperator []) temp.ToArray (typeof (PredefinedOperator));
2182 static void CreateStandardOperatorsTable ()
2184 ArrayList temp = new ArrayList ();
2185 Type bool_type = TypeManager.bool_type;
2187 temp.Add (new PredefinedOperator (TypeManager.int32_type, Operator.ArithmeticMask | Operator.BitwiseMask));
2188 temp.Add (new PredefinedOperator (TypeManager.uint32_type, Operator.ArithmeticMask | Operator.BitwiseMask));
2189 temp.Add (new PredefinedOperator (TypeManager.int64_type, Operator.ArithmeticMask | Operator.BitwiseMask));
2190 temp.Add (new PredefinedOperator (TypeManager.uint64_type, Operator.ArithmeticMask | Operator.BitwiseMask));
2191 temp.Add (new PredefinedOperator (TypeManager.float_type, Operator.ArithmeticMask));
2192 temp.Add (new PredefinedOperator (TypeManager.double_type, Operator.ArithmeticMask));
2194 temp.Add (new PredefinedOperator (TypeManager.int32_type, Operator.ComparisonMask, bool_type));
2195 temp.Add (new PredefinedOperator (TypeManager.uint32_type, Operator.ComparisonMask, bool_type));
2196 temp.Add (new PredefinedOperator (TypeManager.int64_type, Operator.ComparisonMask, bool_type));
2197 temp.Add (new PredefinedOperator (TypeManager.uint64_type, Operator.ComparisonMask, bool_type));
2198 temp.Add (new PredefinedOperator (TypeManager.float_type, Operator.ComparisonMask, bool_type));
2199 temp.Add (new PredefinedOperator (TypeManager.double_type, Operator.ComparisonMask, bool_type));
2201 temp.Add (new PredefinedOperator (TypeManager.string_type, Operator.EqualityMask, bool_type));
2203 temp.Add (new PredefinedStringOperator (TypeManager.string_type, Operator.AdditionMask));
2204 temp.Add (new PredefinedStringOperator (TypeManager.string_type, TypeManager.object_type, Operator.AdditionMask));
2205 temp.Add (new PredefinedStringOperator (TypeManager.object_type, TypeManager.string_type, Operator.AdditionMask));
2207 temp.Add (new PredefinedOperator (bool_type,
2208 Operator.BitwiseMask | Operator.LogicalMask | Operator.EqualityMask, bool_type));
2210 temp.Add (new PredefinedShiftOperator (TypeManager.int32_type, Operator.ShiftMask));
2211 temp.Add (new PredefinedShiftOperator (TypeManager.uint32_type, Operator.ShiftMask));
2212 temp.Add (new PredefinedShiftOperator (TypeManager.int64_type, Operator.ShiftMask));
2213 temp.Add (new PredefinedShiftOperator (TypeManager.uint64_type, Operator.ShiftMask));
2215 standard_operators = (PredefinedOperator []) temp.ToArray (typeof (PredefinedOperator));
2219 // Rules used during binary numeric promotion
2221 static bool DoNumericPromotion (ref Expression prim_expr, ref Expression second_expr, Type type)
2226 Constant c = prim_expr as Constant;
2228 temp = c.ConvertImplicitly (type);
2235 if (type == TypeManager.uint32_type) {
2236 etype = prim_expr.Type;
2237 if (etype == TypeManager.int32_type || etype == TypeManager.short_type || etype == TypeManager.sbyte_type) {
2238 type = TypeManager.int64_type;
2240 if (type != second_expr.Type) {
2241 c = second_expr as Constant;
2243 temp = c.ConvertImplicitly (type);
2245 temp = Convert.ImplicitNumericConversion (second_expr, type);
2251 } else if (type == TypeManager.uint64_type) {
2253 // A compile-time error occurs if the other operand is of type sbyte, short, int, or long
2255 if (type == TypeManager.int32_type || type == TypeManager.int64_type ||
2256 type == TypeManager.sbyte_type || type == TypeManager.sbyte_type)
2260 temp = Convert.ImplicitNumericConversion (prim_expr, type);
2269 // 7.2.6.2 Binary numeric promotions
2271 public bool DoBinaryOperatorPromotion (EmitContext ec)
2273 Type ltype = left.Type;
2274 Type rtype = right.Type;
2277 foreach (Type t in ConstantFold.binary_promotions) {
2279 return t == rtype || DoNumericPromotion (ref right, ref left, t);
2282 return t == ltype || DoNumericPromotion (ref left, ref right, t);
2285 Type int32 = TypeManager.int32_type;
2286 if (ltype != int32) {
2287 Constant c = left as Constant;
2289 temp = c.ConvertImplicitly (int32);
2291 temp = Convert.ImplicitNumericConversion (left, int32);
2298 if (rtype != int32) {
2299 Constant c = right as Constant;
2301 temp = c.ConvertImplicitly (int32);
2303 temp = Convert.ImplicitNumericConversion (right, int32);
2313 public override Expression DoResolve (EmitContext ec)
2318 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2319 left = ((ParenthesizedExpression) left).Expr;
2320 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2324 if (left.eclass == ExprClass.Type) {
2325 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2329 left = left.Resolve (ec);
2334 Constant lc = left as Constant;
2336 if (lc != null && lc.Type == TypeManager.bool_type &&
2337 ((oper == Operator.LogicalAnd && lc.IsDefaultValue) ||
2338 (oper == Operator.LogicalOr && !lc.IsDefaultValue))) {
2340 // FIXME: resolve right expression as unreachable
2341 // right.Resolve (ec);
2343 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2347 right = right.Resolve (ec);
2351 eclass = ExprClass.Value;
2352 Constant rc = right as Constant;
2354 // The conversion rules are ignored in enum context but why
2355 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2356 left = lc = EnumLiftUp (ec, lc, rc, loc);
2360 right = rc = EnumLiftUp (ec, rc, lc, loc);
2365 if (rc != null && lc != null) {
2366 int prev_e = Report.Errors;
2367 Expression e = ConstantFold.BinaryFold (
2368 ec, oper, lc, rc, loc);
2369 if (e != null || Report.Errors != prev_e)
2372 if ((oper == Operator.BitwiseAnd || oper == Operator.LogicalAnd) &&
2373 ((lc != null && lc.IsDefaultValue) || (rc != null && rc.IsDefaultValue))) {
2375 if ((ResolveOperator (ec)) == null) {
2376 Error_OperatorCannotBeApplied (left, right);
2385 // The result is a constant with side-effect
2386 return new SideEffectConstant (lc, right, loc);
2390 // Comparison warnings
2391 if ((oper & Operator.ComparisonMask) != 0) {
2392 if (left.Equals (right)) {
2393 Report.Warning (1718, 3, loc, "A comparison made to same variable. Did you mean to compare something else?");
2395 CheckUselessComparison (lc, right.Type);
2396 CheckUselessComparison (rc, left.Type);
2399 if (RootContext.Version >= LanguageVersion.ISO_2 &&
2400 (TypeManager.IsNullableType (left.Type) || TypeManager.IsNullableType (right.Type) ||
2401 (left is NullLiteral && right.Type.IsValueType) || (right is NullLiteral && left.Type.IsValueType)))
2402 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2404 return DoResolveCore (ec, left, right);
2407 protected Expression DoResolveCore (EmitContext ec, Expression left_orig, Expression right_orig)
2409 Expression expr = ResolveOperator (ec);
2411 Error_OperatorCannotBeApplied (left_orig, right_orig);
2413 if (left == null || right == null)
2414 throw new InternalErrorException ("Invalid conversion");
2416 if (oper == Operator.BitwiseOr)
2417 CheckBitwiseOrOnSignExtended ();
2423 // D operator + (D x, D y)
2424 // D operator - (D x, D y)
2426 Expression ResolveOperatorDelegateBinary (EmitContext ec, Type l, Type r)
2428 if (((right.eclass == ExprClass.MethodGroup) || (r == TypeManager.anonymous_method_type))) {
2429 if ((RootContext.Version != LanguageVersion.ISO_1)) {
2430 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
2437 if (!TypeManager.IsEqual (l, r) && !(right is NullLiteral))
2442 ArrayList args = new ArrayList (2);
2444 args = new ArrayList (2);
2445 args.Add (new Argument (left, Argument.AType.Expression));
2446 args.Add (new Argument (right, Argument.AType.Expression));
2448 if (oper == Operator.Addition) {
2449 if (TypeManager.delegate_combine_delegate_delegate == null) {
2450 TypeManager.delegate_combine_delegate_delegate = TypeManager.GetPredefinedMethod (
2451 TypeManager.delegate_type, "Combine", loc, TypeManager.delegate_type, TypeManager.delegate_type);
2454 method = TypeManager.delegate_combine_delegate_delegate;
2456 if (TypeManager.delegate_remove_delegate_delegate == null) {
2457 TypeManager.delegate_remove_delegate_delegate = TypeManager.GetPredefinedMethod (
2458 TypeManager.delegate_type, "Remove", loc, TypeManager.delegate_type, TypeManager.delegate_type);
2461 method = TypeManager.delegate_remove_delegate_delegate;
2464 return new BinaryDelegate (l, method, args);
2468 // Enumeration operators
2470 Expression ResolveOperatorEnum (EmitContext ec, bool lenum, bool renum, Type ltype, Type rtype)
2474 if (lenum || renum) {
2476 // bool operator == (E x, E y);
2477 // bool operator != (E x, E y);
2478 // bool operator < (E x, E y);
2479 // bool operator > (E x, E y);
2480 // bool operator <= (E x, E y);
2481 // bool operator >= (E x, E y);
2483 if ((oper & Operator.ComparisonMask) != 0) {
2484 type = TypeManager.bool_type;
2485 } else if ((oper & Operator.BitwiseMask) != 0) {
2490 if (!TypeManager.IsEqual (ltype, rtype)) {
2492 temp = Convert.ImplicitConversion (ec, left, rtype, loc);
2497 temp = Convert.ImplicitConversion (ec, right, ltype, loc);
2508 Type underlying_type;
2509 if (lenum && !renum) {
2511 // E operator + (E e, U x)
2512 // E operator - (E e, U x)
2514 if (oper == Operator.Addition || oper == Operator.Subtraction) {
2515 underlying_type = TypeManager.GetEnumUnderlyingType (ltype);
2517 left = EmptyCast.Create (left, underlying_type, true);
2518 if (!DoBinaryOperatorPromotion (ec)) {
2523 enum_conversion = Convert.ExplicitNumericConversion (
2524 new EmptyExpression (left.Type), underlying_type);
2526 return ResolveOperatorPredefined (ec, standard_operators, true, ltype);
2534 // E operator + (U x, E e)
2536 if (oper == Operator.Addition) {
2537 underlying_type = TypeManager.GetEnumUnderlyingType (rtype);
2538 temp = Convert.ImplicitConversion (ec, left, underlying_type, loc);
2549 // U operator - (E e, E f)
2551 if (oper == Operator.Subtraction) {
2552 if (!TypeManager.IsEqual (ltype, rtype))
2555 type = TypeManager.GetEnumUnderlyingType (ltype);
2563 // 7.9.6 Reference type equality operators
2565 Binary ResolveOperatorEqualityRerefence (EmitContext ec, Type l, Type r)
2568 // operator != (object a, object b)
2569 // operator == (object a, object b)
2572 // TODO: this method is almost equivalent to Convert.ImplicitReferenceConversion
2574 if (left.eclass == ExprClass.MethodGroup || right.eclass == ExprClass.MethodGroup)
2577 type = TypeManager.bool_type;
2578 GenericConstraints constraints;
2580 bool lgen = TypeManager.IsGenericParameter (l);
2582 if (TypeManager.IsEqual (l, r)) {
2585 // Only allow to compare same reference type parameter
2587 constraints = TypeManager.GetTypeParameterConstraints (l);
2588 if (constraints != null && constraints.IsReferenceType)
2594 if (l == TypeManager.anonymous_method_type)
2597 if (TypeManager.IsValueType (l))
2603 bool rgen = TypeManager.IsGenericParameter (r);
2606 // a, Both operands are reference-type values or the value null
2607 // b, One operand is a value of type T where T is a type-parameter and
2608 // the other operand is the value null. Furthermore T does not have the
2609 // value type constrain
2611 if (left is NullLiteral || right is NullLiteral) {
2613 constraints = TypeManager.GetTypeParameterConstraints (l);
2614 if (constraints != null && constraints.HasValueTypeConstraint)
2617 left = new BoxedCast (left, TypeManager.object_type);
2622 constraints = TypeManager.GetTypeParameterConstraints (r);
2623 if (constraints != null && constraints.HasValueTypeConstraint)
2626 right = new BoxedCast (right, TypeManager.object_type);
2632 // An interface is converted to the object before the
2633 // standard conversion is applied. It's not clear from the
2634 // standard but it looks like it works like that.
2637 constraints = TypeManager.GetTypeParameterConstraints (l);
2638 if (constraints == null || constraints.IsReferenceType)
2640 } else if (l.IsInterface) {
2641 l = TypeManager.object_type;
2645 constraints = TypeManager.GetTypeParameterConstraints (r);
2646 if (constraints == null || constraints.IsReferenceType)
2648 } else if (r.IsInterface) {
2649 r = TypeManager.object_type;
2652 const string ref_comparison = "Possible unintended reference comparison. " +
2653 "Consider casting the {0} side of the expression to `string' to compare the values";
2656 // A standard implicit conversion exists from the type of either
2657 // operand to the type of the other operand
2659 if (Convert.ImplicitReferenceConversionExists (left, r)) {
2660 if (l == TypeManager.string_type)
2661 Report.Warning (253, 2, loc, ref_comparison, "right");
2666 if (Convert.ImplicitReferenceConversionExists (right, l)) {
2667 if (r == TypeManager.string_type)
2668 Report.Warning (252, 2, loc, ref_comparison, "left");
2677 Expression ResolveOperatorPointer (EmitContext ec, Type l, Type r)
2680 // bool operator == (void* x, void* y);
2681 // bool operator != (void* x, void* y);
2682 // bool operator < (void* x, void* y);
2683 // bool operator > (void* x, void* y);
2684 // bool operator <= (void* x, void* y);
2685 // bool operator >= (void* x, void* y);
2687 if ((oper & Operator.ComparisonMask) != 0) {
2690 temp = Convert.ImplicitConversion (ec, left, r, left.Location);
2697 temp = Convert.ImplicitConversion (ec, right, l, right.Location);
2703 type = TypeManager.bool_type;
2707 if (pointer_operators == null)
2708 CreatePointerOperatorsTable ();
2710 return ResolveOperatorPredefined (ec, pointer_operators, false, null);
2714 // Build-in operators method overloading
2716 protected virtual Expression ResolveOperatorPredefined (EmitContext ec, PredefinedOperator [] operators, bool primitives_only, Type enum_type)
2718 PredefinedOperator best_operator = null;
2720 Operator oper_mask = oper & ~Operator.ValuesOnlyMask;
2722 foreach (PredefinedOperator po in operators) {
2723 if ((po.OperatorsMask & oper_mask) == 0)
2726 if (primitives_only) {
2727 if (!po.IsPrimitiveApplicable (l))
2730 if (!po.IsApplicable (ec, left, right))
2734 if (best_operator == null) {
2736 if (primitives_only)
2742 best_operator = po.ResolveBetterOperator (ec, left, right, best_operator);
2744 if (best_operator == null) {
2745 Report.Error (34, loc, "Operator `{0}' is ambiguous on operands of type `{1}' and `{2}'",
2746 OperName (oper), left.GetSignatureForError (), right.GetSignatureForError ());
2753 if (best_operator == null)
2756 Expression expr = best_operator.ConvertResult (ec, this);
2757 if (enum_type == null)
2761 // HACK: required by enum_conversion
2763 expr.Type = enum_type;
2764 return EmptyCast.Create (expr, enum_type);
2768 // Performs user-operator overloading
2770 protected virtual Expression ResolveUserOperator (EmitContext ec, Type l, Type r)
2773 if (oper == Operator.LogicalAnd)
2774 user_oper = Operator.BitwiseAnd;
2775 else if (oper == Operator.LogicalOr)
2776 user_oper = Operator.BitwiseOr;
2780 string op = GetOperatorMetadataName (user_oper);
2782 MethodGroupExpr union;
2783 MethodGroupExpr left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc) as MethodGroupExpr;
2784 if (!TypeManager.IsEqual (r, l)) {
2785 MethodGroupExpr right_operators = MemberLookup (
2786 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc) as MethodGroupExpr;
2787 union = MethodGroupExpr.MakeUnionSet (left_operators, right_operators, loc);
2789 union = left_operators;
2794 ArrayList args = new ArrayList (2);
2795 Argument larg = new Argument (left);
2797 Argument rarg = new Argument (right);
2800 union = union.OverloadResolve (ec, ref args, true, loc);
2804 Expression oper_expr;
2806 // TODO: CreateExpressionTree is allocated every time
2807 if (user_oper != oper) {
2808 oper_expr = new ConditionalLogicalOperator (union, args, CreateExpressionTree,
2809 oper == Operator.LogicalAnd, loc).Resolve (ec);
2811 oper_expr = new UserOperatorCall (union, args, CreateExpressionTree, loc);
2814 // This is used to check if a test 'x == null' can be optimized to a reference equals,
2815 // and not invoke user operator
2817 if ((oper & Operator.EqualityMask) != 0) {
2818 if ((left is NullLiteral && IsBuildInEqualityOperator (r)) ||
2819 (right is NullLiteral && IsBuildInEqualityOperator (l))) {
2820 type = TypeManager.bool_type;
2821 if (left is NullLiteral || right is NullLiteral)
2822 oper_expr = ReducedExpression.Create (this, oper_expr).Resolve (ec);
2823 } else if (union.DeclaringType == TypeManager.delegate_type && l != r) {
2825 // Two System.Delegate(s) are never equal
2837 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2842 private void CheckUselessComparison (Constant c, Type type)
2844 if (c == null || !IsTypeIntegral (type)
2845 || c is StringConstant
2846 || c is BoolConstant
2847 || c is FloatConstant
2848 || c is DoubleConstant
2849 || c is DecimalConstant
2855 if (c is ULongConstant) {
2856 ulong uvalue = ((ULongConstant) c).Value;
2857 if (uvalue > long.MaxValue) {
2858 if (type == TypeManager.byte_type ||
2859 type == TypeManager.sbyte_type ||
2860 type == TypeManager.short_type ||
2861 type == TypeManager.ushort_type ||
2862 type == TypeManager.int32_type ||
2863 type == TypeManager.uint32_type ||
2864 type == TypeManager.int64_type ||
2865 type == TypeManager.char_type)
2866 WarnUselessComparison (type);
2869 value = (long) uvalue;
2871 else if (c is ByteConstant)
2872 value = ((ByteConstant) c).Value;
2873 else if (c is SByteConstant)
2874 value = ((SByteConstant) c).Value;
2875 else if (c is ShortConstant)
2876 value = ((ShortConstant) c).Value;
2877 else if (c is UShortConstant)
2878 value = ((UShortConstant) c).Value;
2879 else if (c is IntConstant)
2880 value = ((IntConstant) c).Value;
2881 else if (c is UIntConstant)
2882 value = ((UIntConstant) c).Value;
2883 else if (c is LongConstant)
2884 value = ((LongConstant) c).Value;
2885 else if (c is CharConstant)
2886 value = ((CharConstant)c).Value;
2891 if (IsValueOutOfRange (value, type))
2892 WarnUselessComparison (type);
2895 private bool IsValueOutOfRange (long value, Type type)
2897 if (IsTypeUnsigned (type) && value < 0)
2899 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2900 type == TypeManager.byte_type && value >= 0x100 ||
2901 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2902 type == TypeManager.ushort_type && value >= 0x10000 ||
2903 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2904 type == TypeManager.uint32_type && value >= 0x100000000;
2907 static bool IsBuildInEqualityOperator (Type t)
2909 return t == TypeManager.object_type || t == TypeManager.string_type ||
2910 t == TypeManager.delegate_type || TypeManager.IsDelegateType (t);
2913 private static bool IsTypeIntegral (Type type)
2915 return type == TypeManager.uint64_type ||
2916 type == TypeManager.int64_type ||
2917 type == TypeManager.uint32_type ||
2918 type == TypeManager.int32_type ||
2919 type == TypeManager.ushort_type ||
2920 type == TypeManager.short_type ||
2921 type == TypeManager.sbyte_type ||
2922 type == TypeManager.byte_type ||
2923 type == TypeManager.char_type;
2926 private static bool IsTypeUnsigned (Type type)
2928 return type == TypeManager.uint64_type ||
2929 type == TypeManager.uint32_type ||
2930 type == TypeManager.ushort_type ||
2931 type == TypeManager.byte_type ||
2932 type == TypeManager.char_type;
2935 private void WarnUselessComparison (Type type)
2937 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}'",
2938 TypeManager.CSharpName (type));
2942 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2943 /// context of a conditional bool expression. This function will return
2944 /// false if it is was possible to use EmitBranchable, or true if it was.
2946 /// The expression's code is generated, and we will generate a branch to `target'
2947 /// if the resulting expression value is equal to isTrue
2949 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
2951 ILGenerator ig = ec.ig;
2954 // This is more complicated than it looks, but its just to avoid
2955 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2956 // but on top of that we want for == and != to use a special path
2957 // if we are comparing against null
2959 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2960 bool my_on_true = oper == Operator.Inequality ? on_true : !on_true;
2963 // put the constant on the rhs, for simplicity
2965 if (left is Constant) {
2966 Expression swap = right;
2971 if (((Constant) right).IsZeroInteger) {
2972 left.EmitBranchable (ec, target, my_on_true);
2975 if (right.Type == TypeManager.bool_type) {
2976 // right is a boolean, and it's not 'false' => it is 'true'
2977 left.EmitBranchable (ec, target, !my_on_true);
2981 } else if (oper == Operator.LogicalAnd) {
2984 Label tests_end = ig.DefineLabel ();
2986 left.EmitBranchable (ec, tests_end, false);
2987 right.EmitBranchable (ec, target, true);
2988 ig.MarkLabel (tests_end);
2991 // This optimizes code like this
2992 // if (true && i > 4)
2994 if (!(left is Constant))
2995 left.EmitBranchable (ec, target, false);
2997 if (!(right is Constant))
2998 right.EmitBranchable (ec, target, false);
3003 } else if (oper == Operator.LogicalOr){
3005 left.EmitBranchable (ec, target, true);
3006 right.EmitBranchable (ec, target, true);
3009 Label tests_end = ig.DefineLabel ();
3010 left.EmitBranchable (ec, tests_end, true);
3011 right.EmitBranchable (ec, target, false);
3012 ig.MarkLabel (tests_end);
3017 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
3018 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
3019 oper == Operator.Equality || oper == Operator.Inequality)) {
3020 base.EmitBranchable (ec, target, on_true);
3028 bool is_unsigned = IsUnsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
3031 case Operator.Equality:
3033 ig.Emit (OpCodes.Beq, target);
3035 ig.Emit (OpCodes.Bne_Un, target);
3038 case Operator.Inequality:
3040 ig.Emit (OpCodes.Bne_Un, target);
3042 ig.Emit (OpCodes.Beq, target);
3045 case Operator.LessThan:
3048 ig.Emit (OpCodes.Blt_Un, target);
3050 ig.Emit (OpCodes.Blt, target);
3053 ig.Emit (OpCodes.Bge_Un, target);
3055 ig.Emit (OpCodes.Bge, target);
3058 case Operator.GreaterThan:
3061 ig.Emit (OpCodes.Bgt_Un, target);
3063 ig.Emit (OpCodes.Bgt, target);
3066 ig.Emit (OpCodes.Ble_Un, target);
3068 ig.Emit (OpCodes.Ble, target);
3071 case Operator.LessThanOrEqual:
3074 ig.Emit (OpCodes.Ble_Un, target);
3076 ig.Emit (OpCodes.Ble, target);
3079 ig.Emit (OpCodes.Bgt_Un, target);
3081 ig.Emit (OpCodes.Bgt, target);
3085 case Operator.GreaterThanOrEqual:
3088 ig.Emit (OpCodes.Bge_Un, target);
3090 ig.Emit (OpCodes.Bge, target);
3093 ig.Emit (OpCodes.Blt_Un, target);
3095 ig.Emit (OpCodes.Blt, target);
3098 throw new InternalErrorException (oper.ToString ());
3102 public override void Emit (EmitContext ec)
3104 EmitOperator (ec, left.Type);
3107 protected virtual void EmitOperator (EmitContext ec, Type l)
3109 ILGenerator ig = ec.ig;
3112 // Handle short-circuit operators differently
3115 if ((oper & Operator.LogicalMask) != 0) {
3116 Label load_result = ig.DefineLabel ();
3117 Label end = ig.DefineLabel ();
3119 bool is_or = oper == Operator.LogicalOr;
3120 left.EmitBranchable (ec, load_result, is_or);
3122 ig.Emit (OpCodes.Br_S, end);
3124 ig.MarkLabel (load_result);
3125 ig.Emit (is_or ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
3133 // Optimize zero-based operations
3135 // TODO: Implement more optimizations, but it should probably go to PredefinedOperators
3137 if ((oper & Operator.ShiftMask) != 0 || oper == Operator.Addition || oper == Operator.Subtraction) {
3138 Constant rc = right as Constant;
3139 if (rc != null && rc.IsDefaultValue) {
3149 case Operator.Multiply:
3151 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
3152 opcode = OpCodes.Mul_Ovf;
3153 else if (!IsFloat (l))
3154 opcode = OpCodes.Mul_Ovf_Un;
3156 opcode = OpCodes.Mul;
3158 opcode = OpCodes.Mul;
3162 case Operator.Division:
3164 opcode = OpCodes.Div_Un;
3166 opcode = OpCodes.Div;
3169 case Operator.Modulus:
3171 opcode = OpCodes.Rem_Un;
3173 opcode = OpCodes.Rem;
3176 case Operator.Addition:
3178 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
3179 opcode = OpCodes.Add_Ovf;
3180 else if (!IsFloat (l))
3181 opcode = OpCodes.Add_Ovf_Un;
3183 opcode = OpCodes.Add;
3185 opcode = OpCodes.Add;
3188 case Operator.Subtraction:
3190 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
3191 opcode = OpCodes.Sub_Ovf;
3192 else if (!IsFloat (l))
3193 opcode = OpCodes.Sub_Ovf_Un;
3195 opcode = OpCodes.Sub;
3197 opcode = OpCodes.Sub;
3200 case Operator.RightShift:
3202 opcode = OpCodes.Shr_Un;
3204 opcode = OpCodes.Shr;
3207 case Operator.LeftShift:
3208 opcode = OpCodes.Shl;
3211 case Operator.Equality:
3212 opcode = OpCodes.Ceq;
3215 case Operator.Inequality:
3216 ig.Emit (OpCodes.Ceq);
3217 ig.Emit (OpCodes.Ldc_I4_0);
3219 opcode = OpCodes.Ceq;
3222 case Operator.LessThan:
3224 opcode = OpCodes.Clt_Un;
3226 opcode = OpCodes.Clt;
3229 case Operator.GreaterThan:
3231 opcode = OpCodes.Cgt_Un;
3233 opcode = OpCodes.Cgt;
3236 case Operator.LessThanOrEqual:
3237 if (IsUnsigned (l) || IsFloat (l))
3238 ig.Emit (OpCodes.Cgt_Un);
3240 ig.Emit (OpCodes.Cgt);
3241 ig.Emit (OpCodes.Ldc_I4_0);
3243 opcode = OpCodes.Ceq;
3246 case Operator.GreaterThanOrEqual:
3247 if (IsUnsigned (l) || IsFloat (l))
3248 ig.Emit (OpCodes.Clt_Un);
3250 ig.Emit (OpCodes.Clt);
3252 ig.Emit (OpCodes.Ldc_I4_0);
3254 opcode = OpCodes.Ceq;
3257 case Operator.BitwiseOr:
3258 opcode = OpCodes.Or;
3261 case Operator.BitwiseAnd:
3262 opcode = OpCodes.And;
3265 case Operator.ExclusiveOr:
3266 opcode = OpCodes.Xor;
3270 throw new InternalErrorException (oper.ToString ());
3276 // Nullable enum could require underlying type cast and we cannot simply wrap binary
3277 // expression because that would wrap lifted binary operation
3279 if (enum_conversion != null)
3280 enum_conversion.Emit (ec);
3283 public override void EmitSideEffect (EmitContext ec)
3285 if ((oper & Operator.LogicalMask) != 0 ||
3286 (ec.CheckState && (oper == Operator.Multiply || oper == Operator.Addition || oper == Operator.Subtraction))) {
3287 base.EmitSideEffect (ec);
3289 left.EmitSideEffect (ec);
3290 right.EmitSideEffect (ec);
3294 protected override void CloneTo (CloneContext clonectx, Expression t)
3296 Binary target = (Binary) t;
3298 target.left = left.Clone (clonectx);
3299 target.right = right.Clone (clonectx);
3302 public override Expression CreateExpressionTree (EmitContext ec)
3304 return CreateExpressionTree (ec, null);
3307 Expression CreateExpressionTree (EmitContext ec, MethodGroupExpr method)
3310 bool lift_arg = false;
3313 case Operator.Addition:
3314 if (method == null && ec.CheckState && !IsFloat (type))
3315 method_name = "AddChecked";
3317 method_name = "Add";
3319 case Operator.BitwiseAnd:
3320 method_name = "And";
3322 case Operator.BitwiseOr:
3325 case Operator.Division:
3326 method_name = "Divide";
3328 case Operator.Equality:
3329 method_name = "Equal";
3332 case Operator.ExclusiveOr:
3333 method_name = "ExclusiveOr";
3335 case Operator.GreaterThan:
3336 method_name = "GreaterThan";
3339 case Operator.GreaterThanOrEqual:
3340 method_name = "GreaterThanOrEqual";
3343 case Operator.Inequality:
3344 method_name = "NotEqual";
3347 case Operator.LeftShift:
3348 method_name = "LeftShift";
3350 case Operator.LessThan:
3351 method_name = "LessThan";
3354 case Operator.LessThanOrEqual:
3355 method_name = "LessThanOrEqual";
3358 case Operator.LogicalAnd:
3359 method_name = "AndAlso";
3361 case Operator.LogicalOr:
3362 method_name = "OrElse";
3364 case Operator.Modulus:
3365 method_name = "Modulo";
3367 case Operator.Multiply:
3368 if (method == null && ec.CheckState && !IsFloat (type))
3369 method_name = "MultiplyChecked";
3371 method_name = "Multiply";
3373 case Operator.RightShift:
3374 method_name = "RightShift";
3376 case Operator.Subtraction:
3377 if (method == null && ec.CheckState && !IsFloat (type))
3378 method_name = "SubtractChecked";
3380 method_name = "Subtract";
3384 throw new InternalErrorException ("Unknown expression tree binary operator " + oper);
3387 ArrayList args = new ArrayList (2);
3388 args.Add (new Argument (left.CreateExpressionTree (ec)));
3389 args.Add (new Argument (right.CreateExpressionTree (ec)));
3390 if (method != null) {
3392 args.Add (new Argument (new BoolConstant (false, loc)));
3394 args.Add (new Argument (method.CreateExpressionTree (ec)));
3397 return CreateExpressionFactoryCall (method_name, args);
3402 // Object created by Binary when the binary operator uses an method instead of being
3403 // a binary operation that maps to a CIL binary operation.
3405 public class BinaryMethod : Expression {
3406 public MethodBase method;
3407 public ArrayList Arguments;
3409 public BinaryMethod (Type t, MethodBase m, ArrayList args)
3414 eclass = ExprClass.Value;
3417 public override Expression DoResolve (EmitContext ec)
3422 public override void Emit (EmitContext ec)
3424 ILGenerator ig = ec.ig;
3426 Invocation.EmitArguments (ec, Arguments, false, null);
3428 if (method is MethodInfo)
3429 ig.Emit (OpCodes.Call, (MethodInfo) method);
3431 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
3436 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
3437 // b, c, d... may be strings or objects.
3439 public class StringConcat : Expression {
3440 ArrayList arguments;
3442 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
3445 type = TypeManager.string_type;
3446 eclass = ExprClass.Value;
3448 arguments = new ArrayList (2);
3453 public override Expression CreateExpressionTree (EmitContext ec)
3455 Argument arg = (Argument) arguments [0];
3456 return CreateExpressionAddCall (ec, arg, arg.Expr.CreateExpressionTree (ec), 1);
3460 // Creates nested calls tree from an array of arguments used for IL emit
3462 Expression CreateExpressionAddCall (EmitContext ec, Argument left, Expression left_etree, int pos)
3464 ArrayList concat_args = new ArrayList (2);
3465 ArrayList add_args = new ArrayList (3);
3467 concat_args.Add (left);
3468 add_args.Add (new Argument (left_etree));
3470 concat_args.Add (arguments [pos]);
3471 add_args.Add (new Argument (((Argument) arguments [pos]).Expr.CreateExpressionTree (ec)));
3473 MethodGroupExpr method = CreateConcatMemberExpression ().Resolve (ec) as MethodGroupExpr;
3477 method = method.OverloadResolve (ec, ref concat_args, false, loc);
3481 add_args.Add (new Argument (method.CreateExpressionTree (ec)));
3483 Expression expr = CreateExpressionFactoryCall ("Add", add_args);
3484 if (++pos == arguments.Count)
3487 left = new Argument (new EmptyExpression (method.Type));
3488 return CreateExpressionAddCall (ec, left, expr, pos);
3491 public override Expression DoResolve (EmitContext ec)
3496 public void Append (EmitContext ec, Expression operand)
3501 StringConstant sc = operand as StringConstant;
3503 if (arguments.Count != 0) {
3504 Argument last_argument = (Argument) arguments [arguments.Count - 1];
3505 StringConstant last_expr_constant = last_argument.Expr as StringConstant;
3506 if (last_expr_constant != null) {
3507 last_argument.Expr = new StringConstant (
3508 last_expr_constant.Value + sc.Value, sc.Location);
3514 // Multiple (3+) concatenation are resolved as multiple StringConcat instances
3516 StringConcat concat_oper = operand as StringConcat;
3517 if (concat_oper != null) {
3518 arguments.AddRange (concat_oper.arguments);
3523 arguments.Add (new Argument (operand));
3526 Expression CreateConcatMemberExpression ()
3528 return new MemberAccess (new MemberAccess (new QualifiedAliasMember ("global", "System", loc), "String", loc), "Concat", loc);
3531 public override void Emit (EmitContext ec)
3533 Expression concat = new Invocation (CreateConcatMemberExpression (), arguments, true);
3534 concat = concat.Resolve (ec);
3541 // Object created with +/= on delegates
3543 public class BinaryDelegate : Expression {
3547 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
3552 eclass = ExprClass.Value;
3555 public override Expression DoResolve (EmitContext ec)
3560 public override void Emit (EmitContext ec)
3562 ILGenerator ig = ec.ig;
3564 Invocation.EmitArguments (ec, args, false, null);
3566 ig.Emit (OpCodes.Call, (MethodInfo) method);
3567 ig.Emit (OpCodes.Castclass, type);
3570 public Expression Right {
3572 Argument arg = (Argument) args [1];
3577 public bool IsAddition {
3579 return method == TypeManager.delegate_combine_delegate_delegate;
3585 // User-defined conditional logical operator
3587 public class ConditionalLogicalOperator : UserOperatorCall {
3588 readonly bool is_and;
3591 public ConditionalLogicalOperator (MethodGroupExpr oper_method, ArrayList arguments,
3592 ExpressionTreeExpression expr_tree, bool is_and, Location loc)
3593 : base (oper_method, arguments, expr_tree, loc)
3595 this.is_and = is_and;
3598 public override Expression DoResolve (EmitContext ec)
3600 MethodInfo method = (MethodInfo)mg;
3601 type = TypeManager.TypeToCoreType (method.ReturnType);
3602 ParameterData pd = TypeManager.GetParameterData (method);
3603 if (!TypeManager.IsEqual (type, type) || !TypeManager.IsEqual (type, pd.Types [0]) || !TypeManager.IsEqual (type, pd.Types [1])) {
3604 Report.Error (217, loc,
3605 "A user-defined operator `{0}' must have parameters and return values of the same type in order to be applicable as a short circuit operator",
3606 TypeManager.CSharpSignature (method));
3610 Expression left_dup = new EmptyExpression (type);
3611 Expression op_true = GetOperatorTrue (ec, left_dup, loc);
3612 Expression op_false = GetOperatorFalse (ec, left_dup, loc);
3613 if (op_true == null || op_false == null) {
3614 Report.Error (218, loc,
3615 "The type `{0}' must have operator `true' and operator `false' defined when `{1}' is used as a short circuit operator",
3616 TypeManager.CSharpName (type), TypeManager.CSharpSignature (method));
3620 oper = is_and ? op_false : op_true;
3621 eclass = ExprClass.Value;
3625 public override void Emit (EmitContext ec)
3627 ILGenerator ig = ec.ig;
3628 Label end_target = ig.DefineLabel ();
3631 // Emit and duplicate left argument
3633 ((Argument)arguments [0]).Expr.Emit (ec);
3634 ig.Emit (OpCodes.Dup);
3635 arguments.RemoveAt (0);
3637 oper.EmitBranchable (ec, end_target, true);
3639 ig.MarkLabel (end_target);
3643 public class PointerArithmetic : Expression {
3644 Expression left, right;
3648 // We assume that `l' is always a pointer
3650 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3656 is_add = is_addition;
3659 public override Expression DoResolve (EmitContext ec)
3661 eclass = ExprClass.Variable;
3663 if (left.Type == TypeManager.void_ptr_type) {
3664 Error (242, "The operation in question is undefined on void pointers");
3671 public override void Emit (EmitContext ec)
3673 Type op_type = left.Type;
3674 ILGenerator ig = ec.ig;
3676 // It must be either array or fixed buffer
3677 Type element = TypeManager.HasElementType (op_type) ?
3678 element = TypeManager.GetElementType (op_type) :
3679 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3681 int size = GetTypeSize (element);
3682 Type rtype = right.Type;
3684 if (rtype.IsPointer){
3686 // handle (pointer - pointer)
3690 ig.Emit (OpCodes.Sub);
3694 ig.Emit (OpCodes.Sizeof, element);
3696 IntLiteral.EmitInt (ig, size);
3697 ig.Emit (OpCodes.Div);
3699 ig.Emit (OpCodes.Conv_I8);
3702 // handle + and - on (pointer op int)
3705 ig.Emit (OpCodes.Conv_I);
3707 Constant right_const = right as Constant;
3708 if (right_const != null && size != 0) {
3709 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3717 ig.Emit (OpCodes.Sizeof, element);
3719 IntLiteral.EmitInt (ig, size);
3720 if (rtype == TypeManager.int64_type)
3721 ig.Emit (OpCodes.Conv_I8);
3722 else if (rtype == TypeManager.uint64_type)
3723 ig.Emit (OpCodes.Conv_U8);
3724 ig.Emit (OpCodes.Mul);
3728 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3729 ig.Emit (OpCodes.Conv_I);
3732 ig.Emit (OpCodes.Add);
3734 ig.Emit (OpCodes.Sub);
3740 /// Implements the ternary conditional operator (?:)
3742 public class Conditional : Expression {
3743 Expression expr, true_expr, false_expr;
3745 public Conditional (Expression expr, Expression true_expr, Expression false_expr)
3748 this.true_expr = true_expr;
3749 this.false_expr = false_expr;
3750 this.loc = expr.Location;
3753 public Expression Expr {
3759 public Expression TrueExpr {
3765 public Expression FalseExpr {
3771 public override Expression CreateExpressionTree (EmitContext ec)
3773 ArrayList args = new ArrayList (3);
3774 args.Add (new Argument (expr.CreateExpressionTree (ec)));
3775 args.Add (new Argument (true_expr.CreateExpressionTree (ec)));
3776 args.Add (new Argument (false_expr.CreateExpressionTree (ec)));
3777 return CreateExpressionFactoryCall ("Condition", args);
3780 public override Expression DoResolve (EmitContext ec)
3782 expr = expr.Resolve (ec);
3787 if (expr.Type != TypeManager.bool_type){
3788 expr = Expression.ResolveBoolean (
3795 Assign ass = expr as Assign;
3796 if (ass != null && ass.Source is Constant) {
3797 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3800 true_expr = true_expr.Resolve (ec);
3801 false_expr = false_expr.Resolve (ec);
3803 if (true_expr == null || false_expr == null)
3806 eclass = ExprClass.Value;
3807 if (true_expr.Type == false_expr.Type) {
3808 type = true_expr.Type;
3809 if (type == TypeManager.null_type) {
3810 // TODO: probably will have to implement ConditionalConstant
3811 // to call method without return constant as well
3812 Report.Warning (-101, 1, loc, "Conditional expression will always return same value");
3817 Type true_type = true_expr.Type;
3818 Type false_type = false_expr.Type;
3821 // First, if an implicit conversion exists from true_expr
3822 // to false_expr, then the result type is of type false_expr.Type
3824 conv = Convert.ImplicitConversion (ec, true_expr, false_type, loc);
3827 // Check if both can convert implicitl to each other's type
3829 if (Convert.ImplicitConversion (ec, false_expr, true_type, loc) != null){
3831 "Can not compute type of conditional expression " +
3832 "as `" + TypeManager.CSharpName (true_expr.Type) +
3833 "' and `" + TypeManager.CSharpName (false_expr.Type) +
3834 "' convert implicitly to each other");
3839 } else if ((conv = Convert.ImplicitConversion(ec, false_expr, true_type,loc))!= null){
3843 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3844 true_expr.GetSignatureForError (), false_expr.GetSignatureForError ());
3849 // Dead code optimalization
3850 if (expr is BoolConstant){
3851 BoolConstant bc = (BoolConstant) expr;
3853 Report.Warning (429, 4, bc.Value ? false_expr.Location : true_expr.Location, "Unreachable expression code detected");
3854 return bc.Value ? true_expr : false_expr;
3860 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3865 public override void Emit (EmitContext ec)
3867 ILGenerator ig = ec.ig;
3868 Label false_target = ig.DefineLabel ();
3869 Label end_target = ig.DefineLabel ();
3871 expr.EmitBranchable (ec, false_target, false);
3872 true_expr.Emit (ec);
3874 if (type.IsInterface) {
3875 LocalBuilder temp = ec.GetTemporaryLocal (type);
3876 ig.Emit (OpCodes.Stloc, temp);
3877 ig.Emit (OpCodes.Ldloc, temp);
3878 ec.FreeTemporaryLocal (temp, type);
3881 ig.Emit (OpCodes.Br, end_target);
3882 ig.MarkLabel (false_target);
3883 false_expr.Emit (ec);
3884 ig.MarkLabel (end_target);
3887 protected override void CloneTo (CloneContext clonectx, Expression t)
3889 Conditional target = (Conditional) t;
3891 target.expr = expr.Clone (clonectx);
3892 target.true_expr = true_expr.Clone (clonectx);
3893 target.false_expr = false_expr.Clone (clonectx);
3897 public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation {
3899 LocalTemporary temp;
3901 public abstract Variable Variable {
3905 public abstract bool IsRef {
3909 public override void Emit (EmitContext ec)
3914 public override void EmitSideEffect (EmitContext ec)
3920 // This method is used by parameters that are references, that are
3921 // being passed as references: we only want to pass the pointer (that
3922 // is already stored in the parameter, not the address of the pointer,
3923 // and not the value of the variable).
3925 public void EmitLoad (EmitContext ec)
3927 Report.Debug (64, "VARIABLE EMIT LOAD", this, Variable, type, loc);
3929 Variable.EmitInstance (ec);
3933 public void Emit (EmitContext ec, bool leave_copy)
3935 Report.Debug (64, "VARIABLE EMIT", this, Variable, type, IsRef, loc);
3941 // If we are a reference, we loaded on the stack a pointer
3942 // Now lets load the real value
3944 LoadFromPtr (ec.ig, type);
3948 ec.ig.Emit (OpCodes.Dup);
3950 if (IsRef || Variable.NeedsTemporary) {
3951 temp = new LocalTemporary (Type);
3957 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
3958 bool prepare_for_load)
3960 Report.Debug (64, "VARIABLE EMIT ASSIGN", this, Variable, type, IsRef,
3963 ILGenerator ig = ec.ig;
3964 prepared = prepare_for_load;
3966 Variable.EmitInstance (ec);
3967 if (prepare_for_load) {
3968 if (Variable.HasInstance)
3969 ig.Emit (OpCodes.Dup);
3977 // HACK: variable is already emitted when source is an initializer
3978 if (source is NewInitialize) {
3980 Variable.EmitInstance (ec);
3987 ig.Emit (OpCodes.Dup);
3988 if (IsRef || Variable.NeedsTemporary) {
3989 temp = new LocalTemporary (Type);
3995 StoreFromPtr (ig, type);
3997 Variable.EmitAssign (ec);
4005 public void AddressOf (EmitContext ec, AddressOp mode)
4007 Variable.EmitInstance (ec);
4008 Variable.EmitAddressOf (ec);
4015 public class LocalVariableReference : VariableReference, IVariable {
4016 public readonly string Name;
4018 public LocalInfo local_info;
4022 public LocalVariableReference (Block block, string name, Location l)
4027 eclass = ExprClass.Variable;
4031 // Setting `is_readonly' to false will allow you to create a writable
4032 // reference to a read-only variable. This is used by foreach and using.
4034 public LocalVariableReference (Block block, string name, Location l,
4035 LocalInfo local_info, bool is_readonly)
4036 : this (block, name, l)
4038 this.local_info = local_info;
4039 this.is_readonly = is_readonly;
4042 public VariableInfo VariableInfo {
4043 get { return local_info.VariableInfo; }
4046 public override bool IsRef {
4047 get { return false; }
4050 public bool IsReadOnly {
4051 get { return is_readonly; }
4054 public bool VerifyAssigned (EmitContext ec)
4056 VariableInfo variable_info = local_info.VariableInfo;
4057 return variable_info == null || variable_info.IsAssigned (ec, loc);
4060 void ResolveLocalInfo ()
4062 if (local_info == null) {
4063 local_info = Block.GetLocalInfo (Name);
4064 type = local_info.VariableType;
4065 is_readonly = local_info.ReadOnly;
4069 public override Expression CreateExpressionTree (EmitContext ec)
4071 ArrayList arg = new ArrayList (1);
4072 arg.Add (new Argument (this));
4073 return CreateExpressionFactoryCall ("Constant", arg);
4076 protected Expression DoResolveBase (EmitContext ec)
4078 type = local_info.VariableType;
4080 Expression e = Block.GetConstantExpression (Name);
4082 return e.Resolve (ec);
4084 if (!VerifyAssigned (ec))
4088 // If we are referencing a variable from the external block
4089 // flag it for capturing
4091 if (ec.MustCaptureVariable (local_info)) {
4092 if (local_info.AddressTaken){
4093 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
4097 if (!ec.IsInProbingMode)
4099 ScopeInfo scope = local_info.Block.CreateScopeInfo ();
4100 variable = scope.AddLocal (local_info);
4101 type = variable.Type;
4108 public override Expression DoResolve (EmitContext ec)
4110 ResolveLocalInfo ();
4111 local_info.Used = true;
4113 if (type == null && local_info.Type is VarExpr) {
4114 local_info.VariableType = TypeManager.object_type;
4115 Error_VariableIsUsedBeforeItIsDeclared (Name);
4119 return DoResolveBase (ec);
4122 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
4124 ResolveLocalInfo ();
4127 if (right_side == EmptyExpression.OutAccess)
4128 local_info.Used = true;
4130 // Infer implicitly typed local variable
4132 VarExpr ve = local_info.Type as VarExpr;
4134 if (!ve.InferType (ec, right_side))
4136 type = local_info.VariableType = ve.Type;
4143 if (right_side == EmptyExpression.OutAccess) {
4144 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
4145 } else if (right_side == EmptyExpression.LValueMemberAccess) {
4146 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
4147 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
4148 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
4150 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
4152 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
4156 if (VariableInfo != null)
4157 VariableInfo.SetAssigned (ec);
4159 return DoResolveBase (ec);
4162 public bool VerifyFixed ()
4164 // A local Variable is always fixed.
4168 public override int GetHashCode ()
4170 return Name.GetHashCode ();
4173 public override bool Equals (object obj)
4175 LocalVariableReference lvr = obj as LocalVariableReference;
4179 return Name == lvr.Name && Block == lvr.Block;
4182 public override Variable Variable {
4183 get { return variable != null ? variable : local_info.Variable; }
4186 public override string ToString ()
4188 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
4191 protected override void CloneTo (CloneContext clonectx, Expression t)
4193 LocalVariableReference target = (LocalVariableReference) t;
4195 target.Block = clonectx.LookupBlock (Block);
4196 if (local_info != null)
4197 target.local_info = clonectx.LookupVariable (local_info);
4202 /// This represents a reference to a parameter in the intermediate
4205 public class ParameterReference : VariableReference, IVariable {
4206 readonly ToplevelParameterInfo pi;
4207 readonly ToplevelBlock referenced;
4210 public bool is_ref, is_out;
4213 get { return is_out; }
4216 public override bool IsRef {
4217 get { return is_ref; }
4220 public string Name {
4221 get { return Parameter.Name; }
4224 public Parameter Parameter {
4225 get { return pi.Parameter; }
4228 public ParameterReference (ToplevelBlock referenced, ToplevelParameterInfo pi, Location loc)
4231 this.referenced = referenced;
4233 eclass = ExprClass.Variable;
4236 public VariableInfo VariableInfo {
4237 get { return pi.VariableInfo; }
4240 public override Variable Variable {
4241 get { return variable != null ? variable : Parameter.Variable; }
4244 public bool VerifyFixed ()
4246 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
4247 return Parameter.ModFlags == Parameter.Modifier.NONE;
4250 public bool IsAssigned (EmitContext ec, Location loc)
4252 // HACK: Variables are not captured in probing mode
4253 if (ec.IsInProbingMode)
4256 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (VariableInfo))
4259 Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
4263 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
4265 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (VariableInfo, field_name))
4268 Report.Error (170, loc, "Use of possibly unassigned field `{0}'", field_name);
4272 public void SetAssigned (EmitContext ec)
4274 if (is_out && ec.DoFlowAnalysis)
4275 ec.CurrentBranching.SetAssigned (VariableInfo);
4278 public void SetFieldAssigned (EmitContext ec, string field_name)
4280 if (is_out && ec.DoFlowAnalysis)
4281 ec.CurrentBranching.SetFieldAssigned (VariableInfo, field_name);
4284 protected bool DoResolveBase (EmitContext ec)
4286 Parameter par = Parameter;
4287 if (!par.Resolve (ec)) {
4291 type = par.ParameterType;
4292 Parameter.Modifier mod = par.ModFlags;
4293 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
4294 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
4295 eclass = ExprClass.Variable;
4297 AnonymousContainer am = ec.CurrentAnonymousMethod;
4301 ToplevelBlock declared = pi.Block;
4302 if (is_ref && declared != referenced) {
4303 Report.Error (1628, Location,
4304 "Cannot use ref or out parameter `{0}' inside an " +
4305 "anonymous method block", par.Name);
4309 if (!am.IsIterator && declared == referenced)
4312 // Don't capture aruments when the probing is on
4313 if (!ec.IsInProbingMode) {
4314 ScopeInfo scope = declared.CreateScopeInfo ();
4315 variable = scope.AddParameter (par, pi.Index);
4316 type = variable.Type;
4321 public override int GetHashCode ()
4323 return Name.GetHashCode ();
4326 public override bool Equals (object obj)
4328 ParameterReference pr = obj as ParameterReference;
4332 return Name == pr.Name && referenced == pr.referenced;
4335 public override Expression CreateExpressionTree (EmitContext ec)
4337 return Parameter.ExpressionTreeVariableReference ();
4341 // Notice that for ref/out parameters, the type exposed is not the
4342 // same type exposed externally.
4345 // externally we expose "int&"
4346 // here we expose "int".
4348 // We record this in "is_ref". This means that the type system can treat
4349 // the type as it is expected, but when we generate the code, we generate
4350 // the alternate kind of code.
4352 public override Expression DoResolve (EmitContext ec)
4354 if (!DoResolveBase (ec))
4357 if (is_out && ec.DoFlowAnalysis &&
4358 (!ec.OmitStructFlowAnalysis || !VariableInfo.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
4364 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
4366 if (!DoResolveBase (ec))
4369 // HACK: parameters are not captured when probing is on
4370 if (!ec.IsInProbingMode)
4376 static public void EmitLdArg (ILGenerator ig, int x)
4380 case 0: ig.Emit (OpCodes.Ldarg_0); break;
4381 case 1: ig.Emit (OpCodes.Ldarg_1); break;
4382 case 2: ig.Emit (OpCodes.Ldarg_2); break;
4383 case 3: ig.Emit (OpCodes.Ldarg_3); break;
4384 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
4387 ig.Emit (OpCodes.Ldarg, x);
4390 public override string ToString ()
4392 return "ParameterReference[" + Name + "]";
4397 /// Used for arguments to New(), Invocation()
4399 public class Argument {
4400 public enum AType : byte {
4407 public static readonly Argument[] Empty = new Argument [0];
4409 public readonly AType ArgType;
4410 public Expression Expr;
4412 public Argument (Expression expr, AType type)
4415 this.ArgType = type;
4418 public Argument (Expression expr)
4421 this.ArgType = AType.Expression;
4426 if (ArgType == AType.Ref || ArgType == AType.Out)
4427 return TypeManager.GetReferenceType (Expr.Type);
4433 public Parameter.Modifier Modifier
4438 return Parameter.Modifier.OUT;
4441 return Parameter.Modifier.REF;
4444 return Parameter.Modifier.NONE;
4449 public string GetSignatureForError ()
4451 if (Expr.eclass == ExprClass.MethodGroup)
4452 return Expr.ExprClassName;
4454 return Expr.GetSignatureForError ();
4457 public bool ResolveMethodGroup (EmitContext ec)
4459 SimpleName sn = Expr as SimpleName;
4461 Expr = sn.GetMethodGroup ();
4463 // FIXME: csc doesn't report any error if you try to use `ref' or
4464 // `out' in a delegate creation expression.
4465 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4472 public bool Resolve (EmitContext ec, Location loc)
4474 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
4475 // Verify that the argument is readable
4476 if (ArgType != AType.Out)
4477 Expr = Expr.Resolve (ec);
4479 // Verify that the argument is writeable
4480 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
4481 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
4483 return Expr != null;
4487 public void Emit (EmitContext ec)
4489 if (ArgType != AType.Ref && ArgType != AType.Out) {
4494 AddressOp mode = AddressOp.Store;
4495 if (ArgType == AType.Ref)
4496 mode |= AddressOp.Load;
4498 IMemoryLocation ml = (IMemoryLocation) Expr;
4499 ParameterReference pr = ml as ParameterReference;
4502 // ParameterReferences might already be references, so we want
4503 // to pass just the value
4505 if (pr != null && pr.IsRef)
4508 ml.AddressOf (ec, mode);
4511 public Argument Clone (CloneContext clonectx)
4513 return new Argument (Expr.Clone (clonectx), ArgType);
4518 /// Invocation of methods or delegates.
4520 public class Invocation : ExpressionStatement {
4521 protected ArrayList Arguments;
4522 protected Expression expr;
4523 protected MethodGroupExpr mg;
4524 bool arguments_resolved;
4527 // arguments is an ArrayList, but we do not want to typecast,
4528 // as it might be null.
4530 public Invocation (Expression expr, ArrayList arguments)
4532 SimpleName sn = expr as SimpleName;
4534 this.expr = sn.GetMethodGroup ();
4538 Arguments = arguments;
4540 loc = expr.Location;
4543 public Invocation (Expression expr, ArrayList arguments, bool arguments_resolved)
4544 : this (expr, arguments)
4546 this.arguments_resolved = arguments_resolved;
4549 public override Expression CreateExpressionTree (EmitContext ec)
4554 // Special conversion for nested expression trees
4556 if (TypeManager.DropGenericTypeArguments (type) == TypeManager.expression_type) {
4557 args = new ArrayList (1);
4558 args.Add (new Argument (this));
4559 return CreateExpressionFactoryCall ("Quote", args);
4562 args = new ArrayList (Arguments == null ? 2 : Arguments.Count + 2);
4564 args.Add (new Argument (mg.InstanceExpression.CreateExpressionTree (ec)));
4566 args.Add (new Argument (new NullLiteral (loc)));
4568 args.Add (new Argument (mg.CreateExpressionTree (ec)));
4569 if (Arguments != null) {
4570 foreach (Argument a in Arguments) {
4571 Expression e = a.Expr.CreateExpressionTree (ec);
4573 args.Add (new Argument (e));
4577 return CreateExpressionFactoryCall ("Call", args);
4580 public override Expression DoResolve (EmitContext ec)
4582 // Don't resolve already resolved expression
4583 if (eclass != ExprClass.Invalid)
4586 Expression expr_resolved = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4587 if (expr_resolved == null)
4590 mg = expr_resolved as MethodGroupExpr;
4592 Type expr_type = expr_resolved.Type;
4594 if (expr_type != null && TypeManager.IsDelegateType (expr_type)){
4595 return (new DelegateInvocation (
4596 expr_resolved, Arguments, loc)).Resolve (ec);
4599 MemberExpr me = expr_resolved as MemberExpr;
4601 expr_resolved.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4605 mg = ec.TypeContainer.LookupExtensionMethod (me.Type, me.Name, loc);
4607 Report.Error (1955, loc, "The member `{0}' cannot be used as method or delegate",
4608 expr_resolved.GetSignatureForError ());
4612 ((ExtensionMethodGroupExpr)mg).ExtensionExpression = me.InstanceExpression;
4616 // Next, evaluate all the expressions in the argument list
4618 if (Arguments != null && !arguments_resolved) {
4619 for (int i = 0; i < Arguments.Count; ++i)
4621 if (!((Argument)Arguments[i]).Resolve(ec, loc))
4626 mg = DoResolveOverload (ec);
4630 MethodInfo method = (MethodInfo)mg;
4631 if (method != null) {
4632 type = TypeManager.TypeToCoreType (method.ReturnType);
4634 // TODO: this is a copy of mg.ResolveMemberAccess method
4635 Expression iexpr = mg.InstanceExpression;
4636 if (method.IsStatic) {
4637 if (iexpr == null ||
4638 iexpr is This || iexpr is EmptyExpression ||
4639 mg.IdenticalTypeName) {
4640 mg.InstanceExpression = null;
4642 MemberExpr.error176 (loc, mg.GetSignatureForError ());
4648 if (type.IsPointer){
4656 // Only base will allow this invocation to happen.
4658 if (mg.IsBase && method.IsAbstract){
4659 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
4663 if (Arguments == null && method.DeclaringType == TypeManager.object_type && method.Name == "Finalize") {
4665 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
4667 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
4671 if (IsSpecialMethodInvocation (method)) {
4675 if (mg.InstanceExpression != null)
4676 mg.InstanceExpression.CheckMarshalByRefAccess (ec);
4678 eclass = ExprClass.Value;
4682 protected virtual MethodGroupExpr DoResolveOverload (EmitContext ec)
4684 return mg.OverloadResolve (ec, ref Arguments, false, loc);
4687 bool IsSpecialMethodInvocation (MethodBase method)
4689 if (!TypeManager.IsSpecialMethod (method))
4692 Report.SymbolRelatedToPreviousError (method);
4693 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
4694 TypeManager.CSharpSignature (method, true));
4700 /// Emits a list of resolved Arguments that are in the arguments
4703 /// The MethodBase argument might be null if the
4704 /// emission of the arguments is known not to contain
4705 /// a `params' field (for example in constructors or other routines
4706 /// that keep their arguments in this structure)
4708 /// if `dup_args' is true, a copy of the arguments will be left
4709 /// on the stack. If `dup_args' is true, you can specify `this_arg'
4710 /// which will be duplicated before any other args. Only EmitCall
4711 /// should be using this interface.
4713 public static void EmitArguments (EmitContext ec, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
4715 if (arguments == null)
4718 int top = arguments.Count;
4719 LocalTemporary [] temps = null;
4721 if (dup_args && top != 0)
4722 temps = new LocalTemporary [top];
4724 int argument_index = 0;
4726 for (int i = 0; i < top; i++) {
4727 a = (Argument) arguments [argument_index++];
4730 ec.ig.Emit (OpCodes.Dup);
4731 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
4736 if (this_arg != null)
4739 for (int i = 0; i < top; i ++) {
4740 temps [i].Emit (ec);
4741 temps [i].Release (ec);
4746 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
4748 ParameterData pd = TypeManager.GetParameterData (mb);
4750 if (arguments == null)
4751 return new Type [0];
4753 Argument a = (Argument) arguments [pd.Count - 1];
4754 Arglist list = (Arglist) a.Expr;
4756 return list.ArgumentTypes;
4760 /// This checks the ConditionalAttribute on the method
4762 public static bool IsMethodExcluded (MethodBase method)
4764 if (method.IsConstructor)
4767 method = TypeManager.DropGenericMethodArguments (method);
4768 if (method.DeclaringType.Module == CodeGen.Module.Builder) {
4769 IMethodData md = TypeManager.GetMethod (method);
4771 return md.IsExcluded ();
4773 // For some methods (generated by delegate class) GetMethod returns null
4774 // because they are not included in builder_to_method table
4778 return AttributeTester.IsConditionalMethodExcluded (method);
4782 /// is_base tells whether we want to force the use of the `call'
4783 /// opcode instead of using callvirt. Call is required to call
4784 /// a specific method, while callvirt will always use the most
4785 /// recent method in the vtable.
4787 /// is_static tells whether this is an invocation on a static method
4789 /// instance_expr is an expression that represents the instance
4790 /// it must be non-null if is_static is false.
4792 /// method is the method to invoke.
4794 /// Arguments is the list of arguments to pass to the method or constructor.
4796 public static void EmitCall (EmitContext ec, bool is_base,
4797 Expression instance_expr,
4798 MethodBase method, ArrayList Arguments, Location loc)
4800 EmitCall (ec, is_base, instance_expr, method, Arguments, loc, false, false);
4803 // `dup_args' leaves an extra copy of the arguments on the stack
4804 // `omit_args' does not leave any arguments at all.
4805 // So, basically, you could make one call with `dup_args' set to true,
4806 // and then another with `omit_args' set to true, and the two calls
4807 // would have the same set of arguments. However, each argument would
4808 // only have been evaluated once.
4809 public static void EmitCall (EmitContext ec, bool is_base,
4810 Expression instance_expr,
4811 MethodBase method, ArrayList Arguments, Location loc,
4812 bool dup_args, bool omit_args)
4814 ILGenerator ig = ec.ig;
4815 bool struct_call = false;
4816 bool this_call = false;
4817 LocalTemporary this_arg = null;
4819 Type decl_type = method.DeclaringType;
4821 if (!ec.IsInObsoleteScope) {
4823 // This checks ObsoleteAttribute on the method and on the declaring type
4825 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
4827 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
4829 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
4831 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
4835 if (IsMethodExcluded (method))
4838 bool is_static = method.IsStatic;
4840 if (instance_expr == EmptyExpression.Null) {
4841 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
4845 this_call = instance_expr is This;
4846 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
4850 // If this is ourselves, push "this"
4854 Type iexpr_type = instance_expr.Type;
4857 // Push the instance expression
4859 if (TypeManager.IsValueType (iexpr_type)) {
4861 // Special case: calls to a function declared in a
4862 // reference-type with a value-type argument need
4863 // to have their value boxed.
4864 if (decl_type.IsValueType ||
4865 TypeManager.IsGenericParameter (iexpr_type)) {
4867 // If the expression implements IMemoryLocation, then
4868 // we can optimize and use AddressOf on the
4871 // If not we have to use some temporary storage for
4873 if (instance_expr is IMemoryLocation) {
4874 ((IMemoryLocation)instance_expr).
4875 AddressOf (ec, AddressOp.LoadStore);
4877 LocalTemporary temp = new LocalTemporary (iexpr_type);
4878 instance_expr.Emit (ec);
4880 temp.AddressOf (ec, AddressOp.Load);
4883 // avoid the overhead of doing this all the time.
4885 t = TypeManager.GetReferenceType (iexpr_type);
4887 instance_expr.Emit (ec);
4888 ig.Emit (OpCodes.Box, instance_expr.Type);
4889 t = TypeManager.object_type;
4892 instance_expr.Emit (ec);
4893 t = instance_expr.Type;
4897 ig.Emit (OpCodes.Dup);
4898 if (Arguments != null && Arguments.Count != 0) {
4899 this_arg = new LocalTemporary (t);
4900 this_arg.Store (ec);
4907 EmitArguments (ec, Arguments, dup_args, this_arg);
4910 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
4911 ig.Emit (OpCodes.Constrained, instance_expr.Type);
4915 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
4916 call_op = OpCodes.Call;
4918 call_op = OpCodes.Callvirt;
4920 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
4921 Type[] varargs_types = GetVarargsTypes (method, Arguments);
4922 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
4929 // and DoFoo is not virtual, you can omit the callvirt,
4930 // because you don't need the null checking behavior.
4932 if (method is MethodInfo)
4933 ig.Emit (call_op, (MethodInfo) method);
4935 ig.Emit (call_op, (ConstructorInfo) method);
4938 public override void Emit (EmitContext ec)
4940 mg.EmitCall (ec, Arguments);
4943 public override void EmitStatement (EmitContext ec)
4948 // Pop the return value if there is one
4950 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
4951 ec.ig.Emit (OpCodes.Pop);
4954 protected override void CloneTo (CloneContext clonectx, Expression t)
4956 Invocation target = (Invocation) t;
4958 if (Arguments != null) {
4959 target.Arguments = new ArrayList (Arguments.Count);
4960 foreach (Argument a in Arguments)
4961 target.Arguments.Add (a.Clone (clonectx));
4964 target.expr = expr.Clone (clonectx);
4968 public class InvocationOrCast : ExpressionStatement
4971 Expression argument;
4973 public InvocationOrCast (Expression expr, Expression argument)
4976 this.argument = argument;
4977 this.loc = expr.Location;
4980 public override Expression DoResolve (EmitContext ec)
4983 // First try to resolve it as a cast.
4985 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
4986 if ((te != null) && (te.eclass == ExprClass.Type)) {
4987 Cast cast = new Cast (te, argument, loc);
4988 return cast.Resolve (ec);
4992 // This can either be a type or a delegate invocation.
4993 // Let's just resolve it and see what we'll get.
4995 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5000 // Ok, so it's a Cast.
5002 if (expr.eclass == ExprClass.Type) {
5003 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5004 return cast.Resolve (ec);
5008 // It's a delegate invocation.
5010 if (!TypeManager.IsDelegateType (expr.Type)) {
5011 Error (149, "Method name expected");
5015 ArrayList args = new ArrayList ();
5016 args.Add (new Argument (argument, Argument.AType.Expression));
5017 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5018 return invocation.Resolve (ec);
5021 public override ExpressionStatement ResolveStatement (EmitContext ec)
5024 // First try to resolve it as a cast.
5026 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5027 if ((te != null) && (te.eclass == ExprClass.Type)) {
5028 Error_InvalidExpressionStatement ();
5033 // This can either be a type or a delegate invocation.
5034 // Let's just resolve it and see what we'll get.
5036 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5037 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5038 Error_InvalidExpressionStatement ();
5043 // It's a delegate invocation.
5045 if (!TypeManager.IsDelegateType (expr.Type)) {
5046 Error (149, "Method name expected");
5050 ArrayList args = new ArrayList ();
5051 args.Add (new Argument (argument, Argument.AType.Expression));
5052 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5053 return invocation.ResolveStatement (ec);
5056 public override void Emit (EmitContext ec)
5058 throw new Exception ("Cannot happen");
5061 public override void EmitStatement (EmitContext ec)
5063 throw new Exception ("Cannot happen");
5066 protected override void CloneTo (CloneContext clonectx, Expression t)
5068 InvocationOrCast target = (InvocationOrCast) t;
5070 target.expr = expr.Clone (clonectx);
5071 target.argument = argument.Clone (clonectx);
5076 // This class is used to "disable" the code generation for the
5077 // temporary variable when initializing value types.
5079 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5080 public void AddressOf (EmitContext ec, AddressOp Mode)
5087 /// Implements the new expression
5089 public class New : ExpressionStatement, IMemoryLocation {
5090 ArrayList Arguments;
5093 // During bootstrap, it contains the RequestedType,
5094 // but if `type' is not null, it *might* contain a NewDelegate
5095 // (because of field multi-initialization)
5097 public Expression RequestedType;
5099 MethodGroupExpr method;
5102 // If set, the new expression is for a value_target, and
5103 // we will not leave anything on the stack.
5105 protected Expression value_target;
5106 protected bool value_target_set;
5107 bool is_type_parameter = false;
5109 public New (Expression requested_type, ArrayList arguments, Location l)
5111 RequestedType = requested_type;
5112 Arguments = arguments;
5116 public bool SetTargetVariable (Expression value)
5118 value_target = value;
5119 value_target_set = true;
5120 if (!(value_target is IMemoryLocation)){
5121 Error_UnexpectedKind (null, "variable", loc);
5128 // This function is used to disable the following code sequence for
5129 // value type initialization:
5131 // AddressOf (temporary)
5135 // Instead the provide will have provided us with the address on the
5136 // stack to store the results.
5138 static Expression MyEmptyExpression;
5140 public void DisableTemporaryValueType ()
5142 if (MyEmptyExpression == null)
5143 MyEmptyExpression = new EmptyAddressOf ();
5146 // To enable this, look into:
5147 // test-34 and test-89 and self bootstrapping.
5149 // For instance, we can avoid a copy by using `newobj'
5150 // instead of Call + Push-temp on value types.
5151 // value_target = MyEmptyExpression;
5156 /// Converts complex core type syntax like 'new int ()' to simple constant
5158 public static Constant Constantify (Type t)
5160 if (t == TypeManager.int32_type)
5161 return new IntConstant (0, Location.Null);
5162 if (t == TypeManager.uint32_type)
5163 return new UIntConstant (0, Location.Null);
5164 if (t == TypeManager.int64_type)
5165 return new LongConstant (0, Location.Null);
5166 if (t == TypeManager.uint64_type)
5167 return new ULongConstant (0, Location.Null);
5168 if (t == TypeManager.float_type)
5169 return new FloatConstant (0, Location.Null);
5170 if (t == TypeManager.double_type)
5171 return new DoubleConstant (0, Location.Null);
5172 if (t == TypeManager.short_type)
5173 return new ShortConstant (0, Location.Null);
5174 if (t == TypeManager.ushort_type)
5175 return new UShortConstant (0, Location.Null);
5176 if (t == TypeManager.sbyte_type)
5177 return new SByteConstant (0, Location.Null);
5178 if (t == TypeManager.byte_type)
5179 return new ByteConstant (0, Location.Null);
5180 if (t == TypeManager.char_type)
5181 return new CharConstant ('\0', Location.Null);
5182 if (t == TypeManager.bool_type)
5183 return new BoolConstant (false, Location.Null);
5184 if (t == TypeManager.decimal_type)
5185 return new DecimalConstant (0, Location.Null);
5186 if (TypeManager.IsEnumType (t))
5187 return new EnumConstant (Constantify (TypeManager.GetEnumUnderlyingType (t)), t);
5193 // Checks whether the type is an interface that has the
5194 // [ComImport, CoClass] attributes and must be treated
5197 public Expression CheckComImport (EmitContext ec)
5199 if (!type.IsInterface)
5203 // Turn the call into:
5204 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5206 Type real_class = AttributeTester.GetCoClassAttribute (type);
5207 if (real_class == null)
5210 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5211 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5212 return cast.Resolve (ec);
5215 public override Expression CreateExpressionTree (EmitContext ec)
5217 ArrayList args = Arguments == null ?
5218 new ArrayList (1) : new ArrayList (Arguments.Count + 1);
5220 if (method == null) {
5221 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
5223 args.Add (new Argument (method.CreateExpressionTree (ec)));
5224 if (Arguments != null) {
5226 foreach (Argument a in Arguments) {
5227 expr = a.Expr.CreateExpressionTree (ec);
5229 args.Add (new Argument (expr));
5234 return CreateExpressionFactoryCall ("New", args);
5237 public override Expression DoResolve (EmitContext ec)
5240 // The New DoResolve might be called twice when initializing field
5241 // expressions (see EmitFieldInitializers, the call to
5242 // GetInitializerExpression will perform a resolve on the expression,
5243 // and later the assign will trigger another resolution
5245 // This leads to bugs (#37014)
5248 if (RequestedType is NewDelegate)
5249 return RequestedType;
5253 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5259 if (type == TypeManager.void_type) {
5260 Error_VoidInvalidInTheContext (loc);
5264 if (type.IsPointer) {
5265 Report.Error (1919, loc, "Unsafe type `{0}' cannot be used in an object creation expression",
5266 TypeManager.CSharpName (type));
5270 if (Arguments == null) {
5271 Expression c = Constantify (type);
5276 if (TypeManager.IsDelegateType (type)) {
5277 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5278 if (RequestedType != null)
5279 if (!(RequestedType is DelegateCreation))
5280 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5281 return RequestedType;
5285 if (type.IsGenericParameter) {
5286 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5288 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5289 Error (304, String.Format (
5290 "Cannot create an instance of the " +
5291 "variable type '{0}' because it " +
5292 "doesn't have the new() constraint",
5297 if ((Arguments != null) && (Arguments.Count != 0)) {
5298 Error (417, String.Format (
5299 "`{0}': cannot provide arguments " +
5300 "when creating an instance of a " +
5301 "variable type.", type));
5305 if (TypeManager.activator_create_instance == null) {
5306 Type activator_type = TypeManager.CoreLookupType ("System", "Activator", Kind.Class, true);
5307 if (activator_type != null) {
5308 TypeManager.activator_create_instance = TypeManager.GetPredefinedMethod (
5309 activator_type, "CreateInstance", loc, Type.EmptyTypes);
5313 is_type_parameter = true;
5314 eclass = ExprClass.Value;
5319 if (type.IsAbstract && type.IsSealed) {
5320 Report.SymbolRelatedToPreviousError (type);
5321 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5325 if (type.IsInterface || type.IsAbstract){
5326 if (!TypeManager.IsGenericType (type)) {
5327 RequestedType = CheckComImport (ec);
5328 if (RequestedType != null)
5329 return RequestedType;
5332 Report.SymbolRelatedToPreviousError (type);
5333 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5337 bool is_struct = type.IsValueType;
5338 eclass = ExprClass.Value;
5341 // SRE returns a match for .ctor () on structs (the object constructor),
5342 // so we have to manually ignore it.
5344 if (is_struct && Arguments == null)
5347 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5348 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5349 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5351 if (Arguments != null){
5352 foreach (Argument a in Arguments){
5353 if (!a.Resolve (ec, loc))
5361 method = ml as MethodGroupExpr;
5362 if (method == null) {
5363 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5367 method = method.OverloadResolve (ec, ref Arguments, false, loc);
5374 bool DoEmitTypeParameter (EmitContext ec)
5377 ILGenerator ig = ec.ig;
5378 // IMemoryLocation ml;
5380 MethodInfo ci = TypeManager.activator_create_instance.MakeGenericMethod (
5381 new Type [] { type });
5383 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5384 if (gc.HasReferenceTypeConstraint || gc.HasClassConstraint) {
5385 ig.Emit (OpCodes.Call, ci);
5389 // Allow DoEmit() to be called multiple times.
5390 // We need to create a new LocalTemporary each time since
5391 // you can't share LocalBuilders among ILGeneators.
5392 LocalTemporary temp = new LocalTemporary (type);
5394 Label label_activator = ig.DefineLabel ();
5395 Label label_end = ig.DefineLabel ();
5397 temp.AddressOf (ec, AddressOp.Store);
5398 ig.Emit (OpCodes.Initobj, type);
5401 ig.Emit (OpCodes.Box, type);
5402 ig.Emit (OpCodes.Brfalse, label_activator);
5404 temp.AddressOf (ec, AddressOp.Store);
5405 ig.Emit (OpCodes.Initobj, type);
5407 ig.Emit (OpCodes.Br, label_end);
5409 ig.MarkLabel (label_activator);
5411 ig.Emit (OpCodes.Call, ci);
5412 ig.MarkLabel (label_end);
5415 throw new InternalErrorException ();
5420 // This DoEmit can be invoked in two contexts:
5421 // * As a mechanism that will leave a value on the stack (new object)
5422 // * As one that wont (init struct)
5424 // You can control whether a value is required on the stack by passing
5425 // need_value_on_stack. The code *might* leave a value on the stack
5426 // so it must be popped manually
5428 // If we are dealing with a ValueType, we have a few
5429 // situations to deal with:
5431 // * The target is a ValueType, and we have been provided
5432 // the instance (this is easy, we are being assigned).
5434 // * The target of New is being passed as an argument,
5435 // to a boxing operation or a function that takes a
5438 // In this case, we need to create a temporary variable
5439 // that is the argument of New.
5441 // Returns whether a value is left on the stack
5443 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5445 bool is_value_type = TypeManager.IsValueType (type);
5446 ILGenerator ig = ec.ig;
5451 // Allow DoEmit() to be called multiple times.
5452 // We need to create a new LocalTemporary each time since
5453 // you can't share LocalBuilders among ILGeneators.
5454 if (!value_target_set)
5455 value_target = new LocalTemporary (type);
5457 ml = (IMemoryLocation) value_target;
5458 ml.AddressOf (ec, AddressOp.Store);
5462 method.EmitArguments (ec, Arguments);
5466 ig.Emit (OpCodes.Initobj, type);
5468 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5469 if (need_value_on_stack){
5470 value_target.Emit (ec);
5475 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5480 public override void Emit (EmitContext ec)
5482 if (is_type_parameter)
5483 DoEmitTypeParameter (ec);
5488 public override void EmitStatement (EmitContext ec)
5490 bool value_on_stack;
5492 if (is_type_parameter)
5493 value_on_stack = DoEmitTypeParameter (ec);
5495 value_on_stack = DoEmit (ec, false);
5498 ec.ig.Emit (OpCodes.Pop);
5502 public virtual bool HasInitializer {
5508 public void AddressOf (EmitContext ec, AddressOp Mode)
5510 if (is_type_parameter) {
5511 LocalTemporary temp = new LocalTemporary (type);
5512 DoEmitTypeParameter (ec);
5514 temp.AddressOf (ec, Mode);
5518 if (!type.IsValueType){
5520 // We throw an exception. So far, I believe we only need to support
5522 // foreach (int j in new StructType ())
5525 throw new Exception ("AddressOf should not be used for classes");
5528 if (!value_target_set)
5529 value_target = new LocalTemporary (type);
5530 IMemoryLocation ml = (IMemoryLocation) value_target;
5532 ml.AddressOf (ec, AddressOp.Store);
5533 if (method == null) {
5534 ec.ig.Emit (OpCodes.Initobj, type);
5536 method.EmitArguments (ec, Arguments);
5537 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5540 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5543 protected override void CloneTo (CloneContext clonectx, Expression t)
5545 New target = (New) t;
5547 target.RequestedType = RequestedType.Clone (clonectx);
5548 if (Arguments != null){
5549 target.Arguments = new ArrayList ();
5550 foreach (Argument a in Arguments){
5551 target.Arguments.Add (a.Clone (clonectx));
5558 /// 14.5.10.2: Represents an array creation expression.
5562 /// There are two possible scenarios here: one is an array creation
5563 /// expression that specifies the dimensions and optionally the
5564 /// initialization data and the other which does not need dimensions
5565 /// specified but where initialization data is mandatory.
5567 public class ArrayCreation : Expression {
5568 FullNamedExpression requested_base_type;
5569 ArrayList initializers;
5572 // The list of Argument types.
5573 // This is used to construct the `newarray' or constructor signature
5575 protected ArrayList arguments;
5577 protected Type array_element_type;
5578 bool expect_initializers = false;
5579 int num_arguments = 0;
5580 protected int dimensions;
5581 protected readonly string rank;
5583 protected ArrayList array_data;
5587 // The number of constants in array initializers
5588 int const_initializers_count;
5589 bool only_constant_initializers;
5591 public ArrayCreation (FullNamedExpression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5593 this.requested_base_type = requested_base_type;
5594 this.initializers = initializers;
5598 arguments = new ArrayList ();
5600 foreach (Expression e in exprs) {
5601 arguments.Add (new Argument (e, Argument.AType.Expression));
5606 public ArrayCreation (FullNamedExpression requested_base_type, string rank, ArrayList initializers, Location l)
5608 this.requested_base_type = requested_base_type;
5609 this.initializers = initializers;
5613 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5615 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5617 //dimensions = tmp.Length - 1;
5618 expect_initializers = true;
5621 void Error_IncorrectArrayInitializer ()
5623 Error (178, "Invalid rank specifier: expected `,' or `]'");
5626 protected override void Error_NegativeArrayIndex (Location loc)
5628 Report.Error (248, loc, "Cannot create an array with a negative size");
5631 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5633 if (specified_dims) {
5634 Argument a = (Argument) arguments [idx];
5636 if (!a.Resolve (ec, loc))
5639 Constant c = a.Expr as Constant;
5641 c = c.ImplicitConversionRequired (TypeManager.int32_type, a.Expr.Location);
5645 Report.Error (150, a.Expr.Location, "A constant value is expected");
5649 int value = (int) c.GetValue ();
5651 if (value != probe.Count) {
5652 Error_IncorrectArrayInitializer ();
5656 bounds [idx] = value;
5659 int child_bounds = -1;
5660 only_constant_initializers = true;
5661 for (int i = 0; i < probe.Count; ++i) {
5662 object o = probe [i];
5663 if (o is ArrayList) {
5664 ArrayList sub_probe = o as ArrayList;
5665 int current_bounds = sub_probe.Count;
5667 if (child_bounds == -1)
5668 child_bounds = current_bounds;
5670 else if (child_bounds != current_bounds){
5671 Error_IncorrectArrayInitializer ();
5674 if (idx + 1 >= dimensions){
5675 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
5679 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
5683 if (child_bounds != -1){
5684 Error_IncorrectArrayInitializer ();
5688 Expression element = ResolveArrayElement (ec, (Expression) o);
5689 if (element == null)
5692 // Initializers with the default values can be ignored
5693 Constant c = element as Constant;
5695 if (c.IsDefaultInitializer (array_element_type)) {
5699 ++const_initializers_count;
5702 only_constant_initializers = false;
5705 array_data.Add (element);
5712 public override Expression CreateExpressionTree (EmitContext ec)
5716 if (dimensions != 1) {
5717 if (initializers != null) {
5718 Report.Error (838, loc, "An expression tree cannot contain a multidimensional array initializer");
5722 args = new ArrayList (arguments.Count + 1);
5723 args.Add (new Argument (new TypeOf (new TypeExpression (array_element_type, loc), loc)));
5724 foreach (Argument a in arguments)
5725 args.Add (new Argument (a.Expr.CreateExpressionTree (ec)));
5727 return CreateExpressionFactoryCall ("NewArrayBounds", args);
5730 args = new ArrayList (array_data == null ? 1 : array_data.Count + 1);
5731 args.Add (new Argument (new TypeOf (new TypeExpression (array_element_type, loc), loc)));
5732 if (array_data != null) {
5733 foreach (Expression e in array_data)
5734 args.Add (new Argument (e.CreateExpressionTree (ec)));
5737 return CreateExpressionFactoryCall ("NewArrayInit", args);
5740 public void UpdateIndices ()
5743 for (ArrayList probe = initializers; probe != null;) {
5744 if (probe.Count > 0 && probe [0] is ArrayList) {
5745 Expression e = new IntConstant (probe.Count, Location.Null);
5746 arguments.Add (new Argument (e, Argument.AType.Expression));
5748 bounds [i++] = probe.Count;
5750 probe = (ArrayList) probe [0];
5753 Expression e = new IntConstant (probe.Count, Location.Null);
5754 arguments.Add (new Argument (e, Argument.AType.Expression));
5756 bounds [i++] = probe.Count;
5763 Expression first_emit;
5764 LocalTemporary first_emit_temp;
5766 protected virtual Expression ResolveArrayElement (EmitContext ec, Expression element)
5768 element = element.Resolve (ec);
5769 if (element == null)
5772 if (element is CompoundAssign.Helper) {
5773 if (first_emit != null)
5774 throw new InternalErrorException ("Can only handle one mutator at a time");
5775 first_emit = element;
5776 element = first_emit_temp = new LocalTemporary (element.Type);
5779 return Convert.ImplicitConversionRequired (
5780 ec, element, array_element_type, loc);
5783 protected bool ResolveInitializers (EmitContext ec)
5785 if (initializers == null) {
5786 return !expect_initializers;
5790 // We use this to store all the date values in the order in which we
5791 // will need to store them in the byte blob later
5793 array_data = new ArrayList ();
5794 bounds = new System.Collections.Specialized.HybridDictionary ();
5796 if (arguments != null)
5797 return CheckIndices (ec, initializers, 0, true);
5799 arguments = new ArrayList ();
5801 if (!CheckIndices (ec, initializers, 0, false))
5810 // Resolved the type of the array
5812 bool ResolveArrayType (EmitContext ec)
5814 if (requested_base_type == null) {
5815 Report.Error (622, loc, "Can only use array initializer expressions to assign to array types. Try using a new expression instead");
5819 StringBuilder array_qualifier = new StringBuilder (rank);
5822 // `In the first form allocates an array instace of the type that results
5823 // from deleting each of the individual expression from the expression list'
5825 if (num_arguments > 0) {
5826 array_qualifier.Append ("[");
5827 for (int i = num_arguments-1; i > 0; i--)
5828 array_qualifier.Append (",");
5829 array_qualifier.Append ("]");
5835 TypeExpr array_type_expr;
5836 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
5837 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
5838 if (array_type_expr == null)
5841 type = array_type_expr.Type;
5842 array_element_type = TypeManager.GetElementType (type);
5843 dimensions = type.GetArrayRank ();
5848 public override Expression DoResolve (EmitContext ec)
5853 if (!ResolveArrayType (ec))
5856 if ((array_element_type.Attributes & Class.StaticClassAttribute) == Class.StaticClassAttribute) {
5857 Report.Error (719, loc, "`{0}': array elements cannot be of static type",
5858 TypeManager.CSharpName (array_element_type));
5862 // First step is to validate the initializers and fill
5863 // in any missing bits
5865 if (!ResolveInitializers (ec))
5868 if (arguments.Count != dimensions) {
5869 Error_IncorrectArrayInitializer ();
5872 foreach (Argument a in arguments){
5873 if (!a.Resolve (ec, loc))
5876 a.Expr = ConvertExpressionToArrayIndex (ec, a.Expr);
5879 eclass = ExprClass.Value;
5883 MethodInfo GetArrayMethod (int arguments)
5885 ModuleBuilder mb = CodeGen.Module.Builder;
5887 Type[] arg_types = new Type[arguments];
5888 for (int i = 0; i < arguments; i++)
5889 arg_types[i] = TypeManager.int32_type;
5891 MethodInfo mi = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
5895 Report.Error (-6, "New invocation: Can not find a constructor for " +
5896 "this argument list");
5903 byte [] MakeByteBlob ()
5908 int count = array_data.Count;
5910 if (TypeManager.IsEnumType (array_element_type))
5911 array_element_type = TypeManager.GetEnumUnderlyingType (array_element_type);
5913 factor = GetTypeSize (array_element_type);
5915 throw new Exception ("unrecognized type in MakeByteBlob: " + array_element_type);
5917 data = new byte [(count * factor + 3) & ~3];
5920 for (int i = 0; i < count; ++i) {
5921 object v = array_data [i];
5923 if (v is EnumConstant)
5924 v = ((EnumConstant) v).Child;
5926 if (v is Constant && !(v is StringConstant))
5927 v = ((Constant) v).GetValue ();
5933 if (array_element_type == TypeManager.int64_type){
5934 if (!(v is Expression)){
5935 long val = (long) v;
5937 for (int j = 0; j < factor; ++j) {
5938 data [idx + j] = (byte) (val & 0xFF);
5942 } else if (array_element_type == TypeManager.uint64_type){
5943 if (!(v is Expression)){
5944 ulong val = (ulong) v;
5946 for (int j = 0; j < factor; ++j) {
5947 data [idx + j] = (byte) (val & 0xFF);
5951 } else if (array_element_type == TypeManager.float_type) {
5952 if (!(v is Expression)){
5953 element = BitConverter.GetBytes ((float) v);
5955 for (int j = 0; j < factor; ++j)
5956 data [idx + j] = element [j];
5957 if (!BitConverter.IsLittleEndian)
5958 System.Array.Reverse (data, idx, 4);
5960 } else if (array_element_type == TypeManager.double_type) {
5961 if (!(v is Expression)){
5962 element = BitConverter.GetBytes ((double) v);
5964 for (int j = 0; j < factor; ++j)
5965 data [idx + j] = element [j];
5967 // FIXME: Handle the ARM float format.
5968 if (!BitConverter.IsLittleEndian)
5969 System.Array.Reverse (data, idx, 8);
5971 } else if (array_element_type == TypeManager.char_type){
5972 if (!(v is Expression)){
5973 int val = (int) ((char) v);
5975 data [idx] = (byte) (val & 0xff);
5976 data [idx+1] = (byte) (val >> 8);
5978 } else if (array_element_type == TypeManager.short_type){
5979 if (!(v is Expression)){
5980 int val = (int) ((short) v);
5982 data [idx] = (byte) (val & 0xff);
5983 data [idx+1] = (byte) (val >> 8);
5985 } else if (array_element_type == TypeManager.ushort_type){
5986 if (!(v is Expression)){
5987 int val = (int) ((ushort) v);
5989 data [idx] = (byte) (val & 0xff);
5990 data [idx+1] = (byte) (val >> 8);
5992 } else if (array_element_type == TypeManager.int32_type) {
5993 if (!(v is Expression)){
5996 data [idx] = (byte) (val & 0xff);
5997 data [idx+1] = (byte) ((val >> 8) & 0xff);
5998 data [idx+2] = (byte) ((val >> 16) & 0xff);
5999 data [idx+3] = (byte) (val >> 24);
6001 } else if (array_element_type == TypeManager.uint32_type) {
6002 if (!(v is Expression)){
6003 uint val = (uint) v;
6005 data [idx] = (byte) (val & 0xff);
6006 data [idx+1] = (byte) ((val >> 8) & 0xff);
6007 data [idx+2] = (byte) ((val >> 16) & 0xff);
6008 data [idx+3] = (byte) (val >> 24);
6010 } else if (array_element_type == TypeManager.sbyte_type) {
6011 if (!(v is Expression)){
6012 sbyte val = (sbyte) v;
6013 data [idx] = (byte) val;
6015 } else if (array_element_type == TypeManager.byte_type) {
6016 if (!(v is Expression)){
6017 byte val = (byte) v;
6018 data [idx] = (byte) val;
6020 } else if (array_element_type == TypeManager.bool_type) {
6021 if (!(v is Expression)){
6022 bool val = (bool) v;
6023 data [idx] = (byte) (val ? 1 : 0);
6025 } else if (array_element_type == TypeManager.decimal_type){
6026 if (!(v is Expression)){
6027 int [] bits = Decimal.GetBits ((decimal) v);
6030 // FIXME: For some reason, this doesn't work on the MS runtime.
6031 int [] nbits = new int [4];
6032 nbits [0] = bits [3];
6033 nbits [1] = bits [2];
6034 nbits [2] = bits [0];
6035 nbits [3] = bits [1];
6037 for (int j = 0; j < 4; j++){
6038 data [p++] = (byte) (nbits [j] & 0xff);
6039 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6040 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6041 data [p++] = (byte) (nbits [j] >> 24);
6045 throw new Exception ("Unrecognized type in MakeByteBlob: " + array_element_type);
6054 // Emits the initializers for the array
6056 void EmitStaticInitializers (EmitContext ec)
6058 // FIXME: This should go to Resolve !
6059 if (TypeManager.void_initializearray_array_fieldhandle == null) {
6060 TypeManager.void_initializearray_array_fieldhandle = TypeManager.GetPredefinedMethod (
6061 TypeManager.runtime_helpers_type, "InitializeArray", loc,
6062 TypeManager.array_type, TypeManager.runtime_field_handle_type);
6063 if (TypeManager.void_initializearray_array_fieldhandle == null)
6068 // First, the static data
6071 ILGenerator ig = ec.ig;
6073 byte [] data = MakeByteBlob ();
6075 fb = RootContext.MakeStaticData (data);
6077 ig.Emit (OpCodes.Dup);
6078 ig.Emit (OpCodes.Ldtoken, fb);
6079 ig.Emit (OpCodes.Call,
6080 TypeManager.void_initializearray_array_fieldhandle);
6084 // Emits pieces of the array that can not be computed at compile
6085 // time (variables and string locations).
6087 // This always expect the top value on the stack to be the array
6089 void EmitDynamicInitializers (EmitContext ec, bool emitConstants)
6091 ILGenerator ig = ec.ig;
6092 int dims = bounds.Count;
6093 int [] current_pos = new int [dims];
6095 MethodInfo set = null;
6098 Type [] args = new Type [dims + 1];
6100 for (int j = 0; j < dims; j++)
6101 args [j] = TypeManager.int32_type;
6102 args [dims] = array_element_type;
6104 set = CodeGen.Module.Builder.GetArrayMethod (
6106 CallingConventions.HasThis | CallingConventions.Standard,
6107 TypeManager.void_type, args);
6110 for (int i = 0; i < array_data.Count; i++){
6112 Expression e = (Expression)array_data [i];
6114 // Constant can be initialized via StaticInitializer
6115 if (e != null && !(!emitConstants && e is Constant)) {
6116 Type etype = e.Type;
6118 ig.Emit (OpCodes.Dup);
6120 for (int idx = 0; idx < dims; idx++)
6121 IntConstant.EmitInt (ig, current_pos [idx]);
6124 // If we are dealing with a struct, get the
6125 // address of it, so we can store it.
6127 if ((dims == 1) && etype.IsValueType &&
6128 (!TypeManager.IsBuiltinOrEnum (etype) ||
6129 etype == TypeManager.decimal_type)) {
6134 // Let new know that we are providing
6135 // the address where to store the results
6137 n.DisableTemporaryValueType ();
6140 ig.Emit (OpCodes.Ldelema, etype);
6146 bool is_stobj, has_type_arg;
6147 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
6149 ig.Emit (OpCodes.Stobj, etype);
6150 else if (has_type_arg)
6151 ig.Emit (op, etype);
6155 ig.Emit (OpCodes.Call, set);
6162 for (int j = dims - 1; j >= 0; j--){
6164 if (current_pos [j] < (int) bounds [j])
6166 current_pos [j] = 0;
6171 public override void Emit (EmitContext ec)
6173 ILGenerator ig = ec.ig;
6175 if (first_emit != null) {
6176 first_emit.Emit (ec);
6177 first_emit_temp.Store (ec);
6180 foreach (Argument a in arguments)
6183 if (arguments.Count == 1)
6184 ig.Emit (OpCodes.Newarr, array_element_type);
6186 ig.Emit (OpCodes.Newobj, GetArrayMethod (arguments.Count));
6189 if (initializers == null)
6192 // Emit static initializer for arrays which have contain more than 4 items and
6193 // the static initializer will initialize at least 25% of array values.
6194 // NOTE: const_initializers_count does not contain default constant values.
6195 if (const_initializers_count >= 4 && const_initializers_count * 4 > (array_data.Count) &&
6196 TypeManager.IsPrimitiveType (array_element_type)) {
6197 EmitStaticInitializers (ec);
6199 if (!only_constant_initializers)
6200 EmitDynamicInitializers (ec, false);
6202 EmitDynamicInitializers (ec, true);
6205 if (first_emit_temp != null)
6206 first_emit_temp.Release (ec);
6209 public override bool GetAttributableValue (Type value_type, out object value)
6211 if (arguments.Count != 1) {
6212 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6213 return base.GetAttributableValue (null, out value);
6216 if (array_data == null) {
6217 Constant c = (Constant)((Argument)arguments [0]).Expr;
6218 if (c.IsDefaultValue) {
6219 value = Array.CreateInstance (array_element_type, 0);
6222 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6223 return base.GetAttributableValue (null, out value);
6226 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6227 object element_value;
6228 for (int i = 0; i < ret.Length; ++i)
6230 Expression e = (Expression)array_data [i];
6232 // Is null when an initializer is optimized (value == predefined value)
6236 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6240 ret.SetValue (element_value, i);
6246 protected override void CloneTo (CloneContext clonectx, Expression t)
6248 ArrayCreation target = (ArrayCreation) t;
6250 if (requested_base_type != null)
6251 target.requested_base_type = (FullNamedExpression)requested_base_type.Clone (clonectx);
6253 if (arguments != null){
6254 target.arguments = new ArrayList (arguments.Count);
6255 foreach (Argument a in arguments)
6256 target.arguments.Add (a.Clone (clonectx));
6259 if (initializers != null){
6260 target.initializers = new ArrayList (initializers.Count);
6261 foreach (object initializer in initializers)
6262 if (initializer is ArrayList) {
6263 ArrayList this_al = (ArrayList)initializer;
6264 ArrayList al = new ArrayList (this_al.Count);
6265 target.initializers.Add (al);
6266 foreach (Expression e in this_al)
6267 al.Add (e.Clone (clonectx));
6269 target.initializers.Add (((Expression)initializer).Clone (clonectx));
6276 // Represents an implicitly typed array epxression
6278 public class ImplicitlyTypedArrayCreation : ArrayCreation
6280 public ImplicitlyTypedArrayCreation (string rank, ArrayList initializers, Location loc)
6281 : base (null, rank, initializers, loc)
6283 if (RootContext.Version <= LanguageVersion.ISO_2)
6284 Report.FeatureIsNotAvailable (loc, "implicitly typed arrays");
6286 if (rank.Length > 2) {
6287 while (rank [++dimensions] == ',');
6293 public override Expression DoResolve (EmitContext ec)
6298 if (!ResolveInitializers (ec))
6301 if (array_element_type == null || array_element_type == TypeManager.null_type ||
6302 array_element_type == TypeManager.void_type || array_element_type == TypeManager.anonymous_method_type ||
6303 arguments.Count != dimensions) {
6304 Report.Error (826, loc, "The type of an implicitly typed array cannot be inferred from the initializer. Try specifying array type explicitly");
6309 // At this point we found common base type for all initializer elements
6310 // but we have to be sure that all static initializer elements are of
6313 UnifyInitializerElement (ec);
6315 type = TypeManager.GetConstructedType (array_element_type, rank);
6316 eclass = ExprClass.Value;
6321 // Converts static initializer only
6323 void UnifyInitializerElement (EmitContext ec)
6325 for (int i = 0; i < array_data.Count; ++i) {
6326 Expression e = (Expression)array_data[i];
6328 array_data [i] = Convert.ImplicitConversionStandard (ec, e, array_element_type, Location.Null);
6332 protected override Expression ResolveArrayElement (EmitContext ec, Expression element)
6334 element = element.Resolve (ec);
6335 if (element == null)
6338 if (array_element_type == null) {
6339 array_element_type = element.Type;
6343 if (Convert.ImplicitStandardConversionExists (element, array_element_type)) {
6347 if (Convert.ImplicitStandardConversionExists (new TypeExpression (array_element_type, loc), element.Type)) {
6348 array_element_type = element.Type;
6352 element.Error_ValueCannotBeConverted (ec, element.Location, array_element_type, false);
6357 public sealed class CompilerGeneratedThis : This
6359 public static This Instance = new CompilerGeneratedThis ();
6361 private CompilerGeneratedThis ()
6362 : base (Location.Null)
6366 public override Expression DoResolve (EmitContext ec)
6368 eclass = ExprClass.Variable;
6369 type = ec.ContainerType;
6370 variable = new SimpleThis (type);
6376 /// Represents the `this' construct
6379 public class This : VariableReference, IVariable
6382 VariableInfo variable_info;
6383 protected Variable variable;
6386 public This (Block block, Location loc)
6392 public This (Location loc)
6397 public VariableInfo VariableInfo {
6398 get { return variable_info; }
6401 public bool VerifyFixed ()
6403 return !TypeManager.IsValueType (Type);
6406 public override bool IsRef {
6407 get { return is_struct; }
6410 public override Variable Variable {
6411 get { return variable; }
6414 public bool ResolveBase (EmitContext ec)
6416 eclass = ExprClass.Variable;
6418 if (ec.TypeContainer.CurrentType != null)
6419 type = ec.TypeContainer.CurrentType;
6421 type = ec.ContainerType;
6423 is_struct = ec.TypeContainer is Struct;
6426 Error (26, "Keyword `this' is not valid in a static property, " +
6427 "static method, or static field initializer");
6431 if (block != null) {
6432 if (block.Toplevel.ThisVariable != null)
6433 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6435 AnonymousContainer am = ec.CurrentAnonymousMethod;
6436 if (is_struct && (am != null) && !am.IsIterator) {
6437 Report.Error (1673, loc, "Anonymous methods inside structs " +
6438 "cannot access instance members of `this'. " +
6439 "Consider copying `this' to a local variable " +
6440 "outside the anonymous method and using the " +
6444 RootScopeInfo host = block.Toplevel.RootScope;
6445 if ((host != null) && !ec.IsConstructor &&
6446 (!is_struct || host.IsIterator)) {
6447 variable = host.CaptureThis ();
6448 type = variable.Type;
6453 if (variable == null)
6454 variable = new SimpleThis (type);
6460 // Called from Invocation to check if the invocation is correct
6462 public override void CheckMarshalByRefAccess (EmitContext ec)
6464 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) &&
6465 !variable_info.IsAssigned (ec)) {
6466 Error (188, "The `this' object cannot be used before all of its " +
6467 "fields are assigned to");
6468 variable_info.SetAssigned (ec);
6472 public override Expression CreateExpressionTree (EmitContext ec)
6474 ArrayList args = new ArrayList (2);
6475 args.Add (new Argument (this));
6476 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
6477 return CreateExpressionFactoryCall ("Constant", args);
6480 public override Expression DoResolve (EmitContext ec)
6482 if (!ResolveBase (ec))
6486 if (ec.IsInFieldInitializer) {
6487 Error (27, "Keyword `this' is not available in the current context");
6494 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6496 if (!ResolveBase (ec))
6499 if (variable_info != null)
6500 variable_info.SetAssigned (ec);
6502 if (ec.TypeContainer is Class){
6503 Error (1604, "Cannot assign to 'this' because it is read-only");
6509 public override int GetHashCode()
6511 return block.GetHashCode ();
6514 public override bool Equals (object obj)
6516 This t = obj as This;
6520 return block == t.block;
6523 protected class SimpleThis : Variable
6527 public SimpleThis (Type type)
6532 public override Type Type {
6533 get { return type; }
6536 public override bool HasInstance {
6537 get { return false; }
6540 public override bool NeedsTemporary {
6541 get { return false; }
6544 public override void EmitInstance (EmitContext ec)
6549 public override void Emit (EmitContext ec)
6551 ec.ig.Emit (OpCodes.Ldarg_0);
6554 public override void EmitAssign (EmitContext ec)
6556 throw new InvalidOperationException ();
6559 public override void EmitAddressOf (EmitContext ec)
6561 ec.ig.Emit (OpCodes.Ldarg_0);
6565 protected override void CloneTo (CloneContext clonectx, Expression t)
6567 This target = (This) t;
6569 target.block = clonectx.LookupBlock (block);
6574 /// Represents the `__arglist' construct
6576 public class ArglistAccess : Expression
6578 public ArglistAccess (Location loc)
6583 public override Expression DoResolve (EmitContext ec)
6585 eclass = ExprClass.Variable;
6586 type = TypeManager.runtime_argument_handle_type;
6588 if (ec.IsInFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6590 Error (190, "The __arglist construct is valid only within " +
6591 "a variable argument method");
6598 public override void Emit (EmitContext ec)
6600 ec.ig.Emit (OpCodes.Arglist);
6603 protected override void CloneTo (CloneContext clonectx, Expression target)
6610 /// Represents the `__arglist (....)' construct
6612 public class Arglist : Expression
6614 Argument[] Arguments;
6616 public Arglist (Location loc)
6617 : this (Argument.Empty, loc)
6621 public Arglist (Argument[] args, Location l)
6627 public Type[] ArgumentTypes {
6629 Type[] retval = new Type [Arguments.Length];
6630 for (int i = 0; i < Arguments.Length; i++)
6631 retval [i] = Arguments [i].Type;
6636 public override Expression CreateExpressionTree (EmitContext ec)
6638 Report.Error (1952, loc, "An expression tree cannot contain a method with variable arguments");
6642 public override Expression DoResolve (EmitContext ec)
6644 eclass = ExprClass.Variable;
6645 type = TypeManager.runtime_argument_handle_type;
6647 foreach (Argument arg in Arguments) {
6648 if (!arg.Resolve (ec, loc))
6655 public override void Emit (EmitContext ec)
6657 foreach (Argument arg in Arguments)
6661 protected override void CloneTo (CloneContext clonectx, Expression t)
6663 Arglist target = (Arglist) t;
6665 target.Arguments = new Argument [Arguments.Length];
6666 for (int i = 0; i < Arguments.Length; i++)
6667 target.Arguments [i] = Arguments [i].Clone (clonectx);
6672 // This produces the value that renders an instance, used by the iterators code
6674 public class ProxyInstance : Expression, IMemoryLocation {
6675 public override Expression DoResolve (EmitContext ec)
6677 eclass = ExprClass.Variable;
6678 type = ec.ContainerType;
6682 public override void Emit (EmitContext ec)
6684 ec.ig.Emit (OpCodes.Ldarg_0);
6688 public void AddressOf (EmitContext ec, AddressOp mode)
6690 ec.ig.Emit (OpCodes.Ldarg_0);
6695 /// Implements the typeof operator
6697 public class TypeOf : Expression {
6698 Expression QueriedType;
6699 protected Type typearg;
6701 public TypeOf (Expression queried_type, Location l)
6703 QueriedType = queried_type;
6707 public override Expression CreateExpressionTree (EmitContext ec)
6709 ArrayList args = new ArrayList (2);
6710 args.Add (new Argument (this));
6711 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
6712 return CreateExpressionFactoryCall ("Constant", args);
6715 public override Expression DoResolve (EmitContext ec)
6717 if (eclass != ExprClass.Invalid)
6720 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6724 typearg = texpr.Type;
6726 if (typearg == TypeManager.void_type) {
6727 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6731 if (typearg.IsPointer && !ec.InUnsafe){
6736 type = TypeManager.type_type;
6738 return DoResolveBase ();
6741 protected Expression DoResolveBase ()
6743 if (TypeManager.system_type_get_type_from_handle == null) {
6744 TypeManager.system_type_get_type_from_handle = TypeManager.GetPredefinedMethod (
6745 TypeManager.type_type, "GetTypeFromHandle", loc, TypeManager.runtime_handle_type);
6748 // Even though what is returned is a type object, it's treated as a value by the compiler.
6749 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6750 eclass = ExprClass.Value;
6754 public override void Emit (EmitContext ec)
6756 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6757 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6760 public override bool GetAttributableValue (Type value_type, out object value)
6762 if (TypeManager.ContainsGenericParameters (typearg) &&
6763 !TypeManager.IsGenericTypeDefinition (typearg)) {
6764 Report.SymbolRelatedToPreviousError (typearg);
6765 Report.Error (416, loc, "`{0}': an attribute argument cannot use type parameters",
6766 TypeManager.CSharpName (typearg));
6771 if (value_type == TypeManager.object_type) {
6772 value = (object)typearg;
6779 public Type TypeArgument
6787 protected override void CloneTo (CloneContext clonectx, Expression t)
6789 TypeOf target = (TypeOf) t;
6790 if (QueriedType != null)
6791 target.QueriedType = QueriedType.Clone (clonectx);
6796 /// Implements the `typeof (void)' operator
6798 public class TypeOfVoid : TypeOf {
6799 public TypeOfVoid (Location l) : base (null, l)
6804 public override Expression DoResolve (EmitContext ec)
6806 type = TypeManager.type_type;
6807 typearg = TypeManager.void_type;
6809 return DoResolveBase ();
6813 class TypeOfMethodInfo : TypeOfMethod
6815 public TypeOfMethodInfo (MethodBase method, Location loc)
6816 : base (method, loc)
6820 public override Expression DoResolve (EmitContext ec)
6822 type = typeof (MethodInfo);
6823 return base.DoResolve (ec);
6826 public override void Emit (EmitContext ec)
6828 ec.ig.Emit (OpCodes.Ldtoken, (MethodInfo) method);
6830 ec.ig.Emit (OpCodes.Castclass, type);
6834 class TypeOfConstructorInfo : TypeOfMethod
6836 public TypeOfConstructorInfo (MethodBase method, Location loc)
6837 : base (method, loc)
6841 public override Expression DoResolve (EmitContext ec)
6843 type = typeof (ConstructorInfo);
6844 return base.DoResolve (ec);
6847 public override void Emit (EmitContext ec)
6849 ec.ig.Emit (OpCodes.Ldtoken, (ConstructorInfo) method);
6851 ec.ig.Emit (OpCodes.Castclass, type);
6855 abstract class TypeOfMethod : Expression
6857 protected readonly MethodBase method;
6859 protected TypeOfMethod (MethodBase method, Location loc)
6861 this.method = method;
6865 public override Expression CreateExpressionTree (EmitContext ec)
6867 ArrayList args = new ArrayList (2);
6868 args.Add (new Argument (this));
6869 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
6870 return CreateExpressionFactoryCall ("Constant", args);
6873 public override Expression DoResolve (EmitContext ec)
6875 bool is_generic = TypeManager.IsGenericType (method.DeclaringType);
6876 MethodInfo mi = is_generic ?
6877 TypeManager.methodbase_get_type_from_handle_generic :
6878 TypeManager.methodbase_get_type_from_handle;
6881 Type t = TypeManager.CoreLookupType ("System.Reflection", "MethodBase", Kind.Class, true);
6882 Type handle_type = TypeManager.CoreLookupType ("System", "RuntimeMethodHandle", Kind.Class, true);
6884 if (t == null || handle_type == null)
6887 mi = TypeManager.GetPredefinedMethod (t, "GetMethodFromHandle", loc,
6889 new Type[] { handle_type, TypeManager.runtime_handle_type } :
6890 new Type[] { handle_type } );
6893 TypeManager.methodbase_get_type_from_handle_generic = mi;
6895 TypeManager.methodbase_get_type_from_handle = mi;
6898 eclass = ExprClass.Value;
6902 public override void Emit (EmitContext ec)
6904 bool is_generic = TypeManager.IsGenericType (method.DeclaringType);
6907 mi = TypeManager.methodbase_get_type_from_handle_generic;
6908 ec.ig.Emit (OpCodes.Ldtoken, method.DeclaringType);
6910 mi = TypeManager.methodbase_get_type_from_handle;
6913 ec.ig.Emit (OpCodes.Call, mi);
6917 internal class TypeOfField : Expression
6919 readonly FieldInfo field;
6921 public TypeOfField (FieldInfo field, Location loc)
6927 public override Expression DoResolve (EmitContext ec)
6929 if (TypeManager.fieldinfo_get_field_from_handle == null) {
6930 Type t = TypeManager.CoreLookupType ("System.Reflection", "FieldInfo", Kind.Class, true);
6931 Type handle_type = TypeManager.CoreLookupType ("System", "RuntimeFieldHandle", Kind.Class, true);
6933 if (t != null && handle_type != null)
6934 TypeManager.fieldinfo_get_field_from_handle = TypeManager.GetPredefinedMethod (t,
6935 "GetFieldFromHandle", loc, handle_type);
6938 type = typeof (FieldInfo);
6939 eclass = ExprClass.Value;
6943 public override void Emit (EmitContext ec)
6945 ec.ig.Emit (OpCodes.Ldtoken, field);
6946 ec.ig.Emit (OpCodes.Call, TypeManager.fieldinfo_get_field_from_handle);
6951 /// Implements the sizeof expression
6953 public class SizeOf : Expression {
6954 readonly Expression QueriedType;
6957 public SizeOf (Expression queried_type, Location l)
6959 this.QueriedType = queried_type;
6963 public override Expression DoResolve (EmitContext ec)
6965 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6969 type_queried = texpr.Type;
6970 if (TypeManager.IsEnumType (type_queried))
6971 type_queried = TypeManager.GetEnumUnderlyingType (type_queried);
6973 if (type_queried == TypeManager.void_type) {
6974 Expression.Error_VoidInvalidInTheContext (loc);
6978 int size_of = GetTypeSize (type_queried);
6980 return new IntConstant (size_of, loc);
6983 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
6988 Report.Error (233, loc,
6989 "`{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf)",
6990 TypeManager.CSharpName (type_queried));
6993 type = TypeManager.int32_type;
6994 eclass = ExprClass.Value;
6998 public override void Emit (EmitContext ec)
7000 int size = GetTypeSize (type_queried);
7003 ec.ig.Emit (OpCodes.Sizeof, type_queried);
7005 IntConstant.EmitInt (ec.ig, size);
7008 protected override void CloneTo (CloneContext clonectx, Expression t)
7014 /// Implements the qualified-alias-member (::) expression.
7016 public class QualifiedAliasMember : MemberAccess
7018 readonly string alias;
7020 public QualifiedAliasMember (string alias, string identifier, TypeArguments targs, Location l)
7021 : base (null, identifier, targs, l)
7026 public QualifiedAliasMember (string alias, string identifier, Location l)
7027 : base (null, identifier, l)
7032 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7034 if (alias == "global") {
7035 expr = RootNamespace.Global;
7036 return base.ResolveAsTypeStep (ec, silent);
7039 int errors = Report.Errors;
7040 expr = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7042 if (errors == Report.Errors)
7043 Report.Error (432, loc, "Alias `{0}' not found", alias);
7047 FullNamedExpression fne = base.ResolveAsTypeStep (ec, silent);
7051 if (expr.eclass == ExprClass.Type) {
7053 Report.Error (431, loc,
7054 "Alias `{0}' cannot be used with '::' since it denotes a type. Consider replacing '::' with '.'", alias);
7062 public override Expression DoResolve (EmitContext ec)
7064 return ResolveAsTypeStep (ec, false);
7067 protected override void Error_IdentifierNotFound (IResolveContext rc, FullNamedExpression expr_type, string identifier)
7069 Report.Error (687, loc,
7070 "A namespace alias qualifier `{0}' did not resolve to a namespace or a type",
7071 GetSignatureForError ());
7074 public override string GetSignatureForError ()
7077 if (targs != null) {
7078 name = TypeManager.RemoveGenericArity (Name) + "<" +
7079 targs.GetSignatureForError () + ">";
7082 return alias + "::" + name;
7085 protected override void CloneTo (CloneContext clonectx, Expression t)
7092 /// Implements the member access expression
7094 public class MemberAccess : ATypeNameExpression {
7095 protected Expression expr;
7097 public MemberAccess (Expression expr, string id)
7098 : base (id, expr.Location)
7103 public MemberAccess (Expression expr, string identifier, Location loc)
7104 : base (identifier, loc)
7109 public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
7110 : base (identifier, args, loc)
7115 // TODO: this method has very poor performace for Enum fields and
7116 // probably for other constants as well
7117 Expression DoResolve (EmitContext ec, Expression right_side)
7120 throw new Exception ();
7123 // Resolve the expression with flow analysis turned off, we'll do the definite
7124 // assignment checks later. This is because we don't know yet what the expression
7125 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7126 // definite assignment check on the actual field and not on the whole struct.
7129 SimpleName original = expr as SimpleName;
7130 Expression expr_resolved = expr.Resolve (ec,
7131 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7132 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7134 if (expr_resolved == null)
7137 string LookupIdentifier = MemberName.MakeName (Name, targs);
7139 if (expr_resolved is Namespace) {
7140 Namespace ns = (Namespace) expr_resolved;
7141 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, LookupIdentifier, loc);
7143 if ((retval != null) && (targs != null))
7144 retval = new ConstructedType (retval, targs, loc).ResolveAsTypeStep (ec, false);
7148 ns.Error_NamespaceDoesNotExist (ec.DeclContainer, loc, Name);
7152 Type expr_type = expr_resolved.Type;
7153 if (expr_type.IsPointer || expr_type == TypeManager.void_type ||
7154 expr_resolved is NullLiteral || expr_type == TypeManager.anonymous_method_type) {
7155 Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type);
7159 Constant c = expr_resolved as Constant;
7160 if (c != null && c.GetValue () == null) {
7161 Report.Warning (1720, 1, loc, "Expression will always cause a `{0}'",
7162 "System.NullReferenceException");
7165 if (targs != null) {
7166 if (!targs.Resolve (ec))
7170 Expression member_lookup;
7171 member_lookup = MemberLookup (
7172 ec.ContainerType, expr_type, expr_type, Name, loc);
7174 if ((member_lookup == null) && (targs != null)) {
7175 member_lookup = MemberLookup (
7176 ec.ContainerType, expr_type, expr_type, LookupIdentifier, loc);
7179 if (member_lookup == null) {
7180 ExprClass expr_eclass = expr_resolved.eclass;
7183 // Extension methods are not allowed on all expression types
7185 if (expr_eclass == ExprClass.Value || expr_eclass == ExprClass.Variable ||
7186 expr_eclass == ExprClass.IndexerAccess || expr_eclass == ExprClass.PropertyAccess ||
7187 expr_eclass == ExprClass.EventAccess) {
7188 ExtensionMethodGroupExpr ex_method_lookup = ec.TypeContainer.LookupExtensionMethod (expr_type, Name, loc);
7189 if (ex_method_lookup != null) {
7190 ex_method_lookup.ExtensionExpression = expr_resolved;
7192 if (targs != null) {
7193 ex_method_lookup.SetTypeArguments (targs);
7196 return ex_method_lookup.DoResolve (ec);
7200 expr = expr_resolved;
7201 Error_MemberLookupFailed (
7202 ec.ContainerType, expr_type, expr_type, Name, null,
7203 AllMemberTypes, AllBindingFlags);
7207 TypeExpr texpr = member_lookup as TypeExpr;
7208 if (texpr != null) {
7209 if (!(expr_resolved is TypeExpr) &&
7210 (original == null || !original.IdenticalNameAndTypeName (ec, expr_resolved, loc))) {
7211 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7212 Name, member_lookup.GetSignatureForError ());
7216 if (!texpr.CheckAccessLevel (ec.DeclContainer)) {
7217 Report.SymbolRelatedToPreviousError (member_lookup.Type);
7218 ErrorIsInaccesible (loc, TypeManager.CSharpName (member_lookup.Type));
7223 ConstructedType ct = expr_resolved as ConstructedType;
7226 // When looking up a nested type in a generic instance
7227 // via reflection, we always get a generic type definition
7228 // and not a generic instance - so we have to do this here.
7230 // See gtest-172-lib.cs and gtest-172.cs for an example.
7232 ct = new ConstructedType (
7233 member_lookup.Type, ct.TypeArguments, loc);
7235 return ct.ResolveAsTypeStep (ec, false);
7238 return member_lookup;
7241 MemberExpr me = (MemberExpr) member_lookup;
7242 me = me.ResolveMemberAccess (ec, expr_resolved, loc, original);
7246 if (targs != null) {
7247 me.SetTypeArguments (targs);
7250 if (original != null && !TypeManager.IsValueType (expr_type)) {
7251 if (me.IsInstance) {
7252 LocalVariableReference var = expr_resolved as LocalVariableReference;
7253 if (var != null && !var.VerifyAssigned (ec))
7258 // The following DoResolve/DoResolveLValue will do the definite assignment
7261 if (right_side != null)
7262 return me.DoResolveLValue (ec, right_side);
7264 return me.DoResolve (ec);
7267 public override Expression DoResolve (EmitContext ec)
7269 return DoResolve (ec, null);
7272 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7274 return DoResolve (ec, right_side);
7277 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7279 return ResolveNamespaceOrType (ec, silent);
7282 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7284 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
7286 if (new_expr == null)
7289 string LookupIdentifier = MemberName.MakeName (Name, targs);
7291 if (new_expr is Namespace) {
7292 Namespace ns = (Namespace) new_expr;
7293 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, LookupIdentifier, loc);
7295 if ((retval != null) && (targs != null))
7296 retval = new ConstructedType (retval, targs, loc).ResolveAsTypeStep (rc, false);
7298 if (!silent && retval == null)
7299 ns.Error_NamespaceDoesNotExist (rc.DeclContainer, loc, LookupIdentifier);
7303 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
7304 if (tnew_expr == null)
7307 Type expr_type = tnew_expr.Type;
7309 if (expr_type.IsPointer){
7310 Error (23, "The `.' operator can not be applied to pointer operands (" +
7311 TypeManager.CSharpName (expr_type) + ")");
7315 Expression member_lookup = MemberLookup (
7316 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
7317 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7318 if (member_lookup == null) {
7322 Error_IdentifierNotFound (rc, new_expr, LookupIdentifier);
7326 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
7331 TypeArguments the_args = targs;
7332 Type declaring_type = texpr.Type.DeclaringType;
7333 if (TypeManager.HasGenericArguments (declaring_type)) {
7334 while (!TypeManager.IsEqual (TypeManager.DropGenericTypeArguments (expr_type), declaring_type)) {
7335 expr_type = expr_type.BaseType;
7338 TypeArguments new_args = new TypeArguments (loc);
7339 foreach (Type decl in TypeManager.GetTypeArguments (expr_type))
7340 new_args.Add (new TypeExpression (decl, loc));
7343 new_args.Add (targs);
7345 the_args = new_args;
7348 if (the_args != null) {
7349 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
7350 return ctype.ResolveAsTypeStep (rc, false);
7357 protected virtual void Error_IdentifierNotFound (IResolveContext rc, FullNamedExpression expr_type, string identifier)
7359 Expression member_lookup = MemberLookup (
7360 rc.DeclContainer.TypeBuilder, expr_type.Type, expr_type.Type, SimpleName.RemoveGenericArity (identifier),
7361 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7363 if (member_lookup != null) {
7364 expr_type = member_lookup.ResolveAsTypeTerminal (rc, false);
7365 if (expr_type == null)
7368 Namespace.Error_TypeArgumentsCannotBeUsed (expr_type.Type, loc);
7372 member_lookup = MemberLookup (
7373 rc.DeclContainer.TypeBuilder, expr_type.Type, expr_type.Type, identifier,
7374 MemberTypes.All, BindingFlags.Public | BindingFlags.NonPublic, loc);
7376 if (member_lookup == null) {
7377 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7378 Name, expr_type.GetSignatureForError ());
7380 // TODO: Report.SymbolRelatedToPreviousError
7381 member_lookup.Error_UnexpectedKind (null, "type", loc);
7385 protected override void Error_TypeDoesNotContainDefinition (Type type, string name)
7387 if (RootContext.Version > LanguageVersion.ISO_2 &&
7388 ((expr.eclass & (ExprClass.Value | ExprClass.Variable)) != 0)) {
7389 Report.Error (1061, loc, "Type `{0}' does not contain a definition for `{1}' and no " +
7390 "extension method `{1}' of type `{0}' could be found " +
7391 "(are you missing a using directive or an assembly reference?)",
7392 TypeManager.CSharpName (type), name);
7396 base.Error_TypeDoesNotContainDefinition (type, name);
7399 public override string GetSignatureForError ()
7401 return expr.GetSignatureForError () + "." + base.GetSignatureForError ();
7404 protected override void CloneTo (CloneContext clonectx, Expression t)
7406 MemberAccess target = (MemberAccess) t;
7408 target.expr = expr.Clone (clonectx);
7413 /// Implements checked expressions
7415 public class CheckedExpr : Expression {
7417 public Expression Expr;
7419 public CheckedExpr (Expression e, Location l)
7425 public override Expression CreateExpressionTree (EmitContext ec)
7427 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7428 return Expr.CreateExpressionTree (ec);
7431 public override Expression DoResolve (EmitContext ec)
7433 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7434 Expr = Expr.Resolve (ec);
7439 if (Expr is Constant)
7442 eclass = Expr.eclass;
7447 public override void Emit (EmitContext ec)
7449 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7453 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
7455 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7456 Expr.EmitBranchable (ec, target, on_true);
7459 protected override void CloneTo (CloneContext clonectx, Expression t)
7461 CheckedExpr target = (CheckedExpr) t;
7463 target.Expr = Expr.Clone (clonectx);
7468 /// Implements the unchecked expression
7470 public class UnCheckedExpr : Expression {
7472 public Expression Expr;
7474 public UnCheckedExpr (Expression e, Location l)
7480 public override Expression CreateExpressionTree (EmitContext ec)
7482 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7483 return Expr.CreateExpressionTree (ec);
7486 public override Expression DoResolve (EmitContext ec)
7488 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7489 Expr = Expr.Resolve (ec);
7494 if (Expr is Constant)
7497 eclass = Expr.eclass;
7502 public override void Emit (EmitContext ec)
7504 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7508 public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
7510 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7511 Expr.EmitBranchable (ec, target, on_true);
7514 protected override void CloneTo (CloneContext clonectx, Expression t)
7516 UnCheckedExpr target = (UnCheckedExpr) t;
7518 target.Expr = Expr.Clone (clonectx);
7523 /// An Element Access expression.
7525 /// During semantic analysis these are transformed into
7526 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7528 public class ElementAccess : Expression {
7529 public ArrayList Arguments;
7530 public Expression Expr;
7532 public ElementAccess (Expression e, ArrayList e_list)
7541 Arguments = new ArrayList ();
7542 foreach (Expression tmp in e_list)
7543 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7547 bool CommonResolve (EmitContext ec)
7549 Expr = Expr.Resolve (ec);
7551 if (Arguments == null)
7554 foreach (Argument a in Arguments){
7555 if (!a.Resolve (ec, loc))
7559 return Expr != null;
7562 public override Expression CreateExpressionTree (EmitContext ec)
7564 ArrayList args = new ArrayList (Arguments.Count + 1);
7565 args.Add (new Argument (Expr.CreateExpressionTree (ec)));
7566 foreach (Argument a in Arguments)
7567 args.Add (new Argument (a.Expr.CreateExpressionTree (ec)));
7569 return CreateExpressionFactoryCall ("ArrayIndex", args);
7572 Expression MakePointerAccess (EmitContext ec, Type t)
7574 if (t == TypeManager.void_ptr_type){
7575 Error (242, "The array index operation is not valid on void pointers");
7578 if (Arguments.Count != 1){
7579 Error (196, "A pointer must be indexed by only one value");
7584 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7587 return new Indirection (p, loc).Resolve (ec);
7590 public override Expression DoResolve (EmitContext ec)
7592 if (!CommonResolve (ec))
7596 // We perform some simple tests, and then to "split" the emit and store
7597 // code we create an instance of a different class, and return that.
7599 // I am experimenting with this pattern.
7603 if (t == TypeManager.array_type){
7604 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7609 return (new ArrayAccess (this, loc)).Resolve (ec);
7611 return MakePointerAccess (ec, t);
7613 FieldExpr fe = Expr as FieldExpr;
7615 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7617 return MakePointerAccess (ec, ff.ElementType);
7620 return (new IndexerAccess (this, loc)).Resolve (ec);
7623 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7625 if (!CommonResolve (ec))
7630 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7633 return MakePointerAccess (ec, type);
7635 if (Expr.eclass != ExprClass.Variable && type.IsValueType)
7636 Error_CannotModifyIntermediateExpressionValue (ec);
7638 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7641 public override void Emit (EmitContext ec)
7643 throw new Exception ("Should never be reached");
7646 public override string GetSignatureForError ()
7648 return Expr.GetSignatureForError ();
7651 protected override void CloneTo (CloneContext clonectx, Expression t)
7653 ElementAccess target = (ElementAccess) t;
7655 target.Expr = Expr.Clone (clonectx);
7656 target.Arguments = new ArrayList (Arguments.Count);
7657 foreach (Argument a in Arguments)
7658 target.Arguments.Add (a.Clone (clonectx));
7663 /// Implements array access
7665 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7667 // Points to our "data" repository
7671 LocalTemporary temp;
7675 public ArrayAccess (ElementAccess ea_data, Location l)
7678 eclass = ExprClass.Variable;
7682 public override Expression CreateExpressionTree (EmitContext ec)
7684 return ea.CreateExpressionTree (ec);
7687 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7689 return DoResolve (ec);
7692 public override Expression DoResolve (EmitContext ec)
7695 ExprClass eclass = ea.Expr.eclass;
7697 // As long as the type is valid
7698 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7699 eclass == ExprClass.Value)) {
7700 ea.Expr.Error_UnexpectedKind ("variable or value");
7705 Type t = ea.Expr.Type;
7706 int rank = ea.Arguments.Count;
7707 if (t.GetArrayRank () != rank) {
7708 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7709 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7713 if (rank != 1 && TypeManager.int_getlength_int == null) {
7714 TypeManager.int_getlength_int = TypeManager.GetPredefinedMethod (
7715 TypeManager.array_type, "GetLength", loc, TypeManager.int32_type);
7718 type = TypeManager.GetElementType (t);
7719 if (type.IsPointer && !ec.InUnsafe) {
7720 UnsafeError (ea.Location);
7724 foreach (Argument a in ea.Arguments) {
7725 a.Expr = ConvertExpressionToArrayIndex (ec, a.Expr);
7728 eclass = ExprClass.Variable;
7734 /// Emits the right opcode to load an object of Type `t'
7735 /// from an array of T
7737 void EmitLoadOpcode (ILGenerator ig, Type type, int rank)
7740 MethodInfo get = FetchGetMethod ();
7741 ig.Emit (OpCodes.Call, get);
7745 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7746 ig.Emit (OpCodes.Ldelem_U1);
7747 else if (type == TypeManager.sbyte_type)
7748 ig.Emit (OpCodes.Ldelem_I1);
7749 else if (type == TypeManager.short_type)
7750 ig.Emit (OpCodes.Ldelem_I2);
7751 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7752 ig.Emit (OpCodes.Ldelem_U2);
7753 else if (type == TypeManager.int32_type)
7754 ig.Emit (OpCodes.Ldelem_I4);
7755 else if (type == TypeManager.uint32_type)
7756 ig.Emit (OpCodes.Ldelem_U4);
7757 else if (type == TypeManager.uint64_type)
7758 ig.Emit (OpCodes.Ldelem_I8);
7759 else if (type == TypeManager.int64_type)
7760 ig.Emit (OpCodes.Ldelem_I8);
7761 else if (type == TypeManager.float_type)
7762 ig.Emit (OpCodes.Ldelem_R4);
7763 else if (type == TypeManager.double_type)
7764 ig.Emit (OpCodes.Ldelem_R8);
7765 else if (type == TypeManager.intptr_type)
7766 ig.Emit (OpCodes.Ldelem_I);
7767 else if (TypeManager.IsEnumType (type)){
7768 EmitLoadOpcode (ig, TypeManager.GetEnumUnderlyingType (type), rank);
7769 } else if (type.IsValueType){
7770 ig.Emit (OpCodes.Ldelema, type);
7771 ig.Emit (OpCodes.Ldobj, type);
7773 } else if (type.IsGenericParameter) {
7774 ig.Emit (OpCodes.Ldelem, type);
7776 } else if (type.IsPointer)
7777 ig.Emit (OpCodes.Ldelem_I);
7779 ig.Emit (OpCodes.Ldelem_Ref);
7782 protected override void Error_NegativeArrayIndex (Location loc)
7784 Report.Warning (251, 2, loc, "Indexing an array with a negative index (array indices always start at zero)");
7788 /// Returns the right opcode to store an object of Type `t'
7789 /// from an array of T.
7791 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7793 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7794 has_type_arg = false; is_stobj = false;
7795 t = TypeManager.TypeToCoreType (t);
7796 if (TypeManager.IsEnumType (t))
7797 t = TypeManager.GetEnumUnderlyingType (t);
7798 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7799 t == TypeManager.bool_type)
7800 return OpCodes.Stelem_I1;
7801 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7802 t == TypeManager.char_type)
7803 return OpCodes.Stelem_I2;
7804 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7805 return OpCodes.Stelem_I4;
7806 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7807 return OpCodes.Stelem_I8;
7808 else if (t == TypeManager.float_type)
7809 return OpCodes.Stelem_R4;
7810 else if (t == TypeManager.double_type)
7811 return OpCodes.Stelem_R8;
7812 else if (t == TypeManager.intptr_type) {
7813 has_type_arg = true;
7815 return OpCodes.Stobj;
7816 } else if (t.IsValueType) {
7817 has_type_arg = true;
7819 return OpCodes.Stobj;
7821 } else if (t.IsGenericParameter) {
7822 has_type_arg = true;
7823 return OpCodes.Stelem;
7826 } else if (t.IsPointer)
7827 return OpCodes.Stelem_I;
7829 return OpCodes.Stelem_Ref;
7832 MethodInfo FetchGetMethod ()
7834 ModuleBuilder mb = CodeGen.Module.Builder;
7835 int arg_count = ea.Arguments.Count;
7836 Type [] args = new Type [arg_count];
7839 for (int i = 0; i < arg_count; i++){
7840 //args [i++] = a.Type;
7841 args [i] = TypeManager.int32_type;
7844 get = mb.GetArrayMethod (
7845 ea.Expr.Type, "Get",
7846 CallingConventions.HasThis |
7847 CallingConventions.Standard,
7853 MethodInfo FetchAddressMethod ()
7855 ModuleBuilder mb = CodeGen.Module.Builder;
7856 int arg_count = ea.Arguments.Count;
7857 Type [] args = new Type [arg_count];
7861 ret_type = TypeManager.GetReferenceType (type);
7863 for (int i = 0; i < arg_count; i++){
7864 //args [i++] = a.Type;
7865 args [i] = TypeManager.int32_type;
7868 address = mb.GetArrayMethod (
7869 ea.Expr.Type, "Address",
7870 CallingConventions.HasThis |
7871 CallingConventions.Standard,
7878 // Load the array arguments into the stack.
7880 void LoadArrayAndArguments (EmitContext ec)
7884 for (int i = 0; i < ea.Arguments.Count; ++i) {
7885 ((Argument)ea.Arguments [i]).Emit (ec);
7889 public void Emit (EmitContext ec, bool leave_copy)
7891 int rank = ea.Expr.Type.GetArrayRank ();
7892 ILGenerator ig = ec.ig;
7895 LoadFromPtr (ig, this.type);
7897 LoadArrayAndArguments (ec);
7898 EmitLoadOpcode (ig, type, rank);
7902 ig.Emit (OpCodes.Dup);
7903 temp = new LocalTemporary (this.type);
7908 public override void Emit (EmitContext ec)
7913 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7915 int rank = ea.Expr.Type.GetArrayRank ();
7916 ILGenerator ig = ec.ig;
7917 Type t = source.Type;
7918 prepared = prepare_for_load;
7921 AddressOf (ec, AddressOp.LoadStore);
7922 ec.ig.Emit (OpCodes.Dup);
7924 LoadArrayAndArguments (ec);
7928 bool is_stobj, has_type_arg;
7929 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7933 // The stobj opcode used by value types will need
7934 // an address on the stack, not really an array/array
7938 ig.Emit (OpCodes.Ldelema, t);
7943 ec.ig.Emit (OpCodes.Dup);
7944 temp = new LocalTemporary (this.type);
7949 StoreFromPtr (ig, t);
7951 ig.Emit (OpCodes.Stobj, t);
7952 else if (has_type_arg)
7959 ec.ig.Emit (OpCodes.Dup);
7960 temp = new LocalTemporary (this.type);
7965 StoreFromPtr (ig, t);
7967 int arg_count = ea.Arguments.Count;
7968 Type [] args = new Type [arg_count + 1];
7969 for (int i = 0; i < arg_count; i++) {
7970 //args [i++] = a.Type;
7971 args [i] = TypeManager.int32_type;
7973 args [arg_count] = type;
7975 MethodInfo set = CodeGen.Module.Builder.GetArrayMethod (
7976 ea.Expr.Type, "Set",
7977 CallingConventions.HasThis |
7978 CallingConventions.Standard,
7979 TypeManager.void_type, args);
7981 ig.Emit (OpCodes.Call, set);
7991 public void AddressOf (EmitContext ec, AddressOp mode)
7993 int rank = ea.Expr.Type.GetArrayRank ();
7994 ILGenerator ig = ec.ig;
7996 LoadArrayAndArguments (ec);
7999 ig.Emit (OpCodes.Ldelema, type);
8001 MethodInfo address = FetchAddressMethod ();
8002 ig.Emit (OpCodes.Call, address);
8006 public void EmitGetLength (EmitContext ec, int dim)
8008 int rank = ea.Expr.Type.GetArrayRank ();
8009 ILGenerator ig = ec.ig;
8013 ig.Emit (OpCodes.Ldlen);
8014 ig.Emit (OpCodes.Conv_I4);
8016 IntLiteral.EmitInt (ig, dim);
8017 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
8023 /// Expressions that represent an indexer call.
8025 public class IndexerAccess : Expression, IAssignMethod
8027 class IndexerMethodGroupExpr : MethodGroupExpr
8029 public IndexerMethodGroupExpr (Indexers indexers, Location loc)
8032 Methods = (MethodBase []) indexers.Methods.ToArray (typeof (MethodBase));
8035 public override string Name {
8041 protected override int GetApplicableParametersCount (MethodBase method, ParameterData parameters)
8044 // Here is the trick, decrease number of arguments by 1 when only
8045 // available property method is setter. This makes overload resolution
8046 // work correctly for indexers.
8049 if (method.Name [0] == 'g')
8050 return parameters.Count;
8052 return parameters.Count - 1;
8058 // Contains either property getter or setter
8059 public ArrayList Methods;
8060 public ArrayList Properties;
8066 void Append (Type caller_type, MemberInfo [] mi)
8071 foreach (PropertyInfo property in mi) {
8072 MethodInfo accessor = property.GetGetMethod (true);
8073 if (accessor == null)
8074 accessor = property.GetSetMethod (true);
8076 if (Methods == null) {
8077 Methods = new ArrayList ();
8078 Properties = new ArrayList ();
8081 Methods.Add (accessor);
8082 Properties.Add (property);
8086 static MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
8088 string p_name = TypeManager.IndexerPropertyName (lookup_type);
8090 return TypeManager.MemberLookup (
8091 caller_type, caller_type, lookup_type, MemberTypes.Property,
8092 BindingFlags.Public | BindingFlags.Instance |
8093 BindingFlags.DeclaredOnly, p_name, null);
8096 public static Indexers GetIndexersForType (Type caller_type, Type lookup_type)
8098 Indexers ix = new Indexers ();
8101 if (lookup_type.IsGenericParameter) {
8102 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
8106 if (gc.HasClassConstraint)
8107 ix.Append (caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
8109 Type[] ifaces = gc.InterfaceConstraints;
8110 foreach (Type itype in ifaces)
8111 ix.Append (caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8117 Type copy = lookup_type;
8118 while (copy != TypeManager.object_type && copy != null){
8119 ix.Append (caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
8120 copy = copy.BaseType;
8123 if (lookup_type.IsInterface) {
8124 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
8125 if (ifaces != null) {
8126 foreach (Type itype in ifaces)
8127 ix.Append (caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8142 // Points to our "data" repository
8144 MethodInfo get, set;
8145 bool is_base_indexer;
8147 LocalTemporary temp;
8148 LocalTemporary prepared_value;
8149 Expression set_expr;
8151 protected Type indexer_type;
8152 protected Type current_type;
8153 protected Expression instance_expr;
8154 protected ArrayList arguments;
8156 public IndexerAccess (ElementAccess ea, Location loc)
8157 : this (ea.Expr, false, loc)
8159 this.arguments = ea.Arguments;
8162 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
8165 this.instance_expr = instance_expr;
8166 this.is_base_indexer = is_base_indexer;
8167 this.eclass = ExprClass.Value;
8171 static string GetAccessorName (AccessorType at)
8173 if (at == AccessorType.Set)
8176 if (at == AccessorType.Get)
8179 throw new NotImplementedException (at.ToString ());
8182 protected virtual bool CommonResolve (EmitContext ec)
8184 indexer_type = instance_expr.Type;
8185 current_type = ec.ContainerType;
8190 public override Expression DoResolve (EmitContext ec)
8192 return ResolveAccessor (ec, AccessorType.Get);
8195 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8197 if (right_side == EmptyExpression.OutAccess) {
8198 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
8199 GetSignatureForError ());
8203 // if the indexer returns a value type, and we try to set a field in it
8204 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
8205 Error_CannotModifyIntermediateExpressionValue (ec);
8208 Expression e = ResolveAccessor (ec, AccessorType.Set);
8212 set_expr = Convert.ImplicitConversion (ec, right_side, type, loc);
8216 Expression ResolveAccessor (EmitContext ec, AccessorType accessorType)
8218 if (!CommonResolve (ec))
8221 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8222 if (ilist.Methods == null) {
8223 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8224 TypeManager.CSharpName (indexer_type));
8228 MethodGroupExpr mg = new IndexerMethodGroupExpr (ilist, loc);
8229 mg = mg.OverloadResolve (ec, ref arguments, false, loc);
8233 MethodInfo mi = (MethodInfo) mg;
8234 PropertyInfo pi = null;
8235 for (int i = 0; i < ilist.Methods.Count; ++i) {
8236 if (ilist.Methods [i] == mi) {
8237 pi = (PropertyInfo) ilist.Properties [i];
8242 type = TypeManager.TypeToCoreType (pi.PropertyType);
8243 if (type.IsPointer && !ec.InUnsafe)
8246 MethodInfo accessor;
8247 if (accessorType == AccessorType.Get) {
8248 accessor = get = pi.GetGetMethod (true);
8250 accessor = set = pi.GetSetMethod (true);
8251 if (accessor == null && pi.GetGetMethod (true) != null) {
8252 Report.SymbolRelatedToPreviousError (pi);
8253 Report.Error (200, loc, "The read only property or indexer `{0}' cannot be assigned to",
8254 TypeManager.GetFullNameSignature (pi));
8259 if (accessor == null) {
8260 Report.SymbolRelatedToPreviousError (pi);
8261 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks a `{1}' accessor",
8262 TypeManager.GetFullNameSignature (pi), GetAccessorName (accessorType));
8267 // Only base will allow this invocation to happen.
8269 if (accessor.IsAbstract && this is BaseIndexerAccess) {
8270 Error_CannotCallAbstractBase (TypeManager.GetFullNameSignature (pi));
8273 bool must_do_cs1540_check;
8274 if (!IsAccessorAccessible (ec.ContainerType, accessor, out must_do_cs1540_check)) {
8276 set = pi.GetSetMethod (true);
8278 get = pi.GetGetMethod (true);
8280 if (set != null && get != null &&
8281 (set.Attributes & MethodAttributes.MemberAccessMask) != (get.Attributes & MethodAttributes.MemberAccessMask)) {
8282 Report.SymbolRelatedToPreviousError (accessor);
8283 Report.Error (271, loc, "The property or indexer `{0}' cannot be used in this context because a `{1}' accessor is inaccessible",
8284 TypeManager.GetFullNameSignature (pi), GetAccessorName (accessorType));
8286 Report.SymbolRelatedToPreviousError (pi);
8287 ErrorIsInaccesible (loc, TypeManager.GetFullNameSignature (pi));
8291 instance_expr.CheckMarshalByRefAccess (ec);
8292 eclass = ExprClass.IndexerAccess;
8296 public void Emit (EmitContext ec, bool leave_copy)
8299 prepared_value.Emit (ec);
8301 Invocation.EmitCall (ec, is_base_indexer, instance_expr, get,
8302 arguments, loc, false, false);
8306 ec.ig.Emit (OpCodes.Dup);
8307 temp = new LocalTemporary (Type);
8313 // source is ignored, because we already have a copy of it from the
8314 // LValue resolution and we have already constructed a pre-cached
8315 // version of the arguments (ea.set_arguments);
8317 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8319 prepared = prepare_for_load;
8320 Expression value = set_expr;
8323 Invocation.EmitCall (ec, is_base_indexer, instance_expr, get,
8324 arguments, loc, true, false);
8326 prepared_value = new LocalTemporary (type);
8327 prepared_value.Store (ec);
8329 prepared_value.Release (ec);
8332 ec.ig.Emit (OpCodes.Dup);
8333 temp = new LocalTemporary (Type);
8336 } else if (leave_copy) {
8337 temp = new LocalTemporary (Type);
8343 arguments.Add (new Argument (value, Argument.AType.Expression));
8344 Invocation.EmitCall (ec, is_base_indexer, instance_expr, set, arguments, loc, false, prepared);
8352 public override void Emit (EmitContext ec)
8357 public override string GetSignatureForError ()
8359 return TypeManager.CSharpSignature (get != null ? get : set, false);
8362 protected override void CloneTo (CloneContext clonectx, Expression t)
8364 IndexerAccess target = (IndexerAccess) t;
8366 if (arguments != null){
8367 target.arguments = new ArrayList ();
8368 foreach (Argument a in arguments)
8369 target.arguments.Add (a.Clone (clonectx));
8371 if (instance_expr != null)
8372 target.instance_expr = instance_expr.Clone (clonectx);
8377 /// The base operator for method names
8379 public class BaseAccess : Expression {
8380 public readonly string Identifier;
8383 public BaseAccess (string member, Location l)
8385 this.Identifier = member;
8389 public BaseAccess (string member, TypeArguments args, Location l)
8395 public override Expression DoResolve (EmitContext ec)
8397 Expression c = CommonResolve (ec);
8403 // MethodGroups use this opportunity to flag an error on lacking ()
8405 if (!(c is MethodGroupExpr))
8406 return c.Resolve (ec);
8410 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8412 Expression c = CommonResolve (ec);
8418 // MethodGroups use this opportunity to flag an error on lacking ()
8420 if (! (c is MethodGroupExpr))
8421 return c.DoResolveLValue (ec, right_side);
8426 Expression CommonResolve (EmitContext ec)
8428 Expression member_lookup;
8429 Type current_type = ec.ContainerType;
8430 Type base_type = current_type.BaseType;
8433 Error (1511, "Keyword `base' is not available in a static method");
8437 if (ec.IsInFieldInitializer){
8438 Error (1512, "Keyword `base' is not available in the current context");
8442 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8443 AllMemberTypes, AllBindingFlags, loc);
8444 if (member_lookup == null) {
8445 Error_MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier,
8446 null, AllMemberTypes, AllBindingFlags);
8453 left = new TypeExpression (base_type, loc);
8455 left = ec.GetThis (loc);
8457 MemberExpr me = (MemberExpr) member_lookup;
8458 me = me.ResolveMemberAccess (ec, left, loc, null);
8465 me.SetTypeArguments (args);
8471 public override void Emit (EmitContext ec)
8473 throw new Exception ("Should never be called");
8476 protected override void CloneTo (CloneContext clonectx, Expression t)
8478 BaseAccess target = (BaseAccess) t;
8481 target.args = args.Clone ();
8486 /// The base indexer operator
8488 public class BaseIndexerAccess : IndexerAccess {
8489 public BaseIndexerAccess (ArrayList args, Location loc)
8490 : base (null, true, loc)
8492 arguments = new ArrayList ();
8493 foreach (Expression tmp in args)
8494 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8497 protected override bool CommonResolve (EmitContext ec)
8499 instance_expr = ec.GetThis (loc);
8501 current_type = ec.ContainerType.BaseType;
8502 indexer_type = current_type;
8504 foreach (Argument a in arguments){
8505 if (!a.Resolve (ec, loc))
8514 /// This class exists solely to pass the Type around and to be a dummy
8515 /// that can be passed to the conversion functions (this is used by
8516 /// foreach implementation to typecast the object return value from
8517 /// get_Current into the proper type. All code has been generated and
8518 /// we only care about the side effect conversions to be performed
8520 /// This is also now used as a placeholder where a no-action expression
8521 /// is needed (the `New' class).
8523 public class EmptyExpression : Expression {
8524 public static readonly EmptyExpression Null = new EmptyExpression ();
8526 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8527 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8528 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8530 static EmptyExpression temp = new EmptyExpression ();
8531 public static EmptyExpression Grab ()
8533 EmptyExpression retval = temp == null ? new EmptyExpression () : temp;
8538 public static void Release (EmptyExpression e)
8543 // TODO: should be protected
8544 public EmptyExpression ()
8546 type = TypeManager.object_type;
8547 eclass = ExprClass.Value;
8548 loc = Location.Null;
8551 public EmptyExpression (Type t)
8554 eclass = ExprClass.Value;
8555 loc = Location.Null;
8558 public override Expression DoResolve (EmitContext ec)
8563 public override void Emit (EmitContext ec)
8565 // nothing, as we only exist to not do anything.
8568 public override void EmitSideEffect (EmitContext ec)
8573 // This is just because we might want to reuse this bad boy
8574 // instead of creating gazillions of EmptyExpressions.
8575 // (CanImplicitConversion uses it)
8577 public void SetType (Type t)
8584 // Empty statement expression
8586 public sealed class EmptyExpressionStatement : ExpressionStatement
8588 public static readonly EmptyExpressionStatement Instance = new EmptyExpressionStatement ();
8590 private EmptyExpressionStatement ()
8592 type = TypeManager.object_type;
8593 eclass = ExprClass.Value;
8594 loc = Location.Null;
8597 public override void EmitStatement (EmitContext ec)
8602 public override Expression DoResolve (EmitContext ec)
8607 public override void Emit (EmitContext ec)
8613 public class UserCast : Expression {
8617 public UserCast (MethodInfo method, Expression source, Location l)
8619 this.method = method;
8620 this.source = source;
8621 type = TypeManager.TypeToCoreType (method.ReturnType);
8622 eclass = ExprClass.Value;
8626 public Expression Source {
8632 public override Expression CreateExpressionTree (EmitContext ec)
8634 ArrayList args = new ArrayList (2);
8635 args.Add (new Argument (source.CreateExpressionTree (ec)));
8636 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
8637 args.Add (new Argument (new TypeOfMethodInfo (method, loc)));
8638 return CreateExpressionFactoryCall ("Convert", args);
8641 public override Expression DoResolve (EmitContext ec)
8644 // We are born fully resolved
8649 public override void Emit (EmitContext ec)
8652 ec.ig.Emit (OpCodes.Call, method);
8657 // This class is used to "construct" the type during a typecast
8658 // operation. Since the Type.GetType class in .NET can parse
8659 // the type specification, we just use this to construct the type
8660 // one bit at a time.
8662 public class ComposedCast : TypeExpr {
8663 FullNamedExpression left;
8666 public ComposedCast (FullNamedExpression left, string dim)
8667 : this (left, dim, left.Location)
8671 public ComposedCast (FullNamedExpression left, string dim, Location l)
8678 public Expression RemoveNullable ()
8680 if (dim.EndsWith ("?")) {
8681 dim = dim.Substring (0, dim.Length - 1);
8682 if (dim.Length == 0)
8689 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8691 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8695 Type ltype = lexpr.Type;
8696 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8697 Error_VoidInvalidInTheContext (loc);
8702 if ((dim.Length > 0) && (dim [0] == '?')) {
8703 TypeExpr nullable = new Nullable.NullableType (left, loc);
8705 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8706 return nullable.ResolveAsTypeTerminal (ec, false);
8710 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc))
8713 if (dim != "" && dim [0] == '[' &&
8714 (ltype == TypeManager.arg_iterator_type || ltype == TypeManager.typed_reference_type)) {
8715 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (ltype));
8720 type = TypeManager.GetConstructedType (ltype, dim);
8725 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8727 if (type.IsPointer && !ec.IsInUnsafeScope){
8732 eclass = ExprClass.Type;
8736 public override string GetSignatureForError ()
8738 return left.GetSignatureForError () + dim;
8741 protected override void CloneTo (CloneContext clonectx, Expression t)
8743 ComposedCast target = (ComposedCast) t;
8745 target.left = (FullNamedExpression)left.Clone (clonectx);
8749 public class FixedBufferPtr : Expression {
8752 public FixedBufferPtr (Expression array, Type array_type, Location l)
8757 type = TypeManager.GetPointerType (array_type);
8758 eclass = ExprClass.Value;
8761 public override void Emit(EmitContext ec)
8766 public override Expression DoResolve (EmitContext ec)
8769 // We are born fully resolved
8777 // This class is used to represent the address of an array, used
8778 // only by the Fixed statement, this generates "&a [0]" construct
8779 // for fixed (char *pa = a)
8781 public class ArrayPtr : FixedBufferPtr {
8784 public ArrayPtr (Expression array, Type array_type, Location l):
8785 base (array, array_type, l)
8787 this.array_type = array_type;
8790 public override void Emit (EmitContext ec)
8794 ILGenerator ig = ec.ig;
8795 IntLiteral.EmitInt (ig, 0);
8796 ig.Emit (OpCodes.Ldelema, array_type);
8801 // Encapsulates a conversion rules required for array indexes
8803 public class ArrayIndexCast : Expression
8807 public ArrayIndexCast (Expression expr)
8810 this.loc = expr.Location;
8813 public override Expression CreateExpressionTree (EmitContext ec)
8815 ArrayList args = new ArrayList (2);
8816 args.Add (new Argument (expr.CreateExpressionTree (ec)));
8817 args.Add (new Argument (new TypeOf (new TypeExpression (TypeManager.int32_type, loc), loc)));
8818 return CreateExpressionFactoryCall ("ConvertChecked", args);
8821 public override Expression DoResolve (EmitContext ec)
8824 eclass = expr.eclass;
8828 public override void Emit (EmitContext ec)
8832 if (type == TypeManager.int32_type)
8835 if (type == TypeManager.uint32_type)
8836 ec.ig.Emit (OpCodes.Conv_U);
8837 else if (type == TypeManager.int64_type)
8838 ec.ig.Emit (OpCodes.Conv_Ovf_I);
8839 else if (type == TypeManager.uint64_type)
8840 ec.ig.Emit (OpCodes.Conv_Ovf_I_Un);
8842 throw new InternalErrorException ("Cannot emit cast to unknown array element type", type);
8847 // Used by the fixed statement
8849 public class StringPtr : Expression {
8852 public StringPtr (LocalBuilder b, Location l)
8855 eclass = ExprClass.Value;
8856 type = TypeManager.char_ptr_type;
8860 public override Expression DoResolve (EmitContext ec)
8862 // This should never be invoked, we are born in fully
8863 // initialized state.
8868 public override void Emit (EmitContext ec)
8870 if (TypeManager.int_get_offset_to_string_data == null) {
8871 // TODO: Move to resolve !!
8872 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedMethod (
8873 TypeManager.runtime_helpers_type, "get_OffsetToStringData", loc, Type.EmptyTypes);
8876 ILGenerator ig = ec.ig;
8878 ig.Emit (OpCodes.Ldloc, b);
8879 ig.Emit (OpCodes.Conv_I);
8880 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8881 ig.Emit (OpCodes.Add);
8886 // Implements the `stackalloc' keyword
8888 public class StackAlloc : Expression {
8893 public StackAlloc (Expression type, Expression count, Location l)
8900 public override Expression DoResolve (EmitContext ec)
8902 count = count.Resolve (ec);
8906 if (count.Type != TypeManager.int32_type){
8907 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8912 Constant c = count as Constant;
8913 if (c != null && c.IsNegative) {
8914 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8918 if (ec.InCatch || ec.InFinally) {
8919 Error (255, "Cannot use stackalloc in finally or catch");
8923 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8929 if (!TypeManager.VerifyUnManaged (otype, loc))
8932 type = TypeManager.GetPointerType (otype);
8933 eclass = ExprClass.Value;
8938 public override void Emit (EmitContext ec)
8940 int size = GetTypeSize (otype);
8941 ILGenerator ig = ec.ig;
8944 ig.Emit (OpCodes.Sizeof, otype);
8946 IntConstant.EmitInt (ig, size);
8948 ig.Emit (OpCodes.Mul);
8949 ig.Emit (OpCodes.Localloc);
8952 protected override void CloneTo (CloneContext clonectx, Expression t)
8954 StackAlloc target = (StackAlloc) t;
8955 target.count = count.Clone (clonectx);
8956 target.t = t.Clone (clonectx);
8961 // An object initializer expression
8963 public class ElementInitializer : Assign
8965 public readonly string Name;
8967 public ElementInitializer (string name, Expression initializer, Location loc)
8968 : base (null, initializer, loc)
8973 public override Expression CreateExpressionTree (EmitContext ec)
8975 ArrayList args = new ArrayList (2);
8976 FieldExpr fe = target as FieldExpr;
8978 args.Add (new Argument (fe.CreateTypeOfExpression ()));
8980 args.Add (new Argument (((PropertyExpr)target).CreateSetterTypeOfExpression ()));
8982 args.Add (new Argument (source.CreateExpressionTree (ec)));
8983 return CreateExpressionFactoryCall (
8984 source is CollectionOrObjectInitializers ? "ListBind" : "Bind",
8988 public override Expression DoResolve (EmitContext ec)
8991 return EmptyExpressionStatement.Instance;
8993 MemberExpr me = MemberLookupFinal (ec, ec.CurrentInitializerVariable.Type, ec.CurrentInitializerVariable.Type,
8994 Name, MemberTypes.Field | MemberTypes.Property, BindingFlags.Public | BindingFlags.Instance, loc) as MemberExpr;
9000 me.InstanceExpression = ec.CurrentInitializerVariable;
9002 if (source is CollectionOrObjectInitializers) {
9003 Expression previous = ec.CurrentInitializerVariable;
9004 ec.CurrentInitializerVariable = target;
9005 source = source.Resolve (ec);
9006 ec.CurrentInitializerVariable = previous;
9010 eclass = source.eclass;
9015 Expression expr = base.DoResolve (ec);
9020 // Ignore field initializers with default value
9022 Constant c = source as Constant;
9023 if (c != null && c.IsDefaultInitializer (type) && target.eclass == ExprClass.Variable)
9024 return EmptyExpressionStatement.Instance;
9029 protected override Expression Error_MemberLookupFailed (MemberInfo[] members)
9031 MemberInfo member = members [0];
9032 if (member.MemberType != MemberTypes.Property && member.MemberType != MemberTypes.Field)
9033 Report.Error (1913, loc, "Member `{0}' cannot be initialized. An object " +
9034 "initializer may only be used for fields, or properties", TypeManager.GetFullNameSignature (member));
9036 Report.Error (1914, loc, " Static field or property `{0}' cannot be assigned in an object initializer",
9037 TypeManager.GetFullNameSignature (member));
9042 public override void EmitStatement (EmitContext ec)
9044 if (source is CollectionOrObjectInitializers)
9047 base.EmitStatement (ec);
9052 // A collection initializer expression
9054 public class CollectionElementInitializer : Invocation
9056 public class ElementInitializerArgument : Argument
9058 public ElementInitializerArgument (Expression e)
9064 public CollectionElementInitializer (Expression argument)
9065 : base (null, new ArrayList (1), true)
9067 Arguments.Add (argument);
9068 this.loc = argument.Location;
9071 public CollectionElementInitializer (ArrayList arguments, Location loc)
9072 : base (null, arguments, true)
9077 public override Expression CreateExpressionTree (EmitContext ec)
9079 ArrayList args = new ArrayList (2);
9080 args.Add (new Argument (mg.CreateExpressionTree (ec)));
9082 ArrayList expr_initializers = new ArrayList (Arguments.Count);
9083 foreach (Argument a in Arguments)
9084 expr_initializers.Add (a.Expr.CreateExpressionTree (ec));
9086 args.Add (new Argument (new ArrayCreation (
9087 CreateExpressionTypeExpression (loc), "[]", expr_initializers, loc)));
9088 return CreateExpressionFactoryCall ("ElementInit", args);
9091 public override Expression DoResolve (EmitContext ec)
9093 if (eclass != ExprClass.Invalid)
9096 // TODO: We could call a constructor which takes element count argument,
9097 // for known types like List<T>, Dictionary<T, U>
9099 for (int i = 0; i < Arguments.Count; ++i) {
9100 Expression expr = ((Expression) Arguments [i]).Resolve (ec);
9104 Arguments [i] = new ElementInitializerArgument (expr);
9107 base.expr = new MemberAccess (ec.CurrentInitializerVariable, "Add", loc);
9109 return base.DoResolve (ec);
9114 // A block of object or collection initializers
9116 public class CollectionOrObjectInitializers : ExpressionStatement
9118 ArrayList initializers;
9120 public static readonly CollectionOrObjectInitializers Empty =
9121 new CollectionOrObjectInitializers (new ArrayList (0), Location.Null);
9123 public CollectionOrObjectInitializers (ArrayList initializers, Location loc)
9125 this.initializers = initializers;
9129 public bool IsEmpty {
9131 return initializers.Count == 0;
9135 public bool IsCollectionInitializer {
9137 return type == typeof (CollectionOrObjectInitializers);
9141 protected override void CloneTo (CloneContext clonectx, Expression target)
9143 CollectionOrObjectInitializers t = (CollectionOrObjectInitializers) target;
9145 t.initializers = new ArrayList (initializers.Count);
9146 foreach (Expression e in initializers)
9147 t.initializers.Add (e.Clone (clonectx));
9150 public override Expression CreateExpressionTree (EmitContext ec)
9152 ArrayList expr_initializers = new ArrayList (initializers.Count);
9153 foreach (Expression e in initializers) {
9154 Expression expr = e.CreateExpressionTree (ec);
9156 expr_initializers.Add (expr);
9159 return new ImplicitlyTypedArrayCreation ("[]", expr_initializers, loc);
9162 public override Expression DoResolve (EmitContext ec)
9164 if (eclass != ExprClass.Invalid)
9167 bool is_elements_initialization = false;
9168 ArrayList element_names = null;
9169 for (int i = 0; i < initializers.Count; ++i) {
9170 Expression initializer = (Expression) initializers [i];
9171 ElementInitializer element_initializer = initializer as ElementInitializer;
9174 if (element_initializer != null) {
9175 is_elements_initialization = true;
9176 element_names = new ArrayList (initializers.Count);
9177 element_names.Add (element_initializer.Name);
9179 if (!TypeManager.ImplementsInterface (ec.CurrentInitializerVariable.Type,
9180 TypeManager.ienumerable_type)) {
9181 Report.Error (1922, loc, "A field or property `{0}' cannot be initialized with a collection " +
9182 "object initializer because type `{1}' does not implement `{2}' interface",
9183 ec.CurrentInitializerVariable.GetSignatureForError (),
9184 TypeManager.CSharpName (ec.CurrentInitializerVariable.Type),
9185 TypeManager.CSharpName (TypeManager.ienumerable_type));
9190 if (is_elements_initialization == (element_initializer == null)) {
9191 Report.Error (747, initializer.Location, "Inconsistent `{0}' member declaration",
9192 is_elements_initialization ? "object initializer" : "collection initializer");
9196 if (is_elements_initialization) {
9197 if (element_names.Contains (element_initializer.Name)) {
9198 Report.Error (1912, element_initializer.Location,
9199 "An object initializer includes more than one member `{0}' initialization",
9200 element_initializer.Name);
9202 element_names.Add (element_initializer.Name);
9207 Expression e = initializer.Resolve (ec);
9208 if (e == EmptyExpressionStatement.Instance)
9209 initializers.RemoveAt (i--);
9211 initializers [i] = e;
9214 type = is_elements_initialization ? typeof (ElementInitializer) : typeof (CollectionOrObjectInitializers);
9215 eclass = ExprClass.Variable;
9219 public override void Emit (EmitContext ec)
9224 public override void EmitStatement (EmitContext ec)
9226 foreach (ExpressionStatement e in initializers)
9227 e.EmitStatement (ec);
9232 // New expression with element/object initializers
9234 public class NewInitialize : New
9237 // This class serves as a proxy for variable initializer target instances.
9238 // A real variable is assigned later when we resolve left side of an
9241 sealed class InitializerTargetExpression : Expression, IMemoryLocation
9243 NewInitialize new_instance;
9245 public InitializerTargetExpression (NewInitialize newInstance)
9247 this.type = newInstance.type;
9248 this.loc = newInstance.loc;
9249 this.eclass = newInstance.eclass;
9250 this.new_instance = newInstance;
9253 public override Expression CreateExpressionTree (EmitContext ec)
9255 // Should not be reached
9256 throw new NotSupportedException ();
9259 public override Expression DoResolve (EmitContext ec)
9264 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
9269 public override void Emit (EmitContext ec)
9271 new_instance.value_target.Emit (ec);
9274 #region IMemoryLocation Members
9276 public void AddressOf (EmitContext ec, AddressOp mode)
9278 ((IMemoryLocation)new_instance.value_target).AddressOf (ec, mode);
9284 CollectionOrObjectInitializers initializers;
9286 public NewInitialize (Expression requested_type, ArrayList arguments, CollectionOrObjectInitializers initializers, Location l)
9287 : base (requested_type, arguments, l)
9289 this.initializers = initializers;
9292 protected override void CloneTo (CloneContext clonectx, Expression t)
9294 base.CloneTo (clonectx, t);
9296 NewInitialize target = (NewInitialize) t;
9297 target.initializers = (CollectionOrObjectInitializers) initializers.Clone (clonectx);
9300 public override Expression CreateExpressionTree (EmitContext ec)
9302 ArrayList args = new ArrayList (2);
9303 args.Add (new Argument (base.CreateExpressionTree (ec)));
9304 args.Add (new Argument (initializers.CreateExpressionTree (ec)));
9306 return CreateExpressionFactoryCall (
9307 initializers.IsCollectionInitializer ? "ListInit" : "MemberInit",
9311 public override Expression DoResolve (EmitContext ec)
9313 if (eclass != ExprClass.Invalid)
9316 Expression e = base.DoResolve (ec);
9320 // Empty initializer can be optimized to simple new
9321 if (initializers.IsEmpty)
9324 Expression previous = ec.CurrentInitializerVariable;
9325 ec.CurrentInitializerVariable = new InitializerTargetExpression (this);
9326 initializers.Resolve (ec);
9327 ec.CurrentInitializerVariable = previous;
9331 public override void Emit (EmitContext ec)
9336 // If target is a value, let's use it
9338 VariableReference variable = value_target as VariableReference;
9339 if (variable != null) {
9341 StoreFromPtr (ec.ig, type);
9343 variable.Variable.EmitAssign (ec);
9345 if (value_target == null || value_target_set)
9346 value_target = new LocalTemporary (type);
9348 ((LocalTemporary) value_target).Store (ec);
9351 initializers.Emit (ec);
9353 if (variable == null)
9354 value_target.Emit (ec);
9357 public override void EmitStatement (EmitContext ec)
9359 if (initializers.IsEmpty) {
9360 base.EmitStatement (ec);
9366 if (value_target == null) {
9367 LocalTemporary variable = new LocalTemporary (type);
9368 variable.Store (ec);
9369 value_target = variable;
9372 initializers.EmitStatement (ec);
9375 public override bool HasInitializer {
9377 return !initializers.IsEmpty;
9382 public class AnonymousTypeDeclaration : Expression
9384 ArrayList parameters;
9385 readonly TypeContainer parent;
9386 static readonly ArrayList EmptyParameters = new ArrayList (0);
9388 public AnonymousTypeDeclaration (ArrayList parameters, TypeContainer parent, Location loc)
9390 this.parameters = parameters;
9391 this.parent = parent;
9395 protected override void CloneTo (CloneContext clonectx, Expression target)
9397 if (parameters == null)
9400 AnonymousTypeDeclaration t = (AnonymousTypeDeclaration) target;
9401 t.parameters = new ArrayList (parameters.Count);
9402 foreach (AnonymousTypeParameter atp in parameters)
9403 t.parameters.Add (atp.Clone (clonectx));
9406 AnonymousTypeClass CreateAnonymousType (ArrayList parameters)
9408 AnonymousTypeClass type = RootContext.ToplevelTypes.GetAnonymousType (parameters);
9412 type = AnonymousTypeClass.Create (parent, parameters, loc);
9417 type.DefineMembers ();
9421 RootContext.ToplevelTypes.AddAnonymousType (type);
9425 public override Expression DoResolve (EmitContext ec)
9427 AnonymousTypeClass anonymous_type;
9429 if (parameters == null) {
9430 anonymous_type = CreateAnonymousType (EmptyParameters);
9431 return new New (new TypeExpression (anonymous_type.TypeBuilder, loc),
9432 null, loc).Resolve (ec);
9436 ArrayList arguments = new ArrayList (parameters.Count);
9437 TypeExpression [] t_args = new TypeExpression [parameters.Count];
9438 for (int i = 0; i < parameters.Count; ++i) {
9439 Expression e = ((AnonymousTypeParameter) parameters [i]).Resolve (ec);
9445 arguments.Add (new Argument (e));
9446 t_args [i] = new TypeExpression (e.Type, e.Location);
9452 anonymous_type = CreateAnonymousType (parameters);
9453 if (anonymous_type == null)
9456 ConstructedType te = new ConstructedType (anonymous_type.TypeBuilder,
9457 new TypeArguments (loc, t_args), loc);
9459 return new New (te, arguments, loc).Resolve (ec);
9462 public override void Emit (EmitContext ec)
9464 throw new InternalErrorException ("Should not be reached");
9468 public class AnonymousTypeParameter : Expression
9470 public readonly string Name;
9471 Expression initializer;
9473 public AnonymousTypeParameter (Expression initializer, string name, Location loc)
9477 this.initializer = initializer;
9480 public AnonymousTypeParameter (Parameter parameter)
9482 this.Name = parameter.Name;
9483 this.loc = parameter.Location;
9484 this.initializer = new SimpleName (Name, loc);
9487 protected override void CloneTo (CloneContext clonectx, Expression target)
9489 AnonymousTypeParameter t = (AnonymousTypeParameter) target;
9490 t.initializer = initializer.Clone (clonectx);
9493 public override bool Equals (object o)
9495 AnonymousTypeParameter other = o as AnonymousTypeParameter;
9496 return other != null && Name == other.Name;
9499 public override int GetHashCode ()
9501 return Name.GetHashCode ();
9504 public override Expression DoResolve (EmitContext ec)
9506 Expression e = initializer.Resolve (ec);
9511 if (type == TypeManager.void_type || type == TypeManager.null_type ||
9512 type == TypeManager.anonymous_method_type || type.IsPointer) {
9513 Error_InvalidInitializer (e);
9520 protected virtual void Error_InvalidInitializer (Expression initializer)
9522 Report.Error (828, loc, "An anonymous type property `{0}' cannot be initialized with `{1}'",
9523 Name, initializer.GetSignatureForError ());
9526 public override void Emit (EmitContext ec)
9528 throw new InternalErrorException ("Should not be reached");