2 // expression.cs: Expression representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@seznam.cz)
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2003, 2004 Novell, Inc.
13 namespace Mono.CSharp {
15 using System.Collections;
16 using System.Reflection;
17 using System.Reflection.Emit;
21 /// This is just a helper class, it is generated by Unary, UnaryMutator
22 /// when an overloaded method has been found. It just emits the code for a
25 public class StaticCallExpr : ExpressionStatement {
29 public StaticCallExpr (MethodInfo m, ArrayList a, Location l)
35 eclass = ExprClass.Value;
39 public override Expression DoResolve (EmitContext ec)
42 // We are born fully resolved
47 public override void Emit (EmitContext ec)
50 Invocation.EmitArguments (ec, mi, args, false, null);
52 ec.ig.Emit (OpCodes.Call, mi);
56 static public StaticCallExpr MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
57 Expression e, Location loc)
62 args = new ArrayList (1);
63 Argument a = new Argument (e, Argument.AType.Expression);
65 // We need to resolve the arguments before sending them in !
66 if (!a.Resolve (ec, loc))
70 method = Invocation.OverloadResolve (
71 ec, (MethodGroupExpr) mg, args, false, loc);
76 return new StaticCallExpr ((MethodInfo) method, args, loc);
79 public override void EmitStatement (EmitContext ec)
82 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
83 ec.ig.Emit (OpCodes.Pop);
86 public MethodInfo Method {
91 public class ParenthesizedExpression : Expression
93 public Expression Expr;
95 public ParenthesizedExpression (Expression expr)
100 public override Expression DoResolve (EmitContext ec)
102 Expr = Expr.Resolve (ec);
106 public override void Emit (EmitContext ec)
108 throw new Exception ("Should not happen");
111 public override Location Location
114 return Expr.Location;
120 /// Unary expressions.
124 /// Unary implements unary expressions. It derives from
125 /// ExpressionStatement becuase the pre/post increment/decrement
126 /// operators can be used in a statement context.
128 public class Unary : Expression {
129 public enum Operator : byte {
130 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
131 Indirection, AddressOf, TOP
134 public Operator Oper;
135 public Expression Expr;
137 public Unary (Operator op, Expression expr, Location loc)
145 /// Returns a stringified representation of the Operator
147 static public string OperName (Operator oper)
150 case Operator.UnaryPlus:
152 case Operator.UnaryNegation:
154 case Operator.LogicalNot:
156 case Operator.OnesComplement:
158 case Operator.AddressOf:
160 case Operator.Indirection:
164 return oper.ToString ();
167 public static readonly string [] oper_names;
171 oper_names = new string [(int)Operator.TOP];
173 oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus";
174 oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation";
175 oper_names [(int) Operator.LogicalNot] = "op_LogicalNot";
176 oper_names [(int) Operator.OnesComplement] = "op_OnesComplement";
177 oper_names [(int) Operator.Indirection] = "op_Indirection";
178 oper_names [(int) Operator.AddressOf] = "op_AddressOf";
181 public static void Error_OperatorCannotBeApplied (Location loc, string oper, Type t)
183 Error_OperatorCannotBeApplied (loc, oper, TypeManager.CSharpName (t));
186 public static void Error_OperatorCannotBeApplied (Location loc, string oper, string type)
188 Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'",
192 void Error23 (Type t)
194 Error_OperatorCannotBeApplied (loc, OperName (Oper), t);
198 // This routine will attempt to simplify the unary expression when the
199 // argument is a constant.
201 Constant TryReduceConstant (EmitContext ec, Constant e)
203 Type expr_type = e.Type;
206 case Operator.UnaryPlus:
207 // Unary numeric promotions
208 if (expr_type == TypeManager.byte_type)
209 return new IntConstant (((ByteConstant)e).Value, e.Location);
210 if (expr_type == TypeManager.sbyte_type)
211 return new IntConstant (((SByteConstant)e).Value, e.Location);
212 if (expr_type == TypeManager.short_type)
213 return new IntConstant (((ShortConstant)e).Value, e.Location);
214 if (expr_type == TypeManager.ushort_type)
215 return new IntConstant (((UShortConstant)e).Value, e.Location);
216 if (expr_type == TypeManager.char_type)
217 return new IntConstant (((CharConstant)e).Value, e.Location);
219 // Predefined operators
220 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
221 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
222 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
223 expr_type == TypeManager.decimal_type)
230 case Operator.UnaryNegation:
231 // Unary numeric promotions
232 if (expr_type == TypeManager.byte_type)
233 return new IntConstant (-((ByteConstant)e).Value, e.Location);
234 if (expr_type == TypeManager.sbyte_type)
235 return new IntConstant (-((SByteConstant)e).Value, e.Location);
236 if (expr_type == TypeManager.short_type)
237 return new IntConstant (-((ShortConstant)e).Value, e.Location);
238 if (expr_type == TypeManager.ushort_type)
239 return new IntConstant (-((UShortConstant)e).Value, e.Location);
240 if (expr_type == TypeManager.char_type)
241 return new IntConstant (-((CharConstant)e).Value, e.Location);
243 // Predefined operators
244 if (expr_type == TypeManager.int32_type) {
245 int value = ((IntConstant)e).Value;
246 if (value == int.MinValue) {
247 if (ec.ConstantCheckState) {
248 ConstantFold.Error_CompileTimeOverflow (loc);
253 return new IntConstant (-value, e.Location);
255 if (expr_type == TypeManager.int64_type) {
256 long value = ((LongConstant)e).Value;
257 if (value == long.MinValue) {
258 if (ec.ConstantCheckState) {
259 ConstantFold.Error_CompileTimeOverflow (loc);
264 return new LongConstant (-value, e.Location);
267 if (expr_type == TypeManager.uint32_type) {
268 UIntLiteral uil = e as UIntLiteral;
270 if (uil.Value == 2147483648)
271 return new IntLiteral (int.MinValue, e.Location);
272 return new LongLiteral (-uil.Value, e.Location);
274 return new LongConstant (-((UIntConstant)e).Value, e.Location);
277 if (expr_type == TypeManager.uint64_type) {
278 ULongLiteral ull = e as ULongLiteral;
279 if (ull != null && ull.Value == 9223372036854775808)
280 return new LongLiteral (long.MinValue, e.Location);
284 if (expr_type == TypeManager.float_type) {
285 FloatLiteral fl = e as FloatLiteral;
286 // For better error reporting
288 fl.Value = -fl.Value;
291 return new FloatConstant (-((FloatConstant)e).Value, e.Location);
293 if (expr_type == TypeManager.double_type) {
294 DoubleLiteral dl = e as DoubleLiteral;
295 // For better error reporting
297 dl.Value = -dl.Value;
301 return new DoubleConstant (-((DoubleConstant)e).Value, e.Location);
303 if (expr_type == TypeManager.decimal_type)
304 return new DecimalConstant (-((DecimalConstant)e).Value, e.Location);
308 case Operator.LogicalNot:
309 if (expr_type != TypeManager.bool_type)
312 BoolConstant b = (BoolConstant) e;
313 return new BoolConstant (!(b.Value), b.Location);
315 case Operator.OnesComplement:
316 // Unary numeric promotions
317 if (expr_type == TypeManager.byte_type)
318 return new IntConstant (~((ByteConstant)e).Value, e.Location);
319 if (expr_type == TypeManager.sbyte_type)
320 return new IntConstant (~((SByteConstant)e).Value, e.Location);
321 if (expr_type == TypeManager.short_type)
322 return new IntConstant (~((ShortConstant)e).Value, e.Location);
323 if (expr_type == TypeManager.ushort_type)
324 return new IntConstant (~((UShortConstant)e).Value, e.Location);
325 if (expr_type == TypeManager.char_type)
326 return new IntConstant (~((CharConstant)e).Value, e.Location);
328 // Predefined operators
329 if (expr_type == TypeManager.int32_type)
330 return new IntConstant (~((IntConstant)e).Value, e.Location);
331 if (expr_type == TypeManager.uint32_type)
332 return new UIntConstant (~((UIntConstant)e).Value, e.Location);
333 if (expr_type == TypeManager.int64_type)
334 return new LongConstant (~((LongConstant)e).Value, e.Location);
335 if (expr_type == TypeManager.uint64_type)
336 return new ULongConstant (~((UIntConstant)e).Value, e.Location);
337 if (e is EnumConstant) {
338 e = TryReduceConstant (ec, ((EnumConstant)e).Child);
340 e = new EnumConstant (e, expr_type);
345 case Operator.AddressOf:
348 case Operator.Indirection:
351 throw new Exception ("Can not constant fold: " + Oper.ToString());
354 Expression ResolveOperator (EmitContext ec)
357 // Step 1: Default operations on CLI native types.
360 // Attempt to use a constant folding operation.
361 Constant cexpr = Expr as Constant;
363 cexpr = TryReduceConstant (ec, cexpr);
370 // Step 2: Perform Operator Overload location
372 Type expr_type = Expr.Type;
373 string op_name = oper_names [(int) Oper];
375 Expression mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
377 Expression e = StaticCallExpr.MakeSimpleCall (
378 ec, (MethodGroupExpr) mg, Expr, loc);
389 case Operator.LogicalNot:
390 if (expr_type != TypeManager.bool_type) {
391 Expr = ResolveBoolean (ec, Expr, loc);
398 type = TypeManager.bool_type;
401 case Operator.OnesComplement:
402 // Unary numeric promotions
403 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
404 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
405 expr_type == TypeManager.char_type)
407 type = TypeManager.int32_type;
408 return new EmptyCast (this, type);
411 // Predefined operators
412 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
413 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
414 TypeManager.IsEnumType (expr_type))
420 type = TypeManager.int32_type;
421 Expr = Convert.ImplicitUserConversion(ec, Expr, type, loc);
428 case Operator.AddressOf:
434 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){
438 IVariable variable = Expr as IVariable;
439 bool is_fixed = variable != null && variable.VerifyFixed ();
441 if (!ec.InFixedInitializer && !is_fixed) {
442 Error (212, "You can only take the address of unfixed expression inside " +
443 "of a fixed statement initializer");
447 if (ec.InFixedInitializer && is_fixed) {
448 Error (213, "You cannot use the fixed statement to take the address of an already fixed expression");
452 LocalVariableReference lr = Expr as LocalVariableReference;
454 if (lr.local_info.IsCaptured){
455 AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc);
458 lr.local_info.AddressTaken = true;
459 lr.local_info.Used = true;
462 ParameterReference pr = Expr as ParameterReference;
463 if ((pr != null) && pr.Parameter.IsCaptured) {
464 AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc);
468 // According to the specs, a variable is considered definitely assigned if you take
470 if ((variable != null) && (variable.VariableInfo != null)){
471 variable.VariableInfo.SetAssigned (ec);
474 type = TypeManager.GetPointerType (Expr.Type);
477 case Operator.Indirection:
483 if (!expr_type.IsPointer){
484 Error (193, "The * or -> operator must be applied to a pointer");
489 // We create an Indirection expression, because
490 // it can implement the IMemoryLocation.
492 return new Indirection (Expr, loc);
494 case Operator.UnaryPlus:
495 // Unary numeric promotions
496 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
497 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
498 expr_type == TypeManager.char_type)
500 return new EmptyCast (Expr, TypeManager.int32_type);
503 // Predefined operators
504 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
505 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
506 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
507 expr_type == TypeManager.decimal_type)
512 Expr = Convert.ImplicitUserConversion(ec, Expr, TypeManager.int32_type, loc);
514 // Because we can completely ignore unary +
521 case Operator.UnaryNegation:
523 // transform - - expr into expr
525 Unary u = Expr as Unary;
526 if (u != null && u.Oper == Operator.UnaryNegation) {
530 // Unary numeric promotions
531 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
532 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
533 expr_type == TypeManager.char_type)
535 type = TypeManager.int32_type;
536 return new EmptyCast (this, type);
540 // Predefined operators
542 if (expr_type == TypeManager.uint32_type) {
543 type = TypeManager.int64_type;
544 Expr = Convert.ImplicitNumericConversion (Expr, type);
548 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.int64_type ||
549 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
550 expr_type == TypeManager.decimal_type)
559 type = TypeManager.int32_type;
560 Expr = Convert.ImplicitUserConversion(ec, Expr, type, loc);
568 Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" +
569 TypeManager.CSharpName (expr_type) + "'");
573 public override Expression DoResolve (EmitContext ec)
575 if (Oper == Operator.AddressOf) {
576 Expr = Expr.DoResolveLValue (ec, new EmptyExpression ());
578 if (Expr == null || Expr.eclass != ExprClass.Variable){
579 Error (211, "Cannot take the address of the given expression");
584 Expr = Expr.Resolve (ec);
590 if (TypeManager.IsNullableValueType (Expr.Type))
591 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
594 eclass = ExprClass.Value;
595 return ResolveOperator (ec);
598 public override Expression DoResolveLValue (EmitContext ec, Expression right)
600 if (Oper == Operator.Indirection)
601 return DoResolve (ec);
606 public override void Emit (EmitContext ec)
608 ILGenerator ig = ec.ig;
611 case Operator.UnaryPlus:
612 throw new Exception ("This should be caught by Resolve");
614 case Operator.UnaryNegation:
615 if (ec.CheckState && type != TypeManager.float_type && type != TypeManager.double_type) {
616 ig.Emit (OpCodes.Ldc_I4_0);
617 if (type == TypeManager.int64_type)
618 ig.Emit (OpCodes.Conv_U8);
620 ig.Emit (OpCodes.Sub_Ovf);
623 ig.Emit (OpCodes.Neg);
628 case Operator.LogicalNot:
630 ig.Emit (OpCodes.Ldc_I4_0);
631 ig.Emit (OpCodes.Ceq);
634 case Operator.OnesComplement:
636 ig.Emit (OpCodes.Not);
639 case Operator.AddressOf:
640 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
644 throw new Exception ("This should not happen: Operator = "
649 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
651 if (Oper == Operator.LogicalNot)
652 Expr.EmitBranchable (ec, target, !onTrue);
654 base.EmitBranchable (ec, target, onTrue);
657 public override string ToString ()
659 return "Unary (" + Oper + ", " + Expr + ")";
665 // Unary operators are turned into Indirection expressions
666 // after semantic analysis (this is so we can take the address
667 // of an indirection).
669 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
671 LocalTemporary temporary;
674 public Indirection (Expression expr, Location l)
677 type = TypeManager.HasElementType (expr.Type) ? TypeManager.GetElementType (expr.Type) : expr.Type;
678 eclass = ExprClass.Variable;
682 public override void Emit (EmitContext ec)
687 LoadFromPtr (ec.ig, Type);
690 public void Emit (EmitContext ec, bool leave_copy)
694 ec.ig.Emit (OpCodes.Dup);
695 temporary = new LocalTemporary (expr.Type);
696 temporary.Store (ec);
700 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
702 prepared = prepare_for_load;
706 if (prepare_for_load)
707 ec.ig.Emit (OpCodes.Dup);
711 ec.ig.Emit (OpCodes.Dup);
712 temporary = new LocalTemporary (expr.Type);
713 temporary.Store (ec);
716 StoreFromPtr (ec.ig, type);
718 if (temporary != null) {
720 temporary.Release (ec);
724 public void AddressOf (EmitContext ec, AddressOp Mode)
729 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
731 return DoResolve (ec);
734 public override Expression DoResolve (EmitContext ec)
737 // Born fully resolved
742 public override string ToString ()
744 return "*(" + expr + ")";
747 #region IVariable Members
749 public VariableInfo VariableInfo {
753 public bool VerifyFixed ()
755 // A pointer-indirection is always fixed.
763 /// Unary Mutator expressions (pre and post ++ and --)
767 /// UnaryMutator implements ++ and -- expressions. It derives from
768 /// ExpressionStatement becuase the pre/post increment/decrement
769 /// operators can be used in a statement context.
771 /// FIXME: Idea, we could split this up in two classes, one simpler
772 /// for the common case, and one with the extra fields for more complex
773 /// classes (indexers require temporary access; overloaded require method)
776 public class UnaryMutator : ExpressionStatement {
778 public enum Mode : byte {
785 PreDecrement = IsDecrement,
786 PostIncrement = IsPost,
787 PostDecrement = IsPost | IsDecrement
791 bool is_expr = false;
792 bool recurse = false;
797 // This is expensive for the simplest case.
799 StaticCallExpr method;
801 public UnaryMutator (Mode m, Expression e, Location l)
808 static string OperName (Mode mode)
810 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
815 /// Returns whether an object of type `t' can be incremented
816 /// or decremented with add/sub (ie, basically whether we can
817 /// use pre-post incr-decr operations on it, but it is not a
818 /// System.Decimal, which we require operator overloading to catch)
820 static bool IsIncrementableNumber (Type t)
822 return (t == TypeManager.sbyte_type) ||
823 (t == TypeManager.byte_type) ||
824 (t == TypeManager.short_type) ||
825 (t == TypeManager.ushort_type) ||
826 (t == TypeManager.int32_type) ||
827 (t == TypeManager.uint32_type) ||
828 (t == TypeManager.int64_type) ||
829 (t == TypeManager.uint64_type) ||
830 (t == TypeManager.char_type) ||
831 (t.IsSubclassOf (TypeManager.enum_type)) ||
832 (t == TypeManager.float_type) ||
833 (t == TypeManager.double_type) ||
834 (t.IsPointer && t != TypeManager.void_ptr_type);
837 Expression ResolveOperator (EmitContext ec)
839 Type expr_type = expr.Type;
842 // Step 1: Perform Operator Overload location
847 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
848 op_name = "op_Increment";
850 op_name = "op_Decrement";
852 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
855 method = StaticCallExpr.MakeSimpleCall (
856 ec, (MethodGroupExpr) mg, expr, loc);
859 } else if (!IsIncrementableNumber (expr_type)) {
860 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
861 TypeManager.CSharpName (expr_type) + "'");
866 // The operand of the prefix/postfix increment decrement operators
867 // should be an expression that is classified as a variable,
868 // a property access or an indexer access
871 if (expr.eclass == ExprClass.Variable){
872 LocalVariableReference var = expr as LocalVariableReference;
873 if ((var != null) && var.IsReadOnly) {
874 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
877 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
878 expr = expr.ResolveLValue (ec, this, Location);
882 if (expr.eclass == ExprClass.Value) {
883 Error_ValueAssignment (loc);
885 expr.Error_UnexpectedKind (ec.DeclContainer, "variable, indexer or property access", loc);
893 public override Expression DoResolve (EmitContext ec)
895 expr = expr.Resolve (ec);
900 eclass = ExprClass.Value;
903 if (TypeManager.IsNullableValueType (expr.Type))
904 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
907 return ResolveOperator (ec);
910 static int PtrTypeSize (Type t)
912 return GetTypeSize (TypeManager.GetElementType (t));
916 // Loads the proper "1" into the stack based on the type, then it emits the
917 // opcode for the operation requested
919 void LoadOneAndEmitOp (EmitContext ec, Type t)
922 // Measure if getting the typecode and using that is more/less efficient
923 // that comparing types. t.GetTypeCode() is an internal call.
925 ILGenerator ig = ec.ig;
927 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
928 LongConstant.EmitLong (ig, 1);
929 else if (t == TypeManager.double_type)
930 ig.Emit (OpCodes.Ldc_R8, 1.0);
931 else if (t == TypeManager.float_type)
932 ig.Emit (OpCodes.Ldc_R4, 1.0F);
933 else if (t.IsPointer){
934 int n = PtrTypeSize (t);
937 ig.Emit (OpCodes.Sizeof, t);
939 IntConstant.EmitInt (ig, n);
941 ig.Emit (OpCodes.Ldc_I4_1);
944 // Now emit the operation
947 if (t == TypeManager.int32_type ||
948 t == TypeManager.int64_type){
949 if ((mode & Mode.IsDecrement) != 0)
950 ig.Emit (OpCodes.Sub_Ovf);
952 ig.Emit (OpCodes.Add_Ovf);
953 } else if (t == TypeManager.uint32_type ||
954 t == TypeManager.uint64_type){
955 if ((mode & Mode.IsDecrement) != 0)
956 ig.Emit (OpCodes.Sub_Ovf_Un);
958 ig.Emit (OpCodes.Add_Ovf_Un);
960 if ((mode & Mode.IsDecrement) != 0)
961 ig.Emit (OpCodes.Sub_Ovf);
963 ig.Emit (OpCodes.Add_Ovf);
966 if ((mode & Mode.IsDecrement) != 0)
967 ig.Emit (OpCodes.Sub);
969 ig.Emit (OpCodes.Add);
972 if (t == TypeManager.sbyte_type){
974 ig.Emit (OpCodes.Conv_Ovf_I1);
976 ig.Emit (OpCodes.Conv_I1);
977 } else if (t == TypeManager.byte_type){
979 ig.Emit (OpCodes.Conv_Ovf_U1);
981 ig.Emit (OpCodes.Conv_U1);
982 } else if (t == TypeManager.short_type){
984 ig.Emit (OpCodes.Conv_Ovf_I2);
986 ig.Emit (OpCodes.Conv_I2);
987 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
989 ig.Emit (OpCodes.Conv_Ovf_U2);
991 ig.Emit (OpCodes.Conv_U2);
996 void EmitCode (EmitContext ec, bool is_expr)
999 this.is_expr = is_expr;
1000 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1003 public override void Emit (EmitContext ec)
1006 // We use recurse to allow ourselfs to be the source
1007 // of an assignment. This little hack prevents us from
1008 // having to allocate another expression
1011 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1013 LoadOneAndEmitOp (ec, expr.Type);
1015 ec.ig.Emit (OpCodes.Call, method.Method);
1020 EmitCode (ec, true);
1023 public override void EmitStatement (EmitContext ec)
1025 EmitCode (ec, false);
1030 /// Base class for the `Is' and `As' classes.
1034 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1037 public abstract class Probe : Expression {
1038 public Expression ProbeType;
1039 protected Expression expr;
1040 protected TypeExpr probe_type_expr;
1042 public Probe (Expression expr, Expression probe_type, Location l)
1044 ProbeType = probe_type;
1049 public Expression Expr {
1055 public override Expression DoResolve (EmitContext ec)
1057 probe_type_expr = ProbeType.ResolveAsTypeTerminal (ec, false);
1058 if (probe_type_expr == null)
1061 expr = expr.Resolve (ec);
1065 if (expr.Type.IsPointer) {
1066 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1074 /// Implementation of the `is' operator.
1076 public class Is : Probe {
1077 public Is (Expression expr, Expression probe_type, Location l)
1078 : base (expr, probe_type, l)
1083 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1088 public override void Emit (EmitContext ec)
1090 ILGenerator ig = ec.ig;
1095 case Action.AlwaysFalse:
1096 ig.Emit (OpCodes.Pop);
1097 IntConstant.EmitInt (ig, 0);
1099 case Action.AlwaysTrue:
1100 ig.Emit (OpCodes.Pop);
1101 IntConstant.EmitInt (ig, 1);
1103 case Action.LeaveOnStack:
1104 // the `e != null' rule.
1105 ig.Emit (OpCodes.Ldnull);
1106 ig.Emit (OpCodes.Ceq);
1107 ig.Emit (OpCodes.Ldc_I4_0);
1108 ig.Emit (OpCodes.Ceq);
1111 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1112 ig.Emit (OpCodes.Ldnull);
1113 ig.Emit (OpCodes.Cgt_Un);
1116 throw new Exception ("never reached");
1119 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1121 ILGenerator ig = ec.ig;
1124 case Action.AlwaysFalse:
1126 ig.Emit (OpCodes.Br, target);
1129 case Action.AlwaysTrue:
1131 ig.Emit (OpCodes.Br, target);
1134 case Action.LeaveOnStack:
1135 // the `e != null' rule.
1137 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1141 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1142 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1145 throw new Exception ("never reached");
1148 public override Expression DoResolve (EmitContext ec)
1150 Expression e = base.DoResolve (ec);
1152 if ((e == null) || (expr == null))
1155 Type etype = expr.Type;
1156 type = TypeManager.bool_type;
1157 eclass = ExprClass.Value;
1160 // First case, if at compile time, there is an implicit conversion
1161 // then e != null (objects) or true (value types)
1163 Type probe_type = probe_type_expr.Type;
1164 e = Convert.ImplicitConversionStandard (ec, expr, probe_type, loc);
1167 if (etype.IsValueType)
1168 action = Action.AlwaysTrue;
1170 action = Action.LeaveOnStack;
1172 Constant c = e as Constant;
1173 if (c != null && c.GetValue () == null) {
1174 action = Action.AlwaysFalse;
1175 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
1176 TypeManager.CSharpName (probe_type));
1178 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
1179 TypeManager.CSharpName (probe_type));
1184 if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1185 if (TypeManager.IsGenericParameter (etype))
1186 expr = new BoxedCast (expr, etype);
1189 // Second case: explicit reference convresion
1191 if (expr is NullLiteral)
1192 action = Action.AlwaysFalse;
1194 action = Action.Probe;
1195 } else if (TypeManager.ContainsGenericParameters (etype) ||
1196 TypeManager.ContainsGenericParameters (probe_type)) {
1197 expr = new BoxedCast (expr, etype);
1198 action = Action.Probe;
1200 action = Action.AlwaysFalse;
1201 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1202 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1210 /// Implementation of the `as' operator.
1212 public class As : Probe {
1213 public As (Expression expr, Expression probe_type, Location l)
1214 : base (expr, probe_type, l)
1218 bool do_isinst = false;
1219 Expression resolved_type;
1221 public override void Emit (EmitContext ec)
1223 ILGenerator ig = ec.ig;
1228 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1231 static void Error_CannotConvertType (Type source, Type target, Location loc)
1233 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1234 TypeManager.CSharpName (source),
1235 TypeManager.CSharpName (target));
1238 public override Expression DoResolve (EmitContext ec)
1240 if (resolved_type == null) {
1241 resolved_type = base.DoResolve (ec);
1243 if (resolved_type == null)
1247 type = probe_type_expr.Type;
1248 eclass = ExprClass.Value;
1249 Type etype = expr.Type;
1251 if (type.IsValueType) {
1252 Report.Error (77, loc, "The as operator must be used with a reference type (`" +
1253 TypeManager.CSharpName (type) + "' is a value type)");
1260 // If the type is a type parameter, ensure
1261 // that it is constrained by a class
1263 TypeParameterExpr tpe = probe_type_expr as TypeParameterExpr;
1265 Constraints constraints = tpe.TypeParameter.Constraints;
1268 if (constraints == null)
1271 if (!constraints.HasClassConstraint)
1272 if ((constraints.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == 0)
1276 Report.Error (413, loc,
1277 "The as operator requires that the `{0}' type parameter be constrained by a class",
1278 probe_type_expr.GetSignatureForError ());
1284 Expression e = Convert.ImplicitConversion (ec, expr, type, loc);
1291 if (Convert.ExplicitReferenceConversionExists (etype, type)){
1292 if (TypeManager.IsGenericParameter (etype))
1293 expr = new BoxedCast (expr, etype);
1299 if (TypeManager.ContainsGenericParameters (etype) ||
1300 TypeManager.ContainsGenericParameters (type)) {
1301 expr = new BoxedCast (expr, etype);
1306 Error_CannotConvertType (etype, type, loc);
1310 public override bool GetAttributableValue (Type valueType, out object value)
1312 return expr.GetAttributableValue (valueType, out value);
1317 /// This represents a typecast in the source language.
1319 /// FIXME: Cast expressions have an unusual set of parsing
1320 /// rules, we need to figure those out.
1322 public class Cast : Expression {
1323 Expression target_type;
1326 public Cast (Expression cast_type, Expression expr)
1327 : this (cast_type, expr, cast_type.Location)
1331 public Cast (Expression cast_type, Expression expr, Location loc)
1333 this.target_type = cast_type;
1337 if (target_type == TypeManager.system_void_expr)
1338 Error_VoidInvalidInTheContext (loc);
1341 public Expression TargetType {
1342 get { return target_type; }
1345 public Expression Expr {
1346 get { return expr; }
1347 set { expr = value; }
1350 public override Expression DoResolve (EmitContext ec)
1352 expr = expr.Resolve (ec);
1356 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1362 if (type.IsAbstract && type.IsSealed) {
1363 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1367 eclass = ExprClass.Value;
1369 Constant c = expr as Constant;
1371 c = c.TryReduce (ec, type, loc);
1376 if (type.IsPointer && !ec.InUnsafe) {
1380 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1384 public override void Emit (EmitContext ec)
1386 throw new Exception ("Should not happen");
1391 /// Binary operators
1393 public class Binary : Expression {
1394 public enum Operator : byte {
1395 Multiply, Division, Modulus,
1396 Addition, Subtraction,
1397 LeftShift, RightShift,
1398 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1399 Equality, Inequality,
1409 Expression left, right;
1411 // This must be kept in sync with Operator!!!
1412 public static readonly string [] oper_names;
1416 oper_names = new string [(int) Operator.TOP];
1418 oper_names [(int) Operator.Multiply] = "op_Multiply";
1419 oper_names [(int) Operator.Division] = "op_Division";
1420 oper_names [(int) Operator.Modulus] = "op_Modulus";
1421 oper_names [(int) Operator.Addition] = "op_Addition";
1422 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1423 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1424 oper_names [(int) Operator.RightShift] = "op_RightShift";
1425 oper_names [(int) Operator.LessThan] = "op_LessThan";
1426 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1427 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1428 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1429 oper_names [(int) Operator.Equality] = "op_Equality";
1430 oper_names [(int) Operator.Inequality] = "op_Inequality";
1431 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1432 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1433 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1434 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1435 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1438 public Binary (Operator oper, Expression left, Expression right)
1443 this.loc = left.Location;
1446 public Operator Oper {
1455 public Expression Left {
1464 public Expression Right {
1475 /// Returns a stringified representation of the Operator
1477 public static string OperName (Operator oper)
1480 case Operator.Multiply:
1482 case Operator.Division:
1484 case Operator.Modulus:
1486 case Operator.Addition:
1488 case Operator.Subtraction:
1490 case Operator.LeftShift:
1492 case Operator.RightShift:
1494 case Operator.LessThan:
1496 case Operator.GreaterThan:
1498 case Operator.LessThanOrEqual:
1500 case Operator.GreaterThanOrEqual:
1502 case Operator.Equality:
1504 case Operator.Inequality:
1506 case Operator.BitwiseAnd:
1508 case Operator.BitwiseOr:
1510 case Operator.ExclusiveOr:
1512 case Operator.LogicalOr:
1514 case Operator.LogicalAnd:
1518 return oper.ToString ();
1521 public override string ToString ()
1523 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1524 right.ToString () + ")";
1527 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1529 if (expr.Type == target_type)
1532 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1535 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1538 34, loc, "Operator `" + OperName (oper)
1539 + "' is ambiguous on operands of type `"
1540 + TypeManager.CSharpName (l) + "' "
1541 + "and `" + TypeManager.CSharpName (r)
1545 bool IsConvertible (EmitContext ec, Expression le, Expression re, Type t)
1547 return Convert.ImplicitConversionExists (ec, le, t) && Convert.ImplicitConversionExists (ec, re, t);
1550 bool VerifyApplicable_Predefined (EmitContext ec, Type t)
1552 if (!IsConvertible (ec, left, right, t))
1554 left = ForceConversion (ec, left, t);
1555 right = ForceConversion (ec, right, t);
1560 bool IsApplicable_String (EmitContext ec, Expression le, Expression re, Operator oper)
1562 bool l = Convert.ImplicitConversionExists (ec, le, TypeManager.string_type);
1563 bool r = Convert.ImplicitConversionExists (ec, re, TypeManager.string_type);
1565 if (oper == Operator.Equality || oper == Operator.Inequality)
1567 if (oper == Operator.Addition)
1572 bool OverloadResolve_PredefinedString (EmitContext ec, Operator oper)
1574 if (!IsApplicable_String (ec, left, right, oper))
1576 Type t = TypeManager.string_type;
1577 if (Convert.ImplicitConversionExists (ec, left, t))
1578 left = ForceConversion (ec, left, t);
1579 if (Convert.ImplicitConversionExists (ec, right, t))
1580 right = ForceConversion (ec, right, t);
1585 bool OverloadResolve_PredefinedIntegral (EmitContext ec)
1587 return VerifyApplicable_Predefined (ec, TypeManager.int32_type) ||
1588 VerifyApplicable_Predefined (ec, TypeManager.uint32_type) ||
1589 VerifyApplicable_Predefined (ec, TypeManager.int64_type) ||
1590 VerifyApplicable_Predefined (ec, TypeManager.uint64_type) ||
1594 bool OverloadResolve_PredefinedFloating (EmitContext ec)
1596 return VerifyApplicable_Predefined (ec, TypeManager.float_type) ||
1597 VerifyApplicable_Predefined (ec, TypeManager.double_type) ||
1601 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1603 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1606 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1608 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1612 void Error_OperatorCannotBeApplied ()
1614 Error_OperatorCannotBeApplied (Location, OperName (oper), TypeManager.CSharpName (left.Type),
1615 TypeManager.CSharpName(right.Type));
1618 static bool is_unsigned (Type t)
1620 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1621 t == TypeManager.short_type || t == TypeManager.byte_type);
1624 Expression Make32or64 (EmitContext ec, Expression e)
1628 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1629 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1631 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1634 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1637 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1640 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1646 Expression CheckShiftArguments (EmitContext ec)
1648 Expression new_left = Make32or64 (ec, left);
1649 Expression new_right = ForceConversion (ec, right, TypeManager.int32_type);
1650 if (new_left == null || new_right == null) {
1651 Error_OperatorCannotBeApplied ();
1654 type = new_left.Type;
1655 int shiftmask = (type == TypeManager.int32_type || type == TypeManager.uint32_type) ? 31 : 63;
1657 right = new Binary (Binary.Operator.BitwiseAnd, new_right, new IntConstant (shiftmask, loc)).DoResolve (ec);
1662 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1663 // i.e., not invoke op_Equality.
1665 static bool EqualsNullIsReferenceEquals (Type t)
1667 return t == TypeManager.object_type || t == TypeManager.string_type ||
1668 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1671 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1673 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1674 "Possible unintended reference comparison; to get a value comparison, " +
1675 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1678 Expression ResolveOperator (EmitContext ec)
1681 Type r = right.Type;
1683 if (oper == Operator.Equality || oper == Operator.Inequality){
1684 if (TypeManager.IsGenericParameter (l) && (right is NullLiteral)) {
1685 if (l.BaseType == TypeManager.value_type) {
1686 Error_OperatorCannotBeApplied ();
1690 left = new BoxedCast (left, TypeManager.object_type);
1691 Type = TypeManager.bool_type;
1695 if (TypeManager.IsGenericParameter (r) && (left is NullLiteral)) {
1696 if (r.BaseType == TypeManager.value_type) {
1697 Error_OperatorCannotBeApplied ();
1701 right = new BoxedCast (right, TypeManager.object_type);
1702 Type = TypeManager.bool_type;
1707 // Optimize out call to op_Equality in a few cases.
1709 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1710 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1711 Type = TypeManager.bool_type;
1716 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1717 Type = TypeManager.bool_type;
1723 // Do not perform operator overload resolution when both sides are
1726 Expression left_operators = null, right_operators = null;
1727 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))) {
1729 // Step 1: Perform Operator Overload location
1731 string op = oper_names [(int) oper];
1733 MethodGroupExpr union;
1734 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc);
1736 right_operators = MemberLookup (
1737 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc);
1738 union = Invocation.MakeUnionSet (left_operators, right_operators, loc);
1740 union = (MethodGroupExpr) left_operators;
1742 if (union != null) {
1743 ArrayList args = new ArrayList (2);
1744 args.Add (new Argument (left, Argument.AType.Expression));
1745 args.Add (new Argument (right, Argument.AType.Expression));
1747 MethodBase method = Invocation.OverloadResolve (ec, union, args, true, Location.Null);
1749 if (method != null) {
1750 MethodInfo mi = (MethodInfo) method;
1751 return new BinaryMethod (mi.ReturnType, method, args);
1757 // Step 0: String concatenation (because overloading will get this wrong)
1759 if (oper == Operator.Addition){
1761 // If any of the arguments is a string, cast to string
1764 // Simple constant folding
1765 if (left is StringConstant && right is StringConstant)
1766 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1768 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1770 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1771 Error_OperatorCannotBeApplied ();
1775 // try to fold it in on the left
1776 if (left is StringConcat) {
1779 // We have to test here for not-null, since we can be doubly-resolved
1780 // take care of not appending twice
1783 type = TypeManager.string_type;
1784 ((StringConcat) left).Append (ec, right);
1785 return left.Resolve (ec);
1791 // Otherwise, start a new concat expression
1792 return new StringConcat (ec, loc, left, right).Resolve (ec);
1796 // Transform a + ( - b) into a - b
1798 if (right is Unary){
1799 Unary right_unary = (Unary) right;
1801 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1802 oper = Operator.Subtraction;
1803 right = right_unary.Expr;
1809 if (oper == Operator.Equality || oper == Operator.Inequality){
1810 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1811 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1812 Error_OperatorCannotBeApplied ();
1816 type = TypeManager.bool_type;
1820 if (l.IsPointer || r.IsPointer) {
1821 if (l.IsPointer && r.IsPointer) {
1822 type = TypeManager.bool_type;
1826 if (l.IsPointer && r == TypeManager.null_type) {
1827 right = new EmptyCast (NullPointer.Null, l);
1828 type = TypeManager.bool_type;
1832 if (r.IsPointer && l == TypeManager.null_type) {
1833 left = new EmptyCast (NullPointer.Null, r);
1834 type = TypeManager.bool_type;
1840 if (l.IsGenericParameter && r.IsGenericParameter) {
1841 GenericConstraints l_gc, r_gc;
1843 l_gc = TypeManager.GetTypeParameterConstraints (l);
1844 r_gc = TypeManager.GetTypeParameterConstraints (r);
1846 if ((l_gc == null) || (r_gc == null) ||
1847 !(l_gc.HasReferenceTypeConstraint || l_gc.HasClassConstraint) ||
1848 !(r_gc.HasReferenceTypeConstraint || r_gc.HasClassConstraint)) {
1849 Error_OperatorCannotBeApplied ();
1857 // operator != (object a, object b)
1858 // operator == (object a, object b)
1860 // For this to be used, both arguments have to be reference-types.
1861 // Read the rationale on the spec (14.9.6)
1863 if (!(l.IsValueType || r.IsValueType)){
1864 type = TypeManager.bool_type;
1870 // Also, a standard conversion must exist from either one
1872 bool left_to_right =
1873 Convert.ImplicitStandardConversionExists (left, r);
1874 bool right_to_left = !left_to_right &&
1875 Convert.ImplicitStandardConversionExists (right, l);
1877 if (!left_to_right && !right_to_left) {
1878 Error_OperatorCannotBeApplied ();
1882 if (left_to_right && left_operators != null &&
1883 RootContext.WarningLevel >= 2) {
1884 ArrayList args = new ArrayList (2);
1885 args.Add (new Argument (left, Argument.AType.Expression));
1886 args.Add (new Argument (left, Argument.AType.Expression));
1887 MethodBase method = Invocation.OverloadResolve (
1888 ec, (MethodGroupExpr) left_operators, args, true, Location.Null);
1890 Warning_UnintendedReferenceComparison (loc, "right", l);
1893 if (right_to_left && right_operators != null &&
1894 RootContext.WarningLevel >= 2) {
1895 ArrayList args = new ArrayList (2);
1896 args.Add (new Argument (right, Argument.AType.Expression));
1897 args.Add (new Argument (right, Argument.AType.Expression));
1898 MethodBase method = Invocation.OverloadResolve (
1899 ec, (MethodGroupExpr) right_operators, args, true, Location.Null);
1901 Warning_UnintendedReferenceComparison (loc, "left", r);
1905 // We are going to have to convert to an object to compare
1907 if (l != TypeManager.object_type)
1908 left = new EmptyCast (left, TypeManager.object_type);
1909 if (r != TypeManager.object_type)
1910 right = new EmptyCast (right, TypeManager.object_type);
1916 // Only perform numeric promotions on:
1917 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
1919 if (oper == Operator.Addition || oper == Operator.Subtraction) {
1920 if (TypeManager.IsDelegateType (l)){
1921 if (((right.eclass == ExprClass.MethodGroup) ||
1922 (r == TypeManager.anonymous_method_type))){
1923 if ((RootContext.Version != LanguageVersion.ISO_1)){
1924 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
1932 if (TypeManager.IsDelegateType (r) || right is NullLiteral){
1934 ArrayList args = new ArrayList (2);
1936 args = new ArrayList (2);
1937 args.Add (new Argument (left, Argument.AType.Expression));
1938 args.Add (new Argument (right, Argument.AType.Expression));
1940 if (oper == Operator.Addition)
1941 method = TypeManager.delegate_combine_delegate_delegate;
1943 method = TypeManager.delegate_remove_delegate_delegate;
1945 if (!TypeManager.IsEqual (l, r) && !(right is NullLiteral)) {
1946 Error_OperatorCannotBeApplied ();
1950 return new BinaryDelegate (l, method, args);
1955 // Pointer arithmetic:
1957 // T* operator + (T* x, int y);
1958 // T* operator + (T* x, uint y);
1959 // T* operator + (T* x, long y);
1960 // T* operator + (T* x, ulong y);
1962 // T* operator + (int y, T* x);
1963 // T* operator + (uint y, T *x);
1964 // T* operator + (long y, T *x);
1965 // T* operator + (ulong y, T *x);
1967 // T* operator - (T* x, int y);
1968 // T* operator - (T* x, uint y);
1969 // T* operator - (T* x, long y);
1970 // T* operator - (T* x, ulong y);
1972 // long operator - (T* x, T *y)
1975 if (r.IsPointer && oper == Operator.Subtraction){
1977 return new PointerArithmetic (
1978 false, left, right, TypeManager.int64_type,
1981 Expression t = Make32or64 (ec, right);
1983 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
1985 } else if (r.IsPointer && oper == Operator.Addition){
1986 Expression t = Make32or64 (ec, left);
1988 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
1993 // Enumeration operators
1995 bool lie = TypeManager.IsEnumType (l);
1996 bool rie = TypeManager.IsEnumType (r);
2000 // U operator - (E e, E f)
2002 if (oper == Operator.Subtraction){
2004 type = TypeManager.EnumToUnderlying (l);
2007 Error_OperatorCannotBeApplied ();
2013 // operator + (E e, U x)
2014 // operator - (E e, U x)
2016 if (oper == Operator.Addition || oper == Operator.Subtraction){
2017 Type enum_type = lie ? l : r;
2018 Type other_type = lie ? r : l;
2019 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2021 if (underlying_type != other_type){
2022 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2032 Error_OperatorCannotBeApplied ();
2041 temp = Convert.ImplicitConversion (ec, right, l, loc);
2045 Error_OperatorCannotBeApplied ();
2049 temp = Convert.ImplicitConversion (ec, left, r, loc);
2054 Error_OperatorCannotBeApplied ();
2059 if (oper == Operator.Equality || oper == Operator.Inequality ||
2060 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2061 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2062 if (left.Type != right.Type){
2063 Error_OperatorCannotBeApplied ();
2066 type = TypeManager.bool_type;
2070 if (oper == Operator.BitwiseAnd ||
2071 oper == Operator.BitwiseOr ||
2072 oper == Operator.ExclusiveOr){
2073 if (left.Type != right.Type){
2074 Error_OperatorCannotBeApplied ();
2080 Error_OperatorCannotBeApplied ();
2084 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2085 return CheckShiftArguments (ec);
2087 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2088 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2089 type = TypeManager.bool_type;
2093 left_operators = l == TypeManager.bool_type ?
2094 left : Convert.ImplicitUserConversion (ec, left, TypeManager.bool_type, loc);
2095 right_operators = r == TypeManager.bool_type ?
2096 right : Convert.ImplicitUserConversion (ec, right, TypeManager.bool_type, loc);
2098 if (left_operators != null && right_operators != null) {
2099 left = left_operators;
2100 right = right_operators;
2101 type = TypeManager.bool_type;
2105 Expression e = new ConditionalLogicalOperator (
2106 oper == Operator.LogicalAnd, left, right, l, loc);
2107 return e.Resolve (ec);
2110 Expression orig_left = left;
2111 Expression orig_right = right;
2114 // operator & (bool x, bool y)
2115 // operator | (bool x, bool y)
2116 // operator ^ (bool x, bool y)
2118 if (oper == Operator.BitwiseAnd ||
2119 oper == Operator.BitwiseOr ||
2120 oper == Operator.ExclusiveOr) {
2121 if (OverloadResolve_PredefinedIntegral (ec)) {
2122 if (IsConvertible (ec, orig_left, orig_right, TypeManager.bool_type)) {
2123 Error_OperatorAmbiguous (loc, oper, l, r);
2127 if (oper == Operator.BitwiseOr && l != r && !(orig_right is Constant) && right is OpcodeCast &&
2128 (r == TypeManager.sbyte_type || r == TypeManager.short_type ||
2129 r == TypeManager.int32_type || r == TypeManager.int64_type)) {
2130 Report.Warning (675, 3, loc, "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first",
2131 TypeManager.CSharpName (r));
2134 } else if (!VerifyApplicable_Predefined (ec, TypeManager.bool_type)) {
2135 Error_OperatorCannotBeApplied ();
2142 // Pointer comparison
2144 if (l.IsPointer && r.IsPointer){
2145 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2146 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2147 type = TypeManager.bool_type;
2152 if (OverloadResolve_PredefinedIntegral (ec)) {
2153 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2154 Error_OperatorAmbiguous (loc, oper, l, r);
2157 } else if (OverloadResolve_PredefinedFloating (ec)) {
2158 if (IsConvertible (ec, orig_left, orig_right, TypeManager.decimal_type) ||
2159 IsApplicable_String (ec, orig_left, orig_right, oper)) {
2160 Error_OperatorAmbiguous (loc, oper, l, r);
2163 } else if (VerifyApplicable_Predefined (ec, TypeManager.decimal_type)) {
2164 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2165 Error_OperatorAmbiguous (loc, oper, l, r);
2168 } else if (!OverloadResolve_PredefinedString (ec, oper)) {
2169 Error_OperatorCannotBeApplied ();
2173 if (oper == Operator.Equality ||
2174 oper == Operator.Inequality ||
2175 oper == Operator.LessThanOrEqual ||
2176 oper == Operator.LessThan ||
2177 oper == Operator.GreaterThanOrEqual ||
2178 oper == Operator.GreaterThan)
2179 type = TypeManager.bool_type;
2184 if (l == TypeManager.decimal_type || l == TypeManager.string_type || r == TypeManager.string_type) {
2186 if (r == TypeManager.string_type)
2188 MethodGroupExpr ops = (MethodGroupExpr) MemberLookup (
2189 ec.ContainerType, lookup, oper_names [(int) oper],
2190 MemberTypes.Method, AllBindingFlags, loc);
2191 ArrayList args = new ArrayList (2);
2192 args.Add (new Argument (left, Argument.AType.Expression));
2193 args.Add (new Argument (right, Argument.AType.Expression));
2194 MethodBase method = Invocation.OverloadResolve (ec, ops, args, true, Location.Null);
2195 return new BinaryMethod (type, method, args);
2201 Constant EnumLiftUp (Constant left, Constant right)
2204 case Operator.BitwiseOr:
2205 case Operator.BitwiseAnd:
2206 case Operator.ExclusiveOr:
2207 case Operator.Equality:
2208 case Operator.Inequality:
2209 case Operator.LessThan:
2210 case Operator.LessThanOrEqual:
2211 case Operator.GreaterThan:
2212 case Operator.GreaterThanOrEqual:
2213 if (left is EnumConstant)
2216 if (left.IsZeroInteger)
2217 return new EnumConstant (left, right.Type);
2221 case Operator.Addition:
2222 case Operator.Subtraction:
2225 case Operator.Multiply:
2226 case Operator.Division:
2227 case Operator.Modulus:
2228 case Operator.LeftShift:
2229 case Operator.RightShift:
2230 if (right is EnumConstant || left is EnumConstant)
2234 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2238 public override Expression DoResolve (EmitContext ec)
2243 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2244 left = ((ParenthesizedExpression) left).Expr;
2245 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2249 if (left.eclass == ExprClass.Type) {
2250 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2254 left = left.Resolve (ec);
2259 Constant lc = left as Constant;
2260 if (lc != null && lc.Type == TypeManager.bool_type &&
2261 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2262 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2264 // TODO: make a sense to resolve unreachable expression as we do for statement
2265 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2269 right = right.Resolve (ec);
2273 eclass = ExprClass.Value;
2274 Constant rc = right as Constant;
2276 // The conversion rules are ignored in enum context but why
2277 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2278 left = lc = EnumLiftUp (lc, rc);
2282 right = rc = EnumLiftUp (rc, lc);
2287 if (oper == Operator.BitwiseAnd) {
2288 if (rc != null && rc.IsZeroInteger) {
2289 return lc is EnumConstant ?
2290 new EnumConstant (rc, lc.Type):
2294 if (lc != null && lc.IsZeroInteger) {
2295 return rc is EnumConstant ?
2296 new EnumConstant (lc, rc.Type):
2300 else if (oper == Operator.BitwiseOr) {
2301 if (lc is EnumConstant &&
2302 rc != null && rc.IsZeroInteger)
2304 if (rc is EnumConstant &&
2305 lc != null && lc.IsZeroInteger)
2307 } else if (oper == Operator.LogicalAnd) {
2308 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2310 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2314 if (rc != null && lc != null){
2315 int prev_e = Report.Errors;
2316 Expression e = ConstantFold.BinaryFold (
2317 ec, oper, lc, rc, loc);
2318 if (e != null || Report.Errors != prev_e)
2323 if ((left is NullLiteral || left.Type.IsValueType) &&
2324 (right is NullLiteral || right.Type.IsValueType) &&
2325 !(left is NullLiteral && right is NullLiteral) &&
2326 (TypeManager.IsNullableType (left.Type) || TypeManager.IsNullableType (right.Type)))
2327 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2330 // Comparison warnings
2331 if (oper == Operator.Equality || oper == Operator.Inequality ||
2332 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2333 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2334 if (left.Equals (right)) {
2335 Report.Warning (1718, 3, loc, "Comparison made to same variable; did you mean to compare something else?");
2337 CheckUselessComparison (lc, right.Type);
2338 CheckUselessComparison (rc, left.Type);
2341 return ResolveOperator (ec);
2344 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2349 private void CheckUselessComparison (Constant c, Type type)
2351 if (c == null || !IsTypeIntegral (type)
2352 || c is StringConstant
2353 || c is BoolConstant
2354 || c is CharConstant
2355 || c is FloatConstant
2356 || c is DoubleConstant
2357 || c is DecimalConstant
2363 if (c is ULongConstant) {
2364 ulong uvalue = ((ULongConstant) c).Value;
2365 if (uvalue > long.MaxValue) {
2366 if (type == TypeManager.byte_type ||
2367 type == TypeManager.sbyte_type ||
2368 type == TypeManager.short_type ||
2369 type == TypeManager.ushort_type ||
2370 type == TypeManager.int32_type ||
2371 type == TypeManager.uint32_type ||
2372 type == TypeManager.int64_type)
2373 WarnUselessComparison (type);
2376 value = (long) uvalue;
2378 else if (c is ByteConstant)
2379 value = ((ByteConstant) c).Value;
2380 else if (c is SByteConstant)
2381 value = ((SByteConstant) c).Value;
2382 else if (c is ShortConstant)
2383 value = ((ShortConstant) c).Value;
2384 else if (c is UShortConstant)
2385 value = ((UShortConstant) c).Value;
2386 else if (c is IntConstant)
2387 value = ((IntConstant) c).Value;
2388 else if (c is UIntConstant)
2389 value = ((UIntConstant) c).Value;
2390 else if (c is LongConstant)
2391 value = ((LongConstant) c).Value;
2394 if (IsValueOutOfRange (value, type))
2395 WarnUselessComparison (type);
2400 private bool IsValueOutOfRange (long value, Type type)
2402 if (IsTypeUnsigned (type) && value < 0)
2404 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2405 type == TypeManager.byte_type && value >= 0x100 ||
2406 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2407 type == TypeManager.ushort_type && value >= 0x10000 ||
2408 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2409 type == TypeManager.uint32_type && value >= 0x100000000;
2412 private static bool IsTypeIntegral (Type type)
2414 return type == TypeManager.uint64_type ||
2415 type == TypeManager.int64_type ||
2416 type == TypeManager.uint32_type ||
2417 type == TypeManager.int32_type ||
2418 type == TypeManager.ushort_type ||
2419 type == TypeManager.short_type ||
2420 type == TypeManager.sbyte_type ||
2421 type == TypeManager.byte_type;
2424 private static bool IsTypeUnsigned (Type type)
2426 return type == TypeManager.uint64_type ||
2427 type == TypeManager.uint32_type ||
2428 type == TypeManager.ushort_type ||
2429 type == TypeManager.byte_type;
2432 private void WarnUselessComparison (Type type)
2434 Report.Warning (652, 2, loc, "Comparison to integral constant is useless; the constant is outside the range of type `{0}'",
2435 TypeManager.CSharpName (type));
2439 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2440 /// context of a conditional bool expression. This function will return
2441 /// false if it is was possible to use EmitBranchable, or true if it was.
2443 /// The expression's code is generated, and we will generate a branch to `target'
2444 /// if the resulting expression value is equal to isTrue
2446 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2448 ILGenerator ig = ec.ig;
2451 // This is more complicated than it looks, but its just to avoid
2452 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2453 // but on top of that we want for == and != to use a special path
2454 // if we are comparing against null
2456 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2457 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2460 // put the constant on the rhs, for simplicity
2462 if (left is Constant) {
2463 Expression swap = right;
2468 if (((Constant) right).IsZeroInteger) {
2471 ig.Emit (OpCodes.Brtrue, target);
2473 ig.Emit (OpCodes.Brfalse, target);
2476 } else if (right is BoolConstant) {
2478 if (my_on_true != ((BoolConstant) right).Value)
2479 ig.Emit (OpCodes.Brtrue, target);
2481 ig.Emit (OpCodes.Brfalse, target);
2486 } else if (oper == Operator.LogicalAnd) {
2489 Label tests_end = ig.DefineLabel ();
2491 left.EmitBranchable (ec, tests_end, false);
2492 right.EmitBranchable (ec, target, true);
2493 ig.MarkLabel (tests_end);
2496 // This optimizes code like this
2497 // if (true && i > 4)
2499 if (!(left is Constant))
2500 left.EmitBranchable (ec, target, false);
2502 if (!(right is Constant))
2503 right.EmitBranchable (ec, target, false);
2508 } else if (oper == Operator.LogicalOr){
2510 left.EmitBranchable (ec, target, true);
2511 right.EmitBranchable (ec, target, true);
2514 Label tests_end = ig.DefineLabel ();
2515 left.EmitBranchable (ec, tests_end, true);
2516 right.EmitBranchable (ec, target, false);
2517 ig.MarkLabel (tests_end);
2522 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2523 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2524 oper == Operator.Equality || oper == Operator.Inequality)) {
2525 base.EmitBranchable (ec, target, onTrue);
2533 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2536 case Operator.Equality:
2538 ig.Emit (OpCodes.Beq, target);
2540 ig.Emit (OpCodes.Bne_Un, target);
2543 case Operator.Inequality:
2545 ig.Emit (OpCodes.Bne_Un, target);
2547 ig.Emit (OpCodes.Beq, target);
2550 case Operator.LessThan:
2553 ig.Emit (OpCodes.Blt_Un, target);
2555 ig.Emit (OpCodes.Blt, target);
2558 ig.Emit (OpCodes.Bge_Un, target);
2560 ig.Emit (OpCodes.Bge, target);
2563 case Operator.GreaterThan:
2566 ig.Emit (OpCodes.Bgt_Un, target);
2568 ig.Emit (OpCodes.Bgt, target);
2571 ig.Emit (OpCodes.Ble_Un, target);
2573 ig.Emit (OpCodes.Ble, target);
2576 case Operator.LessThanOrEqual:
2579 ig.Emit (OpCodes.Ble_Un, target);
2581 ig.Emit (OpCodes.Ble, target);
2584 ig.Emit (OpCodes.Bgt_Un, target);
2586 ig.Emit (OpCodes.Bgt, target);
2590 case Operator.GreaterThanOrEqual:
2593 ig.Emit (OpCodes.Bge_Un, target);
2595 ig.Emit (OpCodes.Bge, target);
2598 ig.Emit (OpCodes.Blt_Un, target);
2600 ig.Emit (OpCodes.Blt, target);
2603 Console.WriteLine (oper);
2604 throw new Exception ("what is THAT");
2608 public override void Emit (EmitContext ec)
2610 ILGenerator ig = ec.ig;
2615 // Handle short-circuit operators differently
2618 if (oper == Operator.LogicalAnd) {
2619 Label load_zero = ig.DefineLabel ();
2620 Label end = ig.DefineLabel ();
2622 left.EmitBranchable (ec, load_zero, false);
2624 ig.Emit (OpCodes.Br, end);
2626 ig.MarkLabel (load_zero);
2627 ig.Emit (OpCodes.Ldc_I4_0);
2630 } else if (oper == Operator.LogicalOr) {
2631 Label load_one = ig.DefineLabel ();
2632 Label end = ig.DefineLabel ();
2634 left.EmitBranchable (ec, load_one, true);
2636 ig.Emit (OpCodes.Br, end);
2638 ig.MarkLabel (load_one);
2639 ig.Emit (OpCodes.Ldc_I4_1);
2647 bool isUnsigned = is_unsigned (left.Type);
2650 case Operator.Multiply:
2652 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2653 opcode = OpCodes.Mul_Ovf;
2654 else if (isUnsigned)
2655 opcode = OpCodes.Mul_Ovf_Un;
2657 opcode = OpCodes.Mul;
2659 opcode = OpCodes.Mul;
2663 case Operator.Division:
2665 opcode = OpCodes.Div_Un;
2667 opcode = OpCodes.Div;
2670 case Operator.Modulus:
2672 opcode = OpCodes.Rem_Un;
2674 opcode = OpCodes.Rem;
2677 case Operator.Addition:
2679 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2680 opcode = OpCodes.Add_Ovf;
2681 else if (isUnsigned)
2682 opcode = OpCodes.Add_Ovf_Un;
2684 opcode = OpCodes.Add;
2686 opcode = OpCodes.Add;
2689 case Operator.Subtraction:
2691 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2692 opcode = OpCodes.Sub_Ovf;
2693 else if (isUnsigned)
2694 opcode = OpCodes.Sub_Ovf_Un;
2696 opcode = OpCodes.Sub;
2698 opcode = OpCodes.Sub;
2701 case Operator.RightShift:
2703 opcode = OpCodes.Shr_Un;
2705 opcode = OpCodes.Shr;
2708 case Operator.LeftShift:
2709 opcode = OpCodes.Shl;
2712 case Operator.Equality:
2713 opcode = OpCodes.Ceq;
2716 case Operator.Inequality:
2717 ig.Emit (OpCodes.Ceq);
2718 ig.Emit (OpCodes.Ldc_I4_0);
2720 opcode = OpCodes.Ceq;
2723 case Operator.LessThan:
2725 opcode = OpCodes.Clt_Un;
2727 opcode = OpCodes.Clt;
2730 case Operator.GreaterThan:
2732 opcode = OpCodes.Cgt_Un;
2734 opcode = OpCodes.Cgt;
2737 case Operator.LessThanOrEqual:
2738 Type lt = left.Type;
2740 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2741 ig.Emit (OpCodes.Cgt_Un);
2743 ig.Emit (OpCodes.Cgt);
2744 ig.Emit (OpCodes.Ldc_I4_0);
2746 opcode = OpCodes.Ceq;
2749 case Operator.GreaterThanOrEqual:
2750 Type le = left.Type;
2752 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2753 ig.Emit (OpCodes.Clt_Un);
2755 ig.Emit (OpCodes.Clt);
2757 ig.Emit (OpCodes.Ldc_I4_0);
2759 opcode = OpCodes.Ceq;
2762 case Operator.BitwiseOr:
2763 opcode = OpCodes.Or;
2766 case Operator.BitwiseAnd:
2767 opcode = OpCodes.And;
2770 case Operator.ExclusiveOr:
2771 opcode = OpCodes.Xor;
2775 throw new Exception ("This should not happen: Operator = "
2776 + oper.ToString ());
2784 // Object created by Binary when the binary operator uses an method instead of being
2785 // a binary operation that maps to a CIL binary operation.
2787 public class BinaryMethod : Expression {
2788 public MethodBase method;
2789 public ArrayList Arguments;
2791 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2796 eclass = ExprClass.Value;
2799 public override Expression DoResolve (EmitContext ec)
2804 public override void Emit (EmitContext ec)
2806 ILGenerator ig = ec.ig;
2808 if (Arguments != null)
2809 Invocation.EmitArguments (ec, method, Arguments, false, null);
2811 if (method is MethodInfo)
2812 ig.Emit (OpCodes.Call, (MethodInfo) method);
2814 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2819 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2820 // b, c, d... may be strings or objects.
2822 public class StringConcat : Expression {
2824 bool invalid = false;
2825 bool emit_conv_done = false;
2827 // Are we also concating objects?
2829 bool is_strings_only = true;
2831 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2834 type = TypeManager.string_type;
2835 eclass = ExprClass.Value;
2837 operands = new ArrayList (2);
2842 public override Expression DoResolve (EmitContext ec)
2850 public void Append (EmitContext ec, Expression operand)
2855 StringConstant sc = operand as StringConstant;
2857 // TODO: it will be better to do this silently as an optimalization
2859 // string s = "" + i;
2860 // because this code has poor performace
2861 // if (sc.Value.Length == 0)
2862 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2864 if (operands.Count != 0) {
2865 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2866 if (last_operand != null) {
2867 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2874 // Conversion to object
2876 if (operand.Type != TypeManager.string_type) {
2877 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2880 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2886 operands.Add (operand);
2889 public override void Emit (EmitContext ec)
2891 MethodInfo concat_method = null;
2894 // Do conversion to arguments; check for strings only
2897 // This can get called multiple times, so we have to deal with that.
2898 if (!emit_conv_done) {
2899 emit_conv_done = true;
2900 for (int i = 0; i < operands.Count; i ++) {
2901 Expression e = (Expression) operands [i];
2902 is_strings_only &= e.Type == TypeManager.string_type;
2905 for (int i = 0; i < operands.Count; i ++) {
2906 Expression e = (Expression) operands [i];
2908 if (! is_strings_only && e.Type == TypeManager.string_type) {
2909 // need to make sure this is an object, because the EmitParams
2910 // method might look at the type of this expression, see it is a
2911 // string and emit a string [] when we want an object [];
2913 e = new EmptyCast (e, TypeManager.object_type);
2915 operands [i] = new Argument (e, Argument.AType.Expression);
2920 // Find the right method
2922 switch (operands.Count) {
2925 // This should not be possible, because simple constant folding
2926 // is taken care of in the Binary code.
2928 throw new Exception ("how did you get here?");
2931 concat_method = is_strings_only ?
2932 TypeManager.string_concat_string_string :
2933 TypeManager.string_concat_object_object ;
2936 concat_method = is_strings_only ?
2937 TypeManager.string_concat_string_string_string :
2938 TypeManager.string_concat_object_object_object ;
2942 // There is not a 4 param overlaod for object (the one that there is
2943 // is actually a varargs methods, and is only in corlib because it was
2944 // introduced there before.).
2946 if (!is_strings_only)
2949 concat_method = TypeManager.string_concat_string_string_string_string;
2952 concat_method = is_strings_only ?
2953 TypeManager.string_concat_string_dot_dot_dot :
2954 TypeManager.string_concat_object_dot_dot_dot ;
2958 Invocation.EmitArguments (ec, concat_method, operands, false, null);
2959 ec.ig.Emit (OpCodes.Call, concat_method);
2964 // Object created with +/= on delegates
2966 public class BinaryDelegate : Expression {
2970 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
2975 eclass = ExprClass.Value;
2978 public override Expression DoResolve (EmitContext ec)
2983 public override void Emit (EmitContext ec)
2985 ILGenerator ig = ec.ig;
2987 Invocation.EmitArguments (ec, method, args, false, null);
2989 ig.Emit (OpCodes.Call, (MethodInfo) method);
2990 ig.Emit (OpCodes.Castclass, type);
2993 public Expression Right {
2995 Argument arg = (Argument) args [1];
3000 public bool IsAddition {
3002 return method == TypeManager.delegate_combine_delegate_delegate;
3008 // User-defined conditional logical operator
3009 public class ConditionalLogicalOperator : Expression {
3010 Expression left, right;
3013 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
3016 eclass = ExprClass.Value;
3020 this.is_and = is_and;
3023 protected void Error19 ()
3025 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3028 protected void Error218 ()
3030 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3031 "declarations of operator true and operator false");
3034 Expression op_true, op_false, op;
3035 LocalTemporary left_temp;
3037 public override Expression DoResolve (EmitContext ec)
3040 Expression operator_group;
3042 operator_group = MethodLookup (ec.ContainerType, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
3043 if (operator_group == null) {
3048 left_temp = new LocalTemporary (type);
3050 ArrayList arguments = new ArrayList (2);
3051 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3052 arguments.Add (new Argument (right, Argument.AType.Expression));
3053 method = Invocation.OverloadResolve (
3054 ec, (MethodGroupExpr) operator_group, arguments, false, loc)
3056 if (method == null) {
3061 if (method.ReturnType != type) {
3062 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3063 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3067 op = new StaticCallExpr (method, arguments, loc);
3069 op_true = GetOperatorTrue (ec, left_temp, loc);
3070 op_false = GetOperatorFalse (ec, left_temp, loc);
3071 if ((op_true == null) || (op_false == null)) {
3079 public override void Emit (EmitContext ec)
3081 ILGenerator ig = ec.ig;
3082 Label false_target = ig.DefineLabel ();
3083 Label end_target = ig.DefineLabel ();
3086 left_temp.Store (ec);
3088 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3089 left_temp.Emit (ec);
3090 ig.Emit (OpCodes.Br, end_target);
3091 ig.MarkLabel (false_target);
3093 ig.MarkLabel (end_target);
3095 // We release 'left_temp' here since 'op' may refer to it too
3096 left_temp.Release (ec);
3100 public class PointerArithmetic : Expression {
3101 Expression left, right;
3105 // We assume that `l' is always a pointer
3107 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3113 is_add = is_addition;
3116 public override Expression DoResolve (EmitContext ec)
3118 eclass = ExprClass.Variable;
3120 if (left.Type == TypeManager.void_ptr_type) {
3121 Error (242, "The operation in question is undefined on void pointers");
3128 public override void Emit (EmitContext ec)
3130 Type op_type = left.Type;
3131 ILGenerator ig = ec.ig;
3133 // It must be either array or fixed buffer
3134 Type element = TypeManager.HasElementType (op_type) ?
3135 element = TypeManager.GetElementType (op_type) :
3136 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3138 int size = GetTypeSize (element);
3139 Type rtype = right.Type;
3141 if (rtype.IsPointer){
3143 // handle (pointer - pointer)
3147 ig.Emit (OpCodes.Sub);
3151 ig.Emit (OpCodes.Sizeof, element);
3153 IntLiteral.EmitInt (ig, size);
3154 ig.Emit (OpCodes.Div);
3156 ig.Emit (OpCodes.Conv_I8);
3159 // handle + and - on (pointer op int)
3162 ig.Emit (OpCodes.Conv_I);
3164 Constant right_const = right as Constant;
3165 if (right_const != null && size != 0) {
3166 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3174 ig.Emit (OpCodes.Sizeof, element);
3176 IntLiteral.EmitInt (ig, size);
3177 if (rtype == TypeManager.int64_type)
3178 ig.Emit (OpCodes.Conv_I8);
3179 else if (rtype == TypeManager.uint64_type)
3180 ig.Emit (OpCodes.Conv_U8);
3181 ig.Emit (OpCodes.Mul);
3185 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3186 ig.Emit (OpCodes.Conv_I);
3189 ig.Emit (OpCodes.Add);
3191 ig.Emit (OpCodes.Sub);
3197 /// Implements the ternary conditional operator (?:)
3199 public class Conditional : Expression {
3200 Expression expr, trueExpr, falseExpr;
3202 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3205 this.trueExpr = trueExpr;
3206 this.falseExpr = falseExpr;
3207 this.loc = expr.Location;
3210 public Expression Expr {
3216 public Expression TrueExpr {
3222 public Expression FalseExpr {
3228 public override Expression DoResolve (EmitContext ec)
3230 expr = expr.Resolve (ec);
3236 if (TypeManager.IsNullableValueType (expr.Type))
3237 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
3240 if (expr.Type != TypeManager.bool_type){
3241 expr = Expression.ResolveBoolean (
3248 Assign ass = expr as Assign;
3249 if (ass != null && ass.Source is Constant) {
3250 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3253 trueExpr = trueExpr.Resolve (ec);
3254 falseExpr = falseExpr.Resolve (ec);
3256 if (trueExpr == null || falseExpr == null)
3259 eclass = ExprClass.Value;
3260 if (trueExpr.Type == falseExpr.Type) {
3261 type = trueExpr.Type;
3262 if (type == TypeManager.null_type) {
3263 // TODO: probably will have to implement ConditionalConstant
3264 // to call method without return constant as well
3265 Report.Warning (-101, 1, loc, "Conditional expression will always return same value");
3270 Type true_type = trueExpr.Type;
3271 Type false_type = falseExpr.Type;
3274 // First, if an implicit conversion exists from trueExpr
3275 // to falseExpr, then the result type is of type falseExpr.Type
3277 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3280 // Check if both can convert implicitl to each other's type
3282 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3284 "Can not compute type of conditional expression " +
3285 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3286 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3287 "' convert implicitly to each other");
3292 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3296 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3297 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3302 // Dead code optimalization
3303 if (expr is BoolConstant){
3304 BoolConstant bc = (BoolConstant) expr;
3306 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3307 return bc.Value ? trueExpr : falseExpr;
3313 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3318 public override void Emit (EmitContext ec)
3320 ILGenerator ig = ec.ig;
3321 Label false_target = ig.DefineLabel ();
3322 Label end_target = ig.DefineLabel ();
3324 expr.EmitBranchable (ec, false_target, false);
3326 ig.Emit (OpCodes.Br, end_target);
3327 ig.MarkLabel (false_target);
3328 falseExpr.Emit (ec);
3329 ig.MarkLabel (end_target);
3334 public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation {
3336 LocalTemporary temp;
3338 public abstract Variable Variable {
3342 public abstract bool IsRef {
3346 public override void Emit (EmitContext ec)
3352 // This method is used by parameters that are references, that are
3353 // being passed as references: we only want to pass the pointer (that
3354 // is already stored in the parameter, not the address of the pointer,
3355 // and not the value of the variable).
3357 public void EmitLoad (EmitContext ec)
3359 Report.Debug (64, "VARIABLE EMIT LOAD", this, Variable, type, loc);
3361 Variable.EmitInstance (ec);
3365 public void Emit (EmitContext ec, bool leave_copy)
3367 Report.Debug (64, "VARIABLE EMIT", this, Variable, type, IsRef, loc);
3373 ec.ig.Emit (OpCodes.Dup);
3376 // If we are a reference, we loaded on the stack a pointer
3377 // Now lets load the real value
3379 LoadFromPtr (ec.ig, type);
3383 ec.ig.Emit (OpCodes.Dup);
3385 if (IsRef || Variable.NeedsTemporary) {
3386 temp = new LocalTemporary (Type);
3392 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
3393 bool prepare_for_load)
3395 Report.Debug (64, "VARIABLE EMIT ASSIGN", this, Variable, type, IsRef,
3398 ILGenerator ig = ec.ig;
3399 prepared = prepare_for_load;
3401 Variable.EmitInstance (ec);
3402 if (prepare_for_load && Variable.HasInstance)
3403 ig.Emit (OpCodes.Dup);
3404 else if (IsRef && !prepared)
3410 ig.Emit (OpCodes.Dup);
3411 if (IsRef || Variable.NeedsTemporary) {
3412 temp = new LocalTemporary (Type);
3418 StoreFromPtr (ig, type);
3420 Variable.EmitAssign (ec);
3428 public void AddressOf (EmitContext ec, AddressOp mode)
3430 Variable.EmitInstance (ec);
3431 Variable.EmitAddressOf (ec);
3438 public class LocalVariableReference : VariableReference, IVariable {
3439 public readonly string Name;
3440 public readonly Block Block;
3441 public LocalInfo local_info;
3445 public LocalVariableReference (Block block, string name, Location l)
3450 eclass = ExprClass.Variable;
3454 // Setting `is_readonly' to false will allow you to create a writable
3455 // reference to a read-only variable. This is used by foreach and using.
3457 public LocalVariableReference (Block block, string name, Location l,
3458 LocalInfo local_info, bool is_readonly)
3459 : this (block, name, l)
3461 this.local_info = local_info;
3462 this.is_readonly = is_readonly;
3465 public VariableInfo VariableInfo {
3466 get { return local_info.VariableInfo; }
3469 public override bool IsRef {
3470 get { return false; }
3473 public bool IsReadOnly {
3474 get { return is_readonly; }
3477 public bool VerifyAssigned (EmitContext ec)
3479 VariableInfo variable_info = local_info.VariableInfo;
3480 return variable_info == null || variable_info.IsAssigned (ec, loc);
3483 void ResolveLocalInfo ()
3485 if (local_info == null) {
3486 local_info = Block.GetLocalInfo (Name);
3487 is_readonly = local_info.ReadOnly;
3491 protected Expression DoResolveBase (EmitContext ec)
3493 type = local_info.VariableType;
3495 Expression e = Block.GetConstantExpression (Name);
3497 return e.Resolve (ec);
3499 if (!VerifyAssigned (ec))
3503 // If we are referencing a variable from the external block
3504 // flag it for capturing
3506 if (ec.MustCaptureVariable (local_info)) {
3507 if (local_info.AddressTaken){
3508 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3512 ScopeInfo scope = local_info.Block.CreateScopeInfo ();
3513 variable = scope.AddLocal (local_info);
3514 type = variable.Type;
3520 public override Expression DoResolve (EmitContext ec)
3522 ResolveLocalInfo ();
3523 local_info.Used = true;
3524 return DoResolveBase (ec);
3527 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3529 ResolveLocalInfo ();
3534 if (right_side == EmptyExpression.OutAccess) {
3535 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3536 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3537 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3538 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3539 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3541 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
3543 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3548 if (right_side == EmptyExpression.OutAccess)
3549 local_info.Used = true;
3551 if (VariableInfo != null)
3552 VariableInfo.SetAssigned (ec);
3554 return DoResolveBase (ec);
3557 public bool VerifyFixed ()
3559 // A local Variable is always fixed.
3563 public override int GetHashCode ()
3565 return Name.GetHashCode ();
3568 public override bool Equals (object obj)
3570 LocalVariableReference lvr = obj as LocalVariableReference;
3574 return Name == lvr.Name && Block == lvr.Block;
3577 public override Variable Variable {
3578 get { return variable != null ? variable : local_info.Variable; }
3581 public override string ToString ()
3583 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3588 /// This represents a reference to a parameter in the intermediate
3591 public class ParameterReference : VariableReference, IVariable {
3597 public bool is_ref, is_out;
3605 public override bool IsRef {
3611 public string Name {
3617 public Parameter Parameter {
3625 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3628 this.name = par.Name;
3632 eclass = ExprClass.Variable;
3635 public VariableInfo VariableInfo {
3639 public override Variable Variable {
3640 get { return variable != null ? variable : par.Variable; }
3643 public bool VerifyFixed ()
3645 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3646 return par.ModFlags == Parameter.Modifier.NONE;
3649 public bool IsAssigned (EmitContext ec, Location loc)
3651 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3654 Report.Error (269, loc,
3655 "Use of unassigned out parameter `{0}'", par.Name);
3659 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3661 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3664 Report.Error (170, loc,
3665 "Use of possibly unassigned field `" + field_name + "'");
3669 public void SetAssigned (EmitContext ec)
3671 if (is_out && ec.DoFlowAnalysis)
3672 ec.CurrentBranching.SetAssigned (vi);
3675 public void SetFieldAssigned (EmitContext ec, string field_name)
3677 if (is_out && ec.DoFlowAnalysis)
3678 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3681 protected bool DoResolveBase (EmitContext ec)
3683 if (!par.Resolve (ec)) {
3687 type = par.ParameterType;
3688 Parameter.Modifier mod = par.ModFlags;
3689 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3690 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3691 eclass = ExprClass.Variable;
3694 vi = block.ParameterMap [idx];
3696 AnonymousContainer am = ec.CurrentAnonymousMethod;
3700 if (is_ref && !block.Toplevel.IsLocalParameter (name)){
3701 Report.Error (1628, Location,
3702 "Cannot use ref or out parameter `{0}' inside an " +
3703 "anonymous method block", par.Name);
3707 if (!am.IsIterator && block.Toplevel.IsLocalParameter (name))
3710 RootScopeInfo host = null;
3711 ToplevelBlock toplevel = block.Toplevel;
3712 while (toplevel != null) {
3713 if (toplevel.IsLocalParameter (name))
3716 toplevel = toplevel.Container;
3719 ScopeInfo scope = toplevel.CreateScopeInfo ();
3720 variable = scope.AddParameter (par, idx);
3721 type = variable.Type;
3725 public override int GetHashCode()
3727 return name.GetHashCode ();
3730 public override bool Equals (object obj)
3732 ParameterReference pr = obj as ParameterReference;
3736 return name == pr.name && block == pr.block;
3740 // Notice that for ref/out parameters, the type exposed is not the
3741 // same type exposed externally.
3744 // externally we expose "int&"
3745 // here we expose "int".
3747 // We record this in "is_ref". This means that the type system can treat
3748 // the type as it is expected, but when we generate the code, we generate
3749 // the alternate kind of code.
3751 public override Expression DoResolve (EmitContext ec)
3753 if (!DoResolveBase (ec))
3756 if (is_out && ec.DoFlowAnalysis &&
3757 (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3763 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3765 if (!DoResolveBase (ec))
3773 static public void EmitLdArg (ILGenerator ig, int x)
3777 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3778 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3779 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3780 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3781 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3784 ig.Emit (OpCodes.Ldarg, x);
3787 public override string ToString ()
3789 return "ParameterReference[" + name + "]";
3794 /// Used for arguments to New(), Invocation()
3796 public class Argument {
3797 public enum AType : byte {
3804 public readonly AType ArgType;
3805 public Expression Expr;
3807 public Argument (Expression expr, AType type)
3810 this.ArgType = type;
3813 public Argument (Expression expr)
3816 this.ArgType = AType.Expression;
3821 if (ArgType == AType.Ref || ArgType == AType.Out)
3822 return TypeManager.GetReferenceType (Expr.Type);
3828 public Parameter.Modifier Modifier
3833 return Parameter.Modifier.OUT;
3836 return Parameter.Modifier.REF;
3839 return Parameter.Modifier.NONE;
3844 public static string FullDesc (Argument a)
3846 if (a.ArgType == AType.ArgList)
3849 return (a.ArgType == AType.Ref ? "ref " :
3850 (a.ArgType == AType.Out ? "out " : "")) +
3851 TypeManager.CSharpName (a.Expr.Type);
3854 public bool ResolveMethodGroup (EmitContext ec)
3856 SimpleName sn = Expr as SimpleName;
3858 Expr = sn.GetMethodGroup ();
3860 // FIXME: csc doesn't report any error if you try to use `ref' or
3861 // `out' in a delegate creation expression.
3862 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3869 public bool Resolve (EmitContext ec, Location loc)
3871 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3872 // Verify that the argument is readable
3873 if (ArgType != AType.Out)
3874 Expr = Expr.Resolve (ec);
3876 // Verify that the argument is writeable
3877 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3878 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3880 return Expr != null;
3884 public void Emit (EmitContext ec)
3886 if (ArgType != AType.Ref && ArgType != AType.Out) {
3891 AddressOp mode = AddressOp.Store;
3892 if (ArgType == AType.Ref)
3893 mode |= AddressOp.Load;
3895 IMemoryLocation ml = (IMemoryLocation) Expr;
3896 ParameterReference pr = ml as ParameterReference;
3899 // ParameterReferences might already be references, so we want
3900 // to pass just the value
3902 if (pr != null && pr.IsRef)
3905 ml.AddressOf (ec, mode);
3910 /// Invocation of methods or delegates.
3912 public class Invocation : ExpressionStatement {
3913 public readonly ArrayList Arguments;
3916 MethodBase method = null;
3919 // arguments is an ArrayList, but we do not want to typecast,
3920 // as it might be null.
3922 // FIXME: only allow expr to be a method invocation or a
3923 // delegate invocation (7.5.5)
3925 public Invocation (Expression expr, ArrayList arguments)
3928 Arguments = arguments;
3929 loc = expr.Location;
3932 public Expression Expr {
3939 /// Determines "better conversion" as specified in 14.4.2.3
3941 /// Returns : p if a->p is better,
3942 /// q if a->q is better,
3943 /// null if neither is better
3945 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q)
3947 Type argument_type = TypeManager.TypeToCoreType (a.Type);
3948 Expression argument_expr = a.Expr;
3950 if (argument_type == null)
3951 throw new Exception ("Expression of type " + a.Expr +
3952 " does not resolve its type");
3954 if (p == null || q == null)
3955 throw new InternalErrorException ("BetterConversion Got a null conversion");
3960 if (argument_expr is NullLiteral) {
3962 // If the argument is null and one of the types to compare is 'object' and
3963 // the other is a reference type, we prefer the other.
3965 // This follows from the usual rules:
3966 // * There is an implicit conversion from 'null' to type 'object'
3967 // * There is an implicit conversion from 'null' to any reference type
3968 // * There is an implicit conversion from any reference type to type 'object'
3969 // * There is no implicit conversion from type 'object' to other reference types
3970 // => Conversion of 'null' to a reference type is better than conversion to 'object'
3972 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
3973 // null type. I think it used to be 'object' and thus needed a special
3974 // case to avoid the immediately following two checks.
3976 if (!p.IsValueType && q == TypeManager.object_type)
3978 if (!q.IsValueType && p == TypeManager.object_type)
3982 if (argument_type == p)
3985 if (argument_type == q)
3988 Expression p_tmp = new EmptyExpression (p);
3989 Expression q_tmp = new EmptyExpression (q);
3991 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
3992 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
3994 if (p_to_q && !q_to_p)
3997 if (q_to_p && !p_to_q)
4000 if (p == TypeManager.sbyte_type)
4001 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
4002 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4004 if (q == TypeManager.sbyte_type)
4005 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
4006 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4009 if (p == TypeManager.short_type)
4010 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
4011 q == TypeManager.uint64_type)
4013 if (q == TypeManager.short_type)
4014 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
4015 p == TypeManager.uint64_type)
4018 if (p == TypeManager.int32_type)
4019 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4021 if (q == TypeManager.int32_type)
4022 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4025 if (p == TypeManager.int64_type)
4026 if (q == TypeManager.uint64_type)
4028 if (q == TypeManager.int64_type)
4029 if (p == TypeManager.uint64_type)
4035 static Type MoreSpecific (Type p, Type q)
4037 if (TypeManager.IsGenericParameter (p) && !TypeManager.IsGenericParameter (q))
4039 if (!TypeManager.IsGenericParameter (p) && TypeManager.IsGenericParameter (q))
4042 if (TypeManager.HasElementType (p)) {
4043 Type pe = TypeManager.GetElementType (p);
4044 Type qe = TypeManager.GetElementType (q);
4045 Type specific = MoreSpecific (pe, qe);
4050 } else if (TypeManager.IsGenericType (p)) {
4051 Type[] pargs = TypeManager.GetTypeArguments (p);
4052 Type[] qargs = TypeManager.GetTypeArguments (q);
4054 bool p_specific_at_least_once = false;
4055 bool q_specific_at_least_once = false;
4057 for (int i = 0; i < pargs.Length; i++) {
4058 Type specific = MoreSpecific (pargs [i], qargs [i]);
4059 if (specific == pargs [i])
4060 p_specific_at_least_once = true;
4061 if (specific == qargs [i])
4062 q_specific_at_least_once = true;
4065 if (p_specific_at_least_once && !q_specific_at_least_once)
4067 if (!p_specific_at_least_once && q_specific_at_least_once)
4075 /// Determines "Better function" between candidate
4076 /// and the current best match
4079 /// Returns a boolean indicating :
4080 /// false if candidate ain't better
4081 /// true if candidate is better than the current best match
4083 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4084 MethodBase candidate, bool candidate_params,
4085 MethodBase best, bool best_params)
4087 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4088 ParameterData best_pd = TypeManager.GetParameterData (best);
4090 bool better_at_least_one = false;
4092 for (int j = 0, c_idx = 0, b_idx = 0; j < argument_count; ++j, ++c_idx, ++b_idx) {
4093 Argument a = (Argument) args [j];
4095 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (c_idx));
4096 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (b_idx));
4098 if (candidate_params && candidate_pd.ParameterModifier (c_idx) == Parameter.Modifier.PARAMS) {
4099 ct = TypeManager.GetElementType (ct);
4103 if (best_params && best_pd.ParameterModifier (b_idx) == Parameter.Modifier.PARAMS) {
4104 bt = TypeManager.GetElementType (bt);
4112 Type better = BetterConversion (ec, a, ct, bt);
4114 // for each argument, the conversion to 'ct' should be no worse than
4115 // the conversion to 'bt'.
4119 // for at least one argument, the conversion to 'ct' should be better than
4120 // the conversion to 'bt'.
4122 better_at_least_one = true;
4125 if (better_at_least_one)
4129 // This handles the case
4131 // Add (float f1, float f2, float f3);
4132 // Add (params decimal [] foo);
4134 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4135 // first candidate would've chosen as better.
4141 // The two methods have equal parameter types. Now apply tie-breaking rules
4143 if (TypeManager.IsGenericMethod (best) && !TypeManager.IsGenericMethod (candidate))
4145 if (!TypeManager.IsGenericMethod (best) && TypeManager.IsGenericMethod (candidate))
4149 // This handles the following cases:
4151 // Trim () is better than Trim (params char[] chars)
4152 // Concat (string s1, string s2, string s3) is better than
4153 // Concat (string s1, params string [] srest)
4154 // Foo (int, params int [] rest) is better than Foo (params int [] rest)
4156 if (!candidate_params && best_params)
4158 if (candidate_params && !best_params)
4161 int candidate_param_count = candidate_pd.Count;
4162 int best_param_count = best_pd.Count;
4164 if (candidate_param_count != best_param_count)
4165 // can only happen if (candidate_params && best_params)
4166 return candidate_param_count > best_param_count;
4169 // now, both methods have the same number of parameters, and the parameters have the same types
4170 // Pick the "more specific" signature
4173 MethodBase orig_candidate = TypeManager.DropGenericMethodArguments (candidate);
4174 MethodBase orig_best = TypeManager.DropGenericMethodArguments (best);
4176 ParameterData orig_candidate_pd = TypeManager.GetParameterData (orig_candidate);
4177 ParameterData orig_best_pd = TypeManager.GetParameterData (orig_best);
4179 bool specific_at_least_once = false;
4180 for (int j = 0; j < candidate_param_count; ++j) {
4181 Type ct = TypeManager.TypeToCoreType (orig_candidate_pd.ParameterType (j));
4182 Type bt = TypeManager.TypeToCoreType (orig_best_pd.ParameterType (j));
4185 Type specific = MoreSpecific (ct, bt);
4189 specific_at_least_once = true;
4192 if (specific_at_least_once)
4195 // FIXME: handle lifted operators
4201 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4203 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4206 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4207 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4209 if (cand_pd.Count != base_pd.Count)
4212 for (int j = 0; j < cand_pd.Count; ++j) {
4213 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4214 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4215 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4216 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4218 if (cm != bm || ct != bt)
4225 public static string FullMethodDesc (MethodBase mb)
4231 if (mb is MethodInfo) {
4232 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4236 sb = new StringBuilder ();
4238 sb.Append (TypeManager.CSharpSignature (mb));
4239 return sb.ToString ();
4242 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4244 MemberInfo [] miset;
4245 MethodGroupExpr union;
4250 return (MethodGroupExpr) mg2;
4253 return (MethodGroupExpr) mg1;
4256 MethodGroupExpr left_set = null, right_set = null;
4257 int length1 = 0, length2 = 0;
4259 left_set = (MethodGroupExpr) mg1;
4260 length1 = left_set.Methods.Length;
4262 right_set = (MethodGroupExpr) mg2;
4263 length2 = right_set.Methods.Length;
4265 ArrayList common = new ArrayList ();
4267 foreach (MethodBase r in right_set.Methods){
4268 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4272 miset = new MemberInfo [length1 + length2 - common.Count];
4273 left_set.Methods.CopyTo (miset, 0);
4277 foreach (MethodBase r in right_set.Methods) {
4278 if (!common.Contains (r))
4282 union = new MethodGroupExpr (miset, loc);
4287 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4288 ArrayList arguments, int arg_count,
4289 ref MethodBase candidate)
4291 return IsParamsMethodApplicable (
4292 ec, me, arguments, arg_count, false, ref candidate) ||
4293 IsParamsMethodApplicable (
4294 ec, me, arguments, arg_count, true, ref candidate);
4299 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4300 ArrayList arguments, int arg_count,
4301 bool do_varargs, ref MethodBase candidate)
4304 if (!me.HasTypeArguments &&
4305 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4308 if (TypeManager.IsGenericMethodDefinition (candidate))
4309 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4312 return IsParamsMethodApplicable (
4313 ec, arguments, arg_count, candidate, do_varargs);
4317 /// Determines if the candidate method, if a params method, is applicable
4318 /// in its expanded form to the given set of arguments
4320 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4321 int arg_count, MethodBase candidate,
4324 ParameterData pd = TypeManager.GetParameterData (candidate);
4326 int pd_count = pd.Count;
4330 int count = pd_count - 1;
4332 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4334 if (pd_count != arg_count)
4337 if (!(((Argument) arguments [count]).Expr is Arglist))
4345 if (count > arg_count)
4348 if (pd_count == 1 && arg_count == 0)
4352 // If we have come this far, the case which
4353 // remains is when the number of parameters is
4354 // less than or equal to the argument count.
4356 int argument_index = 0;
4358 for (int i = 0; i < pd_count; ++i) {
4360 if ((pd.ParameterModifier (i) & Parameter.Modifier.PARAMS) != 0) {
4361 Type element_type = TypeManager.GetElementType (pd.ParameterType (i));
4362 int params_args_count = arg_count - pd_count;
4363 if (params_args_count < 0)
4367 a = (Argument) arguments [argument_index++];
4369 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4371 } while (params_args_count-- > 0);
4375 a = (Argument) arguments [argument_index++];
4377 Parameter.Modifier a_mod = a.Modifier &
4378 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4379 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4380 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4382 if (a_mod == p_mod) {
4384 if (a_mod == Parameter.Modifier.NONE)
4385 if (!Convert.ImplicitConversionExists (ec,
4387 pd.ParameterType (i)))
4390 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4391 Type pt = pd.ParameterType (i);
4394 pt = TypeManager.GetReferenceType (pt);
4407 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4408 ArrayList arguments, int arg_count,
4409 ref MethodBase candidate)
4412 if (!me.HasTypeArguments &&
4413 !TypeManager.InferTypeArguments (arguments, ref candidate))
4416 if (TypeManager.IsGenericMethodDefinition (candidate))
4417 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4420 return IsApplicable (ec, arguments, arg_count, candidate);
4424 /// Determines if the candidate method is applicable (section 14.4.2.1)
4425 /// to the given set of arguments
4427 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4428 MethodBase candidate)
4430 ParameterData pd = TypeManager.GetParameterData (candidate);
4432 if (arg_count != pd.Count)
4435 for (int i = arg_count; i > 0; ) {
4438 Argument a = (Argument) arguments [i];
4440 Parameter.Modifier a_mod = a.Modifier &
4441 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4443 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4444 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4446 if (a_mod == p_mod) {
4447 Type pt = pd.ParameterType (i);
4449 if (a_mod == Parameter.Modifier.NONE) {
4450 if (!TypeManager.IsEqual (a.Type, pt) &&
4451 !Convert.ImplicitConversionExists (ec, a.Expr, pt))
4465 static internal bool IsAncestralType (Type first_type, Type second_type)
4467 return first_type != second_type &&
4468 (TypeManager.IsSubclassOf (second_type, first_type) ||
4469 TypeManager.ImplementsInterface (second_type, first_type));
4473 /// Find the Applicable Function Members (7.4.2.1)
4475 /// me: Method Group expression with the members to select.
4476 /// it might contain constructors or methods (or anything
4477 /// that maps to a method).
4479 /// Arguments: ArrayList containing resolved Argument objects.
4481 /// loc: The location if we want an error to be reported, or a Null
4482 /// location for "probing" purposes.
4484 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4485 /// that is the best match of me on Arguments.
4488 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4489 ArrayList Arguments, bool may_fail,
4492 MethodBase method = null;
4493 bool method_params = false;
4494 Type applicable_type = null;
4496 ArrayList candidates = new ArrayList (2);
4497 ArrayList candidate_overrides = null;
4500 // Used to keep a map between the candidate
4501 // and whether it is being considered in its
4502 // normal or expanded form
4504 // false is normal form, true is expanded form
4506 Hashtable candidate_to_form = null;
4508 if (Arguments != null)
4509 arg_count = Arguments.Count;
4511 if ((me.Name == "Invoke") &&
4512 TypeManager.IsDelegateType (me.DeclaringType)) {
4513 Error_InvokeOnDelegate (loc);
4517 MethodBase[] methods = me.Methods;
4519 int nmethods = methods.Length;
4523 // Methods marked 'override' don't take part in 'applicable_type'
4524 // computation, nor in the actual overload resolution.
4525 // However, they still need to be emitted instead of a base virtual method.
4526 // So, we salt them away into the 'candidate_overrides' array.
4528 // In case of reflected methods, we replace each overriding method with
4529 // its corresponding base virtual method. This is to improve compatibility
4530 // with non-C# libraries which change the visibility of overrides (#75636)
4533 for (int i = 0; i < methods.Length; ++i) {
4534 MethodBase m = methods [i];
4536 Type [] gen_args = null;
4537 if (m.IsGenericMethod && !m.IsGenericMethodDefinition)
4538 gen_args = m.GetGenericArguments ();
4540 if (TypeManager.IsOverride (m)) {
4541 if (candidate_overrides == null)
4542 candidate_overrides = new ArrayList ();
4543 candidate_overrides.Add (m);
4544 m = TypeManager.TryGetBaseDefinition (m);
4546 if (m != null && gen_args != null) {
4547 if (!m.IsGenericMethodDefinition)
4548 throw new InternalErrorException ("GetBaseDefinition didn't return a GenericMethodDefinition");
4549 m = ((MethodInfo) m).MakeGenericMethod (gen_args);
4559 int applicable_errors = Report.Errors;
4562 // First we construct the set of applicable methods
4564 bool is_sorted = true;
4565 for (int i = 0; i < nmethods; i++){
4566 Type decl_type = methods [i].DeclaringType;
4569 // If we have already found an applicable method
4570 // we eliminate all base types (Section 14.5.5.1)
4572 if (applicable_type != null && IsAncestralType (decl_type, applicable_type))
4576 // Check if candidate is applicable (section 14.4.2.1)
4577 // Is candidate applicable in normal form?
4579 bool is_applicable = IsApplicable (ec, me, Arguments, arg_count, ref methods [i]);
4581 if (!is_applicable && IsParamsMethodApplicable (ec, me, Arguments, arg_count, ref methods [i])) {
4582 MethodBase candidate = methods [i];
4583 if (candidate_to_form == null)
4584 candidate_to_form = new PtrHashtable ();
4585 candidate_to_form [candidate] = candidate;
4586 // Candidate is applicable in expanded form
4587 is_applicable = true;
4593 candidates.Add (methods [i]);
4595 if (applicable_type == null)
4596 applicable_type = decl_type;
4597 else if (applicable_type != decl_type) {
4599 if (IsAncestralType (applicable_type, decl_type))
4600 applicable_type = decl_type;
4604 if (applicable_errors != Report.Errors)
4607 int candidate_top = candidates.Count;
4609 if (applicable_type == null) {
4611 // Okay so we have failed to find anything so we
4612 // return by providing info about the closest match
4614 int errors = Report.Errors;
4615 for (int i = 0; i < nmethods; ++i) {
4616 MethodBase c = (MethodBase) methods [i];
4617 ParameterData pd = TypeManager.GetParameterData (c);
4619 if (pd.Count != arg_count)
4623 if (!TypeManager.InferTypeArguments (Arguments, ref c))
4625 if (TypeManager.IsGenericMethodDefinition (c))
4629 VerifyArgumentsCompat (ec, Arguments, arg_count,
4630 c, false, null, may_fail, loc);
4632 if (!may_fail && errors == Report.Errors)
4633 throw new InternalErrorException (
4634 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4635 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4640 if (!may_fail && errors == Report.Errors) {
4641 string report_name = me.Name;
4642 if (report_name == ".ctor")
4643 report_name = TypeManager.CSharpName (me.DeclaringType);
4649 for (int i = 0; i < methods.Length; ++i) {
4650 MethodBase c = methods [i];
4651 ParameterData pd = TypeManager.GetParameterData (c);
4653 if (pd.Count != arg_count)
4656 if (TypeManager.InferTypeArguments (Arguments, ref c))
4660 411, loc, "The type arguments for " +
4661 "method `{0}' cannot be infered from " +
4662 "the usage. Try specifying the type " +
4663 "arguments explicitly.", report_name);
4668 Error_WrongNumArguments (loc, report_name, arg_count);
4676 // At this point, applicable_type is _one_ of the most derived types
4677 // in the set of types containing the methods in this MethodGroup.
4678 // Filter the candidates so that they only contain methods from the
4679 // most derived types.
4682 int finalized = 0; // Number of finalized candidates
4685 // Invariant: applicable_type is a most derived type
4687 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4688 // eliminating all it's base types. At the same time, we'll also move
4689 // every unrelated type to the end of the array, and pick the next
4690 // 'applicable_type'.
4692 Type next_applicable_type = null;
4693 int j = finalized; // where to put the next finalized candidate
4694 int k = finalized; // where to put the next undiscarded candidate
4695 for (int i = finalized; i < candidate_top; ++i) {
4696 MethodBase candidate = (MethodBase) candidates [i];
4697 Type decl_type = candidate.DeclaringType;
4699 if (decl_type == applicable_type) {
4700 candidates [k++] = candidates [j];
4701 candidates [j++] = candidates [i];
4705 if (IsAncestralType (decl_type, applicable_type))
4708 if (next_applicable_type != null &&
4709 IsAncestralType (decl_type, next_applicable_type))
4712 candidates [k++] = candidates [i];
4714 if (next_applicable_type == null ||
4715 IsAncestralType (next_applicable_type, decl_type))
4716 next_applicable_type = decl_type;
4719 applicable_type = next_applicable_type;
4722 } while (applicable_type != null);
4726 // Now we actually find the best method
4729 method = (MethodBase) candidates [0];
4730 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4731 for (int ix = 1; ix < candidate_top; ix++){
4732 MethodBase candidate = (MethodBase) candidates [ix];
4734 if (candidate == method)
4737 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4739 if (BetterFunction (ec, Arguments, arg_count,
4740 candidate, cand_params,
4741 method, method_params)) {
4743 method_params = cand_params;
4747 // Now check that there are no ambiguities i.e the selected method
4748 // should be better than all the others
4750 MethodBase ambiguous = null;
4751 for (int ix = 0; ix < candidate_top; ix++){
4752 MethodBase candidate = (MethodBase) candidates [ix];
4754 if (candidate == method)
4757 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4758 if (!BetterFunction (ec, Arguments, arg_count,
4759 method, method_params,
4760 candidate, cand_params)) {
4761 Report.SymbolRelatedToPreviousError (candidate);
4762 ambiguous = candidate;
4766 if (ambiguous != null) {
4767 Report.SymbolRelatedToPreviousError (method);
4768 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4769 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4774 // If the method is a virtual function, pick an override closer to the LHS type.
4776 if (!me.IsBase && method.IsVirtual) {
4777 if (TypeManager.IsOverride (method))
4778 throw new InternalErrorException (
4779 "Should not happen. An 'override' method took part in overload resolution: " + method);
4781 if (candidate_overrides != null)
4782 foreach (MethodBase candidate in candidate_overrides) {
4783 if (IsOverride (candidate, method))
4789 // And now check if the arguments are all
4790 // compatible, perform conversions if
4791 // necessary etc. and return if everything is
4794 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4795 method_params, null, may_fail, loc))
4801 MethodBase the_method = TypeManager.DropGenericMethodArguments (method);
4803 if (the_method.IsGenericMethodDefinition &&
4804 !ConstraintChecker.CheckConstraints (ec, the_method, method, loc))
4808 IMethodData data = TypeManager.GetMethod (the_method);
4810 data.SetMemberIsUsed ();
4815 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4817 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4818 name, arg_count.ToString ());
4821 static void Error_InvokeOnDelegate (Location loc)
4823 Report.Error (1533, loc,
4824 "Invoke cannot be called directly on a delegate");
4827 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4828 Type delegate_type, Argument a, ParameterData expected_par)
4830 if (delegate_type == null)
4831 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4832 TypeManager.CSharpSignature (method));
4834 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4835 TypeManager.CSharpName (delegate_type));
4837 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4839 string index = (idx + 1).ToString ();
4840 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4841 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4842 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4843 index, Parameter.GetModifierSignature (a.Modifier));
4845 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4846 index, Parameter.GetModifierSignature (mod));
4848 string p1 = Argument.FullDesc (a);
4849 string p2 = expected_par.ParameterDesc (idx);
4852 // The parameter names are the same, most likely they come from different
4856 Report.Error (1503, loc,
4857 "Argument {0}: Cannot conver from equally named types from different " +
4858 "assemblies {0} (from {1}) and {2} (from {3})",
4859 p1, a.Expr.Type.Assembly.FullName, p2,
4860 expected_par.ParameterType (idx).Assembly.FullName);
4862 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4867 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4868 int arg_count, MethodBase method,
4869 bool chose_params_expanded,
4870 Type delegate_type, bool may_fail,
4873 ParameterData pd = TypeManager.GetParameterData (method);
4877 for (j = 0; j < pd.Count; j++) {
4878 Type parameter_type = pd.ParameterType (j);
4879 Parameter.Modifier pm = pd.ParameterModifier (j);
4881 if (pm == Parameter.Modifier.ARGLIST) {
4882 a = (Argument) Arguments [a_idx];
4883 if (!(a.Expr is Arglist))
4889 int params_arg_count = 1;
4890 if (pm == Parameter.Modifier.PARAMS) {
4891 pm = Parameter.Modifier.NONE;
4892 params_arg_count = arg_count - pd.Count + 1;
4893 if (chose_params_expanded)
4894 parameter_type = TypeManager.GetElementType (parameter_type);
4897 while (params_arg_count > 0) {
4898 a = (Argument) Arguments [a_idx];
4899 if (pm != a.Modifier)
4902 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4903 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4906 Expression conv = Convert.ImplicitConversion (ec, a.Expr, parameter_type, loc);
4910 // Update the argument with the implicit conversion
4918 if (params_arg_count > 0)
4921 if (parameter_type.IsPointer && !ec.InUnsafe) {
4927 if (a_idx == arg_count)
4931 Error_InvalidArguments (loc, a_idx, method, delegate_type, a, pd);
4935 private bool resolved = false;
4936 public override Expression DoResolve (EmitContext ec)
4939 return this.method == null ? null : this;
4943 // First, resolve the expression that is used to
4944 // trigger the invocation
4946 SimpleName sn = expr as SimpleName;
4948 expr = sn.GetMethodGroup ();
4950 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4954 if (!(expr is MethodGroupExpr)) {
4955 Type expr_type = expr.Type;
4957 if (expr_type != null){
4958 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4960 return (new DelegateInvocation (
4961 this.expr, Arguments, loc)).Resolve (ec);
4965 if (!(expr is MethodGroupExpr)){
4966 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4971 // Next, evaluate all the expressions in the argument list
4973 if (Arguments != null){
4974 foreach (Argument a in Arguments){
4975 if (!a.Resolve (ec, loc))
4980 MethodGroupExpr mg = (MethodGroupExpr) expr;
4981 MethodBase method = OverloadResolve (ec, mg, Arguments, false, loc);
4986 MethodInfo mi = method as MethodInfo;
4988 type = TypeManager.TypeToCoreType (mi.ReturnType);
4989 Expression iexpr = mg.InstanceExpression;
4991 if (iexpr == null ||
4992 iexpr is This || iexpr is EmptyExpression ||
4993 mg.IdenticalTypeName) {
4994 mg.InstanceExpression = null;
4996 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
5000 if (iexpr == null || iexpr is EmptyExpression) {
5001 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
5007 if (type.IsPointer){
5015 // Only base will allow this invocation to happen.
5017 if (mg.IsBase && method.IsAbstract){
5018 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
5022 if (Arguments == null && method.Name == "Finalize") {
5024 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
5026 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
5030 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
5034 if (mg.InstanceExpression != null)
5035 mg.InstanceExpression.CheckMarshalByRefAccess ();
5037 eclass = ExprClass.Value;
5038 this.method = method;
5042 bool IsSpecialMethodInvocation (MethodBase method)
5044 IMethodData md = TypeManager.GetMethod (method);
5046 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
5049 if (!TypeManager.IsSpecialMethod (method))
5052 int args = TypeManager.GetParameterData (method).Count;
5053 if (method.Name.StartsWith ("get_") && args > 0)
5055 else if (method.Name.StartsWith ("set_") && args > 2)
5058 // TODO: check operators and events as well ?
5061 Report.SymbolRelatedToPreviousError (method);
5062 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
5063 TypeManager.CSharpSignature (method, true));
5069 // Emits the list of arguments as an array
5071 static void EmitParams (EmitContext ec, ArrayList arguments, int idx, int count)
5073 ILGenerator ig = ec.ig;
5075 for (int j = 0; j < count; j++){
5076 Argument a = (Argument) arguments [j + idx];
5079 IntConstant.EmitInt (ig, count);
5080 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
5083 ig.Emit (OpCodes.Dup);
5084 IntConstant.EmitInt (ig, j);
5086 bool is_stobj, has_type_arg;
5087 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
5089 ig.Emit (OpCodes.Ldelema, t);
5101 /// Emits a list of resolved Arguments that are in the arguments
5104 /// The MethodBase argument might be null if the
5105 /// emission of the arguments is known not to contain
5106 /// a `params' field (for example in constructors or other routines
5107 /// that keep their arguments in this structure)
5109 /// if `dup_args' is true, a copy of the arguments will be left
5110 /// on the stack. If `dup_args' is true, you can specify `this_arg'
5111 /// which will be duplicated before any other args. Only EmitCall
5112 /// should be using this interface.
5114 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
5116 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
5118 LocalTemporary [] temps = null;
5120 if (dup_args && top != 0)
5121 temps = new LocalTemporary [top];
5123 int argument_index = 0;
5125 for (int i = 0; i < top; i++){
5127 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
5128 //Type element_type = TypeManager.GetElementType (pd.ParameterType (i));
5129 int params_args_count = arguments == null ?
5130 0 : arguments.Count - top + 1;
5132 // Fill not provided argument
5133 if (params_args_count <= 0) {
5134 ILGenerator ig = ec.ig;
5135 IntConstant.EmitInt (ig, 0);
5136 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (i)));
5141 // Special case if we are passing the same data as the
5142 // params argument, we do not need to recreate an array.
5144 a = (Argument) arguments [argument_index];
5145 if (params_args_count == 1 && pd.ParameterType (i) == a.Type) {
5151 EmitParams (ec, arguments, i, params_args_count);
5152 argument_index += params_args_count;
5157 a = (Argument) arguments [argument_index++];
5160 ec.ig.Emit (OpCodes.Dup);
5161 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
5166 if (this_arg != null)
5169 for (int i = 0; i < top; i ++) {
5170 temps [i].Emit (ec);
5171 temps [i].Release (ec);
5176 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
5178 ParameterData pd = TypeManager.GetParameterData (mb);
5180 if (arguments == null)
5181 return new Type [0];
5183 Argument a = (Argument) arguments [pd.Count - 1];
5184 Arglist list = (Arglist) a.Expr;
5186 return list.ArgumentTypes;
5190 /// This checks the ConditionalAttribute on the method
5192 static bool IsMethodExcluded (MethodBase method)
5194 if (method.IsConstructor)
5197 IMethodData md = TypeManager.GetMethod (method);
5199 return md.IsExcluded ();
5201 // For some methods (generated by delegate class) GetMethod returns null
5202 // because they are not included in builder_to_method table
5203 if (method.DeclaringType is TypeBuilder)
5206 return AttributeTester.IsConditionalMethodExcluded (method);
5210 /// is_base tells whether we want to force the use of the `call'
5211 /// opcode instead of using callvirt. Call is required to call
5212 /// a specific method, while callvirt will always use the most
5213 /// recent method in the vtable.
5215 /// is_static tells whether this is an invocation on a static method
5217 /// instance_expr is an expression that represents the instance
5218 /// it must be non-null if is_static is false.
5220 /// method is the method to invoke.
5222 /// Arguments is the list of arguments to pass to the method or constructor.
5224 public static void EmitCall (EmitContext ec, bool is_base,
5225 bool is_static, Expression instance_expr,
5226 MethodBase method, ArrayList Arguments, Location loc)
5228 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
5231 // `dup_args' leaves an extra copy of the arguments on the stack
5232 // `omit_args' does not leave any arguments at all.
5233 // So, basically, you could make one call with `dup_args' set to true,
5234 // and then another with `omit_args' set to true, and the two calls
5235 // would have the same set of arguments. However, each argument would
5236 // only have been evaluated once.
5237 public static void EmitCall (EmitContext ec, bool is_base,
5238 bool is_static, Expression instance_expr,
5239 MethodBase method, ArrayList Arguments, Location loc,
5240 bool dup_args, bool omit_args)
5242 ILGenerator ig = ec.ig;
5243 bool struct_call = false;
5244 bool this_call = false;
5245 LocalTemporary this_arg = null;
5247 Type decl_type = method.DeclaringType;
5249 if (!RootContext.StdLib) {
5250 // Replace any calls to the system's System.Array type with calls to
5251 // the newly created one.
5252 if (method == TypeManager.system_int_array_get_length)
5253 method = TypeManager.int_array_get_length;
5254 else if (method == TypeManager.system_int_array_get_rank)
5255 method = TypeManager.int_array_get_rank;
5256 else if (method == TypeManager.system_object_array_clone)
5257 method = TypeManager.object_array_clone;
5258 else if (method == TypeManager.system_int_array_get_length_int)
5259 method = TypeManager.int_array_get_length_int;
5260 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5261 method = TypeManager.int_array_get_lower_bound_int;
5262 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5263 method = TypeManager.int_array_get_upper_bound_int;
5264 else if (method == TypeManager.system_void_array_copyto_array_int)
5265 method = TypeManager.void_array_copyto_array_int;
5268 if (!ec.IsInObsoleteScope) {
5270 // This checks ObsoleteAttribute on the method and on the declaring type
5272 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5274 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5276 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5278 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5282 if (IsMethodExcluded (method))
5286 if (instance_expr == EmptyExpression.Null) {
5287 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5291 this_call = instance_expr is This;
5292 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5296 // If this is ourselves, push "this"
5300 Type iexpr_type = instance_expr.Type;
5303 // Push the instance expression
5305 if (TypeManager.IsValueType (iexpr_type)) {
5307 // Special case: calls to a function declared in a
5308 // reference-type with a value-type argument need
5309 // to have their value boxed.
5310 if (decl_type.IsValueType ||
5311 TypeManager.IsGenericParameter (iexpr_type)) {
5313 // If the expression implements IMemoryLocation, then
5314 // we can optimize and use AddressOf on the
5317 // If not we have to use some temporary storage for
5319 if (instance_expr is IMemoryLocation) {
5320 ((IMemoryLocation)instance_expr).
5321 AddressOf (ec, AddressOp.LoadStore);
5323 LocalTemporary temp = new LocalTemporary (iexpr_type);
5324 instance_expr.Emit (ec);
5326 temp.AddressOf (ec, AddressOp.Load);
5329 // avoid the overhead of doing this all the time.
5331 t = TypeManager.GetReferenceType (iexpr_type);
5333 instance_expr.Emit (ec);
5334 ig.Emit (OpCodes.Box, instance_expr.Type);
5335 t = TypeManager.object_type;
5338 instance_expr.Emit (ec);
5339 t = instance_expr.Type;
5343 ig.Emit (OpCodes.Dup);
5344 if (Arguments != null && Arguments.Count != 0) {
5345 this_arg = new LocalTemporary (t);
5346 this_arg.Store (ec);
5353 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5356 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
5357 ig.Emit (OpCodes.Constrained, instance_expr.Type);
5361 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5362 call_op = OpCodes.Call;
5364 call_op = OpCodes.Callvirt;
5366 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5367 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5368 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5375 // and DoFoo is not virtual, you can omit the callvirt,
5376 // because you don't need the null checking behavior.
5378 if (method is MethodInfo)
5379 ig.Emit (call_op, (MethodInfo) method);
5381 ig.Emit (call_op, (ConstructorInfo) method);
5384 public override void Emit (EmitContext ec)
5386 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5388 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5391 public override void EmitStatement (EmitContext ec)
5396 // Pop the return value if there is one
5398 if (method is MethodInfo){
5399 Type ret = ((MethodInfo)method).ReturnType;
5400 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5401 ec.ig.Emit (OpCodes.Pop);
5406 public class InvocationOrCast : ExpressionStatement
5409 Expression argument;
5411 public InvocationOrCast (Expression expr, Expression argument)
5414 this.argument = argument;
5415 this.loc = expr.Location;
5418 public override Expression DoResolve (EmitContext ec)
5421 // First try to resolve it as a cast.
5423 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5424 if ((te != null) && (te.eclass == ExprClass.Type)) {
5425 Cast cast = new Cast (te, argument, loc);
5426 return cast.Resolve (ec);
5430 // This can either be a type or a delegate invocation.
5431 // Let's just resolve it and see what we'll get.
5433 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5438 // Ok, so it's a Cast.
5440 if (expr.eclass == ExprClass.Type) {
5441 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5442 return cast.Resolve (ec);
5446 // It's a delegate invocation.
5448 if (!TypeManager.IsDelegateType (expr.Type)) {
5449 Error (149, "Method name expected");
5453 ArrayList args = new ArrayList ();
5454 args.Add (new Argument (argument, Argument.AType.Expression));
5455 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5456 return invocation.Resolve (ec);
5461 Error (201, "Only assignment, call, increment, decrement and new object " +
5462 "expressions can be used as a statement");
5465 public override ExpressionStatement ResolveStatement (EmitContext ec)
5468 // First try to resolve it as a cast.
5470 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5471 if ((te != null) && (te.eclass == ExprClass.Type)) {
5477 // This can either be a type or a delegate invocation.
5478 // Let's just resolve it and see what we'll get.
5480 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5481 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5487 // It's a delegate invocation.
5489 if (!TypeManager.IsDelegateType (expr.Type)) {
5490 Error (149, "Method name expected");
5494 ArrayList args = new ArrayList ();
5495 args.Add (new Argument (argument, Argument.AType.Expression));
5496 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5497 return invocation.ResolveStatement (ec);
5500 public override void Emit (EmitContext ec)
5502 throw new Exception ("Cannot happen");
5505 public override void EmitStatement (EmitContext ec)
5507 throw new Exception ("Cannot happen");
5512 // This class is used to "disable" the code generation for the
5513 // temporary variable when initializing value types.
5515 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5516 public void AddressOf (EmitContext ec, AddressOp Mode)
5523 /// Implements the new expression
5525 public class New : ExpressionStatement, IMemoryLocation {
5526 public readonly ArrayList Arguments;
5529 // During bootstrap, it contains the RequestedType,
5530 // but if `type' is not null, it *might* contain a NewDelegate
5531 // (because of field multi-initialization)
5533 public Expression RequestedType;
5535 MethodBase method = null;
5538 // If set, the new expression is for a value_target, and
5539 // we will not leave anything on the stack.
5541 Expression value_target;
5542 bool value_target_set = false;
5543 bool is_type_parameter = false;
5545 public New (Expression requested_type, ArrayList arguments, Location l)
5547 RequestedType = requested_type;
5548 Arguments = arguments;
5552 public bool SetValueTypeVariable (Expression value)
5554 value_target = value;
5555 value_target_set = true;
5556 if (!(value_target is IMemoryLocation)){
5557 Error_UnexpectedKind (null, "variable", loc);
5564 // This function is used to disable the following code sequence for
5565 // value type initialization:
5567 // AddressOf (temporary)
5571 // Instead the provide will have provided us with the address on the
5572 // stack to store the results.
5574 static Expression MyEmptyExpression;
5576 public void DisableTemporaryValueType ()
5578 if (MyEmptyExpression == null)
5579 MyEmptyExpression = new EmptyAddressOf ();
5582 // To enable this, look into:
5583 // test-34 and test-89 and self bootstrapping.
5585 // For instance, we can avoid a copy by using `newobj'
5586 // instead of Call + Push-temp on value types.
5587 // value_target = MyEmptyExpression;
5592 /// Converts complex core type syntax like 'new int ()' to simple constant
5594 public static Constant Constantify (Type t)
5596 if (t == TypeManager.int32_type)
5597 return new IntConstant (0, Location.Null);
5598 if (t == TypeManager.uint32_type)
5599 return new UIntConstant (0, Location.Null);
5600 if (t == TypeManager.int64_type)
5601 return new LongConstant (0, Location.Null);
5602 if (t == TypeManager.uint64_type)
5603 return new ULongConstant (0, Location.Null);
5604 if (t == TypeManager.float_type)
5605 return new FloatConstant (0, Location.Null);
5606 if (t == TypeManager.double_type)
5607 return new DoubleConstant (0, Location.Null);
5608 if (t == TypeManager.short_type)
5609 return new ShortConstant (0, Location.Null);
5610 if (t == TypeManager.ushort_type)
5611 return new UShortConstant (0, Location.Null);
5612 if (t == TypeManager.sbyte_type)
5613 return new SByteConstant (0, Location.Null);
5614 if (t == TypeManager.byte_type)
5615 return new ByteConstant (0, Location.Null);
5616 if (t == TypeManager.char_type)
5617 return new CharConstant ('\0', Location.Null);
5618 if (t == TypeManager.bool_type)
5619 return new BoolConstant (false, Location.Null);
5620 if (t == TypeManager.decimal_type)
5621 return new DecimalConstant (0, Location.Null);
5622 if (TypeManager.IsEnumType (t))
5623 return new EnumConstant (Constantify (TypeManager.EnumToUnderlying (t)), t);
5629 // Checks whether the type is an interface that has the
5630 // [ComImport, CoClass] attributes and must be treated
5633 public Expression CheckComImport (EmitContext ec)
5635 if (!type.IsInterface)
5639 // Turn the call into:
5640 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5642 Type real_class = AttributeTester.GetCoClassAttribute (type);
5643 if (real_class == null)
5646 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5647 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5648 return cast.Resolve (ec);
5651 public override Expression DoResolve (EmitContext ec)
5654 // The New DoResolve might be called twice when initializing field
5655 // expressions (see EmitFieldInitializers, the call to
5656 // GetInitializerExpression will perform a resolve on the expression,
5657 // and later the assign will trigger another resolution
5659 // This leads to bugs (#37014)
5662 if (RequestedType is NewDelegate)
5663 return RequestedType;
5667 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5673 if (type == TypeManager.void_type) {
5674 Error_VoidInvalidInTheContext (loc);
5678 if (Arguments == null) {
5679 Expression c = Constantify (type);
5684 if (TypeManager.IsDelegateType (type)) {
5685 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5686 if (RequestedType != null)
5687 if (!(RequestedType is DelegateCreation))
5688 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5689 return RequestedType;
5693 if (type.IsGenericParameter) {
5694 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5696 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5697 Error (304, String.Format (
5698 "Cannot create an instance of the " +
5699 "variable type '{0}' because it " +
5700 "doesn't have the new() constraint",
5705 if ((Arguments != null) && (Arguments.Count != 0)) {
5706 Error (417, String.Format (
5707 "`{0}': cannot provide arguments " +
5708 "when creating an instance of a " +
5709 "variable type.", type));
5713 is_type_parameter = true;
5714 eclass = ExprClass.Value;
5719 if (type.IsAbstract && type.IsSealed) {
5720 Report.SymbolRelatedToPreviousError (type);
5721 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5725 if (type.IsInterface || type.IsAbstract){
5726 RequestedType = CheckComImport (ec);
5727 if (RequestedType != null)
5728 return RequestedType;
5730 Report.SymbolRelatedToPreviousError (type);
5731 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5735 bool is_struct = type.IsValueType;
5736 eclass = ExprClass.Value;
5739 // SRE returns a match for .ctor () on structs (the object constructor),
5740 // so we have to manually ignore it.
5742 if (is_struct && Arguments == null)
5745 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5746 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5747 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5752 MethodGroupExpr mg = ml as MethodGroupExpr;
5755 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5759 if (Arguments != null){
5760 foreach (Argument a in Arguments){
5761 if (!a.Resolve (ec, loc))
5766 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5767 if (method == null) {
5768 if (almostMatchedMembers.Count != 0)
5769 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5776 bool DoEmitTypeParameter (EmitContext ec)
5779 ILGenerator ig = ec.ig;
5781 ig.Emit (OpCodes.Ldtoken, type);
5782 ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
5783 ig.Emit (OpCodes.Call, TypeManager.activator_create_instance);
5784 ig.Emit (OpCodes.Unbox_Any, type);
5787 throw new InternalErrorException ();
5792 // This DoEmit can be invoked in two contexts:
5793 // * As a mechanism that will leave a value on the stack (new object)
5794 // * As one that wont (init struct)
5796 // You can control whether a value is required on the stack by passing
5797 // need_value_on_stack. The code *might* leave a value on the stack
5798 // so it must be popped manually
5800 // If we are dealing with a ValueType, we have a few
5801 // situations to deal with:
5803 // * The target is a ValueType, and we have been provided
5804 // the instance (this is easy, we are being assigned).
5806 // * The target of New is being passed as an argument,
5807 // to a boxing operation or a function that takes a
5810 // In this case, we need to create a temporary variable
5811 // that is the argument of New.
5813 // Returns whether a value is left on the stack
5815 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5817 bool is_value_type = TypeManager.IsValueType (type);
5818 ILGenerator ig = ec.ig;
5823 // Allow DoEmit() to be called multiple times.
5824 // We need to create a new LocalTemporary each time since
5825 // you can't share LocalBuilders among ILGeneators.
5826 if (!value_target_set)
5827 value_target = new LocalTemporary (type);
5829 ml = (IMemoryLocation) value_target;
5830 ml.AddressOf (ec, AddressOp.Store);
5834 Invocation.EmitArguments (ec, method, Arguments, false, null);
5838 ig.Emit (OpCodes.Initobj, type);
5840 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5841 if (need_value_on_stack){
5842 value_target.Emit (ec);
5847 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5852 public override void Emit (EmitContext ec)
5854 if (is_type_parameter)
5855 DoEmitTypeParameter (ec);
5860 public override void EmitStatement (EmitContext ec)
5862 if (is_type_parameter)
5863 throw new InvalidOperationException ();
5865 if (DoEmit (ec, false))
5866 ec.ig.Emit (OpCodes.Pop);
5869 public void AddressOf (EmitContext ec, AddressOp Mode)
5871 if (is_type_parameter)
5872 throw new InvalidOperationException ();
5874 if (!type.IsValueType){
5876 // We throw an exception. So far, I believe we only need to support
5878 // foreach (int j in new StructType ())
5881 throw new Exception ("AddressOf should not be used for classes");
5884 if (!value_target_set)
5885 value_target = new LocalTemporary (type);
5887 IMemoryLocation ml = (IMemoryLocation) value_target;
5888 ml.AddressOf (ec, AddressOp.Store);
5890 Invocation.EmitArguments (ec, method, Arguments, false, null);
5893 ec.ig.Emit (OpCodes.Initobj, type);
5895 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5897 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5902 /// 14.5.10.2: Represents an array creation expression.
5906 /// There are two possible scenarios here: one is an array creation
5907 /// expression that specifies the dimensions and optionally the
5908 /// initialization data and the other which does not need dimensions
5909 /// specified but where initialization data is mandatory.
5911 public class ArrayCreation : Expression {
5912 Expression requested_base_type;
5913 ArrayList initializers;
5916 // The list of Argument types.
5917 // This is used to construct the `newarray' or constructor signature
5919 ArrayList arguments;
5922 // Method used to create the array object.
5924 MethodBase new_method = null;
5926 Type array_element_type;
5927 Type underlying_type;
5928 bool is_one_dimensional = false;
5929 bool is_builtin_type = false;
5930 bool expect_initializers = false;
5931 int num_arguments = 0;
5935 ArrayList array_data;
5939 // The number of constants in array initializers
5940 int const_initializers_count;
5942 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5944 this.requested_base_type = requested_base_type;
5945 this.initializers = initializers;
5949 arguments = new ArrayList ();
5951 foreach (Expression e in exprs) {
5952 arguments.Add (new Argument (e, Argument.AType.Expression));
5957 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5959 this.requested_base_type = requested_base_type;
5960 this.initializers = initializers;
5964 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5966 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5968 //dimensions = tmp.Length - 1;
5969 expect_initializers = true;
5972 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5974 StringBuilder sb = new StringBuilder (rank);
5977 for (int i = 1; i < idx_count; i++)
5982 return new ComposedCast (base_type, sb.ToString (), loc);
5985 void Error_IncorrectArrayInitializer ()
5987 Error (178, "Invalid rank specifier: expected `,' or `]'");
5990 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5992 if (specified_dims) {
5993 Argument a = (Argument) arguments [idx];
5995 if (!a.Resolve (ec, loc))
5998 Constant c = a.Expr as Constant;
6000 c = c.ImplicitConversionRequired (TypeManager.int32_type, a.Expr.Location);
6004 Report.Error (150, a.Expr.Location, "A constant value is expected");
6008 int value = (int) c.GetValue ();
6010 if (value != probe.Count) {
6011 Error_IncorrectArrayInitializer ();
6015 bounds [idx] = value;
6018 int child_bounds = -1;
6019 for (int i = 0; i < probe.Count; ++i) {
6020 object o = probe [i];
6021 if (o is ArrayList) {
6022 ArrayList sub_probe = o as ArrayList;
6023 int current_bounds = sub_probe.Count;
6025 if (child_bounds == -1)
6026 child_bounds = current_bounds;
6028 else if (child_bounds != current_bounds){
6029 Error_IncorrectArrayInitializer ();
6032 if (idx + 1 >= dimensions){
6033 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
6037 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
6041 if (child_bounds != -1){
6042 Error_IncorrectArrayInitializer ();
6046 Expression tmp = (Expression) o;
6047 tmp = tmp.Resolve (ec);
6051 Expression conv = Convert.ImplicitConversionRequired (
6052 ec, tmp, underlying_type, loc);
6057 // Initializers with the default values can be ignored
6058 Constant c = tmp as Constant;
6060 if (c.IsDefaultInitializer (array_element_type)) {
6064 ++const_initializers_count;
6067 // Used to invalidate static initializer
6068 const_initializers_count = int.MinValue;
6071 array_data.Add (conv);
6078 public void UpdateIndices ()
6081 for (ArrayList probe = initializers; probe != null;) {
6082 if (probe.Count > 0 && probe [0] is ArrayList) {
6083 Expression e = new IntConstant (probe.Count, Location.Null);
6084 arguments.Add (new Argument (e, Argument.AType.Expression));
6086 bounds [i++] = probe.Count;
6088 probe = (ArrayList) probe [0];
6091 Expression e = new IntConstant (probe.Count, Location.Null);
6092 arguments.Add (new Argument (e, Argument.AType.Expression));
6094 bounds [i++] = probe.Count;
6101 bool ResolveInitializers (EmitContext ec)
6103 if (initializers == null) {
6104 return !expect_initializers;
6107 if (underlying_type == null)
6111 // We use this to store all the date values in the order in which we
6112 // will need to store them in the byte blob later
6114 array_data = new ArrayList ();
6115 bounds = new System.Collections.Specialized.HybridDictionary ();
6117 if (arguments != null)
6118 return CheckIndices (ec, initializers, 0, true);
6120 arguments = new ArrayList ();
6122 if (!CheckIndices (ec, initializers, 0, false))
6127 if (arguments.Count != dimensions) {
6128 Error_IncorrectArrayInitializer ();
6136 // Creates the type of the array
6138 bool LookupType (EmitContext ec)
6140 StringBuilder array_qualifier = new StringBuilder (rank);
6143 // `In the first form allocates an array instace of the type that results
6144 // from deleting each of the individual expression from the expression list'
6146 if (num_arguments > 0) {
6147 array_qualifier.Append ("[");
6148 for (int i = num_arguments-1; i > 0; i--)
6149 array_qualifier.Append (",");
6150 array_qualifier.Append ("]");
6156 TypeExpr array_type_expr;
6157 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
6158 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
6159 if (array_type_expr == null)
6162 type = array_type_expr.Type;
6163 underlying_type = TypeManager.GetElementType (type);
6164 dimensions = type.GetArrayRank ();
6169 public override Expression DoResolve (EmitContext ec)
6174 if (!LookupType (ec))
6177 array_element_type = TypeManager.GetElementType (type);
6178 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
6179 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
6184 // First step is to validate the initializers and fill
6185 // in any missing bits
6187 if (!ResolveInitializers (ec))
6191 if (arguments == null)
6194 arg_count = arguments.Count;
6195 foreach (Argument a in arguments){
6196 if (!a.Resolve (ec, loc))
6199 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
6200 if (real_arg == null)
6207 if (arg_count == 1) {
6208 is_one_dimensional = true;
6209 eclass = ExprClass.Value;
6213 is_builtin_type = TypeManager.IsBuiltinType (type);
6215 if (is_builtin_type) {
6218 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
6219 AllBindingFlags, loc);
6221 if (!(ml is MethodGroupExpr)) {
6222 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
6227 Error (-6, "New invocation: Can not find a constructor for " +
6228 "this argument list");
6232 new_method = Invocation.OverloadResolve (
6233 ec, (MethodGroupExpr) ml, arguments, false, loc);
6235 if (new_method == null) {
6236 Error (-6, "New invocation: Can not find a constructor for " +
6237 "this argument list");
6241 eclass = ExprClass.Value;
6244 ModuleBuilder mb = CodeGen.Module.Builder;
6245 ArrayList args = new ArrayList ();
6247 if (arguments != null) {
6248 for (int i = 0; i < arg_count; i++)
6249 args.Add (TypeManager.int32_type);
6252 Type [] arg_types = null;
6255 arg_types = new Type [args.Count];
6257 args.CopyTo (arg_types, 0);
6259 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6262 if (new_method == null) {
6263 Error (-6, "New invocation: Can not find a constructor for " +
6264 "this argument list");
6268 eclass = ExprClass.Value;
6273 byte [] MakeByteBlob ()
6278 int count = array_data.Count;
6280 if (underlying_type.IsEnum)
6281 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
6283 factor = GetTypeSize (underlying_type);
6285 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
6287 data = new byte [(count * factor + 4) & ~3];
6290 for (int i = 0; i < count; ++i) {
6291 object v = array_data [i];
6293 if (v is EnumConstant)
6294 v = ((EnumConstant) v).Child;
6296 if (v is Constant && !(v is StringConstant))
6297 v = ((Constant) v).GetValue ();
6303 if (underlying_type == TypeManager.int64_type){
6304 if (!(v is Expression)){
6305 long val = (long) v;
6307 for (int j = 0; j < factor; ++j) {
6308 data [idx + j] = (byte) (val & 0xFF);
6312 } else if (underlying_type == TypeManager.uint64_type){
6313 if (!(v is Expression)){
6314 ulong val = (ulong) v;
6316 for (int j = 0; j < factor; ++j) {
6317 data [idx + j] = (byte) (val & 0xFF);
6321 } else if (underlying_type == TypeManager.float_type) {
6322 if (!(v is Expression)){
6323 element = BitConverter.GetBytes ((float) v);
6325 for (int j = 0; j < factor; ++j)
6326 data [idx + j] = element [j];
6328 } else if (underlying_type == TypeManager.double_type) {
6329 if (!(v is Expression)){
6330 element = BitConverter.GetBytes ((double) v);
6332 for (int j = 0; j < factor; ++j)
6333 data [idx + j] = element [j];
6335 } else if (underlying_type == TypeManager.char_type){
6336 if (!(v is Expression)){
6337 int val = (int) ((char) v);
6339 data [idx] = (byte) (val & 0xff);
6340 data [idx+1] = (byte) (val >> 8);
6342 } else if (underlying_type == TypeManager.short_type){
6343 if (!(v is Expression)){
6344 int val = (int) ((short) v);
6346 data [idx] = (byte) (val & 0xff);
6347 data [idx+1] = (byte) (val >> 8);
6349 } else if (underlying_type == TypeManager.ushort_type){
6350 if (!(v is Expression)){
6351 int val = (int) ((ushort) v);
6353 data [idx] = (byte) (val & 0xff);
6354 data [idx+1] = (byte) (val >> 8);
6356 } else if (underlying_type == TypeManager.int32_type) {
6357 if (!(v is Expression)){
6360 data [idx] = (byte) (val & 0xff);
6361 data [idx+1] = (byte) ((val >> 8) & 0xff);
6362 data [idx+2] = (byte) ((val >> 16) & 0xff);
6363 data [idx+3] = (byte) (val >> 24);
6365 } else if (underlying_type == TypeManager.uint32_type) {
6366 if (!(v is Expression)){
6367 uint val = (uint) v;
6369 data [idx] = (byte) (val & 0xff);
6370 data [idx+1] = (byte) ((val >> 8) & 0xff);
6371 data [idx+2] = (byte) ((val >> 16) & 0xff);
6372 data [idx+3] = (byte) (val >> 24);
6374 } else if (underlying_type == TypeManager.sbyte_type) {
6375 if (!(v is Expression)){
6376 sbyte val = (sbyte) v;
6377 data [idx] = (byte) val;
6379 } else if (underlying_type == TypeManager.byte_type) {
6380 if (!(v is Expression)){
6381 byte val = (byte) v;
6382 data [idx] = (byte) val;
6384 } else if (underlying_type == TypeManager.bool_type) {
6385 if (!(v is Expression)){
6386 bool val = (bool) v;
6387 data [idx] = (byte) (val ? 1 : 0);
6389 } else if (underlying_type == TypeManager.decimal_type){
6390 if (!(v is Expression)){
6391 int [] bits = Decimal.GetBits ((decimal) v);
6394 // FIXME: For some reason, this doesn't work on the MS runtime.
6395 int [] nbits = new int [4];
6396 nbits [0] = bits [3];
6397 nbits [1] = bits [2];
6398 nbits [2] = bits [0];
6399 nbits [3] = bits [1];
6401 for (int j = 0; j < 4; j++){
6402 data [p++] = (byte) (nbits [j] & 0xff);
6403 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6404 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6405 data [p++] = (byte) (nbits [j] >> 24);
6409 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6418 // Emits the initializers for the array
6420 void EmitStaticInitializers (EmitContext ec)
6423 // First, the static data
6426 ILGenerator ig = ec.ig;
6428 byte [] data = MakeByteBlob ();
6430 fb = RootContext.MakeStaticData (data);
6432 ig.Emit (OpCodes.Dup);
6433 ig.Emit (OpCodes.Ldtoken, fb);
6434 ig.Emit (OpCodes.Call,
6435 TypeManager.void_initializearray_array_fieldhandle);
6439 // Emits pieces of the array that can not be computed at compile
6440 // time (variables and string locations).
6442 // This always expect the top value on the stack to be the array
6444 void EmitDynamicInitializers (EmitContext ec)
6446 ILGenerator ig = ec.ig;
6447 int dims = bounds.Count;
6448 int [] current_pos = new int [dims];
6450 MethodInfo set = null;
6453 Type [] args = new Type [dims + 1];
6455 for (int j = 0; j < dims; j++)
6456 args [j] = TypeManager.int32_type;
6457 args [dims] = array_element_type;
6459 set = CodeGen.Module.Builder.GetArrayMethod (
6461 CallingConventions.HasThis | CallingConventions.Standard,
6462 TypeManager.void_type, args);
6465 for (int i = 0; i < array_data.Count; i++){
6467 Expression e = (Expression)array_data [i];
6470 Type etype = e.Type;
6472 ig.Emit (OpCodes.Dup);
6474 for (int idx = 0; idx < dims; idx++)
6475 IntConstant.EmitInt (ig, current_pos [idx]);
6478 // If we are dealing with a struct, get the
6479 // address of it, so we can store it.
6481 if ((dims == 1) && etype.IsValueType &&
6482 (!TypeManager.IsBuiltinOrEnum (etype) ||
6483 etype == TypeManager.decimal_type)) {
6488 // Let new know that we are providing
6489 // the address where to store the results
6491 n.DisableTemporaryValueType ();
6494 ig.Emit (OpCodes.Ldelema, etype);
6500 bool is_stobj, has_type_arg;
6501 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
6503 ig.Emit (OpCodes.Stobj, etype);
6504 else if (has_type_arg)
6505 ig.Emit (op, etype);
6509 ig.Emit (OpCodes.Call, set);
6516 for (int j = dims - 1; j >= 0; j--){
6518 if (current_pos [j] < (int) bounds [j])
6520 current_pos [j] = 0;
6525 void EmitArrayArguments (EmitContext ec)
6527 ILGenerator ig = ec.ig;
6529 foreach (Argument a in arguments) {
6530 Type atype = a.Type;
6533 if (atype == TypeManager.uint64_type)
6534 ig.Emit (OpCodes.Conv_Ovf_U4);
6535 else if (atype == TypeManager.int64_type)
6536 ig.Emit (OpCodes.Conv_Ovf_I4);
6540 public override void Emit (EmitContext ec)
6542 ILGenerator ig = ec.ig;
6544 EmitArrayArguments (ec);
6545 if (is_one_dimensional)
6546 ig.Emit (OpCodes.Newarr, array_element_type);
6548 if (is_builtin_type)
6549 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6551 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6554 if (initializers == null)
6557 // This is a treshold for static initializers
6558 // I tried to make more accurate but it seems to me that Array.Initialize is
6559 // always slower (managed -> unmanaged switch?)
6560 const int max_automatic_initializers = 200;
6562 if (const_initializers_count > max_automatic_initializers && TypeManager.IsPrimitiveType (array_element_type)) {
6563 EmitStaticInitializers (ec);
6567 EmitDynamicInitializers (ec);
6570 public override bool GetAttributableValue (Type valueType, out object value)
6572 if (!is_one_dimensional){
6573 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6574 return base.GetAttributableValue (null, out value);
6577 if (array_data == null) {
6578 Constant c = (Constant)((Argument)arguments [0]).Expr;
6579 if (c.IsDefaultValue) {
6580 value = Array.CreateInstance (array_element_type, 0);
6583 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6584 return base.GetAttributableValue (null, out value);
6587 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6588 object element_value;
6589 for (int i = 0; i < ret.Length; ++i)
6591 Expression e = (Expression)array_data [i];
6593 // Is null when an initializer is optimized (value == predefined value)
6597 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6601 ret.SetValue (element_value, i);
6608 public sealed class CompilerGeneratedThis : This
6610 public static This Instance = new CompilerGeneratedThis ();
6612 private CompilerGeneratedThis ()
6613 : base (Location.Null)
6617 public override Expression DoResolve (EmitContext ec)
6619 eclass = ExprClass.Variable;
6620 type = ec.ContainerType;
6621 variable = new SimpleThis (type);
6627 /// Represents the `this' construct
6630 public class This : VariableReference, IVariable
6633 VariableInfo variable_info;
6634 protected Variable variable;
6637 public This (Block block, Location loc)
6643 public This (Location loc)
6648 public VariableInfo VariableInfo {
6649 get { return variable_info; }
6652 public bool VerifyFixed ()
6654 return !TypeManager.IsValueType (Type);
6657 public override bool IsRef {
6658 get { return is_struct; }
6661 public override Variable Variable {
6662 get { return variable; }
6665 public bool ResolveBase (EmitContext ec)
6667 eclass = ExprClass.Variable;
6669 if (ec.TypeContainer.CurrentType != null)
6670 type = ec.TypeContainer.CurrentType;
6672 type = ec.ContainerType;
6674 is_struct = ec.TypeContainer is Struct;
6677 Error (26, "Keyword `this' is not valid in a static property, " +
6678 "static method, or static field initializer");
6682 if (block != null) {
6683 if (block.Toplevel.ThisVariable != null)
6684 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6686 AnonymousContainer am = ec.CurrentAnonymousMethod;
6687 if (is_struct && (am != null) && !am.IsIterator) {
6688 Report.Error (1673, loc, "Anonymous methods inside structs " +
6689 "cannot access instance members of `this'. " +
6690 "Consider copying `this' to a local variable " +
6691 "outside the anonymous method and using the " +
6696 RootScopeInfo host = block.Toplevel.RootScope;
6697 if ((host != null) && !ec.IsConstructor &&
6698 (!is_struct || host.IsIterator)) {
6699 variable = host.CaptureThis ();
6700 type = variable.Type;
6705 if (variable == null)
6706 variable = new SimpleThis (type);
6711 public override Expression DoResolve (EmitContext ec)
6713 if (!ResolveBase (ec))
6716 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) &&
6717 !variable_info.IsAssigned (ec)) {
6718 Error (188, "The `this' object cannot be used before all of its " +
6719 "fields are assigned to");
6720 variable_info.SetAssigned (ec);
6724 if (ec.IsFieldInitializer) {
6725 Error (27, "Keyword `this' is not available in the current context");
6732 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6734 if (!ResolveBase (ec))
6737 if (variable_info != null)
6738 variable_info.SetAssigned (ec);
6740 if (ec.TypeContainer is Class){
6741 Error (1604, "Cannot assign to 'this' because it is read-only");
6747 public override int GetHashCode()
6749 return block.GetHashCode ();
6752 public override bool Equals (object obj)
6754 This t = obj as This;
6758 return block == t.block;
6761 protected class SimpleThis : Variable
6765 public SimpleThis (Type type)
6770 public override Type Type {
6771 get { return type; }
6774 public override bool HasInstance {
6775 get { return false; }
6778 public override bool NeedsTemporary {
6779 get { return false; }
6782 public override void EmitInstance (EmitContext ec)
6787 public override void Emit (EmitContext ec)
6789 ec.ig.Emit (OpCodes.Ldarg_0);
6792 public override void EmitAssign (EmitContext ec)
6794 throw new InvalidOperationException ();
6797 public override void EmitAddressOf (EmitContext ec)
6799 ec.ig.Emit (OpCodes.Ldarg_0);
6805 /// Represents the `__arglist' construct
6807 public class ArglistAccess : Expression
6809 public ArglistAccess (Location loc)
6814 public override Expression DoResolve (EmitContext ec)
6816 eclass = ExprClass.Variable;
6817 type = TypeManager.runtime_argument_handle_type;
6819 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6821 Error (190, "The __arglist construct is valid only within " +
6822 "a variable argument method");
6829 public override void Emit (EmitContext ec)
6831 ec.ig.Emit (OpCodes.Arglist);
6836 /// Represents the `__arglist (....)' construct
6838 public class Arglist : Expression
6840 public readonly Argument[] Arguments;
6842 public Arglist (Argument[] args, Location l)
6848 public Type[] ArgumentTypes {
6850 Type[] retval = new Type [Arguments.Length];
6851 for (int i = 0; i < Arguments.Length; i++)
6852 retval [i] = Arguments [i].Type;
6857 public override Expression DoResolve (EmitContext ec)
6859 eclass = ExprClass.Variable;
6860 type = TypeManager.runtime_argument_handle_type;
6862 foreach (Argument arg in Arguments) {
6863 if (!arg.Resolve (ec, loc))
6870 public override void Emit (EmitContext ec)
6872 foreach (Argument arg in Arguments)
6878 // This produces the value that renders an instance, used by the iterators code
6880 public class ProxyInstance : Expression, IMemoryLocation {
6881 public override Expression DoResolve (EmitContext ec)
6883 eclass = ExprClass.Variable;
6884 type = ec.ContainerType;
6888 public override void Emit (EmitContext ec)
6890 ec.ig.Emit (OpCodes.Ldarg_0);
6894 public void AddressOf (EmitContext ec, AddressOp mode)
6896 ec.ig.Emit (OpCodes.Ldarg_0);
6901 /// Implements the typeof operator
6903 public class TypeOf : Expression {
6904 readonly Expression QueriedType;
6905 protected Type typearg;
6907 public TypeOf (Expression queried_type, Location l)
6909 QueriedType = queried_type;
6913 public override Expression DoResolve (EmitContext ec)
6915 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6919 typearg = texpr.Type;
6921 if (typearg == TypeManager.void_type) {
6922 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6926 if (typearg.IsPointer && !ec.InUnsafe){
6931 type = TypeManager.type_type;
6932 // Even though what is returned is a type object, it's treated as a value by the compiler.
6933 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6934 eclass = ExprClass.Value;
6938 public override void Emit (EmitContext ec)
6940 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6941 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6944 public override bool GetAttributableValue (Type valueType, out object value)
6946 if (TypeManager.ContainsGenericParameters (typearg)) {
6947 Report.SymbolRelatedToPreviousError(typearg);
6948 Report.Error(416, loc, "`{0}': an attribute argument cannot use type parameters",
6949 TypeManager.CSharpName(typearg));
6954 if (valueType == TypeManager.object_type) {
6955 value = (object)typearg;
6962 public Type TypeArgument
6972 /// Implements the `typeof (void)' operator
6974 public class TypeOfVoid : TypeOf {
6975 public TypeOfVoid (Location l) : base (null, l)
6980 public override Expression DoResolve (EmitContext ec)
6982 type = TypeManager.type_type;
6983 typearg = TypeManager.void_type;
6984 // See description in TypeOf.
6985 eclass = ExprClass.Value;
6991 /// Implements the sizeof expression
6993 public class SizeOf : Expression {
6994 public Expression QueriedType;
6997 public SizeOf (Expression queried_type, Location l)
6999 this.QueriedType = queried_type;
7003 public override Expression DoResolve (EmitContext ec)
7005 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
7010 if (texpr is TypeParameterExpr){
7011 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
7016 type_queried = texpr.Type;
7017 if (type_queried.IsEnum)
7018 type_queried = TypeManager.EnumToUnderlying (type_queried);
7020 if (type_queried == TypeManager.void_type) {
7021 Expression.Error_VoidInvalidInTheContext (loc);
7025 int size_of = GetTypeSize (type_queried);
7027 return new IntConstant (size_of, loc);
7031 Report.Error (233, loc, "`{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf)",
7032 TypeManager.CSharpName (type_queried));
7036 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
7040 type = TypeManager.int32_type;
7041 eclass = ExprClass.Value;
7045 public override void Emit (EmitContext ec)
7047 int size = GetTypeSize (type_queried);
7050 ec.ig.Emit (OpCodes.Sizeof, type_queried);
7052 IntConstant.EmitInt (ec.ig, size);
7057 /// Implements the qualified-alias-member (::) expression.
7059 public class QualifiedAliasMember : Expression
7061 string alias, identifier;
7063 public QualifiedAliasMember (string alias, string identifier, Location l)
7066 this.identifier = identifier;
7070 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7072 if (alias == "global")
7073 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
7075 int errors = Report.Errors;
7076 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7078 if (errors == Report.Errors)
7079 Report.Error (432, loc, "Alias `{0}' not found", alias);
7082 if (fne.eclass != ExprClass.Namespace) {
7084 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7087 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
7090 public override Expression DoResolve (EmitContext ec)
7092 FullNamedExpression fne;
7093 if (alias == "global") {
7094 fne = RootNamespace.Global;
7096 int errors = Report.Errors;
7097 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7099 if (errors == Report.Errors)
7100 Report.Error (432, loc, "Alias `{0}' not found", alias);
7105 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
7109 if (!(retval is FullNamedExpression)) {
7110 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
7114 // We defer this check till the end to match the behaviour of CSC
7115 if (fne.eclass != ExprClass.Namespace) {
7116 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7122 public override void Emit (EmitContext ec)
7124 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
7128 public override string ToString ()
7130 return alias + "::" + identifier;
7133 public override string GetSignatureForError ()
7140 /// Implements the member access expression
7142 public class MemberAccess : Expression {
7143 public readonly string Identifier;
7146 public MemberAccess (Expression expr, string id)
7147 : this (expr, id, expr.Location)
7151 public MemberAccess (Expression expr, string identifier, Location loc)
7154 Identifier = identifier;
7158 public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
7159 : this (expr, identifier, loc)
7166 public Expression Expr {
7167 get { return expr; }
7170 protected string LookupIdentifier {
7171 get { return MemberName.MakeName (Identifier, args); }
7174 // TODO: this method has very poor performace for Enum fields and
7175 // probably for other constants as well
7176 Expression DoResolve (EmitContext ec, Expression right_side)
7179 throw new Exception ();
7182 // Resolve the expression with flow analysis turned off, we'll do the definite
7183 // assignment checks later. This is because we don't know yet what the expression
7184 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7185 // definite assignment check on the actual field and not on the whole struct.
7188 SimpleName original = expr as SimpleName;
7189 Expression new_expr = expr.Resolve (ec,
7190 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7191 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7193 if (new_expr == null)
7196 if (new_expr is Namespace) {
7197 Namespace ns = (Namespace) new_expr;
7198 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, LookupIdentifier, loc);
7200 if ((retval != null) && (args != null))
7201 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
7205 ns.Error_NamespaceDoesNotExist (ec.DeclContainer, loc, Identifier);
7209 Type expr_type = new_expr.Type;
7210 if (expr_type.IsPointer || expr_type == TypeManager.void_type || new_expr is NullLiteral){
7211 Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type);
7214 if (expr_type == TypeManager.anonymous_method_type){
7215 Unary.Error_OperatorCannotBeApplied (loc, ".", "anonymous method");
7219 Constant c = new_expr as Constant;
7220 if (c != null && c.GetValue () == null) {
7221 Report.Warning (1720, 1, loc, "Expression will always cause a `{0}'",
7222 "System.NullReferenceException");
7225 Expression member_lookup;
7226 member_lookup = MemberLookup (
7227 ec.ContainerType, expr_type, expr_type, Identifier, loc);
7229 if ((member_lookup == null) && (args != null)) {
7230 member_lookup = MemberLookup (
7231 ec.ContainerType, expr_type, expr_type, LookupIdentifier, loc);
7234 if (member_lookup == null) {
7235 MemberLookupFailed (
7236 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
7240 TypeExpr texpr = member_lookup as TypeExpr;
7241 if (texpr != null) {
7242 if (!(new_expr is TypeExpr) &&
7243 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
7244 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7245 Identifier, member_lookup.GetSignatureForError ());
7249 if (!texpr.CheckAccessLevel (ec.DeclContainer)) {
7250 Report.SymbolRelatedToPreviousError (member_lookup.Type);
7251 ErrorIsInaccesible (loc, TypeManager.CSharpName (member_lookup.Type));
7256 ConstructedType ct = new_expr as ConstructedType;
7259 // When looking up a nested type in a generic instance
7260 // via reflection, we always get a generic type definition
7261 // and not a generic instance - so we have to do this here.
7263 // See gtest-172-lib.cs and gtest-172.cs for an example.
7265 ct = new ConstructedType (
7266 member_lookup.Type, ct.TypeArguments, loc);
7268 return ct.ResolveAsTypeStep (ec, false);
7271 return member_lookup;
7274 MemberExpr me = (MemberExpr) member_lookup;
7275 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
7276 if (member_lookup == null)
7280 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
7282 throw new InternalErrorException ();
7284 return mg.ResolveGeneric (ec, args);
7287 if (original != null && !TypeManager.IsValueType (expr_type)) {
7288 me = member_lookup as MemberExpr;
7289 if (me != null && me.IsInstance) {
7290 LocalVariableReference var = new_expr as LocalVariableReference;
7291 if (var != null && !var.VerifyAssigned (ec))
7296 // The following DoResolve/DoResolveLValue will do the definite assignment
7299 if (right_side != null)
7300 return member_lookup.DoResolveLValue (ec, right_side);
7302 return member_lookup.DoResolve (ec);
7305 public override Expression DoResolve (EmitContext ec)
7307 return DoResolve (ec, null);
7310 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7312 return DoResolve (ec, right_side);
7315 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7317 return ResolveNamespaceOrType (ec, silent);
7320 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7322 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
7324 if (new_expr == null)
7327 if (new_expr is Namespace) {
7328 Namespace ns = (Namespace) new_expr;
7329 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, LookupIdentifier, loc);
7331 if ((retval != null) && (args != null))
7332 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
7334 if (!silent && retval == null)
7335 ns.Error_NamespaceDoesNotExist (rc.DeclContainer, loc, LookupIdentifier);
7339 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
7340 if (tnew_expr == null)
7343 Type expr_type = tnew_expr.Type;
7345 if (expr_type.IsPointer){
7346 Error (23, "The `.' operator can not be applied to pointer operands (" +
7347 TypeManager.CSharpName (expr_type) + ")");
7351 Expression member_lookup = MemberLookup (
7352 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
7353 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7354 if (member_lookup == null) {
7358 member_lookup = MemberLookup(
7359 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
7360 MemberTypes.All, BindingFlags.Public | BindingFlags.NonPublic, loc);
7362 if (member_lookup == null) {
7363 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7364 Identifier, new_expr.GetSignatureForError ());
7366 // TODO: Report.SymbolRelatedToPreviousError
7367 member_lookup.Error_UnexpectedKind (null, "type", loc);
7372 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
7377 TypeArguments the_args = args;
7378 if (TypeManager.HasGenericArguments (expr_type)) {
7379 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
7381 TypeArguments new_args = new TypeArguments (loc);
7382 foreach (Type decl in decl_args)
7383 new_args.Add (new TypeExpression (decl, loc));
7386 new_args.Add (args);
7388 the_args = new_args;
7391 if (the_args != null) {
7392 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
7393 return ctype.ResolveAsTypeStep (rc, false);
7400 public override void Emit (EmitContext ec)
7402 throw new Exception ("Should not happen");
7405 public override string ToString ()
7407 return expr + "." + MemberName.MakeName (Identifier, args);
7410 public override string GetSignatureForError ()
7412 return expr.GetSignatureForError () + "." + Identifier;
7417 /// Implements checked expressions
7419 public class CheckedExpr : Expression {
7421 public Expression Expr;
7423 public CheckedExpr (Expression e, Location l)
7429 public override Expression DoResolve (EmitContext ec)
7431 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7432 Expr = Expr.Resolve (ec);
7437 if (Expr is Constant)
7440 eclass = Expr.eclass;
7445 public override void Emit (EmitContext ec)
7447 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7451 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7453 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7454 Expr.EmitBranchable (ec, target, onTrue);
7459 /// Implements the unchecked expression
7461 public class UnCheckedExpr : Expression {
7463 public Expression Expr;
7465 public UnCheckedExpr (Expression e, Location l)
7471 public override Expression DoResolve (EmitContext ec)
7473 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7474 Expr = Expr.Resolve (ec);
7479 if (Expr is Constant)
7482 eclass = Expr.eclass;
7487 public override void Emit (EmitContext ec)
7489 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7493 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7495 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7496 Expr.EmitBranchable (ec, target, onTrue);
7501 /// An Element Access expression.
7503 /// During semantic analysis these are transformed into
7504 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7506 public class ElementAccess : Expression {
7507 public ArrayList Arguments;
7508 public Expression Expr;
7510 public ElementAccess (Expression e, ArrayList e_list)
7519 Arguments = new ArrayList ();
7520 foreach (Expression tmp in e_list)
7521 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7525 bool CommonResolve (EmitContext ec)
7527 Expr = Expr.Resolve (ec);
7532 if (Arguments == null)
7535 foreach (Argument a in Arguments){
7536 if (!a.Resolve (ec, loc))
7543 Expression MakePointerAccess (EmitContext ec, Type t)
7545 if (t == TypeManager.void_ptr_type){
7546 Error (242, "The array index operation is not valid on void pointers");
7549 if (Arguments.Count != 1){
7550 Error (196, "A pointer must be indexed by only one value");
7555 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7558 return new Indirection (p, loc).Resolve (ec);
7561 public override Expression DoResolve (EmitContext ec)
7563 if (!CommonResolve (ec))
7567 // We perform some simple tests, and then to "split" the emit and store
7568 // code we create an instance of a different class, and return that.
7570 // I am experimenting with this pattern.
7574 if (t == TypeManager.array_type){
7575 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7580 return (new ArrayAccess (this, loc)).Resolve (ec);
7582 return MakePointerAccess (ec, Expr.Type);
7584 FieldExpr fe = Expr as FieldExpr;
7586 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7588 return MakePointerAccess (ec, ff.ElementType);
7591 return (new IndexerAccess (this, loc)).Resolve (ec);
7594 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7596 if (!CommonResolve (ec))
7601 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7604 return MakePointerAccess (ec, Expr.Type);
7606 FieldExpr fe = Expr as FieldExpr;
7608 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7610 if (!(fe.InstanceExpression is LocalVariableReference) &&
7611 !(fe.InstanceExpression is This)) {
7612 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7615 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7616 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7619 return MakePointerAccess (ec, ff.ElementType);
7622 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7625 public override void Emit (EmitContext ec)
7627 throw new Exception ("Should never be reached");
7632 /// Implements array access
7634 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7636 // Points to our "data" repository
7640 LocalTemporary temp;
7643 public ArrayAccess (ElementAccess ea_data, Location l)
7646 eclass = ExprClass.Variable;
7650 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7652 return DoResolve (ec);
7655 public override Expression DoResolve (EmitContext ec)
7658 ExprClass eclass = ea.Expr.eclass;
7660 // As long as the type is valid
7661 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7662 eclass == ExprClass.Value)) {
7663 ea.Expr.Error_UnexpectedKind ("variable or value");
7668 Type t = ea.Expr.Type;
7669 if (t.GetArrayRank () != ea.Arguments.Count){
7670 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7671 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7675 type = TypeManager.GetElementType (t);
7676 if (type.IsPointer && !ec.InUnsafe){
7677 UnsafeError (ea.Location);
7681 foreach (Argument a in ea.Arguments){
7682 Type argtype = a.Type;
7684 if (argtype == TypeManager.int32_type ||
7685 argtype == TypeManager.uint32_type ||
7686 argtype == TypeManager.int64_type ||
7687 argtype == TypeManager.uint64_type) {
7688 Constant c = a.Expr as Constant;
7689 if (c != null && c.IsNegative) {
7690 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7696 // Mhm. This is strage, because the Argument.Type is not the same as
7697 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7699 // Wonder if I will run into trouble for this.
7701 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7706 eclass = ExprClass.Variable;
7712 /// Emits the right opcode to load an object of Type `t'
7713 /// from an array of T
7715 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7717 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7718 ig.Emit (OpCodes.Ldelem_U1);
7719 else if (type == TypeManager.sbyte_type)
7720 ig.Emit (OpCodes.Ldelem_I1);
7721 else if (type == TypeManager.short_type)
7722 ig.Emit (OpCodes.Ldelem_I2);
7723 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7724 ig.Emit (OpCodes.Ldelem_U2);
7725 else if (type == TypeManager.int32_type)
7726 ig.Emit (OpCodes.Ldelem_I4);
7727 else if (type == TypeManager.uint32_type)
7728 ig.Emit (OpCodes.Ldelem_U4);
7729 else if (type == TypeManager.uint64_type)
7730 ig.Emit (OpCodes.Ldelem_I8);
7731 else if (type == TypeManager.int64_type)
7732 ig.Emit (OpCodes.Ldelem_I8);
7733 else if (type == TypeManager.float_type)
7734 ig.Emit (OpCodes.Ldelem_R4);
7735 else if (type == TypeManager.double_type)
7736 ig.Emit (OpCodes.Ldelem_R8);
7737 else if (type == TypeManager.intptr_type)
7738 ig.Emit (OpCodes.Ldelem_I);
7739 else if (TypeManager.IsEnumType (type)){
7740 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7741 } else if (type.IsValueType){
7742 ig.Emit (OpCodes.Ldelema, type);
7743 ig.Emit (OpCodes.Ldobj, type);
7745 } else if (type.IsGenericParameter) {
7747 ig.Emit (OpCodes.Ldelem, type);
7749 ig.Emit (OpCodes.Ldelem_Any, type);
7752 } else if (type.IsPointer)
7753 ig.Emit (OpCodes.Ldelem_I);
7755 ig.Emit (OpCodes.Ldelem_Ref);
7759 /// Returns the right opcode to store an object of Type `t'
7760 /// from an array of T.
7762 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7764 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7765 has_type_arg = false; is_stobj = false;
7766 t = TypeManager.TypeToCoreType (t);
7767 if (TypeManager.IsEnumType (t))
7768 t = TypeManager.EnumToUnderlying (t);
7769 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7770 t == TypeManager.bool_type)
7771 return OpCodes.Stelem_I1;
7772 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7773 t == TypeManager.char_type)
7774 return OpCodes.Stelem_I2;
7775 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7776 return OpCodes.Stelem_I4;
7777 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7778 return OpCodes.Stelem_I8;
7779 else if (t == TypeManager.float_type)
7780 return OpCodes.Stelem_R4;
7781 else if (t == TypeManager.double_type)
7782 return OpCodes.Stelem_R8;
7783 else if (t == TypeManager.intptr_type) {
7784 has_type_arg = true;
7786 return OpCodes.Stobj;
7787 } else if (t.IsValueType) {
7788 has_type_arg = true;
7790 return OpCodes.Stobj;
7792 } else if (t.IsGenericParameter) {
7793 has_type_arg = true;
7795 return OpCodes.Stelem;
7797 return OpCodes.Stelem_Any;
7801 } else if (t.IsPointer)
7802 return OpCodes.Stelem_I;
7804 return OpCodes.Stelem_Ref;
7807 MethodInfo FetchGetMethod ()
7809 ModuleBuilder mb = CodeGen.Module.Builder;
7810 int arg_count = ea.Arguments.Count;
7811 Type [] args = new Type [arg_count];
7814 for (int i = 0; i < arg_count; i++){
7815 //args [i++] = a.Type;
7816 args [i] = TypeManager.int32_type;
7819 get = mb.GetArrayMethod (
7820 ea.Expr.Type, "Get",
7821 CallingConventions.HasThis |
7822 CallingConventions.Standard,
7828 MethodInfo FetchAddressMethod ()
7830 ModuleBuilder mb = CodeGen.Module.Builder;
7831 int arg_count = ea.Arguments.Count;
7832 Type [] args = new Type [arg_count];
7836 ret_type = TypeManager.GetReferenceType (type);
7838 for (int i = 0; i < arg_count; i++){
7839 //args [i++] = a.Type;
7840 args [i] = TypeManager.int32_type;
7843 address = mb.GetArrayMethod (
7844 ea.Expr.Type, "Address",
7845 CallingConventions.HasThis |
7846 CallingConventions.Standard,
7853 // Load the array arguments into the stack.
7855 // If we have been requested to cache the values (cached_locations array
7856 // initialized), then load the arguments the first time and store them
7857 // in locals. otherwise load from local variables.
7859 void LoadArrayAndArguments (EmitContext ec)
7861 ILGenerator ig = ec.ig;
7864 foreach (Argument a in ea.Arguments){
7865 Type argtype = a.Expr.Type;
7869 if (argtype == TypeManager.int64_type)
7870 ig.Emit (OpCodes.Conv_Ovf_I);
7871 else if (argtype == TypeManager.uint64_type)
7872 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7876 public void Emit (EmitContext ec, bool leave_copy)
7878 int rank = ea.Expr.Type.GetArrayRank ();
7879 ILGenerator ig = ec.ig;
7882 LoadArrayAndArguments (ec);
7885 EmitLoadOpcode (ig, type);
7889 method = FetchGetMethod ();
7890 ig.Emit (OpCodes.Call, method);
7893 LoadFromPtr (ec.ig, this.type);
7896 ec.ig.Emit (OpCodes.Dup);
7897 temp = new LocalTemporary (this.type);
7902 public override void Emit (EmitContext ec)
7907 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7909 int rank = ea.Expr.Type.GetArrayRank ();
7910 ILGenerator ig = ec.ig;
7911 Type t = source.Type;
7912 prepared = prepare_for_load;
7914 if (prepare_for_load) {
7915 AddressOf (ec, AddressOp.LoadStore);
7916 ec.ig.Emit (OpCodes.Dup);
7919 ec.ig.Emit (OpCodes.Dup);
7920 temp = new LocalTemporary (this.type);
7923 StoreFromPtr (ec.ig, t);
7933 LoadArrayAndArguments (ec);
7936 bool is_stobj, has_type_arg;
7937 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7939 // The stobj opcode used by value types will need
7940 // an address on the stack, not really an array/array
7944 ig.Emit (OpCodes.Ldelema, t);
7948 ec.ig.Emit (OpCodes.Dup);
7949 temp = new LocalTemporary (this.type);
7954 ig.Emit (OpCodes.Stobj, t);
7955 else if (has_type_arg)
7960 ModuleBuilder mb = CodeGen.Module.Builder;
7961 int arg_count = ea.Arguments.Count;
7962 Type [] args = new Type [arg_count + 1];
7967 ec.ig.Emit (OpCodes.Dup);
7968 temp = new LocalTemporary (this.type);
7972 for (int i = 0; i < arg_count; i++){
7973 //args [i++] = a.Type;
7974 args [i] = TypeManager.int32_type;
7977 args [arg_count] = type;
7979 set = mb.GetArrayMethod (
7980 ea.Expr.Type, "Set",
7981 CallingConventions.HasThis |
7982 CallingConventions.Standard,
7983 TypeManager.void_type, args);
7985 ig.Emit (OpCodes.Call, set);
7994 public void AddressOf (EmitContext ec, AddressOp mode)
7996 int rank = ea.Expr.Type.GetArrayRank ();
7997 ILGenerator ig = ec.ig;
7999 LoadArrayAndArguments (ec);
8002 ig.Emit (OpCodes.Ldelema, type);
8004 MethodInfo address = FetchAddressMethod ();
8005 ig.Emit (OpCodes.Call, address);
8009 public void EmitGetLength (EmitContext ec, int dim)
8011 int rank = ea.Expr.Type.GetArrayRank ();
8012 ILGenerator ig = ec.ig;
8016 ig.Emit (OpCodes.Ldlen);
8017 ig.Emit (OpCodes.Conv_I4);
8019 IntLiteral.EmitInt (ig, dim);
8020 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
8026 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
8027 public readonly ArrayList Properties;
8028 static Indexers empty;
8030 public struct Indexer {
8031 public readonly PropertyInfo PropertyInfo;
8032 public readonly MethodInfo Getter, Setter;
8034 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
8036 this.PropertyInfo = property_info;
8044 empty = new Indexers (null);
8047 Indexers (ArrayList array)
8052 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
8057 foreach (PropertyInfo property in mi){
8058 MethodInfo get, set;
8060 get = property.GetGetMethod (true);
8061 set = property.GetSetMethod (true);
8062 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
8064 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
8066 if (get != null || set != null) {
8068 ix = new Indexers (new ArrayList ());
8069 ix.Properties.Add (new Indexer (property, get, set));
8074 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
8076 string p_name = TypeManager.IndexerPropertyName (lookup_type);
8078 return TypeManager.MemberLookup (
8079 caller_type, caller_type, lookup_type, MemberTypes.Property,
8080 BindingFlags.Public | BindingFlags.Instance |
8081 BindingFlags.DeclaredOnly, p_name, null);
8084 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
8086 Indexers ix = empty;
8089 if (lookup_type.IsGenericParameter) {
8090 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
8094 if (gc.HasClassConstraint)
8095 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
8097 Type[] ifaces = gc.InterfaceConstraints;
8098 foreach (Type itype in ifaces)
8099 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8105 Type copy = lookup_type;
8106 while (copy != TypeManager.object_type && copy != null){
8107 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
8108 copy = copy.BaseType;
8111 if (lookup_type.IsInterface) {
8112 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
8113 if (ifaces != null) {
8114 foreach (Type itype in ifaces)
8115 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8124 /// Expressions that represent an indexer call.
8126 public class IndexerAccess : Expression, IAssignMethod {
8128 // Points to our "data" repository
8130 MethodInfo get, set;
8131 ArrayList set_arguments;
8132 bool is_base_indexer;
8134 protected Type indexer_type;
8135 protected Type current_type;
8136 protected Expression instance_expr;
8137 protected ArrayList arguments;
8139 public IndexerAccess (ElementAccess ea, Location loc)
8140 : this (ea.Expr, false, loc)
8142 this.arguments = ea.Arguments;
8145 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
8148 this.instance_expr = instance_expr;
8149 this.is_base_indexer = is_base_indexer;
8150 this.eclass = ExprClass.Value;
8154 protected virtual bool CommonResolve (EmitContext ec)
8156 indexer_type = instance_expr.Type;
8157 current_type = ec.ContainerType;
8162 public override Expression DoResolve (EmitContext ec)
8164 if (!CommonResolve (ec))
8168 // Step 1: Query for all `Item' *properties*. Notice
8169 // that the actual methods are pointed from here.
8171 // This is a group of properties, piles of them.
8173 ArrayList AllGetters = null;
8175 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8176 if (ilist.Properties != null) {
8177 AllGetters = new ArrayList(ilist.Properties.Count);
8178 foreach (Indexers.Indexer ix in ilist.Properties) {
8179 if (ix.Getter != null)
8180 AllGetters.Add (ix.Getter);
8184 if (AllGetters == null) {
8185 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8186 TypeManager.CSharpName (indexer_type));
8190 if (AllGetters.Count == 0) {
8191 // FIXME: we cannot simply select first one as the error message is missleading when
8192 // multiple indexers exist
8193 Indexers.Indexer first_indexer = (Indexers.Indexer)ilist.Properties[ilist.Properties.Count - 1];
8194 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
8195 TypeManager.GetFullNameSignature (first_indexer.PropertyInfo));
8199 get = (MethodInfo)Invocation.OverloadResolve (ec, new MethodGroupExpr (AllGetters, loc),
8200 arguments, false, loc);
8203 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8208 // Only base will allow this invocation to happen.
8210 if (get.IsAbstract && this is BaseIndexerAccess){
8211 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
8215 type = get.ReturnType;
8216 if (type.IsPointer && !ec.InUnsafe){
8221 instance_expr.CheckMarshalByRefAccess ();
8223 eclass = ExprClass.IndexerAccess;
8227 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8229 if (right_side == EmptyExpression.OutAccess) {
8230 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
8231 GetSignatureForError ());
8235 // if the indexer returns a value type, and we try to set a field in it
8236 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
8237 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
8238 GetSignatureForError ());
8242 ArrayList AllSetters = new ArrayList();
8243 if (!CommonResolve (ec))
8246 bool found_any = false, found_any_setters = false;
8248 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8249 if (ilist.Properties != null) {
8251 foreach (Indexers.Indexer ix in ilist.Properties) {
8252 if (ix.Setter != null)
8253 AllSetters.Add (ix.Setter);
8256 if (AllSetters.Count > 0) {
8257 found_any_setters = true;
8258 set_arguments = (ArrayList) arguments.Clone ();
8259 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
8260 set = (MethodInfo) Invocation.OverloadResolve (
8261 ec, new MethodGroupExpr (AllSetters, loc),
8262 set_arguments, false, loc);
8266 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8267 TypeManager.CSharpName (indexer_type));
8271 if (!found_any_setters) {
8272 Error (154, "indexer can not be used in this context, because " +
8273 "it lacks a `set' accessor");
8278 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8283 // Only base will allow this invocation to happen.
8285 if (set.IsAbstract && this is BaseIndexerAccess){
8286 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
8291 // Now look for the actual match in the list of indexers to set our "return" type
8293 type = TypeManager.void_type; // default value
8294 foreach (Indexers.Indexer ix in ilist.Properties){
8295 if (ix.Setter == set){
8296 type = ix.PropertyInfo.PropertyType;
8301 instance_expr.CheckMarshalByRefAccess ();
8303 eclass = ExprClass.IndexerAccess;
8307 bool prepared = false;
8308 LocalTemporary temp;
8310 public void Emit (EmitContext ec, bool leave_copy)
8312 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
8314 ec.ig.Emit (OpCodes.Dup);
8315 temp = new LocalTemporary (Type);
8321 // source is ignored, because we already have a copy of it from the
8322 // LValue resolution and we have already constructed a pre-cached
8323 // version of the arguments (ea.set_arguments);
8325 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8327 prepared = prepare_for_load;
8328 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
8333 ec.ig.Emit (OpCodes.Dup);
8334 temp = new LocalTemporary (Type);
8337 } else if (leave_copy) {
8338 temp = new LocalTemporary (Type);
8344 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
8353 public override void Emit (EmitContext ec)
8358 public override string GetSignatureForError ()
8360 // FIXME: print the argument list of the indexer
8361 return instance_expr.GetSignatureForError () + ".this[...]";
8366 /// The base operator for method names
8368 public class BaseAccess : Expression {
8369 public readonly string Identifier;
8371 public BaseAccess (string member, Location l)
8373 this.Identifier = member;
8377 public BaseAccess (string member, TypeArguments args, Location l)
8385 public override Expression DoResolve (EmitContext ec)
8387 Expression c = CommonResolve (ec);
8393 // MethodGroups use this opportunity to flag an error on lacking ()
8395 if (!(c is MethodGroupExpr))
8396 return c.Resolve (ec);
8400 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8402 Expression c = CommonResolve (ec);
8408 // MethodGroups use this opportunity to flag an error on lacking ()
8410 if (! (c is MethodGroupExpr))
8411 return c.DoResolveLValue (ec, right_side);
8416 Expression CommonResolve (EmitContext ec)
8418 Expression member_lookup;
8419 Type current_type = ec.ContainerType;
8420 Type base_type = current_type.BaseType;
8423 Error (1511, "Keyword `base' is not available in a static method");
8427 if (ec.IsFieldInitializer){
8428 Error (1512, "Keyword `base' is not available in the current context");
8432 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8433 AllMemberTypes, AllBindingFlags, loc);
8434 if (member_lookup == null) {
8435 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8442 left = new TypeExpression (base_type, loc);
8444 left = ec.GetThis (loc);
8446 MemberExpr me = (MemberExpr) member_lookup;
8448 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8450 if (e is PropertyExpr) {
8451 PropertyExpr pe = (PropertyExpr) e;
8456 MethodGroupExpr mg = e as MethodGroupExpr;
8462 return mg.ResolveGeneric (ec, args);
8464 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8472 public override void Emit (EmitContext ec)
8474 throw new Exception ("Should never be called");
8479 /// The base indexer operator
8481 public class BaseIndexerAccess : IndexerAccess {
8482 public BaseIndexerAccess (ArrayList args, Location loc)
8483 : base (null, true, loc)
8485 arguments = new ArrayList ();
8486 foreach (Expression tmp in args)
8487 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8490 protected override bool CommonResolve (EmitContext ec)
8492 instance_expr = ec.GetThis (loc);
8494 current_type = ec.ContainerType.BaseType;
8495 indexer_type = current_type;
8497 foreach (Argument a in arguments){
8498 if (!a.Resolve (ec, loc))
8507 /// This class exists solely to pass the Type around and to be a dummy
8508 /// that can be passed to the conversion functions (this is used by
8509 /// foreach implementation to typecast the object return value from
8510 /// get_Current into the proper type. All code has been generated and
8511 /// we only care about the side effect conversions to be performed
8513 /// This is also now used as a placeholder where a no-action expression
8514 /// is needed (the `New' class).
8516 public class EmptyExpression : Expression {
8517 public static readonly EmptyExpression Null = new EmptyExpression ();
8519 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8520 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8521 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8523 static EmptyExpression temp = new EmptyExpression ();
8524 public static EmptyExpression Grab ()
8526 EmptyExpression retval = temp == null ? new EmptyExpression () : temp;
8531 public static void Release (EmptyExpression e)
8536 // TODO: should be protected
8537 public EmptyExpression ()
8539 type = TypeManager.object_type;
8540 eclass = ExprClass.Value;
8541 loc = Location.Null;
8544 public EmptyExpression (Type t)
8547 eclass = ExprClass.Value;
8548 loc = Location.Null;
8551 public override Expression DoResolve (EmitContext ec)
8556 public override void Emit (EmitContext ec)
8558 // nothing, as we only exist to not do anything.
8562 // This is just because we might want to reuse this bad boy
8563 // instead of creating gazillions of EmptyExpressions.
8564 // (CanImplicitConversion uses it)
8566 public void SetType (Type t)
8572 public class UserCast : Expression {
8576 public UserCast (MethodInfo method, Expression source, Location l)
8578 this.method = method;
8579 this.source = source;
8580 type = method.ReturnType;
8581 eclass = ExprClass.Value;
8585 public Expression Source {
8591 public override Expression DoResolve (EmitContext ec)
8594 // We are born fully resolved
8599 public override void Emit (EmitContext ec)
8601 ILGenerator ig = ec.ig;
8605 if (method is MethodInfo)
8606 ig.Emit (OpCodes.Call, (MethodInfo) method);
8608 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8614 // This class is used to "construct" the type during a typecast
8615 // operation. Since the Type.GetType class in .NET can parse
8616 // the type specification, we just use this to construct the type
8617 // one bit at a time.
8619 public class ComposedCast : TypeExpr {
8623 public ComposedCast (Expression left, string dim)
8624 : this (left, dim, left.Location)
8628 public ComposedCast (Expression left, string dim, Location l)
8636 public Expression RemoveNullable ()
8638 if (dim.EndsWith ("?")) {
8639 dim = dim.Substring (0, dim.Length - 1);
8648 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8650 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8654 Type ltype = lexpr.Type;
8655 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8656 Error_VoidInvalidInTheContext (loc);
8661 if ((dim.Length > 0) && (dim [0] == '?')) {
8662 TypeExpr nullable = new NullableType (left, loc);
8664 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8665 return nullable.ResolveAsTypeTerminal (ec, false);
8669 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc))
8672 if (dim != "" && dim [0] == '[' &&
8673 (ltype == TypeManager.arg_iterator_type || ltype == TypeManager.typed_reference_type)) {
8674 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (ltype));
8679 type = TypeManager.GetConstructedType (ltype, dim);
8684 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8686 if (type.IsPointer && !ec.IsInUnsafeScope){
8691 eclass = ExprClass.Type;
8695 public override string Name {
8696 get { return left + dim; }
8699 public override string FullName {
8700 get { return type.FullName; }
8703 public override string GetSignatureForError ()
8705 return left.GetSignatureForError () + dim;
8709 public class FixedBufferPtr : Expression {
8712 public FixedBufferPtr (Expression array, Type array_type, Location l)
8717 type = TypeManager.GetPointerType (array_type);
8718 eclass = ExprClass.Value;
8721 public override void Emit(EmitContext ec)
8726 public override Expression DoResolve (EmitContext ec)
8729 // We are born fully resolved
8737 // This class is used to represent the address of an array, used
8738 // only by the Fixed statement, this generates "&a [0]" construct
8739 // for fixed (char *pa = a)
8741 public class ArrayPtr : FixedBufferPtr {
8744 public ArrayPtr (Expression array, Type array_type, Location l):
8745 base (array, array_type, l)
8747 this.array_type = array_type;
8750 public override void Emit (EmitContext ec)
8754 ILGenerator ig = ec.ig;
8755 IntLiteral.EmitInt (ig, 0);
8756 ig.Emit (OpCodes.Ldelema, array_type);
8761 // Used by the fixed statement
8763 public class StringPtr : Expression {
8766 public StringPtr (LocalBuilder b, Location l)
8769 eclass = ExprClass.Value;
8770 type = TypeManager.char_ptr_type;
8774 public override Expression DoResolve (EmitContext ec)
8776 // This should never be invoked, we are born in fully
8777 // initialized state.
8782 public override void Emit (EmitContext ec)
8784 ILGenerator ig = ec.ig;
8786 ig.Emit (OpCodes.Ldloc, b);
8787 ig.Emit (OpCodes.Conv_I);
8788 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8789 ig.Emit (OpCodes.Add);
8794 // Implements the `stackalloc' keyword
8796 public class StackAlloc : Expression {
8801 public StackAlloc (Expression type, Expression count, Location l)
8808 public override Expression DoResolve (EmitContext ec)
8810 count = count.Resolve (ec);
8814 if (count.Type != TypeManager.int32_type){
8815 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8820 Constant c = count as Constant;
8821 if (c != null && c.IsNegative) {
8822 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8826 if (ec.InCatch || ec.InFinally) {
8827 Error (255, "Cannot use stackalloc in finally or catch");
8831 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8837 if (!TypeManager.VerifyUnManaged (otype, loc))
8840 type = TypeManager.GetPointerType (otype);
8841 eclass = ExprClass.Value;
8846 public override void Emit (EmitContext ec)
8848 int size = GetTypeSize (otype);
8849 ILGenerator ig = ec.ig;
8852 ig.Emit (OpCodes.Sizeof, otype);
8854 IntConstant.EmitInt (ig, size);
8856 ig.Emit (OpCodes.Mul);
8857 ig.Emit (OpCodes.Localloc);