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 void Error23 (Type t)
183 Report.Error (23, loc, "Operator `{0}' cannot be applied to operand of type `{1}'",
184 OperName (Oper), TypeManager.CSharpName (t));
188 /// The result has been already resolved:
190 /// FIXME: a minus constant -128 sbyte cant be turned into a
193 static Expression TryReduceNegative (Constant expr)
197 if (expr is IntConstant)
198 e = new IntConstant (-((IntConstant) expr).Value, expr.Location);
199 else if (expr is UIntConstant){
200 uint value = ((UIntConstant) expr).Value;
202 if (value < 2147483649)
203 return new IntConstant (-(int)value, expr.Location);
205 e = new LongConstant (-value, expr.Location);
207 else if (expr is LongConstant)
208 e = new LongConstant (-((LongConstant) expr).Value, expr.Location);
209 else if (expr is ULongConstant){
210 ulong value = ((ULongConstant) expr).Value;
212 if (value < 9223372036854775809)
213 return new LongConstant(-(long)value, expr.Location);
215 else if (expr is FloatConstant)
216 e = new FloatConstant (-((FloatConstant) expr).Value, expr.Location);
217 else if (expr is DoubleConstant)
218 e = new DoubleConstant (-((DoubleConstant) expr).Value, expr.Location);
219 else if (expr is DecimalConstant)
220 e = new DecimalConstant (-((DecimalConstant) expr).Value, expr.Location);
221 else if (expr is ShortConstant)
222 e = new IntConstant (-((ShortConstant) expr).Value, expr.Location);
223 else if (expr is UShortConstant)
224 e = new IntConstant (-((UShortConstant) expr).Value, expr.Location);
225 else if (expr is SByteConstant)
226 e = new IntConstant (-((SByteConstant) expr).Value, expr.Location);
227 else if (expr is ByteConstant)
228 e = new IntConstant (-((ByteConstant) expr).Value, expr.Location);
233 // This routine will attempt to simplify the unary expression when the
234 // argument is a constant. The result is returned in `result' and the
235 // function returns true or false depending on whether a reduction
236 // was performed or not
238 bool Reduce (EmitContext ec, Constant e, out Expression result)
240 Type expr_type = e.Type;
243 case Operator.UnaryPlus:
244 if (expr_type == TypeManager.bool_type){
253 case Operator.UnaryNegation:
254 result = TryReduceNegative (e);
255 return result != null;
257 case Operator.LogicalNot:
258 if (expr_type != TypeManager.bool_type) {
264 BoolConstant b = (BoolConstant) e;
265 result = new BoolConstant (!(b.Value), b.Location);
268 case Operator.OnesComplement:
269 if (!((expr_type == TypeManager.int32_type) ||
270 (expr_type == TypeManager.uint32_type) ||
271 (expr_type == TypeManager.int64_type) ||
272 (expr_type == TypeManager.uint64_type) ||
273 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
276 if (Convert.ImplicitConversionExists (ec, e, TypeManager.int32_type)){
277 result = new Cast (new TypeExpression (TypeManager.int32_type, loc), e, loc);
278 result = result.Resolve (ec);
279 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.uint32_type)){
280 result = new Cast (new TypeExpression (TypeManager.uint32_type, loc), e, loc);
281 result = result.Resolve (ec);
282 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.int64_type)){
283 result = new Cast (new TypeExpression (TypeManager.int64_type, loc), e, loc);
284 result = result.Resolve (ec);
285 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.uint64_type)){
286 result = new Cast (new TypeExpression (TypeManager.uint64_type, loc), e, loc);
287 result = result.Resolve (ec);
290 if (result == null || !(result is Constant)){
296 expr_type = result.Type;
297 e = (Constant) result;
300 if (e is EnumConstant){
301 EnumConstant enum_constant = (EnumConstant) e;
304 if (Reduce (ec, enum_constant.Child, out reduced)){
305 result = new EnumConstant ((Constant) reduced, enum_constant.Type);
313 if (expr_type == TypeManager.int32_type){
314 result = new IntConstant (~ ((IntConstant) e).Value, e.Location);
315 } else if (expr_type == TypeManager.uint32_type){
316 result = new UIntConstant (~ ((UIntConstant) e).Value, e.Location);
317 } else if (expr_type == TypeManager.int64_type){
318 result = new LongConstant (~ ((LongConstant) e).Value, e.Location);
319 } else if (expr_type == TypeManager.uint64_type){
320 result = new ULongConstant (~ ((ULongConstant) e).Value, e.Location);
328 case Operator.AddressOf:
332 case Operator.Indirection:
336 throw new Exception ("Can not constant fold: " + Oper.ToString());
339 Expression ResolveOperator (EmitContext ec)
342 // Step 1: Default operations on CLI native types.
345 // Attempt to use a constant folding operation.
346 if (Expr is Constant){
349 if (Reduce (ec, (Constant) Expr, out result))
354 // Step 2: Perform Operator Overload location
356 Type expr_type = Expr.Type;
360 op_name = oper_names [(int) Oper];
362 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
365 Expression e = StaticCallExpr.MakeSimpleCall (
366 ec, (MethodGroupExpr) mg, Expr, loc);
376 // Only perform numeric promotions on:
379 if (expr_type == null)
383 case Operator.LogicalNot:
384 if (expr_type != TypeManager.bool_type) {
385 Expr = ResolveBoolean (ec, Expr, loc);
392 type = TypeManager.bool_type;
395 case Operator.OnesComplement:
396 if (!((expr_type == TypeManager.int32_type) ||
397 (expr_type == TypeManager.uint32_type) ||
398 (expr_type == TypeManager.int64_type) ||
399 (expr_type == TypeManager.uint64_type) ||
400 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
403 e = Convert.ImplicitConversion (ec, Expr, TypeManager.int32_type, loc);
406 e = Convert.ImplicitConversion (ec, Expr, TypeManager.uint32_type, loc);
409 e = Convert.ImplicitConversion (ec, Expr, TypeManager.int64_type, loc);
412 e = Convert.ImplicitConversion (ec, Expr, TypeManager.uint64_type, loc);
425 case Operator.AddressOf:
431 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){
435 IVariable variable = Expr as IVariable;
436 bool is_fixed = variable != null && variable.VerifyFixed ();
438 if (!ec.InFixedInitializer && !is_fixed) {
439 Error (212, "You can only take the address of unfixed expression inside " +
440 "of a fixed statement initializer");
444 if (ec.InFixedInitializer && is_fixed) {
445 Error (213, "You cannot use the fixed statement to take the address of an already fixed expression");
449 LocalVariableReference lr = Expr as LocalVariableReference;
451 if (lr.local_info.IsCaptured){
452 AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc);
455 lr.local_info.AddressTaken = true;
456 lr.local_info.Used = true;
459 ParameterReference pr = Expr as ParameterReference;
460 if ((pr != null) && (ec.capture_context != null) &&
461 ec.capture_context.IsParameterCaptured (pr.Name)) {
462 AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc);
466 // According to the specs, a variable is considered definitely assigned if you take
468 if ((variable != null) && (variable.VariableInfo != null)){
469 variable.VariableInfo.SetAssigned (ec);
472 type = TypeManager.GetPointerType (Expr.Type);
475 case Operator.Indirection:
481 if (!expr_type.IsPointer){
482 Error (193, "The * or -> operator must be applied to a pointer");
487 // We create an Indirection expression, because
488 // it can implement the IMemoryLocation.
490 return new Indirection (Expr, loc);
492 case Operator.UnaryPlus:
494 // A plus in front of something is just a no-op, so return the child.
498 case Operator.UnaryNegation:
500 // Deals with -literals
501 // int operator- (int x)
502 // long operator- (long x)
503 // float operator- (float f)
504 // double operator- (double d)
505 // decimal operator- (decimal d)
507 Expression expr = null;
510 // transform - - expr into expr
513 Unary unary = (Unary) Expr;
515 if (unary.Oper == Operator.UnaryNegation)
520 // perform numeric promotions to int,
524 // The following is inneficient, because we call
525 // ImplicitConversion too many times.
527 // It is also not clear if we should convert to Float
528 // or Double initially.
530 if (expr_type == TypeManager.uint32_type){
532 // FIXME: handle exception to this rule that
533 // permits the int value -2147483648 (-2^31) to
534 // bt wrote as a decimal interger literal
536 type = TypeManager.int64_type;
537 Expr = Convert.ImplicitConversion (ec, Expr, type, loc);
541 if (expr_type == TypeManager.uint64_type){
543 // FIXME: Handle exception of `long value'
544 // -92233720368547758087 (-2^63) to be wrote as
545 // decimal integer literal.
551 if (expr_type == TypeManager.float_type){
556 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int32_type, loc);
563 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int64_type, loc);
570 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.double_type, loc);
581 Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" +
582 TypeManager.CSharpName (expr_type) + "'");
586 public override Expression DoResolve (EmitContext ec)
588 if (Oper == Operator.AddressOf) {
589 Expr = Expr.DoResolveLValue (ec, new EmptyExpression ());
591 if (Expr == null || Expr.eclass != ExprClass.Variable){
592 Error (211, "Cannot take the address of the given expression");
597 Expr = Expr.Resolve (ec);
602 if (TypeManager.IsNullableValueType (Expr.Type))
603 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
605 eclass = ExprClass.Value;
606 return ResolveOperator (ec);
609 public override Expression DoResolveLValue (EmitContext ec, Expression right)
611 if (Oper == Operator.Indirection)
612 return DoResolve (ec);
617 public override void Emit (EmitContext ec)
619 ILGenerator ig = ec.ig;
622 case Operator.UnaryPlus:
623 throw new Exception ("This should be caught by Resolve");
625 case Operator.UnaryNegation:
626 if (ec.CheckState && type != TypeManager.float_type && type != TypeManager.double_type) {
627 ig.Emit (OpCodes.Ldc_I4_0);
628 if (type == TypeManager.int64_type)
629 ig.Emit (OpCodes.Conv_U8);
631 ig.Emit (OpCodes.Sub_Ovf);
634 ig.Emit (OpCodes.Neg);
639 case Operator.LogicalNot:
641 ig.Emit (OpCodes.Ldc_I4_0);
642 ig.Emit (OpCodes.Ceq);
645 case Operator.OnesComplement:
647 ig.Emit (OpCodes.Not);
650 case Operator.AddressOf:
651 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
655 throw new Exception ("This should not happen: Operator = "
660 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
662 if (Oper == Operator.LogicalNot)
663 Expr.EmitBranchable (ec, target, !onTrue);
665 base.EmitBranchable (ec, target, onTrue);
668 public override string ToString ()
670 return "Unary (" + Oper + ", " + Expr + ")";
676 // Unary operators are turned into Indirection expressions
677 // after semantic analysis (this is so we can take the address
678 // of an indirection).
680 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
682 LocalTemporary temporary;
685 public Indirection (Expression expr, Location l)
688 type = TypeManager.HasElementType (expr.Type) ? TypeManager.GetElementType (expr.Type) : expr.Type;
689 eclass = ExprClass.Variable;
693 public override void Emit (EmitContext ec)
698 LoadFromPtr (ec.ig, Type);
701 public void Emit (EmitContext ec, bool leave_copy)
705 ec.ig.Emit (OpCodes.Dup);
706 temporary = new LocalTemporary (expr.Type);
707 temporary.Store (ec);
711 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
713 prepared = prepare_for_load;
717 if (prepare_for_load)
718 ec.ig.Emit (OpCodes.Dup);
722 ec.ig.Emit (OpCodes.Dup);
723 temporary = new LocalTemporary (expr.Type);
724 temporary.Store (ec);
727 StoreFromPtr (ec.ig, type);
729 if (temporary != null)
733 public void AddressOf (EmitContext ec, AddressOp Mode)
738 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
740 return DoResolve (ec);
743 public override Expression DoResolve (EmitContext ec)
746 // Born fully resolved
751 public override string ToString ()
753 return "*(" + expr + ")";
756 #region IVariable Members
758 public VariableInfo VariableInfo {
764 public bool VerifyFixed ()
766 // A pointer-indirection is always fixed.
774 /// Unary Mutator expressions (pre and post ++ and --)
778 /// UnaryMutator implements ++ and -- expressions. It derives from
779 /// ExpressionStatement becuase the pre/post increment/decrement
780 /// operators can be used in a statement context.
782 /// FIXME: Idea, we could split this up in two classes, one simpler
783 /// for the common case, and one with the extra fields for more complex
784 /// classes (indexers require temporary access; overloaded require method)
787 public class UnaryMutator : ExpressionStatement {
789 public enum Mode : byte {
796 PreDecrement = IsDecrement,
797 PostIncrement = IsPost,
798 PostDecrement = IsPost | IsDecrement
802 bool is_expr = false;
803 bool recurse = false;
808 // This is expensive for the simplest case.
810 StaticCallExpr method;
812 public UnaryMutator (Mode m, Expression e, Location l)
819 static string OperName (Mode mode)
821 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
826 /// Returns whether an object of type `t' can be incremented
827 /// or decremented with add/sub (ie, basically whether we can
828 /// use pre-post incr-decr operations on it, but it is not a
829 /// System.Decimal, which we require operator overloading to catch)
831 static bool IsIncrementableNumber (Type t)
833 return (t == TypeManager.sbyte_type) ||
834 (t == TypeManager.byte_type) ||
835 (t == TypeManager.short_type) ||
836 (t == TypeManager.ushort_type) ||
837 (t == TypeManager.int32_type) ||
838 (t == TypeManager.uint32_type) ||
839 (t == TypeManager.int64_type) ||
840 (t == TypeManager.uint64_type) ||
841 (t == TypeManager.char_type) ||
842 (t.IsSubclassOf (TypeManager.enum_type)) ||
843 (t == TypeManager.float_type) ||
844 (t == TypeManager.double_type) ||
845 (t.IsPointer && t != TypeManager.void_ptr_type);
848 Expression ResolveOperator (EmitContext ec)
850 Type expr_type = expr.Type;
853 // Step 1: Perform Operator Overload location
858 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
859 op_name = "op_Increment";
861 op_name = "op_Decrement";
863 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
866 method = StaticCallExpr.MakeSimpleCall (
867 ec, (MethodGroupExpr) mg, expr, loc);
870 } else if (!IsIncrementableNumber (expr_type)) {
871 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
872 TypeManager.CSharpName (expr_type) + "'");
877 // The operand of the prefix/postfix increment decrement operators
878 // should be an expression that is classified as a variable,
879 // a property access or an indexer access
882 if (expr.eclass == ExprClass.Variable){
883 LocalVariableReference var = expr as LocalVariableReference;
884 if ((var != null) && var.IsReadOnly) {
885 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
888 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
889 expr = expr.ResolveLValue (ec, this, Location);
893 expr.Error_UnexpectedKind (ec.DeclContainer, "variable, indexer or property access", loc);
900 public override Expression DoResolve (EmitContext ec)
902 expr = expr.Resolve (ec);
907 eclass = ExprClass.Value;
909 if (TypeManager.IsNullableValueType (expr.Type))
910 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
912 return ResolveOperator (ec);
915 static int PtrTypeSize (Type t)
917 return GetTypeSize (TypeManager.GetElementType (t));
921 // Loads the proper "1" into the stack based on the type, then it emits the
922 // opcode for the operation requested
924 void LoadOneAndEmitOp (EmitContext ec, Type t)
927 // Measure if getting the typecode and using that is more/less efficient
928 // that comparing types. t.GetTypeCode() is an internal call.
930 ILGenerator ig = ec.ig;
932 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
933 LongConstant.EmitLong (ig, 1);
934 else if (t == TypeManager.double_type)
935 ig.Emit (OpCodes.Ldc_R8, 1.0);
936 else if (t == TypeManager.float_type)
937 ig.Emit (OpCodes.Ldc_R4, 1.0F);
938 else if (t.IsPointer){
939 int n = PtrTypeSize (t);
942 ig.Emit (OpCodes.Sizeof, t);
944 IntConstant.EmitInt (ig, n);
946 ig.Emit (OpCodes.Ldc_I4_1);
949 // Now emit the operation
952 if (t == TypeManager.int32_type ||
953 t == TypeManager.int64_type){
954 if ((mode & Mode.IsDecrement) != 0)
955 ig.Emit (OpCodes.Sub_Ovf);
957 ig.Emit (OpCodes.Add_Ovf);
958 } else if (t == TypeManager.uint32_type ||
959 t == TypeManager.uint64_type){
960 if ((mode & Mode.IsDecrement) != 0)
961 ig.Emit (OpCodes.Sub_Ovf_Un);
963 ig.Emit (OpCodes.Add_Ovf_Un);
965 if ((mode & Mode.IsDecrement) != 0)
966 ig.Emit (OpCodes.Sub_Ovf);
968 ig.Emit (OpCodes.Add_Ovf);
971 if ((mode & Mode.IsDecrement) != 0)
972 ig.Emit (OpCodes.Sub);
974 ig.Emit (OpCodes.Add);
977 if (t == TypeManager.sbyte_type){
979 ig.Emit (OpCodes.Conv_Ovf_I1);
981 ig.Emit (OpCodes.Conv_I1);
982 } else if (t == TypeManager.byte_type){
984 ig.Emit (OpCodes.Conv_Ovf_U1);
986 ig.Emit (OpCodes.Conv_U1);
987 } else if (t == TypeManager.short_type){
989 ig.Emit (OpCodes.Conv_Ovf_I2);
991 ig.Emit (OpCodes.Conv_I2);
992 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
994 ig.Emit (OpCodes.Conv_Ovf_U2);
996 ig.Emit (OpCodes.Conv_U2);
1001 void EmitCode (EmitContext ec, bool is_expr)
1004 this.is_expr = is_expr;
1005 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1008 public override void Emit (EmitContext ec)
1011 // We use recurse to allow ourselfs to be the source
1012 // of an assignment. This little hack prevents us from
1013 // having to allocate another expression
1016 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1018 LoadOneAndEmitOp (ec, expr.Type);
1020 ec.ig.Emit (OpCodes.Call, method.Method);
1025 EmitCode (ec, true);
1028 public override void EmitStatement (EmitContext ec)
1030 EmitCode (ec, false);
1035 /// Base class for the `Is' and `As' classes.
1039 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1042 public abstract class Probe : Expression {
1043 public Expression ProbeType;
1044 protected Expression expr;
1045 protected TypeExpr probe_type_expr;
1047 public Probe (Expression expr, Expression probe_type, Location l)
1049 ProbeType = probe_type;
1054 public Expression Expr {
1060 public override Expression DoResolve (EmitContext ec)
1062 probe_type_expr = ProbeType.ResolveAsTypeTerminal (ec, false);
1063 if (probe_type_expr == null)
1066 expr = expr.Resolve (ec);
1070 if (expr.Type.IsPointer) {
1071 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1079 /// Implementation of the `is' operator.
1081 public class Is : Probe {
1082 public Is (Expression expr, Expression probe_type, Location l)
1083 : base (expr, probe_type, l)
1088 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1093 public override void Emit (EmitContext ec)
1095 ILGenerator ig = ec.ig;
1100 case Action.AlwaysFalse:
1101 ig.Emit (OpCodes.Pop);
1102 IntConstant.EmitInt (ig, 0);
1104 case Action.AlwaysTrue:
1105 ig.Emit (OpCodes.Pop);
1106 IntConstant.EmitInt (ig, 1);
1108 case Action.LeaveOnStack:
1109 // the `e != null' rule.
1110 ig.Emit (OpCodes.Ldnull);
1111 ig.Emit (OpCodes.Ceq);
1112 ig.Emit (OpCodes.Ldc_I4_0);
1113 ig.Emit (OpCodes.Ceq);
1116 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1117 ig.Emit (OpCodes.Ldnull);
1118 ig.Emit (OpCodes.Cgt_Un);
1121 throw new Exception ("never reached");
1124 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1126 ILGenerator ig = ec.ig;
1129 case Action.AlwaysFalse:
1131 ig.Emit (OpCodes.Br, target);
1134 case Action.AlwaysTrue:
1136 ig.Emit (OpCodes.Br, target);
1139 case Action.LeaveOnStack:
1140 // the `e != null' rule.
1142 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1146 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1147 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1150 throw new Exception ("never reached");
1153 public override Expression DoResolve (EmitContext ec)
1155 Expression e = base.DoResolve (ec);
1157 if ((e == null) || (expr == null))
1160 Type etype = expr.Type;
1161 bool warning_always_matches = false;
1162 bool warning_never_matches = false;
1164 type = TypeManager.bool_type;
1165 eclass = ExprClass.Value;
1168 // First case, if at compile time, there is an implicit conversion
1169 // then e != null (objects) or true (value types)
1171 Type probe_type = probe_type_expr.Type;
1172 e = Convert.ImplicitConversionStandard (ec, expr, probe_type, loc);
1173 if (e != null && !(e is NullCast)){
1175 if (etype.IsValueType)
1176 action = Action.AlwaysTrue;
1178 action = Action.LeaveOnStack;
1180 warning_always_matches = true;
1181 } else if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1182 if (etype.IsGenericParameter)
1183 expr = new BoxedCast (expr, etype);
1186 // Second case: explicit reference convresion
1188 if (expr is NullLiteral)
1189 action = Action.AlwaysFalse;
1191 action = Action.Probe;
1192 } else if (etype.ContainsGenericParameters || probe_type.ContainsGenericParameters) {
1193 expr = new BoxedCast (expr, etype);
1194 action = Action.Probe;
1196 action = Action.AlwaysFalse;
1197 warning_never_matches = true;
1200 if (warning_always_matches)
1201 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1202 else if (warning_never_matches){
1203 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1204 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1212 /// Implementation of the `as' operator.
1214 public class As : Probe {
1215 public As (Expression expr, Expression probe_type, Location l)
1216 : base (expr, probe_type, l)
1220 bool do_isinst = false;
1221 Expression resolved_type;
1223 public override void Emit (EmitContext ec)
1225 ILGenerator ig = ec.ig;
1230 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1233 static void Error_CannotConvertType (Type source, Type target, Location loc)
1235 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1236 TypeManager.CSharpName (source),
1237 TypeManager.CSharpName (target));
1240 public override Expression DoResolve (EmitContext ec)
1242 if (resolved_type == null) {
1243 resolved_type = base.DoResolve (ec);
1245 if (resolved_type == null)
1249 type = probe_type_expr.Type;
1250 eclass = ExprClass.Value;
1251 Type etype = expr.Type;
1253 if (type.IsValueType) {
1254 Report.Error (77, loc, "The as operator must be used with a reference type (`" +
1255 TypeManager.CSharpName (type) + "' is a value type)");
1261 // If the type is a type parameter, ensure
1262 // that it is constrained by a class
1264 TypeParameterExpr tpe = probe_type_expr as TypeParameterExpr;
1266 Constraints constraints = tpe.TypeParameter.Constraints;
1269 if (constraints == null)
1272 if (!constraints.HasClassConstraint)
1273 if ((constraints.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == 0)
1277 Report.Error (413, loc,
1278 "The as operator requires that the `{0}' type parameter be constrained by a class",
1279 probe_type_expr.GetSignatureForError ());
1284 Expression e = Convert.ImplicitConversion (ec, expr, type, loc);
1291 if (Convert.ExplicitReferenceConversionExists (etype, type)){
1292 if (etype.IsGenericParameter)
1293 expr = new BoxedCast (expr, etype);
1299 if (etype.ContainsGenericParameters || type.ContainsGenericParameters) {
1300 expr = new BoxedCast (expr, etype);
1305 Error_CannotConvertType (etype, type, loc);
1309 public override bool GetAttributableValue (Type valueType, out object value)
1311 return expr.GetAttributableValue (valueType, out value);
1316 /// This represents a typecast in the source language.
1318 /// FIXME: Cast expressions have an unusual set of parsing
1319 /// rules, we need to figure those out.
1321 public class Cast : Expression {
1322 Expression target_type;
1325 public Cast (Expression cast_type, Expression expr)
1326 : this (cast_type, expr, cast_type.Location)
1330 public Cast (Expression cast_type, Expression expr, Location loc)
1332 this.target_type = cast_type;
1336 if (target_type == TypeManager.system_void_expr)
1337 Report.Error (1547, loc, "Keyword `void' cannot be used in this context");
1340 public Expression TargetType {
1341 get { return target_type; }
1344 public Expression Expr {
1345 get { return expr; }
1346 set { expr = value; }
1349 public override Expression DoResolve (EmitContext ec)
1351 expr = expr.Resolve (ec);
1355 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1361 if (type.IsAbstract && type.IsSealed) {
1362 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1366 eclass = ExprClass.Value;
1368 Constant c = expr as Constant;
1371 c = c.TryReduce (ec, type, loc);
1375 catch (OverflowException) {
1380 if (type.IsPointer && !ec.InUnsafe) {
1384 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1388 public override void Emit (EmitContext ec)
1390 throw new Exception ("Should not happen");
1395 /// Binary operators
1397 public class Binary : Expression {
1398 public enum Operator : byte {
1399 Multiply, Division, Modulus,
1400 Addition, Subtraction,
1401 LeftShift, RightShift,
1402 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1403 Equality, Inequality,
1413 Expression left, right;
1415 // This must be kept in sync with Operator!!!
1416 public static readonly string [] oper_names;
1420 oper_names = new string [(int) Operator.TOP];
1422 oper_names [(int) Operator.Multiply] = "op_Multiply";
1423 oper_names [(int) Operator.Division] = "op_Division";
1424 oper_names [(int) Operator.Modulus] = "op_Modulus";
1425 oper_names [(int) Operator.Addition] = "op_Addition";
1426 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1427 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1428 oper_names [(int) Operator.RightShift] = "op_RightShift";
1429 oper_names [(int) Operator.LessThan] = "op_LessThan";
1430 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1431 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1432 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1433 oper_names [(int) Operator.Equality] = "op_Equality";
1434 oper_names [(int) Operator.Inequality] = "op_Inequality";
1435 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1436 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1437 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1438 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1439 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1442 public Binary (Operator oper, Expression left, Expression right)
1447 this.loc = left.Location;
1450 public Operator Oper {
1459 public Expression Left {
1468 public Expression Right {
1479 /// Returns a stringified representation of the Operator
1481 public static string OperName (Operator oper)
1484 case Operator.Multiply:
1486 case Operator.Division:
1488 case Operator.Modulus:
1490 case Operator.Addition:
1492 case Operator.Subtraction:
1494 case Operator.LeftShift:
1496 case Operator.RightShift:
1498 case Operator.LessThan:
1500 case Operator.GreaterThan:
1502 case Operator.LessThanOrEqual:
1504 case Operator.GreaterThanOrEqual:
1506 case Operator.Equality:
1508 case Operator.Inequality:
1510 case Operator.BitwiseAnd:
1512 case Operator.BitwiseOr:
1514 case Operator.ExclusiveOr:
1516 case Operator.LogicalOr:
1518 case Operator.LogicalAnd:
1522 return oper.ToString ();
1525 public override string ToString ()
1527 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1528 right.ToString () + ")";
1531 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1533 if (expr.Type == target_type)
1536 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1539 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1542 34, loc, "Operator `" + OperName (oper)
1543 + "' is ambiguous on operands of type `"
1544 + TypeManager.CSharpName (l) + "' "
1545 + "and `" + TypeManager.CSharpName (r)
1549 bool IsConvertible (EmitContext ec, Expression le, Expression re, Type t)
1551 return Convert.ImplicitConversionExists (ec, le, t) && Convert.ImplicitConversionExists (ec, re, t);
1554 bool VerifyApplicable_Predefined (EmitContext ec, Type t)
1556 if (!IsConvertible (ec, left, right, t))
1558 left = ForceConversion (ec, left, t);
1559 right = ForceConversion (ec, right, t);
1564 bool IsApplicable_String (EmitContext ec, Expression le, Expression re, Operator oper)
1566 bool l = Convert.ImplicitConversionExists (ec, le, TypeManager.string_type);
1567 bool r = Convert.ImplicitConversionExists (ec, re, TypeManager.string_type);
1569 if (oper == Operator.Equality || oper == Operator.Inequality)
1571 if (oper == Operator.Addition)
1576 bool OverloadResolve_PredefinedString (EmitContext ec, Operator oper)
1578 if (!IsApplicable_String (ec, left, right, oper))
1580 Type t = TypeManager.string_type;
1581 if (Convert.ImplicitConversionExists (ec, left, t))
1582 left = ForceConversion (ec, left, t);
1583 if (Convert.ImplicitConversionExists (ec, right, t))
1584 right = ForceConversion (ec, right, t);
1589 bool OverloadResolve_PredefinedIntegral (EmitContext ec)
1591 return VerifyApplicable_Predefined (ec, TypeManager.int32_type) ||
1592 VerifyApplicable_Predefined (ec, TypeManager.uint32_type) ||
1593 VerifyApplicable_Predefined (ec, TypeManager.int64_type) ||
1594 VerifyApplicable_Predefined (ec, TypeManager.uint64_type) ||
1598 bool OverloadResolve_PredefinedFloating (EmitContext ec)
1600 return VerifyApplicable_Predefined (ec, TypeManager.float_type) ||
1601 VerifyApplicable_Predefined (ec, TypeManager.double_type) ||
1605 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1607 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1610 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1612 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1616 void Error_OperatorCannotBeApplied ()
1618 Error_OperatorCannotBeApplied (Location, OperName (oper), left.GetSignatureForError (), right.GetSignatureForError ());
1621 static bool is_unsigned (Type t)
1623 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1624 t == TypeManager.short_type || t == TypeManager.byte_type);
1627 Expression Make32or64 (EmitContext ec, Expression e)
1631 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1632 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1634 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1637 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1640 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1643 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1649 Expression CheckShiftArguments (EmitContext ec)
1651 Expression new_left = Make32or64 (ec, left);
1652 Expression new_right = ForceConversion (ec, right, TypeManager.int32_type);
1653 if (new_left == null || new_right == null) {
1654 Error_OperatorCannotBeApplied ();
1657 type = new_left.Type;
1658 int shiftmask = (type == TypeManager.int32_type || type == TypeManager.uint32_type) ? 31 : 63;
1660 right = new Binary (Binary.Operator.BitwiseAnd, new_right, new IntConstant (shiftmask, loc)).DoResolve (ec);
1665 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1666 // i.e., not invoke op_Equality.
1668 static bool EqualsNullIsReferenceEquals (Type t)
1670 return t == TypeManager.object_type || t == TypeManager.string_type ||
1671 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1674 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1676 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1677 "Possible unintended reference comparison; to get a value comparison, " +
1678 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1681 Expression ResolveOperator (EmitContext ec)
1684 Type r = right.Type;
1686 if (oper == Operator.Equality || oper == Operator.Inequality){
1687 if (l.IsGenericParameter && (right is NullLiteral)) {
1688 if (l.BaseType == TypeManager.value_type) {
1689 Error_OperatorCannotBeApplied ();
1693 left = new BoxedCast (left, TypeManager.object_type);
1694 Type = TypeManager.bool_type;
1698 if (r.IsGenericParameter && (left is NullLiteral)) {
1699 if (r.BaseType == TypeManager.value_type) {
1700 Error_OperatorCannotBeApplied ();
1704 right = new BoxedCast (right, TypeManager.object_type);
1705 Type = TypeManager.bool_type;
1710 // Optimize out call to op_Equality in a few cases.
1712 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1713 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1714 Type = TypeManager.bool_type;
1719 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1720 Type = TypeManager.bool_type;
1727 // Do not perform operator overload resolution when both sides are
1730 Expression left_operators = null, right_operators = null;
1731 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))) {
1733 // Step 1: Perform Operator Overload location
1735 string op = oper_names [(int) oper];
1737 MethodGroupExpr union;
1738 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc);
1740 right_operators = MemberLookup (
1741 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc);
1742 union = Invocation.MakeUnionSet (left_operators, right_operators, loc);
1744 union = (MethodGroupExpr) left_operators;
1746 if (union != null) {
1747 ArrayList args = new ArrayList (2);
1748 args.Add (new Argument (left, Argument.AType.Expression));
1749 args.Add (new Argument (right, Argument.AType.Expression));
1751 MethodBase method = Invocation.OverloadResolve (ec, union, args, true, Location.Null);
1753 if (method != null) {
1754 MethodInfo mi = (MethodInfo) method;
1755 return new BinaryMethod (mi.ReturnType, method, args);
1761 // Step 0: String concatenation (because overloading will get this wrong)
1763 if (oper == Operator.Addition){
1765 // If any of the arguments is a string, cast to string
1768 // Simple constant folding
1769 if (left is StringConstant && right is StringConstant)
1770 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1772 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1774 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1775 Error_OperatorCannotBeApplied ();
1779 // try to fold it in on the left
1780 if (left is StringConcat) {
1783 // We have to test here for not-null, since we can be doubly-resolved
1784 // take care of not appending twice
1787 type = TypeManager.string_type;
1788 ((StringConcat) left).Append (ec, right);
1789 return left.Resolve (ec);
1795 // Otherwise, start a new concat expression
1796 return new StringConcat (ec, loc, left, right).Resolve (ec);
1800 // Transform a + ( - b) into a - b
1802 if (right is Unary){
1803 Unary right_unary = (Unary) right;
1805 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1806 oper = Operator.Subtraction;
1807 right = right_unary.Expr;
1813 if (oper == Operator.Equality || oper == Operator.Inequality){
1814 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1815 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1816 Error_OperatorCannotBeApplied ();
1820 type = TypeManager.bool_type;
1824 if (l.IsPointer || r.IsPointer) {
1825 if (l.IsPointer && r.IsPointer) {
1826 type = TypeManager.bool_type;
1830 if (l.IsPointer && r == TypeManager.null_type) {
1831 right = new EmptyCast (NullPointer.Null, l);
1832 type = TypeManager.bool_type;
1836 if (r.IsPointer && l == TypeManager.null_type) {
1837 left = new EmptyCast (NullPointer.Null, r);
1838 type = TypeManager.bool_type;
1843 if (l.IsGenericParameter && r.IsGenericParameter) {
1844 GenericConstraints l_gc, r_gc;
1846 l_gc = TypeManager.GetTypeParameterConstraints (l);
1847 r_gc = TypeManager.GetTypeParameterConstraints (r);
1849 if ((l_gc == null) || (r_gc == null) ||
1850 !(l_gc.HasReferenceTypeConstraint || l_gc.HasClassConstraint) ||
1851 !(r_gc.HasReferenceTypeConstraint || r_gc.HasClassConstraint)) {
1852 Error_OperatorCannotBeApplied ();
1859 // operator != (object a, object b)
1860 // operator == (object a, object b)
1862 // For this to be used, both arguments have to be reference-types.
1863 // Read the rationale on the spec (14.9.6)
1865 if (!(l.IsValueType || r.IsValueType)){
1866 type = TypeManager.bool_type;
1872 // Also, a standard conversion must exist from either one
1874 bool left_to_right =
1875 Convert.ImplicitStandardConversionExists (left, r);
1876 bool right_to_left = !left_to_right &&
1877 Convert.ImplicitStandardConversionExists (right, l);
1879 if (!left_to_right && !right_to_left) {
1880 Error_OperatorCannotBeApplied ();
1884 if (left_to_right && left_operators != null &&
1885 RootContext.WarningLevel >= 2) {
1886 ArrayList args = new ArrayList (2);
1887 args.Add (new Argument (left, Argument.AType.Expression));
1888 args.Add (new Argument (left, Argument.AType.Expression));
1889 MethodBase method = Invocation.OverloadResolve (
1890 ec, (MethodGroupExpr) left_operators, args, true, Location.Null);
1892 Warning_UnintendedReferenceComparison (loc, "right", l);
1895 if (right_to_left && right_operators != null &&
1896 RootContext.WarningLevel >= 2) {
1897 ArrayList args = new ArrayList (2);
1898 args.Add (new Argument (right, Argument.AType.Expression));
1899 args.Add (new Argument (right, Argument.AType.Expression));
1900 MethodBase method = Invocation.OverloadResolve (
1901 ec, (MethodGroupExpr) right_operators, args, true, Location.Null);
1903 Warning_UnintendedReferenceComparison (loc, "left", r);
1907 // We are going to have to convert to an object to compare
1909 if (l != TypeManager.object_type)
1910 left = new EmptyCast (left, TypeManager.object_type);
1911 if (r != TypeManager.object_type)
1912 right = new EmptyCast (right, TypeManager.object_type);
1918 // Only perform numeric promotions on:
1919 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
1921 if (oper == Operator.Addition || oper == Operator.Subtraction) {
1922 if (TypeManager.IsDelegateType (l)){
1923 if (((right.eclass == ExprClass.MethodGroup) ||
1924 (r == TypeManager.anonymous_method_type))){
1925 if ((RootContext.Version != LanguageVersion.ISO_1)){
1926 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
1934 if (TypeManager.IsDelegateType (r) || right is NullLiteral){
1936 ArrayList args = new ArrayList (2);
1938 args = new ArrayList (2);
1939 args.Add (new Argument (left, Argument.AType.Expression));
1940 args.Add (new Argument (right, Argument.AType.Expression));
1942 if (oper == Operator.Addition)
1943 method = TypeManager.delegate_combine_delegate_delegate;
1945 method = TypeManager.delegate_remove_delegate_delegate;
1947 if (!TypeManager.IsEqual (l, r) && !(right is NullLiteral)) {
1948 Error_OperatorCannotBeApplied ();
1952 return new BinaryDelegate (l, method, args);
1957 // Pointer arithmetic:
1959 // T* operator + (T* x, int y);
1960 // T* operator + (T* x, uint y);
1961 // T* operator + (T* x, long y);
1962 // T* operator + (T* x, ulong y);
1964 // T* operator + (int y, T* x);
1965 // T* operator + (uint y, T *x);
1966 // T* operator + (long y, T *x);
1967 // T* operator + (ulong y, T *x);
1969 // T* operator - (T* x, int y);
1970 // T* operator - (T* x, uint y);
1971 // T* operator - (T* x, long y);
1972 // T* operator - (T* x, ulong y);
1974 // long operator - (T* x, T *y)
1977 if (r.IsPointer && oper == Operator.Subtraction){
1979 return new PointerArithmetic (
1980 false, left, right, TypeManager.int64_type,
1983 Expression t = Make32or64 (ec, right);
1985 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
1987 } else if (r.IsPointer && oper == Operator.Addition){
1988 Expression t = Make32or64 (ec, left);
1990 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
1995 // Enumeration operators
1997 bool lie = TypeManager.IsEnumType (l);
1998 bool rie = TypeManager.IsEnumType (r);
2002 // U operator - (E e, E f)
2004 if (oper == Operator.Subtraction){
2006 type = TypeManager.EnumToUnderlying (l);
2009 Error_OperatorCannotBeApplied ();
2015 // operator + (E e, U x)
2016 // operator - (E e, U x)
2018 if (oper == Operator.Addition || oper == Operator.Subtraction){
2019 Type enum_type = lie ? l : r;
2020 Type other_type = lie ? r : l;
2021 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2023 if (underlying_type != other_type){
2024 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2034 Error_OperatorCannotBeApplied ();
2043 temp = Convert.ImplicitConversion (ec, right, l, loc);
2047 Error_OperatorCannotBeApplied ();
2051 temp = Convert.ImplicitConversion (ec, left, r, loc);
2056 Error_OperatorCannotBeApplied ();
2061 if (oper == Operator.Equality || oper == Operator.Inequality ||
2062 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2063 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2064 if (left.Type != right.Type){
2065 Error_OperatorCannotBeApplied ();
2068 type = TypeManager.bool_type;
2072 if (oper == Operator.BitwiseAnd ||
2073 oper == Operator.BitwiseOr ||
2074 oper == Operator.ExclusiveOr){
2075 if (left.Type != right.Type){
2076 Error_OperatorCannotBeApplied ();
2082 Error_OperatorCannotBeApplied ();
2086 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2087 return CheckShiftArguments (ec);
2089 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2090 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2091 type = TypeManager.bool_type;
2096 Error_OperatorCannotBeApplied ();
2100 Expression e = new ConditionalLogicalOperator (
2101 oper == Operator.LogicalAnd, left, right, l, loc);
2102 return e.Resolve (ec);
2105 Expression orig_left = left;
2106 Expression orig_right = right;
2109 // operator & (bool x, bool y)
2110 // operator | (bool x, bool y)
2111 // operator ^ (bool x, bool y)
2113 if (oper == Operator.BitwiseAnd ||
2114 oper == Operator.BitwiseOr ||
2115 oper == Operator.ExclusiveOr) {
2116 if (OverloadResolve_PredefinedIntegral (ec)) {
2117 if (IsConvertible (ec, orig_left, orig_right, TypeManager.bool_type)) {
2118 Error_OperatorAmbiguous (loc, oper, l, r);
2121 } else if (!VerifyApplicable_Predefined (ec, TypeManager.bool_type)) {
2122 Error_OperatorCannotBeApplied ();
2129 // Pointer comparison
2131 if (l.IsPointer && r.IsPointer){
2132 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2133 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2134 type = TypeManager.bool_type;
2139 if (OverloadResolve_PredefinedIntegral (ec)) {
2140 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2141 Error_OperatorAmbiguous (loc, oper, l, r);
2144 } else if (OverloadResolve_PredefinedFloating (ec)) {
2145 if (IsConvertible (ec, orig_left, orig_right, TypeManager.decimal_type) ||
2146 IsApplicable_String (ec, orig_left, orig_right, oper)) {
2147 Error_OperatorAmbiguous (loc, oper, l, r);
2150 } else if (VerifyApplicable_Predefined (ec, TypeManager.decimal_type)) {
2151 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2152 Error_OperatorAmbiguous (loc, oper, l, r);
2155 } else if (!OverloadResolve_PredefinedString (ec, oper)) {
2156 Error_OperatorCannotBeApplied ();
2160 if (oper == Operator.Equality ||
2161 oper == Operator.Inequality ||
2162 oper == Operator.LessThanOrEqual ||
2163 oper == Operator.LessThan ||
2164 oper == Operator.GreaterThanOrEqual ||
2165 oper == Operator.GreaterThan)
2166 type = TypeManager.bool_type;
2171 if (l == TypeManager.decimal_type || l == TypeManager.string_type || r == TypeManager.string_type) {
2173 if (r == TypeManager.string_type)
2175 MethodGroupExpr ops = (MethodGroupExpr) MemberLookup (
2176 ec.ContainerType, lookup, oper_names [(int) oper],
2177 MemberTypes.Method, AllBindingFlags, loc);
2178 ArrayList args = new ArrayList (2);
2179 args.Add (new Argument (left, Argument.AType.Expression));
2180 args.Add (new Argument (right, Argument.AType.Expression));
2181 MethodBase method = Invocation.OverloadResolve (ec, ops, args, true, Location.Null);
2182 return new BinaryMethod (type, method, args);
2188 Constant EnumLiftUp (Constant left, Constant right)
2191 case Operator.BitwiseOr:
2192 case Operator.BitwiseAnd:
2193 case Operator.ExclusiveOr:
2194 case Operator.Equality:
2195 case Operator.Inequality:
2196 case Operator.LessThan:
2197 case Operator.LessThanOrEqual:
2198 case Operator.GreaterThan:
2199 case Operator.GreaterThanOrEqual:
2200 if (left is EnumConstant)
2203 if (left.IsZeroInteger)
2204 return new EnumConstant (left, right.Type);
2208 case Operator.Addition:
2209 case Operator.Subtraction:
2212 case Operator.Multiply:
2213 case Operator.Division:
2214 case Operator.Modulus:
2215 case Operator.LeftShift:
2216 case Operator.RightShift:
2217 if (right is EnumConstant || left is EnumConstant)
2221 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2225 public override Expression DoResolve (EmitContext ec)
2230 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2231 left = ((ParenthesizedExpression) left).Expr;
2232 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2236 if (left.eclass == ExprClass.Type) {
2237 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2241 left = left.Resolve (ec);
2246 Constant lc = left as Constant;
2247 if (lc != null && lc.Type == TypeManager.bool_type &&
2248 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2249 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2251 // TODO: make a sense to resolve unreachable expression as we do for statement
2252 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2256 right = right.Resolve (ec);
2260 eclass = ExprClass.Value;
2261 Constant rc = right as Constant;
2263 // The conversion rules are ignored in enum context but why
2264 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2265 left = lc = EnumLiftUp (lc, rc);
2269 right = rc = EnumLiftUp (rc, lc);
2274 if (oper == Operator.BitwiseAnd) {
2275 if (rc != null && rc.IsZeroInteger) {
2276 return lc is EnumConstant ?
2277 new EnumConstant (rc, lc.Type):
2281 if (lc != null && lc.IsZeroInteger) {
2282 return rc is EnumConstant ?
2283 new EnumConstant (lc, rc.Type):
2287 else if (oper == Operator.BitwiseOr) {
2288 if (lc is EnumConstant &&
2289 rc != null && rc.IsZeroInteger)
2291 if (rc is EnumConstant &&
2292 lc != null && lc.IsZeroInteger)
2294 } else if (oper == Operator.LogicalAnd) {
2295 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2297 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2301 if (rc != null && lc != null){
2302 int prev_e = Report.Errors;
2303 Expression e = ConstantFold.BinaryFold (
2304 ec, oper, lc, rc, loc);
2305 if (e != null || Report.Errors != prev_e)
2309 Type ltype = left.Type, rtype = right.Type;
2310 if ((left is NullLiteral || ltype.IsValueType) &&
2311 (right is NullLiteral || rtype.IsValueType) &&
2312 !(left is NullLiteral && right is NullLiteral) &&
2313 (TypeManager.IsNullableType (ltype) || TypeManager.IsNullableType (rtype)))
2314 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2316 // Comparison warnings
2317 if (oper == Operator.Equality || oper == Operator.Inequality ||
2318 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2319 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2320 if (left.Equals (right)) {
2321 Report.Warning (1718, 3, loc, "Comparison made to same variable; did you mean to compare something else?");
2323 CheckUselessComparison (lc, right.Type);
2324 CheckUselessComparison (rc, left.Type);
2327 return ResolveOperator (ec);
2330 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2335 private void CheckUselessComparison (Constant c, Type type)
2337 if (c == null || !IsTypeIntegral (type)
2338 || c is StringConstant
2339 || c is BoolConstant
2340 || c is CharConstant
2341 || c is FloatConstant
2342 || c is DoubleConstant
2343 || c is DecimalConstant
2349 if (c is ULongConstant) {
2350 ulong uvalue = ((ULongConstant) c).Value;
2351 if (uvalue > long.MaxValue) {
2352 if (type == TypeManager.byte_type ||
2353 type == TypeManager.sbyte_type ||
2354 type == TypeManager.short_type ||
2355 type == TypeManager.ushort_type ||
2356 type == TypeManager.int32_type ||
2357 type == TypeManager.uint32_type ||
2358 type == TypeManager.int64_type)
2359 WarnUselessComparison (type);
2362 value = (long) uvalue;
2364 else if (c is ByteConstant)
2365 value = ((ByteConstant) c).Value;
2366 else if (c is SByteConstant)
2367 value = ((SByteConstant) c).Value;
2368 else if (c is ShortConstant)
2369 value = ((ShortConstant) c).Value;
2370 else if (c is UShortConstant)
2371 value = ((UShortConstant) c).Value;
2372 else if (c is IntConstant)
2373 value = ((IntConstant) c).Value;
2374 else if (c is UIntConstant)
2375 value = ((UIntConstant) c).Value;
2376 else if (c is LongConstant)
2377 value = ((LongConstant) c).Value;
2380 if (IsValueOutOfRange (value, type))
2381 WarnUselessComparison (type);
2386 private bool IsValueOutOfRange (long value, Type type)
2388 if (IsTypeUnsigned (type) && value < 0)
2390 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2391 type == TypeManager.byte_type && value >= 0x100 ||
2392 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2393 type == TypeManager.ushort_type && value >= 0x10000 ||
2394 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2395 type == TypeManager.uint32_type && value >= 0x100000000;
2398 private static bool IsTypeIntegral (Type type)
2400 return type == TypeManager.uint64_type ||
2401 type == TypeManager.int64_type ||
2402 type == TypeManager.uint32_type ||
2403 type == TypeManager.int32_type ||
2404 type == TypeManager.ushort_type ||
2405 type == TypeManager.short_type ||
2406 type == TypeManager.sbyte_type ||
2407 type == TypeManager.byte_type;
2410 private static bool IsTypeUnsigned (Type type)
2412 return type == TypeManager.uint64_type ||
2413 type == TypeManager.uint32_type ||
2414 type == TypeManager.ushort_type ||
2415 type == TypeManager.byte_type;
2418 private void WarnUselessComparison (Type type)
2420 Report.Warning (652, 2, loc, "Comparison to integral constant is useless; the constant is outside the range of type `{0}'",
2421 TypeManager.CSharpName (type));
2425 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2426 /// context of a conditional bool expression. This function will return
2427 /// false if it is was possible to use EmitBranchable, or true if it was.
2429 /// The expression's code is generated, and we will generate a branch to `target'
2430 /// if the resulting expression value is equal to isTrue
2432 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2434 ILGenerator ig = ec.ig;
2437 // This is more complicated than it looks, but its just to avoid
2438 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2439 // but on top of that we want for == and != to use a special path
2440 // if we are comparing against null
2442 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2443 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2446 // put the constant on the rhs, for simplicity
2448 if (left is Constant) {
2449 Expression swap = right;
2454 if (((Constant) right).IsZeroInteger) {
2457 ig.Emit (OpCodes.Brtrue, target);
2459 ig.Emit (OpCodes.Brfalse, target);
2462 } else if (right is BoolConstant) {
2464 if (my_on_true != ((BoolConstant) right).Value)
2465 ig.Emit (OpCodes.Brtrue, target);
2467 ig.Emit (OpCodes.Brfalse, target);
2472 } else if (oper == Operator.LogicalAnd) {
2475 Label tests_end = ig.DefineLabel ();
2477 left.EmitBranchable (ec, tests_end, false);
2478 right.EmitBranchable (ec, target, true);
2479 ig.MarkLabel (tests_end);
2481 left.EmitBranchable (ec, target, false);
2482 right.EmitBranchable (ec, target, false);
2487 } else if (oper == Operator.LogicalOr){
2489 left.EmitBranchable (ec, target, true);
2490 right.EmitBranchable (ec, target, true);
2493 Label tests_end = ig.DefineLabel ();
2494 left.EmitBranchable (ec, tests_end, true);
2495 right.EmitBranchable (ec, target, false);
2496 ig.MarkLabel (tests_end);
2501 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2502 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2503 oper == Operator.Equality || oper == Operator.Inequality)) {
2504 base.EmitBranchable (ec, target, onTrue);
2512 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2515 case Operator.Equality:
2517 ig.Emit (OpCodes.Beq, target);
2519 ig.Emit (OpCodes.Bne_Un, target);
2522 case Operator.Inequality:
2524 ig.Emit (OpCodes.Bne_Un, target);
2526 ig.Emit (OpCodes.Beq, target);
2529 case Operator.LessThan:
2532 ig.Emit (OpCodes.Blt_Un, target);
2534 ig.Emit (OpCodes.Blt, target);
2537 ig.Emit (OpCodes.Bge_Un, target);
2539 ig.Emit (OpCodes.Bge, target);
2542 case Operator.GreaterThan:
2545 ig.Emit (OpCodes.Bgt_Un, target);
2547 ig.Emit (OpCodes.Bgt, target);
2550 ig.Emit (OpCodes.Ble_Un, target);
2552 ig.Emit (OpCodes.Ble, target);
2555 case Operator.LessThanOrEqual:
2558 ig.Emit (OpCodes.Ble_Un, target);
2560 ig.Emit (OpCodes.Ble, target);
2563 ig.Emit (OpCodes.Bgt_Un, target);
2565 ig.Emit (OpCodes.Bgt, target);
2569 case Operator.GreaterThanOrEqual:
2572 ig.Emit (OpCodes.Bge_Un, target);
2574 ig.Emit (OpCodes.Bge, target);
2577 ig.Emit (OpCodes.Blt_Un, target);
2579 ig.Emit (OpCodes.Blt, target);
2582 Console.WriteLine (oper);
2583 throw new Exception ("what is THAT");
2587 public override void Emit (EmitContext ec)
2589 ILGenerator ig = ec.ig;
2594 // Handle short-circuit operators differently
2597 if (oper == Operator.LogicalAnd) {
2598 Label load_zero = ig.DefineLabel ();
2599 Label end = ig.DefineLabel ();
2601 left.EmitBranchable (ec, load_zero, false);
2603 ig.Emit (OpCodes.Br, end);
2605 ig.MarkLabel (load_zero);
2606 ig.Emit (OpCodes.Ldc_I4_0);
2609 } else if (oper == Operator.LogicalOr) {
2610 Label load_one = ig.DefineLabel ();
2611 Label end = ig.DefineLabel ();
2613 left.EmitBranchable (ec, load_one, true);
2615 ig.Emit (OpCodes.Br, end);
2617 ig.MarkLabel (load_one);
2618 ig.Emit (OpCodes.Ldc_I4_1);
2626 bool isUnsigned = is_unsigned (left.Type);
2629 case Operator.Multiply:
2631 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2632 opcode = OpCodes.Mul_Ovf;
2633 else if (isUnsigned)
2634 opcode = OpCodes.Mul_Ovf_Un;
2636 opcode = OpCodes.Mul;
2638 opcode = OpCodes.Mul;
2642 case Operator.Division:
2644 opcode = OpCodes.Div_Un;
2646 opcode = OpCodes.Div;
2649 case Operator.Modulus:
2651 opcode = OpCodes.Rem_Un;
2653 opcode = OpCodes.Rem;
2656 case Operator.Addition:
2658 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2659 opcode = OpCodes.Add_Ovf;
2660 else if (isUnsigned)
2661 opcode = OpCodes.Add_Ovf_Un;
2663 opcode = OpCodes.Add;
2665 opcode = OpCodes.Add;
2668 case Operator.Subtraction:
2670 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2671 opcode = OpCodes.Sub_Ovf;
2672 else if (isUnsigned)
2673 opcode = OpCodes.Sub_Ovf_Un;
2675 opcode = OpCodes.Sub;
2677 opcode = OpCodes.Sub;
2680 case Operator.RightShift:
2682 opcode = OpCodes.Shr_Un;
2684 opcode = OpCodes.Shr;
2687 case Operator.LeftShift:
2688 opcode = OpCodes.Shl;
2691 case Operator.Equality:
2692 opcode = OpCodes.Ceq;
2695 case Operator.Inequality:
2696 ig.Emit (OpCodes.Ceq);
2697 ig.Emit (OpCodes.Ldc_I4_0);
2699 opcode = OpCodes.Ceq;
2702 case Operator.LessThan:
2704 opcode = OpCodes.Clt_Un;
2706 opcode = OpCodes.Clt;
2709 case Operator.GreaterThan:
2711 opcode = OpCodes.Cgt_Un;
2713 opcode = OpCodes.Cgt;
2716 case Operator.LessThanOrEqual:
2717 Type lt = left.Type;
2719 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2720 ig.Emit (OpCodes.Cgt_Un);
2722 ig.Emit (OpCodes.Cgt);
2723 ig.Emit (OpCodes.Ldc_I4_0);
2725 opcode = OpCodes.Ceq;
2728 case Operator.GreaterThanOrEqual:
2729 Type le = left.Type;
2731 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2732 ig.Emit (OpCodes.Clt_Un);
2734 ig.Emit (OpCodes.Clt);
2736 ig.Emit (OpCodes.Ldc_I4_0);
2738 opcode = OpCodes.Ceq;
2741 case Operator.BitwiseOr:
2742 opcode = OpCodes.Or;
2745 case Operator.BitwiseAnd:
2746 opcode = OpCodes.And;
2749 case Operator.ExclusiveOr:
2750 opcode = OpCodes.Xor;
2754 throw new Exception ("This should not happen: Operator = "
2755 + oper.ToString ());
2763 // Object created by Binary when the binary operator uses an method instead of being
2764 // a binary operation that maps to a CIL binary operation.
2766 public class BinaryMethod : Expression {
2767 public MethodBase method;
2768 public ArrayList Arguments;
2770 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2775 eclass = ExprClass.Value;
2778 public override Expression DoResolve (EmitContext ec)
2783 public override void Emit (EmitContext ec)
2785 ILGenerator ig = ec.ig;
2787 if (Arguments != null)
2788 Invocation.EmitArguments (ec, method, Arguments, false, null);
2790 if (method is MethodInfo)
2791 ig.Emit (OpCodes.Call, (MethodInfo) method);
2793 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2798 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2799 // b, c, d... may be strings or objects.
2801 public class StringConcat : Expression {
2803 bool invalid = false;
2804 bool emit_conv_done = false;
2806 // Are we also concating objects?
2808 bool is_strings_only = true;
2810 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2813 type = TypeManager.string_type;
2814 eclass = ExprClass.Value;
2816 operands = new ArrayList (2);
2821 public override Expression DoResolve (EmitContext ec)
2829 public void Append (EmitContext ec, Expression operand)
2834 StringConstant sc = operand as StringConstant;
2836 // TODO: it will be better to do this silently as an optimalization
2838 // string s = "" + i;
2839 // because this code has poor performace
2840 // if (sc.Value.Length == 0)
2841 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2843 if (operands.Count != 0) {
2844 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2845 if (last_operand != null) {
2846 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2853 // Conversion to object
2855 if (operand.Type != TypeManager.string_type) {
2856 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2859 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2865 operands.Add (operand);
2868 public override void Emit (EmitContext ec)
2870 MethodInfo concat_method = null;
2873 // Do conversion to arguments; check for strings only
2876 // This can get called multiple times, so we have to deal with that.
2877 if (!emit_conv_done) {
2878 emit_conv_done = true;
2879 for (int i = 0; i < operands.Count; i ++) {
2880 Expression e = (Expression) operands [i];
2881 is_strings_only &= e.Type == TypeManager.string_type;
2884 for (int i = 0; i < operands.Count; i ++) {
2885 Expression e = (Expression) operands [i];
2887 if (! is_strings_only && e.Type == TypeManager.string_type) {
2888 // need to make sure this is an object, because the EmitParams
2889 // method might look at the type of this expression, see it is a
2890 // string and emit a string [] when we want an object [];
2892 e = new EmptyCast (e, TypeManager.object_type);
2894 operands [i] = new Argument (e, Argument.AType.Expression);
2899 // Find the right method
2901 switch (operands.Count) {
2904 // This should not be possible, because simple constant folding
2905 // is taken care of in the Binary code.
2907 throw new Exception ("how did you get here?");
2910 concat_method = is_strings_only ?
2911 TypeManager.string_concat_string_string :
2912 TypeManager.string_concat_object_object ;
2915 concat_method = is_strings_only ?
2916 TypeManager.string_concat_string_string_string :
2917 TypeManager.string_concat_object_object_object ;
2921 // There is not a 4 param overlaod for object (the one that there is
2922 // is actually a varargs methods, and is only in corlib because it was
2923 // introduced there before.).
2925 if (!is_strings_only)
2928 concat_method = TypeManager.string_concat_string_string_string_string;
2931 concat_method = is_strings_only ?
2932 TypeManager.string_concat_string_dot_dot_dot :
2933 TypeManager.string_concat_object_dot_dot_dot ;
2937 Invocation.EmitArguments (ec, concat_method, operands, false, null);
2938 ec.ig.Emit (OpCodes.Call, concat_method);
2943 // Object created with +/= on delegates
2945 public class BinaryDelegate : Expression {
2949 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
2954 eclass = ExprClass.Value;
2957 public override Expression DoResolve (EmitContext ec)
2962 public override void Emit (EmitContext ec)
2964 ILGenerator ig = ec.ig;
2966 Invocation.EmitArguments (ec, method, args, false, null);
2968 ig.Emit (OpCodes.Call, (MethodInfo) method);
2969 ig.Emit (OpCodes.Castclass, type);
2972 public Expression Right {
2974 Argument arg = (Argument) args [1];
2979 public bool IsAddition {
2981 return method == TypeManager.delegate_combine_delegate_delegate;
2987 // User-defined conditional logical operator
2988 public class ConditionalLogicalOperator : Expression {
2989 Expression left, right;
2992 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
2995 eclass = ExprClass.Value;
2999 this.is_and = is_and;
3002 protected void Error19 ()
3004 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3007 protected void Error218 ()
3009 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3010 "declarations of operator true and operator false");
3013 Expression op_true, op_false, op;
3014 LocalTemporary left_temp;
3016 public override Expression DoResolve (EmitContext ec)
3019 Expression operator_group;
3021 operator_group = MethodLookup (ec, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
3022 if (operator_group == null) {
3027 left_temp = new LocalTemporary (type);
3029 ArrayList arguments = new ArrayList ();
3030 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3031 arguments.Add (new Argument (right, Argument.AType.Expression));
3032 method = Invocation.OverloadResolve (
3033 ec, (MethodGroupExpr) operator_group, arguments, false, loc)
3035 if (method == null) {
3040 if (method.ReturnType != type) {
3041 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3042 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3046 op = new StaticCallExpr (method, arguments, loc);
3048 op_true = GetOperatorTrue (ec, left_temp, loc);
3049 op_false = GetOperatorFalse (ec, left_temp, loc);
3050 if ((op_true == null) || (op_false == null)) {
3058 public override void Emit (EmitContext ec)
3060 ILGenerator ig = ec.ig;
3061 Label false_target = ig.DefineLabel ();
3062 Label end_target = ig.DefineLabel ();
3065 left_temp.Store (ec);
3067 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3068 left_temp.Emit (ec);
3069 ig.Emit (OpCodes.Br, end_target);
3070 ig.MarkLabel (false_target);
3072 ig.MarkLabel (end_target);
3076 public class PointerArithmetic : Expression {
3077 Expression left, right;
3081 // We assume that `l' is always a pointer
3083 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3089 is_add = is_addition;
3092 public override Expression DoResolve (EmitContext ec)
3094 eclass = ExprClass.Variable;
3096 if (left.Type == TypeManager.void_ptr_type) {
3097 Error (242, "The operation in question is undefined on void pointers");
3104 public override void Emit (EmitContext ec)
3106 Type op_type = left.Type;
3107 ILGenerator ig = ec.ig;
3109 // It must be either array or fixed buffer
3110 Type element = TypeManager.HasElementType (op_type) ?
3111 element = TypeManager.GetElementType (op_type) :
3112 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3114 int size = GetTypeSize (element);
3115 Type rtype = right.Type;
3117 if (rtype.IsPointer){
3119 // handle (pointer - pointer)
3123 ig.Emit (OpCodes.Sub);
3127 ig.Emit (OpCodes.Sizeof, element);
3129 IntLiteral.EmitInt (ig, size);
3130 ig.Emit (OpCodes.Div);
3132 ig.Emit (OpCodes.Conv_I8);
3135 // handle + and - on (pointer op int)
3138 ig.Emit (OpCodes.Conv_I);
3140 Constant right_const = right as Constant;
3141 if (right_const != null && size != 0) {
3142 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3150 ig.Emit (OpCodes.Sizeof, element);
3152 IntLiteral.EmitInt (ig, size);
3153 if (rtype == TypeManager.int64_type)
3154 ig.Emit (OpCodes.Conv_I8);
3155 else if (rtype == TypeManager.uint64_type)
3156 ig.Emit (OpCodes.Conv_U8);
3157 ig.Emit (OpCodes.Mul);
3161 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3162 ig.Emit (OpCodes.Conv_I);
3165 ig.Emit (OpCodes.Add);
3167 ig.Emit (OpCodes.Sub);
3173 /// Implements the ternary conditional operator (?:)
3175 public class Conditional : Expression {
3176 Expression expr, trueExpr, falseExpr;
3178 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3181 this.trueExpr = trueExpr;
3182 this.falseExpr = falseExpr;
3183 this.loc = expr.Location;
3186 public Expression Expr {
3192 public Expression TrueExpr {
3198 public Expression FalseExpr {
3204 public override Expression DoResolve (EmitContext ec)
3206 expr = expr.Resolve (ec);
3211 if (TypeManager.IsNullableValueType (expr.Type))
3212 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
3214 if (expr.Type != TypeManager.bool_type){
3215 expr = Expression.ResolveBoolean (
3222 Assign ass = expr as Assign;
3223 if (ass != null && ass.Source is Constant) {
3224 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3227 trueExpr = trueExpr.Resolve (ec);
3228 falseExpr = falseExpr.Resolve (ec);
3230 if (trueExpr == null || falseExpr == null)
3233 eclass = ExprClass.Value;
3234 if (trueExpr.Type == falseExpr.Type)
3235 type = trueExpr.Type;
3238 Type true_type = trueExpr.Type;
3239 Type false_type = falseExpr.Type;
3242 // First, if an implicit conversion exists from trueExpr
3243 // to falseExpr, then the result type is of type falseExpr.Type
3245 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3248 // Check if both can convert implicitl to each other's type
3250 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3252 "Can not compute type of conditional expression " +
3253 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3254 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3255 "' convert implicitly to each other");
3260 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3264 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3265 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3270 // Dead code optimalization
3271 if (expr is BoolConstant){
3272 BoolConstant bc = (BoolConstant) expr;
3274 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3275 return bc.Value ? trueExpr : falseExpr;
3281 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3286 public override void Emit (EmitContext ec)
3288 ILGenerator ig = ec.ig;
3289 Label false_target = ig.DefineLabel ();
3290 Label end_target = ig.DefineLabel ();
3292 expr.EmitBranchable (ec, false_target, false);
3294 ig.Emit (OpCodes.Br, end_target);
3295 ig.MarkLabel (false_target);
3296 falseExpr.Emit (ec);
3297 ig.MarkLabel (end_target);
3305 public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3306 public readonly string Name;
3307 public readonly Block Block;
3308 public LocalInfo local_info;
3311 LocalTemporary temp;
3313 public LocalVariableReference (Block block, string name, Location l)
3318 eclass = ExprClass.Variable;
3322 // Setting `is_readonly' to false will allow you to create a writable
3323 // reference to a read-only variable. This is used by foreach and using.
3325 public LocalVariableReference (Block block, string name, Location l,
3326 LocalInfo local_info, bool is_readonly)
3327 : this (block, name, l)
3329 this.local_info = local_info;
3330 this.is_readonly = is_readonly;
3333 public VariableInfo VariableInfo {
3334 get { return local_info.VariableInfo; }
3337 public bool IsReadOnly {
3338 get { return is_readonly; }
3341 public bool VerifyAssigned (EmitContext ec)
3343 VariableInfo variable_info = local_info.VariableInfo;
3344 return variable_info == null || variable_info.IsAssigned (ec, loc);
3347 void ResolveLocalInfo ()
3349 if (local_info == null) {
3350 local_info = Block.GetLocalInfo (Name);
3351 is_readonly = local_info.ReadOnly;
3355 protected Expression DoResolveBase (EmitContext ec)
3357 type = local_info.VariableType;
3359 Expression e = Block.GetConstantExpression (Name);
3361 return e.Resolve (ec);
3363 if (!VerifyAssigned (ec))
3366 if (ec.CurrentAnonymousMethod != null){
3368 // If we are referencing a variable from the external block
3369 // flag it for capturing
3371 if ((local_info.Block.Toplevel != ec.CurrentBlock.Toplevel) ||
3372 ec.CurrentAnonymousMethod.IsIterator)
3374 if (local_info.AddressTaken){
3375 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3378 ec.CaptureVariable (local_info);
3385 public override Expression DoResolve (EmitContext ec)
3387 ResolveLocalInfo ();
3388 local_info.Used = true;
3389 return DoResolveBase (ec);
3392 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3394 ResolveLocalInfo ();
3399 if (right_side == EmptyExpression.OutAccess) {
3400 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3401 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3402 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3403 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3404 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3406 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
3408 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3413 if (right_side == EmptyExpression.OutAccess)
3414 local_info.Used = true;
3416 if (VariableInfo != null)
3417 VariableInfo.SetAssigned (ec);
3419 return DoResolveBase (ec);
3422 public bool VerifyFixed ()
3424 // A local Variable is always fixed.
3428 public override int GetHashCode ()
3430 return Name.GetHashCode ();
3433 public override bool Equals (object obj)
3435 LocalVariableReference lvr = obj as LocalVariableReference;
3439 return Name == lvr.Name && Block == lvr.Block;
3442 public override void Emit (EmitContext ec)
3444 ILGenerator ig = ec.ig;
3446 if (local_info.FieldBuilder == null){
3448 // A local variable on the local CLR stack
3450 ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
3453 // A local variable captured by anonymous methods.
3456 ec.EmitCapturedVariableInstance (local_info);
3458 ig.Emit (OpCodes.Ldfld, local_info.FieldBuilder);
3462 public void Emit (EmitContext ec, bool leave_copy)
3466 ec.ig.Emit (OpCodes.Dup);
3467 if (local_info.FieldBuilder != null){
3468 temp = new LocalTemporary (Type);
3474 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3476 ILGenerator ig = ec.ig;
3477 prepared = prepare_for_load;
3479 if (local_info.FieldBuilder == null){
3481 // A local variable on the local CLR stack
3483 if (local_info.LocalBuilder == null)
3484 throw new Exception ("This should not happen: both Field and Local are null");
3488 ec.ig.Emit (OpCodes.Dup);
3489 ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
3492 // A local variable captured by anonymous methods or itereators.
3494 ec.EmitCapturedVariableInstance (local_info);
3496 if (prepare_for_load)
3497 ig.Emit (OpCodes.Dup);
3500 ig.Emit (OpCodes.Dup);
3501 temp = new LocalTemporary (Type);
3504 ig.Emit (OpCodes.Stfld, local_info.FieldBuilder);
3510 public void AddressOf (EmitContext ec, AddressOp mode)
3512 ILGenerator ig = ec.ig;
3514 if (local_info.FieldBuilder == null){
3516 // A local variable on the local CLR stack
3518 ig.Emit (OpCodes.Ldloca, local_info.LocalBuilder);
3521 // A local variable captured by anonymous methods or iterators
3523 ec.EmitCapturedVariableInstance (local_info);
3524 ig.Emit (OpCodes.Ldflda, local_info.FieldBuilder);
3528 public override string ToString ()
3530 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3535 /// This represents a reference to a parameter in the intermediate
3538 public class ParameterReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3544 public bool is_ref, is_out, prepared;
3558 public string Name {
3564 LocalTemporary temp;
3566 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3569 this.name = par.Name;
3573 eclass = ExprClass.Variable;
3576 public VariableInfo VariableInfo {
3580 public bool VerifyFixed ()
3582 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3583 return par.ModFlags == Parameter.Modifier.NONE;
3586 public bool IsAssigned (EmitContext ec, Location loc)
3588 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3591 Report.Error (269, loc,
3592 "Use of unassigned out parameter `{0}'", par.Name);
3596 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3598 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3601 Report.Error (170, loc,
3602 "Use of possibly unassigned field `" + field_name + "'");
3606 public void SetAssigned (EmitContext ec)
3608 if (is_out && ec.DoFlowAnalysis)
3609 ec.CurrentBranching.SetAssigned (vi);
3612 public void SetFieldAssigned (EmitContext ec, string field_name)
3614 if (is_out && ec.DoFlowAnalysis)
3615 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3618 protected bool DoResolveBase (EmitContext ec)
3620 if (!par.Resolve (ec)) {
3624 type = par.ParameterType;
3625 Parameter.Modifier mod = par.ModFlags;
3626 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3627 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3628 eclass = ExprClass.Variable;
3631 vi = block.ParameterMap [idx];
3633 if (ec.CurrentAnonymousMethod != null){
3634 if (is_ref && !block.Toplevel.IsLocalParameter (name)){
3635 Report.Error (1628, Location, "Cannot use ref or out parameter `{0}' inside an anonymous method block",
3641 // If we are referencing the parameter from the external block
3642 // flag it for capturing
3644 //Console.WriteLine ("Is parameter `{0}' local? {1}", name, block.IsLocalParameter (name));
3645 if (!block.Toplevel.IsLocalParameter (name)){
3646 ec.CaptureParameter (name, type, idx);
3653 public override int GetHashCode()
3655 return name.GetHashCode ();
3658 public override bool Equals (object obj)
3660 ParameterReference pr = obj as ParameterReference;
3664 return name == pr.name && block == pr.block;
3668 // Notice that for ref/out parameters, the type exposed is not the
3669 // same type exposed externally.
3672 // externally we expose "int&"
3673 // here we expose "int".
3675 // We record this in "is_ref". This means that the type system can treat
3676 // the type as it is expected, but when we generate the code, we generate
3677 // the alternate kind of code.
3679 public override Expression DoResolve (EmitContext ec)
3681 if (!DoResolveBase (ec))
3684 if (is_out && ec.DoFlowAnalysis && (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3690 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3692 if (!DoResolveBase (ec))
3700 static public void EmitLdArg (ILGenerator ig, int x)
3704 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3705 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3706 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3707 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3708 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3711 ig.Emit (OpCodes.Ldarg, x);
3715 // This method is used by parameters that are references, that are
3716 // being passed as references: we only want to pass the pointer (that
3717 // is already stored in the parameter, not the address of the pointer,
3718 // and not the value of the variable).
3720 public void EmitLoad (EmitContext ec)
3722 ILGenerator ig = ec.ig;
3725 if (!ec.MethodIsStatic)
3728 EmitLdArg (ig, arg_idx);
3731 // FIXME: Review for anonymous methods
3735 public override void Emit (EmitContext ec)
3740 public void Emit (EmitContext ec, bool leave_copy)
3742 ILGenerator ig = ec.ig;
3745 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3746 ec.EmitParameter (name, leave_copy, prepared, ref temp);
3750 if (!ec.MethodIsStatic)
3753 EmitLdArg (ig, arg_idx);
3757 ec.ig.Emit (OpCodes.Dup);
3760 // If we are a reference, we loaded on the stack a pointer
3761 // Now lets load the real value
3763 LoadFromPtr (ig, type);
3767 ec.ig.Emit (OpCodes.Dup);
3770 temp = new LocalTemporary (type);
3776 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3778 prepared = prepare_for_load;
3779 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3780 ec.EmitAssignParameter (name, source, leave_copy, prepare_for_load, ref temp);
3784 ILGenerator ig = ec.ig;
3789 if (!ec.MethodIsStatic)
3792 if (is_ref && !prepared)
3793 EmitLdArg (ig, arg_idx);
3798 ec.ig.Emit (OpCodes.Dup);
3802 temp = new LocalTemporary (type);
3806 StoreFromPtr (ig, type);
3812 ig.Emit (OpCodes.Starg_S, (byte) arg_idx);
3814 ig.Emit (OpCodes.Starg, arg_idx);
3818 public void AddressOf (EmitContext ec, AddressOp mode)
3820 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3821 ec.EmitAddressOfParameter (name);
3827 if (!ec.MethodIsStatic)
3832 ec.ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx);
3834 ec.ig.Emit (OpCodes.Ldarg, arg_idx);
3837 ec.ig.Emit (OpCodes.Ldarga_S, (byte) arg_idx);
3839 ec.ig.Emit (OpCodes.Ldarga, arg_idx);
3843 public override string ToString ()
3845 return "ParameterReference[" + name + "]";
3850 /// Used for arguments to New(), Invocation()
3852 public class Argument {
3853 public enum AType : byte {
3860 public readonly AType ArgType;
3861 public Expression Expr;
3863 public Argument (Expression expr, AType type)
3866 this.ArgType = type;
3869 public Argument (Expression expr)
3872 this.ArgType = AType.Expression;
3877 if (ArgType == AType.Ref || ArgType == AType.Out)
3878 return TypeManager.GetReferenceType (Expr.Type);
3884 public Parameter.Modifier Modifier
3889 return Parameter.Modifier.OUT;
3892 return Parameter.Modifier.REF;
3895 return Parameter.Modifier.NONE;
3900 public static string FullDesc (Argument a)
3902 if (a.ArgType == AType.ArgList)
3905 return (a.ArgType == AType.Ref ? "ref " :
3906 (a.ArgType == AType.Out ? "out " : "")) +
3907 TypeManager.CSharpName (a.Expr.Type);
3910 public bool ResolveMethodGroup (EmitContext ec)
3912 SimpleName sn = Expr as SimpleName;
3914 Expr = sn.GetMethodGroup ();
3916 // FIXME: csc doesn't report any error if you try to use `ref' or
3917 // `out' in a delegate creation expression.
3918 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3925 public bool Resolve (EmitContext ec, Location loc)
3927 bool old_do_flow_analysis = ec.DoFlowAnalysis;
3928 ec.DoFlowAnalysis = true;
3930 // Verify that the argument is readable
3931 if (ArgType != AType.Out)
3932 Expr = Expr.Resolve (ec);
3934 // Verify that the argument is writeable
3935 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3936 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3938 ec.DoFlowAnalysis = old_do_flow_analysis;
3940 return Expr != null;
3943 public void Emit (EmitContext ec)
3945 if (ArgType != AType.Ref && ArgType != AType.Out) {
3950 AddressOp mode = AddressOp.Store;
3951 if (ArgType == AType.Ref)
3952 mode |= AddressOp.Load;
3954 IMemoryLocation ml = (IMemoryLocation) Expr;
3955 ParameterReference pr = ml as ParameterReference;
3958 // ParameterReferences might already be references, so we want
3959 // to pass just the value
3961 if (pr != null && pr.IsRef)
3964 ml.AddressOf (ec, mode);
3969 /// Invocation of methods or delegates.
3971 public class Invocation : ExpressionStatement {
3972 public readonly ArrayList Arguments;
3975 MethodBase method = null;
3978 // arguments is an ArrayList, but we do not want to typecast,
3979 // as it might be null.
3981 // FIXME: only allow expr to be a method invocation or a
3982 // delegate invocation (7.5.5)
3984 public Invocation (Expression expr, ArrayList arguments)
3987 Arguments = arguments;
3988 loc = expr.Location;
3991 public Expression Expr {
3998 /// Determines "better conversion" as specified in 14.4.2.3
4000 /// Returns : p if a->p is better,
4001 /// q if a->q is better,
4002 /// null if neither is better
4004 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q)
4006 Type argument_type = TypeManager.TypeToCoreType (a.Type);
4007 Expression argument_expr = a.Expr;
4009 // p = TypeManager.TypeToCoreType (p);
4010 // q = TypeManager.TypeToCoreType (q);
4012 if (argument_type == null)
4013 throw new Exception ("Expression of type " + a.Expr +
4014 " does not resolve its type");
4016 if (p == null || q == null)
4017 throw new InternalErrorException ("BetterConversion Got a null conversion");
4022 if (argument_expr is NullLiteral) {
4024 // If the argument is null and one of the types to compare is 'object' and
4025 // the other is a reference type, we prefer the other.
4027 // This follows from the usual rules:
4028 // * There is an implicit conversion from 'null' to type 'object'
4029 // * There is an implicit conversion from 'null' to any reference type
4030 // * There is an implicit conversion from any reference type to type 'object'
4031 // * There is no implicit conversion from type 'object' to other reference types
4032 // => Conversion of 'null' to a reference type is better than conversion to 'object'
4034 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
4035 // null type. I think it used to be 'object' and thus needed a special
4036 // case to avoid the immediately following two checks.
4038 if (!p.IsValueType && q == TypeManager.object_type)
4040 if (!q.IsValueType && p == TypeManager.object_type)
4044 if (argument_type == p)
4047 if (argument_type == q)
4050 Expression p_tmp = new EmptyExpression (p);
4051 Expression q_tmp = new EmptyExpression (q);
4053 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
4054 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
4056 if (p_to_q && !q_to_p)
4059 if (q_to_p && !p_to_q)
4062 if (p == TypeManager.sbyte_type)
4063 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
4064 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4066 if (q == TypeManager.sbyte_type)
4067 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
4068 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4071 if (p == TypeManager.short_type)
4072 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
4073 q == TypeManager.uint64_type)
4075 if (q == TypeManager.short_type)
4076 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
4077 p == TypeManager.uint64_type)
4080 if (p == TypeManager.int32_type)
4081 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4083 if (q == TypeManager.int32_type)
4084 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4087 if (p == TypeManager.int64_type)
4088 if (q == TypeManager.uint64_type)
4090 if (q == TypeManager.int64_type)
4091 if (p == TypeManager.uint64_type)
4097 static Type MoreSpecific (Type p, Type q)
4099 if (p.IsGenericParameter && !q.IsGenericParameter)
4101 if (!p.IsGenericParameter && q.IsGenericParameter)
4104 if (p.IsGenericType) {
4105 Type[] pargs = TypeManager.GetTypeArguments (p);
4106 Type[] qargs = TypeManager.GetTypeArguments (q);
4108 bool p_specific_at_least_once = false;
4109 bool q_specific_at_least_once = false;
4111 for (int i = 0; i < pargs.Length; i++) {
4112 Type specific = MoreSpecific (pargs [i], qargs [i]);
4113 if (specific == pargs [i])
4114 p_specific_at_least_once = true;
4115 if (specific == qargs [i])
4116 q_specific_at_least_once = true;
4119 if (p_specific_at_least_once && !q_specific_at_least_once)
4121 if (!p_specific_at_least_once && q_specific_at_least_once)
4123 } else if (TypeManager.HasElementType (p)) {
4124 Type pe = TypeManager.GetElementType (p);
4125 Type qe = TypeManager.GetElementType (q);
4126 Type specific = MoreSpecific (pe, qe);
4137 /// Determines "Better function" between candidate
4138 /// and the current best match
4141 /// Returns a boolean indicating :
4142 /// false if candidate ain't better
4143 /// true if candidate is better than the current best match
4145 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4146 MethodBase candidate, bool candidate_params,
4147 MethodBase best, bool best_params)
4149 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4150 ParameterData best_pd = TypeManager.GetParameterData (best);
4152 bool better_at_least_one = false;
4154 for (int j = 0; j < argument_count; ++j) {
4155 Argument a = (Argument) args [j];
4157 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
4158 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
4160 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4161 if (candidate_params)
4162 ct = TypeManager.GetElementType (ct);
4164 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4166 bt = TypeManager.GetElementType (bt);
4172 Type better = BetterConversion (ec, a, ct, bt);
4174 // for each argument, the conversion to 'ct' should be no worse than
4175 // the conversion to 'bt'.
4179 // for at least one argument, the conversion to 'ct' should be better than
4180 // the conversion to 'bt'.
4182 better_at_least_one = true;
4185 if (better_at_least_one)
4189 // This handles the case
4191 // Add (float f1, float f2, float f3);
4192 // Add (params decimal [] foo);
4194 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4195 // first candidate would've chosen as better.
4201 // The two methods have equal parameter types. Now apply tie-breaking rules
4203 if (TypeManager.IsGenericMethod (best) && !TypeManager.IsGenericMethod (candidate))
4205 if (!TypeManager.IsGenericMethod (best) && TypeManager.IsGenericMethod (candidate))
4209 // This handles the following cases:
4211 // Trim () is better than Trim (params char[] chars)
4212 // Concat (string s1, string s2, string s3) is better than
4213 // Concat (string s1, params string [] srest)
4214 // Foo (int, params int [] rest) is better than Foo (params int [] rest)
4216 if (!candidate_params && best_params)
4218 if (candidate_params && !best_params)
4221 int candidate_param_count = candidate_pd.Count;
4222 int best_param_count = best_pd.Count;
4224 if (candidate_param_count != best_param_count)
4225 // can only happen if (candidate_params && best_params)
4226 return candidate_param_count > best_param_count;
4229 // now, both methods have the same number of parameters, and the parameters have the same types
4230 // Pick the "more specific" signature
4233 MethodBase orig_candidate = TypeManager.DropGenericMethodArguments (candidate);
4234 MethodBase orig_best = TypeManager.DropGenericMethodArguments (best);
4236 ParameterData orig_candidate_pd = TypeManager.GetParameterData (orig_candidate);
4237 ParameterData orig_best_pd = TypeManager.GetParameterData (orig_best);
4239 bool specific_at_least_once = false;
4240 for (int j = 0; j < candidate_param_count; ++j) {
4241 Type ct = TypeManager.TypeToCoreType (orig_candidate_pd.ParameterType (j));
4242 Type bt = TypeManager.TypeToCoreType (orig_best_pd.ParameterType (j));
4245 Type specific = MoreSpecific (ct, bt);
4249 specific_at_least_once = true;
4252 if (specific_at_least_once)
4255 // FIXME: handle lifted operators
4261 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4263 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4266 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4267 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4269 if (cand_pd.Count != base_pd.Count)
4272 for (int j = 0; j < cand_pd.Count; ++j) {
4273 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4274 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4275 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4276 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4278 if (cm != bm || ct != bt)
4285 public static string FullMethodDesc (MethodBase mb)
4291 if (mb is MethodInfo) {
4292 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4296 sb = new StringBuilder ();
4298 sb.Append (TypeManager.CSharpSignature (mb));
4299 return sb.ToString ();
4302 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4304 MemberInfo [] miset;
4305 MethodGroupExpr union;
4310 return (MethodGroupExpr) mg2;
4313 return (MethodGroupExpr) mg1;
4316 MethodGroupExpr left_set = null, right_set = null;
4317 int length1 = 0, length2 = 0;
4319 left_set = (MethodGroupExpr) mg1;
4320 length1 = left_set.Methods.Length;
4322 right_set = (MethodGroupExpr) mg2;
4323 length2 = right_set.Methods.Length;
4325 ArrayList common = new ArrayList ();
4327 foreach (MethodBase r in right_set.Methods){
4328 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4332 miset = new MemberInfo [length1 + length2 - common.Count];
4333 left_set.Methods.CopyTo (miset, 0);
4337 foreach (MethodBase r in right_set.Methods) {
4338 if (!common.Contains (r))
4342 union = new MethodGroupExpr (miset, loc);
4347 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4348 ArrayList arguments, int arg_count,
4349 ref MethodBase candidate)
4351 return IsParamsMethodApplicable (
4352 ec, me, arguments, arg_count, false, ref candidate) ||
4353 IsParamsMethodApplicable (
4354 ec, me, arguments, arg_count, true, ref candidate);
4359 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4360 ArrayList arguments, int arg_count,
4361 bool do_varargs, ref MethodBase candidate)
4363 if (!me.HasTypeArguments &&
4364 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4367 if (TypeManager.IsGenericMethodDefinition (candidate))
4368 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4370 return IsParamsMethodApplicable (
4371 ec, arguments, arg_count, candidate, do_varargs);
4375 /// Determines if the candidate method, if a params method, is applicable
4376 /// in its expanded form to the given set of arguments
4378 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4379 int arg_count, MethodBase candidate,
4382 ParameterData pd = TypeManager.GetParameterData (candidate);
4384 int pd_count = pd.Count;
4388 int count = pd_count - 1;
4390 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4392 if (pd_count != arg_count)
4399 if (count > arg_count)
4402 if (pd_count == 1 && arg_count == 0)
4406 // If we have come this far, the case which
4407 // remains is when the number of parameters is
4408 // less than or equal to the argument count.
4410 for (int i = 0; i < count; ++i) {
4412 Argument a = (Argument) arguments [i];
4414 Parameter.Modifier a_mod = a.Modifier &
4415 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4416 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4417 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4419 if (a_mod == p_mod) {
4421 if (a_mod == Parameter.Modifier.NONE)
4422 if (!Convert.ImplicitConversionExists (ec,
4424 pd.ParameterType (i)))
4427 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4428 Type pt = pd.ParameterType (i);
4431 pt = TypeManager.GetReferenceType (pt);
4442 Argument a = (Argument) arguments [count];
4443 if (!(a.Expr is Arglist))
4449 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
4451 for (int i = pd_count - 1; i < arg_count; i++) {
4452 Argument a = (Argument) arguments [i];
4454 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4461 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4462 ArrayList arguments, int arg_count,
4463 ref MethodBase candidate)
4465 if (!me.HasTypeArguments &&
4466 !TypeManager.InferTypeArguments (arguments, ref candidate))
4469 if (TypeManager.IsGenericMethodDefinition (candidate))
4470 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4472 return IsApplicable (ec, arguments, arg_count, candidate);
4476 /// Determines if the candidate method is applicable (section 14.4.2.1)
4477 /// to the given set of arguments
4479 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4480 MethodBase candidate)
4482 ParameterData pd = TypeManager.GetParameterData (candidate);
4484 if (arg_count != pd.Count)
4487 for (int i = arg_count; i > 0; ) {
4490 Argument a = (Argument) arguments [i];
4492 Parameter.Modifier a_mod = a.Modifier &
4493 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4495 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4496 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4498 if (a_mod == p_mod) {
4499 Type pt = pd.ParameterType (i);
4501 if (a_mod == Parameter.Modifier.NONE) {
4502 if (!TypeManager.IsEqual (a.Type, pt) &&
4503 !Convert.ImplicitConversionExists (ec, a.Expr, pt))
4517 static internal bool IsAncestralType (Type first_type, Type second_type)
4519 return first_type != second_type &&
4520 (TypeManager.IsSubclassOf (second_type, first_type) ||
4521 TypeManager.ImplementsInterface (second_type, first_type));
4525 /// Find the Applicable Function Members (7.4.2.1)
4527 /// me: Method Group expression with the members to select.
4528 /// it might contain constructors or methods (or anything
4529 /// that maps to a method).
4531 /// Arguments: ArrayList containing resolved Argument objects.
4533 /// loc: The location if we want an error to be reported, or a Null
4534 /// location for "probing" purposes.
4536 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4537 /// that is the best match of me on Arguments.
4540 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4541 ArrayList Arguments, bool may_fail,
4544 MethodBase method = null;
4545 bool method_params = false;
4546 Type applicable_type = null;
4548 ArrayList candidates = new ArrayList (2);
4549 ArrayList candidate_overrides = null;
4552 // Used to keep a map between the candidate
4553 // and whether it is being considered in its
4554 // normal or expanded form
4556 // false is normal form, true is expanded form
4558 Hashtable candidate_to_form = null;
4560 if (Arguments != null)
4561 arg_count = Arguments.Count;
4563 if ((me.Name == "Invoke") &&
4564 TypeManager.IsDelegateType (me.DeclaringType)) {
4565 Error_InvokeOnDelegate (loc);
4569 MethodBase[] methods = me.Methods;
4571 int nmethods = methods.Length;
4575 // Methods marked 'override' don't take part in 'applicable_type'
4576 // computation, nor in the actual overload resolution.
4577 // However, they still need to be emitted instead of a base virtual method.
4578 // So, we salt them away into the 'candidate_overrides' array.
4580 // In case of reflected methods, we replace each overriding method with
4581 // its corresponding base virtual method. This is to improve compatibility
4582 // with non-C# libraries which change the visibility of overrides (#75636)
4585 for (int i = 0; i < methods.Length; ++i) {
4586 MethodBase m = methods [i];
4587 Type [] gen_args = m.IsGenericMethod && !m.IsGenericMethodDefinition ? m.GetGenericArguments () : null;
4588 if (TypeManager.IsOverride (m)) {
4589 if (candidate_overrides == null)
4590 candidate_overrides = new ArrayList ();
4591 candidate_overrides.Add (m);
4592 m = TypeManager.TryGetBaseDefinition (m);
4593 if (m != null && gen_args != null) {
4594 if (!m.IsGenericMethodDefinition)
4595 throw new InternalErrorException ("GetBaseDefinition didn't return a GenericMethodDefinition");
4596 m = ((MethodInfo) m).MakeGenericMethod (gen_args);
4605 int applicable_errors = Report.Errors;
4608 // First we construct the set of applicable methods
4610 bool is_sorted = true;
4611 for (int i = 0; i < nmethods; i++){
4612 Type decl_type = methods [i].DeclaringType;
4615 // If we have already found an applicable method
4616 // we eliminate all base types (Section 14.5.5.1)
4618 if (applicable_type != null && IsAncestralType (decl_type, applicable_type))
4622 // Check if candidate is applicable (section 14.4.2.1)
4623 // Is candidate applicable in normal form?
4625 bool is_applicable = IsApplicable (ec, me, Arguments, arg_count, ref methods [i]);
4627 if (!is_applicable && IsParamsMethodApplicable (ec, me, Arguments, arg_count, ref methods [i])) {
4628 MethodBase candidate = methods [i];
4629 if (candidate_to_form == null)
4630 candidate_to_form = new PtrHashtable ();
4631 candidate_to_form [candidate] = candidate;
4632 // Candidate is applicable in expanded form
4633 is_applicable = true;
4639 candidates.Add (methods [i]);
4641 if (applicable_type == null)
4642 applicable_type = decl_type;
4643 else if (applicable_type != decl_type) {
4645 if (IsAncestralType (applicable_type, decl_type))
4646 applicable_type = decl_type;
4650 if (applicable_errors != Report.Errors)
4653 int candidate_top = candidates.Count;
4655 if (applicable_type == null) {
4657 // Okay so we have failed to find anything so we
4658 // return by providing info about the closest match
4660 int errors = Report.Errors;
4661 for (int i = 0; i < nmethods; ++i) {
4662 MethodBase c = (MethodBase) methods [i];
4663 ParameterData pd = TypeManager.GetParameterData (c);
4665 if (pd.Count != arg_count)
4668 if (!TypeManager.InferTypeArguments (Arguments, ref c))
4671 if (TypeManager.IsGenericMethodDefinition (c))
4674 VerifyArgumentsCompat (ec, Arguments, arg_count,
4675 c, false, null, may_fail, loc);
4677 if (!may_fail && errors == Report.Errors)
4678 throw new InternalErrorException (
4679 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4680 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4685 if (!may_fail && errors == Report.Errors) {
4686 string report_name = me.Name;
4687 if (report_name == ".ctor")
4688 report_name = me.DeclaringType.ToString ();
4690 for (int i = 0; i < methods.Length; ++i) {
4691 MethodBase c = methods [i];
4692 ParameterData pd = TypeManager.GetParameterData (c);
4694 if (pd.Count != arg_count)
4697 if (TypeManager.InferTypeArguments (Arguments, ref c))
4701 411, loc, "The type arguments for " +
4702 "method `{0}' cannot be infered from " +
4703 "the usage. Try specifying the type " +
4704 "arguments explicitly.", report_name);
4708 Error_WrongNumArguments (loc, report_name, arg_count);
4716 // At this point, applicable_type is _one_ of the most derived types
4717 // in the set of types containing the methods in this MethodGroup.
4718 // Filter the candidates so that they only contain methods from the
4719 // most derived types.
4722 int finalized = 0; // Number of finalized candidates
4725 // Invariant: applicable_type is a most derived type
4727 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4728 // eliminating all it's base types. At the same time, we'll also move
4729 // every unrelated type to the end of the array, and pick the next
4730 // 'applicable_type'.
4732 Type next_applicable_type = null;
4733 int j = finalized; // where to put the next finalized candidate
4734 int k = finalized; // where to put the next undiscarded candidate
4735 for (int i = finalized; i < candidate_top; ++i) {
4736 MethodBase candidate = (MethodBase) candidates [i];
4737 Type decl_type = candidate.DeclaringType;
4739 if (decl_type == applicable_type) {
4740 candidates [k++] = candidates [j];
4741 candidates [j++] = candidates [i];
4745 if (IsAncestralType (decl_type, applicable_type))
4748 if (next_applicable_type != null &&
4749 IsAncestralType (decl_type, next_applicable_type))
4752 candidates [k++] = candidates [i];
4754 if (next_applicable_type == null ||
4755 IsAncestralType (next_applicable_type, decl_type))
4756 next_applicable_type = decl_type;
4759 applicable_type = next_applicable_type;
4762 } while (applicable_type != null);
4766 // Now we actually find the best method
4769 method = (MethodBase) candidates [0];
4770 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4771 for (int ix = 1; ix < candidate_top; ix++){
4772 MethodBase candidate = (MethodBase) candidates [ix];
4774 if (candidate == method)
4777 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4779 if (BetterFunction (ec, Arguments, arg_count,
4780 candidate, cand_params,
4781 method, method_params)) {
4783 method_params = cand_params;
4787 // Now check that there are no ambiguities i.e the selected method
4788 // should be better than all the others
4790 MethodBase ambiguous = null;
4791 for (int ix = 0; ix < candidate_top; ix++){
4792 MethodBase candidate = (MethodBase) candidates [ix];
4794 if (candidate == method)
4797 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4798 if (!BetterFunction (ec, Arguments, arg_count,
4799 method, method_params,
4800 candidate, cand_params)) {
4801 Report.SymbolRelatedToPreviousError (candidate);
4802 ambiguous = candidate;
4806 if (ambiguous != null) {
4807 Report.SymbolRelatedToPreviousError (method);
4808 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4809 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4814 // If the method is a virtual function, pick an override closer to the LHS type.
4816 if (!me.IsBase && method.IsVirtual) {
4817 if (TypeManager.IsOverride (method))
4818 throw new InternalErrorException (
4819 "Should not happen. An 'override' method took part in overload resolution: " + method);
4821 if (candidate_overrides != null)
4822 foreach (MethodBase candidate in candidate_overrides) {
4823 if (IsOverride (candidate, method))
4829 // And now check if the arguments are all
4830 // compatible, perform conversions if
4831 // necessary etc. and return if everything is
4834 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4835 method_params, null, may_fail, loc))
4841 MethodBase the_method = TypeManager.DropGenericMethodArguments (method);
4842 if (the_method.IsGenericMethodDefinition &&
4843 !ConstraintChecker.CheckConstraints (ec, the_method, method, loc))
4846 IMethodData data = TypeManager.GetMethod (the_method);
4848 data.SetMemberIsUsed ();
4853 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4855 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4856 name, arg_count.ToString ());
4859 static void Error_InvokeOnDelegate (Location loc)
4861 Report.Error (1533, loc,
4862 "Invoke cannot be called directly on a delegate");
4865 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4866 Type delegate_type, Argument a, ParameterData expected_par)
4868 if (delegate_type == null)
4869 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4870 TypeManager.CSharpSignature (method));
4872 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4873 TypeManager.CSharpName (delegate_type));
4875 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4877 string index = (idx + 1).ToString ();
4878 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4879 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4880 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4881 index, Parameter.GetModifierSignature (a.Modifier));
4883 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4884 index, Parameter.GetModifierSignature (mod));
4886 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4887 index, Argument.FullDesc (a), expected_par.ParameterDesc (idx));
4891 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4892 int arg_count, MethodBase method,
4893 bool chose_params_expanded,
4894 Type delegate_type, bool may_fail,
4897 ParameterData pd = TypeManager.GetParameterData (method);
4899 for (j = 0; j < arg_count; j++) {
4900 Argument a = (Argument) Arguments [j];
4901 Expression a_expr = a.Expr;
4902 Type parameter_type = pd.ParameterType (j);
4903 Parameter.Modifier pm = pd.ParameterModifier (j);
4904 Parameter.Modifier am = a.Modifier;
4906 if (pm == Parameter.Modifier.ARGLIST) {
4907 if (!(a.Expr is Arglist))
4912 if (pm == Parameter.Modifier.PARAMS) {
4913 pm = Parameter.Modifier.NONE;
4914 if (chose_params_expanded)
4915 parameter_type = TypeManager.GetElementType (parameter_type);
4921 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4922 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4925 Expression conv = Convert.ImplicitConversion (ec, a_expr, parameter_type, loc);
4929 // Update the argument with the implicit conversion
4934 if (parameter_type.IsPointer && !ec.InUnsafe) {
4944 Error_InvalidArguments (loc, j, method, delegate_type, (Argument) Arguments [j], pd);
4948 private bool resolved = false;
4949 public override Expression DoResolve (EmitContext ec)
4952 return this.method == null ? null : this;
4956 // First, resolve the expression that is used to
4957 // trigger the invocation
4959 SimpleName sn = expr as SimpleName;
4961 expr = sn.GetMethodGroup ();
4963 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4967 if (!(expr is MethodGroupExpr)) {
4968 Type expr_type = expr.Type;
4970 if (expr_type != null){
4971 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4973 return (new DelegateInvocation (
4974 this.expr, Arguments, loc)).Resolve (ec);
4978 if (!(expr is MethodGroupExpr)){
4979 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4984 // Next, evaluate all the expressions in the argument list
4986 if (Arguments != null){
4987 foreach (Argument a in Arguments){
4988 if (!a.Resolve (ec, loc))
4993 MethodGroupExpr mg = (MethodGroupExpr) expr;
4994 MethodBase method = OverloadResolve (ec, mg, Arguments, false, loc);
4999 MethodInfo mi = method as MethodInfo;
5001 type = TypeManager.TypeToCoreType (mi.ReturnType);
5002 Expression iexpr = mg.InstanceExpression;
5004 if (iexpr == null ||
5005 iexpr is This || iexpr is EmptyExpression ||
5006 mg.IdenticalTypeName) {
5007 mg.InstanceExpression = null;
5009 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
5013 if (iexpr == null || iexpr is EmptyExpression) {
5014 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
5020 if (type.IsPointer){
5028 // Only base will allow this invocation to happen.
5030 if (mg.IsBase && method.IsAbstract){
5031 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
5035 if (Arguments == null && method.Name == "Finalize") {
5037 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
5039 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
5043 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
5047 if (mg.InstanceExpression != null)
5048 mg.InstanceExpression.CheckMarshalByRefAccess ();
5050 eclass = ExprClass.Value;
5051 this.method = method;
5055 bool IsSpecialMethodInvocation (MethodBase method)
5057 IMethodData md = TypeManager.GetMethod (method);
5059 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
5062 if (!TypeManager.IsSpecialMethod (method))
5065 int args = TypeManager.GetParameterData (method).Count;
5066 if (method.Name.StartsWith ("get_") && args > 0)
5068 else if (method.Name.StartsWith ("set_") && args > 2)
5071 // TODO: check operators and events as well ?
5074 Report.SymbolRelatedToPreviousError (method);
5075 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
5076 TypeManager.CSharpSignature (method, true));
5082 // Emits the list of arguments as an array
5084 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
5086 ILGenerator ig = ec.ig;
5087 int count = arguments.Count - idx;
5088 Argument a = (Argument) arguments [idx];
5089 Type t = a.Expr.Type;
5091 IntConstant.EmitInt (ig, count);
5092 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
5094 int top = arguments.Count;
5095 for (int j = idx; j < top; j++){
5096 a = (Argument) arguments [j];
5098 ig.Emit (OpCodes.Dup);
5099 IntConstant.EmitInt (ig, j - idx);
5101 bool is_stobj, has_type_arg;
5102 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
5104 ig.Emit (OpCodes.Ldelema, t);
5116 /// Emits a list of resolved Arguments that are in the arguments
5119 /// The MethodBase argument might be null if the
5120 /// emission of the arguments is known not to contain
5121 /// a `params' field (for example in constructors or other routines
5122 /// that keep their arguments in this structure)
5124 /// if `dup_args' is true, a copy of the arguments will be left
5125 /// on the stack. If `dup_args' is true, you can specify `this_arg'
5126 /// which will be duplicated before any other args. Only EmitCall
5127 /// should be using this interface.
5129 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
5131 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
5132 int top = arguments == null ? 0 : arguments.Count;
5133 LocalTemporary [] temps = null;
5135 if (dup_args && top != 0)
5136 temps = new LocalTemporary [top];
5138 for (int i = 0; i < top; i++){
5139 Argument a = (Argument) arguments [i];
5142 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
5144 // Special case if we are passing the same data as the
5145 // params argument, do not put it in an array.
5147 if (pd.ParameterType (i) == a.Type)
5150 EmitParams (ec, i, arguments);
5157 ec.ig.Emit (OpCodes.Dup);
5158 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
5163 if (this_arg != null)
5166 for (int i = 0; i < top; i ++)
5167 temps [i].Emit (ec);
5170 if (pd != null && pd.Count > top &&
5171 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
5172 ILGenerator ig = ec.ig;
5174 IntConstant.EmitInt (ig, 0);
5175 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
5179 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
5181 ParameterData pd = TypeManager.GetParameterData (mb);
5183 if (arguments == null)
5184 return new Type [0];
5186 Argument a = (Argument) arguments [pd.Count - 1];
5187 Arglist list = (Arglist) a.Expr;
5189 return list.ArgumentTypes;
5193 /// This checks the ConditionalAttribute on the method
5195 static bool IsMethodExcluded (MethodBase method)
5197 if (method.IsConstructor)
5200 IMethodData md = TypeManager.GetMethod (method);
5202 return md.IsExcluded ();
5204 // For some methods (generated by delegate class) GetMethod returns null
5205 // because they are not included in builder_to_method table
5206 if (method.DeclaringType is TypeBuilder)
5209 return AttributeTester.IsConditionalMethodExcluded (method);
5213 /// is_base tells whether we want to force the use of the `call'
5214 /// opcode instead of using callvirt. Call is required to call
5215 /// a specific method, while callvirt will always use the most
5216 /// recent method in the vtable.
5218 /// is_static tells whether this is an invocation on a static method
5220 /// instance_expr is an expression that represents the instance
5221 /// it must be non-null if is_static is false.
5223 /// method is the method to invoke.
5225 /// Arguments is the list of arguments to pass to the method or constructor.
5227 public static void EmitCall (EmitContext ec, bool is_base,
5228 bool is_static, Expression instance_expr,
5229 MethodBase method, ArrayList Arguments, Location loc)
5231 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
5234 // `dup_args' leaves an extra copy of the arguments on the stack
5235 // `omit_args' does not leave any arguments at all.
5236 // So, basically, you could make one call with `dup_args' set to true,
5237 // and then another with `omit_args' set to true, and the two calls
5238 // would have the same set of arguments. However, each argument would
5239 // only have been evaluated once.
5240 public static void EmitCall (EmitContext ec, bool is_base,
5241 bool is_static, Expression instance_expr,
5242 MethodBase method, ArrayList Arguments, Location loc,
5243 bool dup_args, bool omit_args)
5245 ILGenerator ig = ec.ig;
5246 bool struct_call = false;
5247 bool this_call = false;
5248 LocalTemporary this_arg = null;
5250 Type decl_type = method.DeclaringType;
5252 if (!RootContext.StdLib) {
5253 // Replace any calls to the system's System.Array type with calls to
5254 // the newly created one.
5255 if (method == TypeManager.system_int_array_get_length)
5256 method = TypeManager.int_array_get_length;
5257 else if (method == TypeManager.system_int_array_get_rank)
5258 method = TypeManager.int_array_get_rank;
5259 else if (method == TypeManager.system_object_array_clone)
5260 method = TypeManager.object_array_clone;
5261 else if (method == TypeManager.system_int_array_get_length_int)
5262 method = TypeManager.int_array_get_length_int;
5263 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5264 method = TypeManager.int_array_get_lower_bound_int;
5265 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5266 method = TypeManager.int_array_get_upper_bound_int;
5267 else if (method == TypeManager.system_void_array_copyto_array_int)
5268 method = TypeManager.void_array_copyto_array_int;
5271 if (!ec.IsInObsoleteScope) {
5273 // This checks ObsoleteAttribute on the method and on the declaring type
5275 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5277 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5279 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5281 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5285 if (IsMethodExcluded (method))
5289 if (instance_expr == EmptyExpression.Null) {
5290 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5294 this_call = instance_expr is This;
5295 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5299 // If this is ourselves, push "this"
5303 Type iexpr_type = instance_expr.Type;
5306 // Push the instance expression
5308 if (TypeManager.IsValueType (iexpr_type)) {
5310 // Special case: calls to a function declared in a
5311 // reference-type with a value-type argument need
5312 // to have their value boxed.
5313 if (decl_type.IsValueType ||
5314 iexpr_type.IsGenericParameter) {
5316 // If the expression implements IMemoryLocation, then
5317 // we can optimize and use AddressOf on the
5320 // If not we have to use some temporary storage for
5322 if (instance_expr is IMemoryLocation) {
5323 ((IMemoryLocation)instance_expr).
5324 AddressOf (ec, AddressOp.LoadStore);
5326 LocalTemporary temp = new LocalTemporary (iexpr_type);
5327 instance_expr.Emit (ec);
5329 temp.AddressOf (ec, AddressOp.Load);
5332 // avoid the overhead of doing this all the time.
5334 t = TypeManager.GetReferenceType (iexpr_type);
5336 instance_expr.Emit (ec);
5337 ig.Emit (OpCodes.Box, instance_expr.Type);
5338 t = TypeManager.object_type;
5341 instance_expr.Emit (ec);
5342 t = instance_expr.Type;
5346 ig.Emit (OpCodes.Dup);
5347 if (Arguments != null && Arguments.Count != 0) {
5348 this_arg = new LocalTemporary (t);
5349 this_arg.Store (ec);
5356 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5358 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
5359 ig.Emit (OpCodes.Constrained, instance_expr.Type);
5362 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5363 call_op = OpCodes.Call;
5365 call_op = OpCodes.Callvirt;
5367 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5368 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5369 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5376 // and DoFoo is not virtual, you can omit the callvirt,
5377 // because you don't need the null checking behavior.
5379 if (method is MethodInfo)
5380 ig.Emit (call_op, (MethodInfo) method);
5382 ig.Emit (call_op, (ConstructorInfo) method);
5385 public override void Emit (EmitContext ec)
5387 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5389 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5392 public override void EmitStatement (EmitContext ec)
5397 // Pop the return value if there is one
5399 if (method is MethodInfo){
5400 Type ret = ((MethodInfo)method).ReturnType;
5401 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5402 ec.ig.Emit (OpCodes.Pop);
5407 public class InvocationOrCast : ExpressionStatement
5410 Expression argument;
5412 public InvocationOrCast (Expression expr, Expression argument)
5415 this.argument = argument;
5416 this.loc = expr.Location;
5419 public override Expression DoResolve (EmitContext ec)
5422 // First try to resolve it as a cast.
5424 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5425 if ((te != null) && (te.eclass == ExprClass.Type)) {
5426 Cast cast = new Cast (te, argument, loc);
5427 return cast.Resolve (ec);
5431 // This can either be a type or a delegate invocation.
5432 // Let's just resolve it and see what we'll get.
5434 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5439 // Ok, so it's a Cast.
5441 if (expr.eclass == ExprClass.Type) {
5442 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5443 return cast.Resolve (ec);
5447 // It's a delegate invocation.
5449 if (!TypeManager.IsDelegateType (expr.Type)) {
5450 Error (149, "Method name expected");
5454 ArrayList args = new ArrayList ();
5455 args.Add (new Argument (argument, Argument.AType.Expression));
5456 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5457 return invocation.Resolve (ec);
5462 Error (201, "Only assignment, call, increment, decrement and new object " +
5463 "expressions can be used as a statement");
5466 public override ExpressionStatement ResolveStatement (EmitContext ec)
5469 // First try to resolve it as a cast.
5471 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5472 if ((te != null) && (te.eclass == ExprClass.Type)) {
5478 // This can either be a type or a delegate invocation.
5479 // Let's just resolve it and see what we'll get.
5481 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5482 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5488 // It's a delegate invocation.
5490 if (!TypeManager.IsDelegateType (expr.Type)) {
5491 Error (149, "Method name expected");
5495 ArrayList args = new ArrayList ();
5496 args.Add (new Argument (argument, Argument.AType.Expression));
5497 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5498 return invocation.ResolveStatement (ec);
5501 public override void Emit (EmitContext ec)
5503 throw new Exception ("Cannot happen");
5506 public override void EmitStatement (EmitContext ec)
5508 throw new Exception ("Cannot happen");
5513 // This class is used to "disable" the code generation for the
5514 // temporary variable when initializing value types.
5516 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5517 public void AddressOf (EmitContext ec, AddressOp Mode)
5524 /// Implements the new expression
5526 public class New : ExpressionStatement, IMemoryLocation {
5527 public readonly ArrayList Arguments;
5530 // During bootstrap, it contains the RequestedType,
5531 // but if `type' is not null, it *might* contain a NewDelegate
5532 // (because of field multi-initialization)
5534 public Expression RequestedType;
5536 MethodBase method = null;
5539 // If set, the new expression is for a value_target, and
5540 // we will not leave anything on the stack.
5542 Expression value_target;
5543 bool value_target_set = false;
5544 bool is_type_parameter = false;
5546 public New (Expression requested_type, ArrayList arguments, Location l)
5548 RequestedType = requested_type;
5549 Arguments = arguments;
5553 public bool SetValueTypeVariable (Expression value)
5555 value_target = value;
5556 value_target_set = true;
5557 if (!(value_target is IMemoryLocation)){
5558 Error_UnexpectedKind (null, "variable", loc);
5565 // This function is used to disable the following code sequence for
5566 // value type initialization:
5568 // AddressOf (temporary)
5572 // Instead the provide will have provided us with the address on the
5573 // stack to store the results.
5575 static Expression MyEmptyExpression;
5577 public void DisableTemporaryValueType ()
5579 if (MyEmptyExpression == null)
5580 MyEmptyExpression = new EmptyAddressOf ();
5583 // To enable this, look into:
5584 // test-34 and test-89 and self bootstrapping.
5586 // For instance, we can avoid a copy by using `newobj'
5587 // instead of Call + Push-temp on value types.
5588 // value_target = MyEmptyExpression;
5593 /// Converts complex core type syntax like 'new int ()' to simple constant
5595 public static Constant Constantify (Type t)
5597 if (t == TypeManager.int32_type)
5598 return new IntConstant (0, Location.Null);
5599 if (t == TypeManager.uint32_type)
5600 return new UIntConstant (0, Location.Null);
5601 if (t == TypeManager.int64_type)
5602 return new LongConstant (0, Location.Null);
5603 if (t == TypeManager.uint64_type)
5604 return new ULongConstant (0, Location.Null);
5605 if (t == TypeManager.float_type)
5606 return new FloatConstant (0, Location.Null);
5607 if (t == TypeManager.double_type)
5608 return new DoubleConstant (0, Location.Null);
5609 if (t == TypeManager.short_type)
5610 return new ShortConstant (0, Location.Null);
5611 if (t == TypeManager.ushort_type)
5612 return new UShortConstant (0, Location.Null);
5613 if (t == TypeManager.sbyte_type)
5614 return new SByteConstant (0, Location.Null);
5615 if (t == TypeManager.byte_type)
5616 return new ByteConstant (0, Location.Null);
5617 if (t == TypeManager.char_type)
5618 return new CharConstant ('\0', Location.Null);
5619 if (t == TypeManager.bool_type)
5620 return new BoolConstant (false, Location.Null);
5621 if (t == TypeManager.decimal_type)
5622 return new DecimalConstant (0, Location.Null);
5628 // Checks whether the type is an interface that has the
5629 // [ComImport, CoClass] attributes and must be treated
5632 public Expression CheckComImport (EmitContext ec)
5634 if (!type.IsInterface)
5638 // Turn the call into:
5639 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5641 Type real_class = AttributeTester.GetCoClassAttribute (type);
5642 if (real_class == null)
5645 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5646 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5647 return cast.Resolve (ec);
5650 public override Expression DoResolve (EmitContext ec)
5653 // The New DoResolve might be called twice when initializing field
5654 // expressions (see EmitFieldInitializers, the call to
5655 // GetInitializerExpression will perform a resolve on the expression,
5656 // and later the assign will trigger another resolution
5658 // This leads to bugs (#37014)
5661 if (RequestedType is NewDelegate)
5662 return RequestedType;
5666 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5672 if (Arguments == null) {
5673 Expression c = Constantify (type);
5678 if (TypeManager.IsDelegateType (type)) {
5679 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5680 if (RequestedType != null)
5681 if (!(RequestedType is DelegateCreation))
5682 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5683 return RequestedType;
5686 if (type.IsGenericParameter) {
5687 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5689 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5690 Error (304, String.Format (
5691 "Cannot create an instance of the " +
5692 "variable type '{0}' because it " +
5693 "doesn't have the new() constraint",
5698 if ((Arguments != null) && (Arguments.Count != 0)) {
5699 Error (417, String.Format (
5700 "`{0}': cannot provide arguments " +
5701 "when creating an instance of a " +
5702 "variable type.", type));
5706 is_type_parameter = true;
5707 eclass = ExprClass.Value;
5711 if (type.IsAbstract && type.IsSealed) {
5712 Report.SymbolRelatedToPreviousError (type);
5713 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5717 if (type.IsInterface || type.IsAbstract){
5718 RequestedType = CheckComImport (ec);
5719 if (RequestedType != null)
5720 return RequestedType;
5722 Report.SymbolRelatedToPreviousError (type);
5723 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5727 bool is_struct = type.IsValueType;
5728 eclass = ExprClass.Value;
5731 // SRE returns a match for .ctor () on structs (the object constructor),
5732 // so we have to manually ignore it.
5734 if (is_struct && Arguments == null)
5737 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5738 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5739 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5744 MethodGroupExpr mg = ml as MethodGroupExpr;
5747 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5751 if (Arguments != null){
5752 foreach (Argument a in Arguments){
5753 if (!a.Resolve (ec, loc))
5758 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5759 if (method == null) {
5760 if (almostMatchedMembers.Count != 0)
5761 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5768 bool DoEmitTypeParameter (EmitContext ec)
5770 ILGenerator ig = ec.ig;
5772 ig.Emit (OpCodes.Ldtoken, type);
5773 ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
5774 ig.Emit (OpCodes.Call, TypeManager.activator_create_instance);
5775 ig.Emit (OpCodes.Unbox_Any, type);
5781 // This DoEmit can be invoked in two contexts:
5782 // * As a mechanism that will leave a value on the stack (new object)
5783 // * As one that wont (init struct)
5785 // You can control whether a value is required on the stack by passing
5786 // need_value_on_stack. The code *might* leave a value on the stack
5787 // so it must be popped manually
5789 // If we are dealing with a ValueType, we have a few
5790 // situations to deal with:
5792 // * The target is a ValueType, and we have been provided
5793 // the instance (this is easy, we are being assigned).
5795 // * The target of New is being passed as an argument,
5796 // to a boxing operation or a function that takes a
5799 // In this case, we need to create a temporary variable
5800 // that is the argument of New.
5802 // Returns whether a value is left on the stack
5804 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5806 bool is_value_type = TypeManager.IsValueType (type);
5807 ILGenerator ig = ec.ig;
5812 // Allow DoEmit() to be called multiple times.
5813 // We need to create a new LocalTemporary each time since
5814 // you can't share LocalBuilders among ILGeneators.
5815 if (!value_target_set)
5816 value_target = new LocalTemporary (type);
5818 ml = (IMemoryLocation) value_target;
5819 ml.AddressOf (ec, AddressOp.Store);
5823 Invocation.EmitArguments (ec, method, Arguments, false, null);
5827 ig.Emit (OpCodes.Initobj, type);
5829 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5830 if (need_value_on_stack){
5831 value_target.Emit (ec);
5836 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5841 public override void Emit (EmitContext ec)
5843 if (is_type_parameter)
5844 DoEmitTypeParameter (ec);
5849 public override void EmitStatement (EmitContext ec)
5851 if (is_type_parameter)
5852 throw new InvalidOperationException ();
5854 if (DoEmit (ec, false))
5855 ec.ig.Emit (OpCodes.Pop);
5858 public void AddressOf (EmitContext ec, AddressOp Mode)
5860 if (is_type_parameter)
5861 throw new InvalidOperationException ();
5863 if (!type.IsValueType){
5865 // We throw an exception. So far, I believe we only need to support
5867 // foreach (int j in new StructType ())
5870 throw new Exception ("AddressOf should not be used for classes");
5873 if (!value_target_set)
5874 value_target = new LocalTemporary (type);
5876 IMemoryLocation ml = (IMemoryLocation) value_target;
5877 ml.AddressOf (ec, AddressOp.Store);
5879 Invocation.EmitArguments (ec, method, Arguments, false, null);
5882 ec.ig.Emit (OpCodes.Initobj, type);
5884 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5886 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5891 /// 14.5.10.2: Represents an array creation expression.
5895 /// There are two possible scenarios here: one is an array creation
5896 /// expression that specifies the dimensions and optionally the
5897 /// initialization data and the other which does not need dimensions
5898 /// specified but where initialization data is mandatory.
5900 public class ArrayCreation : Expression {
5901 Expression requested_base_type;
5902 ArrayList initializers;
5905 // The list of Argument types.
5906 // This is used to construct the `newarray' or constructor signature
5908 ArrayList arguments;
5911 // Method used to create the array object.
5913 MethodBase new_method = null;
5915 Type array_element_type;
5916 Type underlying_type;
5917 bool is_one_dimensional = false;
5918 bool is_builtin_type = false;
5919 bool expect_initializers = false;
5920 int num_arguments = 0;
5924 ArrayList array_data;
5928 // The number of constants in array initializers
5929 int const_initializers_count;
5931 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5933 this.requested_base_type = requested_base_type;
5934 this.initializers = initializers;
5938 arguments = new ArrayList ();
5940 foreach (Expression e in exprs) {
5941 arguments.Add (new Argument (e, Argument.AType.Expression));
5946 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5948 this.requested_base_type = requested_base_type;
5949 this.initializers = initializers;
5953 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5955 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5957 //dimensions = tmp.Length - 1;
5958 expect_initializers = true;
5961 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5963 StringBuilder sb = new StringBuilder (rank);
5966 for (int i = 1; i < idx_count; i++)
5971 return new ComposedCast (base_type, sb.ToString (), loc);
5974 void Error_IncorrectArrayInitializer ()
5976 Error (178, "Invalid rank specifier: expected `,' or `]'");
5979 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5981 if (specified_dims) {
5982 Argument a = (Argument) arguments [idx];
5984 if (!a.Resolve (ec, loc))
5987 Constant c = a.Expr as Constant;
5989 c = c.ToType (TypeManager.int32_type, a.Expr.Location);
5993 Report.Error (150, a.Expr.Location, "A constant value is expected");
5997 int value = (int) c.GetValue ();
5999 if (value != probe.Count) {
6000 Error_IncorrectArrayInitializer ();
6004 bounds [idx] = value;
6007 int child_bounds = -1;
6008 for (int i = 0; i < probe.Count; ++i) {
6009 object o = probe [i];
6010 if (o is ArrayList) {
6011 ArrayList sub_probe = o as ArrayList;
6012 int current_bounds = sub_probe.Count;
6014 if (child_bounds == -1)
6015 child_bounds = current_bounds;
6017 else if (child_bounds != current_bounds){
6018 Error_IncorrectArrayInitializer ();
6021 if (idx + 1 >= dimensions){
6022 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
6026 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
6030 if (child_bounds != -1){
6031 Error_IncorrectArrayInitializer ();
6035 Expression tmp = (Expression) o;
6036 tmp = tmp.Resolve (ec);
6040 Expression conv = Convert.ImplicitConversionRequired (
6041 ec, tmp, underlying_type, loc);
6046 // Initializers with the default values can be ignored
6047 Constant c = tmp as Constant;
6049 if (c.IsDefaultInitializer (array_element_type)) {
6053 ++const_initializers_count;
6056 // Used to invalidate static initializer
6057 const_initializers_count = int.MinValue;
6060 array_data.Add (conv);
6067 public void UpdateIndices ()
6070 for (ArrayList probe = initializers; probe != null;) {
6071 if (probe.Count > 0 && probe [0] is ArrayList) {
6072 Expression e = new IntConstant (probe.Count, Location.Null);
6073 arguments.Add (new Argument (e, Argument.AType.Expression));
6075 bounds [i++] = probe.Count;
6077 probe = (ArrayList) probe [0];
6080 Expression e = new IntConstant (probe.Count, Location.Null);
6081 arguments.Add (new Argument (e, Argument.AType.Expression));
6083 bounds [i++] = probe.Count;
6090 bool ResolveInitializers (EmitContext ec)
6092 if (initializers == null) {
6093 return !expect_initializers;
6096 if (underlying_type == null)
6100 // We use this to store all the date values in the order in which we
6101 // will need to store them in the byte blob later
6103 array_data = new ArrayList ();
6104 bounds = new System.Collections.Specialized.HybridDictionary ();
6106 if (arguments != null)
6107 return CheckIndices (ec, initializers, 0, true);
6109 arguments = new ArrayList ();
6111 if (!CheckIndices (ec, initializers, 0, false))
6116 if (arguments.Count != dimensions) {
6117 Error_IncorrectArrayInitializer ();
6125 // Creates the type of the array
6127 bool LookupType (EmitContext ec)
6129 StringBuilder array_qualifier = new StringBuilder (rank);
6132 // `In the first form allocates an array instace of the type that results
6133 // from deleting each of the individual expression from the expression list'
6135 if (num_arguments > 0) {
6136 array_qualifier.Append ("[");
6137 for (int i = num_arguments-1; i > 0; i--)
6138 array_qualifier.Append (",");
6139 array_qualifier.Append ("]");
6145 TypeExpr array_type_expr;
6146 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
6147 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
6148 if (array_type_expr == null)
6151 type = array_type_expr.Type;
6152 underlying_type = TypeManager.GetElementType (type);
6153 dimensions = type.GetArrayRank ();
6158 public override Expression DoResolve (EmitContext ec)
6163 if (!LookupType (ec))
6166 array_element_type = TypeManager.GetElementType (type);
6167 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
6168 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
6173 // First step is to validate the initializers and fill
6174 // in any missing bits
6176 if (!ResolveInitializers (ec))
6180 if (arguments == null)
6183 arg_count = arguments.Count;
6184 foreach (Argument a in arguments){
6185 if (!a.Resolve (ec, loc))
6188 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
6189 if (real_arg == null)
6196 if (arg_count == 1) {
6197 is_one_dimensional = true;
6198 eclass = ExprClass.Value;
6202 is_builtin_type = TypeManager.IsBuiltinType (type);
6204 if (is_builtin_type) {
6207 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
6208 AllBindingFlags, loc);
6210 if (!(ml is MethodGroupExpr)) {
6211 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
6216 Error (-6, "New invocation: Can not find a constructor for " +
6217 "this argument list");
6221 new_method = Invocation.OverloadResolve (
6222 ec, (MethodGroupExpr) ml, arguments, false, loc);
6224 if (new_method == null) {
6225 Error (-6, "New invocation: Can not find a constructor for " +
6226 "this argument list");
6230 eclass = ExprClass.Value;
6233 ModuleBuilder mb = CodeGen.Module.Builder;
6234 ArrayList args = new ArrayList ();
6236 if (arguments != null) {
6237 for (int i = 0; i < arg_count; i++)
6238 args.Add (TypeManager.int32_type);
6241 Type [] arg_types = null;
6244 arg_types = new Type [args.Count];
6246 args.CopyTo (arg_types, 0);
6248 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6251 if (new_method == null) {
6252 Error (-6, "New invocation: Can not find a constructor for " +
6253 "this argument list");
6257 eclass = ExprClass.Value;
6262 byte [] MakeByteBlob ()
6267 int count = array_data.Count;
6269 if (underlying_type.IsEnum)
6270 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
6272 factor = GetTypeSize (underlying_type);
6274 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
6276 data = new byte [(count * factor + 4) & ~3];
6279 for (int i = 0; i < count; ++i) {
6280 object v = array_data [i];
6282 if (v is EnumConstant)
6283 v = ((EnumConstant) v).Child;
6285 if (v is Constant && !(v is StringConstant))
6286 v = ((Constant) v).GetValue ();
6292 if (underlying_type == TypeManager.int64_type){
6293 if (!(v is Expression)){
6294 long val = (long) v;
6296 for (int j = 0; j < factor; ++j) {
6297 data [idx + j] = (byte) (val & 0xFF);
6301 } else if (underlying_type == TypeManager.uint64_type){
6302 if (!(v is Expression)){
6303 ulong val = (ulong) v;
6305 for (int j = 0; j < factor; ++j) {
6306 data [idx + j] = (byte) (val & 0xFF);
6310 } else if (underlying_type == TypeManager.float_type) {
6311 if (!(v is Expression)){
6312 element = BitConverter.GetBytes ((float) v);
6314 for (int j = 0; j < factor; ++j)
6315 data [idx + j] = element [j];
6317 } else if (underlying_type == TypeManager.double_type) {
6318 if (!(v is Expression)){
6319 element = BitConverter.GetBytes ((double) v);
6321 for (int j = 0; j < factor; ++j)
6322 data [idx + j] = element [j];
6324 } else if (underlying_type == TypeManager.char_type){
6325 if (!(v is Expression)){
6326 int val = (int) ((char) v);
6328 data [idx] = (byte) (val & 0xff);
6329 data [idx+1] = (byte) (val >> 8);
6331 } else if (underlying_type == TypeManager.short_type){
6332 if (!(v is Expression)){
6333 int val = (int) ((short) v);
6335 data [idx] = (byte) (val & 0xff);
6336 data [idx+1] = (byte) (val >> 8);
6338 } else if (underlying_type == TypeManager.ushort_type){
6339 if (!(v is Expression)){
6340 int val = (int) ((ushort) v);
6342 data [idx] = (byte) (val & 0xff);
6343 data [idx+1] = (byte) (val >> 8);
6345 } else if (underlying_type == TypeManager.int32_type) {
6346 if (!(v is Expression)){
6349 data [idx] = (byte) (val & 0xff);
6350 data [idx+1] = (byte) ((val >> 8) & 0xff);
6351 data [idx+2] = (byte) ((val >> 16) & 0xff);
6352 data [idx+3] = (byte) (val >> 24);
6354 } else if (underlying_type == TypeManager.uint32_type) {
6355 if (!(v is Expression)){
6356 uint val = (uint) v;
6358 data [idx] = (byte) (val & 0xff);
6359 data [idx+1] = (byte) ((val >> 8) & 0xff);
6360 data [idx+2] = (byte) ((val >> 16) & 0xff);
6361 data [idx+3] = (byte) (val >> 24);
6363 } else if (underlying_type == TypeManager.sbyte_type) {
6364 if (!(v is Expression)){
6365 sbyte val = (sbyte) v;
6366 data [idx] = (byte) val;
6368 } else if (underlying_type == TypeManager.byte_type) {
6369 if (!(v is Expression)){
6370 byte val = (byte) v;
6371 data [idx] = (byte) val;
6373 } else if (underlying_type == TypeManager.bool_type) {
6374 if (!(v is Expression)){
6375 bool val = (bool) v;
6376 data [idx] = (byte) (val ? 1 : 0);
6378 } else if (underlying_type == TypeManager.decimal_type){
6379 if (!(v is Expression)){
6380 int [] bits = Decimal.GetBits ((decimal) v);
6383 // FIXME: For some reason, this doesn't work on the MS runtime.
6384 int [] nbits = new int [4];
6385 nbits [0] = bits [3];
6386 nbits [1] = bits [2];
6387 nbits [2] = bits [0];
6388 nbits [3] = bits [1];
6390 for (int j = 0; j < 4; j++){
6391 data [p++] = (byte) (nbits [j] & 0xff);
6392 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6393 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6394 data [p++] = (byte) (nbits [j] >> 24);
6398 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6407 // Emits the initializers for the array
6409 void EmitStaticInitializers (EmitContext ec)
6412 // First, the static data
6415 ILGenerator ig = ec.ig;
6417 byte [] data = MakeByteBlob ();
6419 fb = RootContext.MakeStaticData (data);
6421 ig.Emit (OpCodes.Dup);
6422 ig.Emit (OpCodes.Ldtoken, fb);
6423 ig.Emit (OpCodes.Call,
6424 TypeManager.void_initializearray_array_fieldhandle);
6428 // Emits pieces of the array that can not be computed at compile
6429 // time (variables and string locations).
6431 // This always expect the top value on the stack to be the array
6433 void EmitDynamicInitializers (EmitContext ec)
6435 ILGenerator ig = ec.ig;
6436 int dims = bounds.Count;
6437 int [] current_pos = new int [dims];
6439 MethodInfo set = null;
6442 Type [] args = new Type [dims + 1];
6444 for (int j = 0; j < dims; j++)
6445 args [j] = TypeManager.int32_type;
6446 args [dims] = array_element_type;
6448 set = CodeGen.Module.Builder.GetArrayMethod (
6450 CallingConventions.HasThis | CallingConventions.Standard,
6451 TypeManager.void_type, args);
6454 for (int i = 0; i < array_data.Count; i++){
6456 Expression e = (Expression)array_data [i];
6459 Type etype = e.Type;
6461 ig.Emit (OpCodes.Dup);
6463 for (int idx = 0; idx < dims; idx++)
6464 IntConstant.EmitInt (ig, current_pos [idx]);
6467 // If we are dealing with a struct, get the
6468 // address of it, so we can store it.
6471 TypeManager.IsValueType (etype) &&
6472 (!TypeManager.IsBuiltinOrEnum (etype) ||
6473 etype == TypeManager.decimal_type)) {
6478 // Let new know that we are providing
6479 // the address where to store the results
6481 n.DisableTemporaryValueType ();
6484 ig.Emit (OpCodes.Ldelema, etype);
6490 bool is_stobj, has_type_arg;
6491 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
6493 ig.Emit (OpCodes.Stobj, etype);
6494 else if (has_type_arg)
6495 ig.Emit (op, etype);
6499 ig.Emit (OpCodes.Call, set);
6506 for (int j = dims - 1; j >= 0; j--){
6508 if (current_pos [j] < (int) bounds [j])
6510 current_pos [j] = 0;
6515 void EmitArrayArguments (EmitContext ec)
6517 ILGenerator ig = ec.ig;
6519 foreach (Argument a in arguments) {
6520 Type atype = a.Type;
6523 if (atype == TypeManager.uint64_type)
6524 ig.Emit (OpCodes.Conv_Ovf_U4);
6525 else if (atype == TypeManager.int64_type)
6526 ig.Emit (OpCodes.Conv_Ovf_I4);
6530 public override void Emit (EmitContext ec)
6532 ILGenerator ig = ec.ig;
6534 EmitArrayArguments (ec);
6535 if (is_one_dimensional)
6536 ig.Emit (OpCodes.Newarr, array_element_type);
6538 if (is_builtin_type)
6539 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6541 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6544 if (initializers == null)
6547 // This is a treshold for static initializers
6548 // I tried to make more accurate but it seems to me that Array.Initialize is
6549 // always slower (managed -> unmanaged switch?)
6550 const int max_automatic_initializers = 200;
6552 if (const_initializers_count > max_automatic_initializers && TypeManager.IsPrimitiveType (array_element_type)) {
6553 EmitStaticInitializers (ec);
6557 EmitDynamicInitializers (ec);
6560 public override bool GetAttributableValue (Type valueType, out object value)
6562 if (!is_one_dimensional){
6563 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6564 return base.GetAttributableValue (null, out value);
6567 if (array_data == null) {
6568 Constant c = (Constant)((Argument)arguments [0]).Expr;
6569 if (c.IsDefaultValue) {
6570 value = Array.CreateInstance (array_element_type, 0);
6573 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6574 return base.GetAttributableValue (null, out value);
6577 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6578 object element_value;
6579 for (int i = 0; i < ret.Length; ++i)
6581 Expression e = (Expression)array_data [i];
6582 if (e == null) // Is null when initializer is optimized away
6583 e = (Expression)initializers [i];
6585 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6589 ret.SetValue (element_value, i);
6596 public sealed class CompilerGeneratedThis : This
6598 public static This Instance = new CompilerGeneratedThis ();
6600 private CompilerGeneratedThis ()
6601 : base (Location.Null)
6605 public override Expression DoResolve (EmitContext ec)
6607 eclass = ExprClass.Variable;
6608 type = ec.ContainerType;
6614 /// Represents the `this' construct
6616 public class This : Expression, IAssignMethod, IMemoryLocation, IVariable {
6619 VariableInfo variable_info;
6621 public This (Block block, Location loc)
6627 public This (Location loc)
6632 public VariableInfo VariableInfo {
6633 get { return variable_info; }
6636 public bool VerifyFixed ()
6638 return !TypeManager.IsValueType (Type);
6641 public bool ResolveBase (EmitContext ec)
6643 eclass = ExprClass.Variable;
6645 if (ec.TypeContainer.CurrentType != null)
6646 type = ec.TypeContainer.CurrentType;
6648 type = ec.ContainerType;
6651 Error (26, "Keyword `this' is not valid in a static property, static method, or static field initializer");
6655 if (block != null && block.Toplevel.ThisVariable != null)
6656 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6658 if (ec.CurrentAnonymousMethod != null)
6664 public override Expression DoResolve (EmitContext ec)
6666 if (!ResolveBase (ec))
6669 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) && !variable_info.IsAssigned (ec)) {
6670 Error (188, "The `this' object cannot be used before all of its fields are assigned to");
6671 variable_info.SetAssigned (ec);
6675 if (ec.IsFieldInitializer) {
6676 Error (27, "Keyword `this' is not available in the current context");
6683 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6685 if (!ResolveBase (ec))
6688 if (variable_info != null)
6689 variable_info.SetAssigned (ec);
6691 if (ec.TypeContainer is Class){
6692 Error (1604, "Cannot assign to 'this' because it is read-only");
6699 public void Emit (EmitContext ec, bool leave_copy)
6703 ec.ig.Emit (OpCodes.Dup);
6706 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
6708 ILGenerator ig = ec.ig;
6710 if (ec.TypeContainer is Struct){
6711 ec.EmitThis (false);
6714 LocalTemporary t = null;
6716 t = new LocalTemporary (type);
6717 ec.ig.Emit (OpCodes.Dup);
6721 ig.Emit (OpCodes.Stobj, type);
6726 throw new Exception ("how did you get here");
6730 public override void Emit (EmitContext ec)
6732 ILGenerator ig = ec.ig;
6734 ec.EmitThis (false);
6735 if (ec.TypeContainer is Struct)
6736 ig.Emit (OpCodes.Ldobj, type);
6739 public override int GetHashCode()
6741 return block.GetHashCode ();
6744 public override bool Equals (object obj)
6746 This t = obj as This;
6750 return block == t.block;
6753 public void AddressOf (EmitContext ec, AddressOp mode)
6758 // FIGURE OUT WHY LDARG_S does not work
6760 // consider: struct X { int val; int P { set { val = value; }}}
6762 // Yes, this looks very bad. Look at `NOTAS' for
6764 // ec.ig.Emit (OpCodes.Ldarga_S, (byte) 0);
6769 /// Represents the `__arglist' construct
6771 public class ArglistAccess : Expression
6773 public ArglistAccess (Location loc)
6778 public override Expression DoResolve (EmitContext ec)
6780 eclass = ExprClass.Variable;
6781 type = TypeManager.runtime_argument_handle_type;
6783 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6785 Error (190, "The __arglist construct is valid only within " +
6786 "a variable argument method");
6793 public override void Emit (EmitContext ec)
6795 ec.ig.Emit (OpCodes.Arglist);
6800 /// Represents the `__arglist (....)' construct
6802 public class Arglist : Expression
6804 public readonly Argument[] Arguments;
6806 public Arglist (Argument[] args, Location l)
6812 public Type[] ArgumentTypes {
6814 Type[] retval = new Type [Arguments.Length];
6815 for (int i = 0; i < Arguments.Length; i++)
6816 retval [i] = Arguments [i].Type;
6821 public override Expression DoResolve (EmitContext ec)
6823 eclass = ExprClass.Variable;
6824 type = TypeManager.runtime_argument_handle_type;
6826 foreach (Argument arg in Arguments) {
6827 if (!arg.Resolve (ec, loc))
6834 public override void Emit (EmitContext ec)
6836 foreach (Argument arg in Arguments)
6842 // This produces the value that renders an instance, used by the iterators code
6844 public class ProxyInstance : Expression, IMemoryLocation {
6845 public override Expression DoResolve (EmitContext ec)
6847 eclass = ExprClass.Variable;
6848 type = ec.ContainerType;
6852 public override void Emit (EmitContext ec)
6854 ec.ig.Emit (OpCodes.Ldarg_0);
6858 public void AddressOf (EmitContext ec, AddressOp mode)
6860 ec.ig.Emit (OpCodes.Ldarg_0);
6865 /// Implements the typeof operator
6867 public class TypeOf : Expression {
6868 readonly Expression QueriedType;
6869 protected Type typearg;
6871 public TypeOf (Expression queried_type, Location l)
6873 QueriedType = queried_type;
6877 public override Expression DoResolve (EmitContext ec)
6879 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6883 typearg = texpr.Type;
6885 if (typearg == TypeManager.void_type) {
6886 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6890 if (typearg.IsPointer && !ec.InUnsafe){
6895 type = TypeManager.type_type;
6896 // Even though what is returned is a type object, it's treated as a value by the compiler.
6897 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6898 eclass = ExprClass.Value;
6902 public override void Emit (EmitContext ec)
6904 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6905 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6908 public override bool GetAttributableValue (Type valueType, out object value)
6910 if (valueType == TypeManager.object_type) {
6911 value = (object)typearg;
6918 public Type TypeArgument
6928 /// Implements the `typeof (void)' operator
6930 public class TypeOfVoid : TypeOf {
6931 public TypeOfVoid (Location l) : base (null, l)
6936 public override Expression DoResolve (EmitContext ec)
6938 type = TypeManager.type_type;
6939 typearg = TypeManager.void_type;
6940 // See description in TypeOf.
6941 eclass = ExprClass.Value;
6947 /// Implements the sizeof expression
6949 public class SizeOf : Expression {
6950 public Expression QueriedType;
6953 public SizeOf (Expression queried_type, Location l)
6955 this.QueriedType = queried_type;
6959 public override Expression DoResolve (EmitContext ec)
6961 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6965 if (texpr is TypeParameterExpr){
6966 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
6970 type_queried = texpr.Type;
6972 int size_of = GetTypeSize (type_queried);
6974 return new IntConstant (size_of, loc);
6978 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)",
6979 TypeManager.CSharpName (type_queried));
6983 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
6987 type = TypeManager.int32_type;
6988 eclass = ExprClass.Value;
6992 public override void Emit (EmitContext ec)
6994 int size = GetTypeSize (type_queried);
6997 ec.ig.Emit (OpCodes.Sizeof, type_queried);
6999 IntConstant.EmitInt (ec.ig, size);
7004 /// Implements the qualified-alias-member (::) expression.
7006 public class QualifiedAliasMember : Expression
7008 string alias, identifier;
7010 public QualifiedAliasMember (string alias, string identifier, Location l)
7013 this.identifier = identifier;
7017 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7019 if (alias == "global")
7020 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
7022 int errors = Report.Errors;
7023 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7025 if (errors == Report.Errors)
7026 Report.Error (432, loc, "Alias `{0}' not found", alias);
7029 if (fne.eclass != ExprClass.Namespace) {
7031 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7034 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
7037 public override Expression DoResolve (EmitContext ec)
7039 FullNamedExpression fne;
7040 if (alias == "global") {
7041 fne = RootNamespace.Global;
7043 int errors = Report.Errors;
7044 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7046 if (errors == Report.Errors)
7047 Report.Error (432, loc, "Alias `{0}' not found", alias);
7052 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
7056 if (!(retval is FullNamedExpression)) {
7057 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
7061 // We defer this check till the end to match the behaviour of CSC
7062 if (fne.eclass != ExprClass.Namespace) {
7063 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7069 public override void Emit (EmitContext ec)
7071 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
7075 public override string ToString ()
7077 return alias + "::" + identifier;
7080 public override string GetSignatureForError ()
7087 /// Implements the member access expression
7089 public class MemberAccess : Expression {
7090 public readonly string Identifier;
7094 public MemberAccess (Expression expr, string id)
7095 : this (expr, id, expr.Location)
7099 public MemberAccess (Expression expr, string identifier, Location loc)
7102 Identifier = identifier;
7106 public MemberAccess (Expression expr, string id, TypeArguments args)
7112 public Expression Expr {
7113 get { return expr; }
7116 // TODO: this method has very poor performace for Enum fields and
7117 // probably for other constants as well
7118 Expression DoResolve (EmitContext ec, Expression right_side)
7121 throw new Exception ();
7124 // Resolve the expression with flow analysis turned off, we'll do the definite
7125 // assignment checks later. This is because we don't know yet what the expression
7126 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7127 // definite assignment check on the actual field and not on the whole struct.
7130 SimpleName original = expr as SimpleName;
7131 Expression new_expr = expr.Resolve (ec,
7132 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7133 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7135 if (new_expr == null)
7138 if (new_expr is Namespace) {
7139 Namespace ns = (Namespace) new_expr;
7140 string lookup_id = MemberName.MakeName (Identifier, args);
7141 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, lookup_id, loc);
7142 if ((retval != null) && (args != null))
7143 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
7145 ns.Error_NamespaceDoesNotExist (loc, Identifier);
7149 Type expr_type = new_expr.Type;
7150 if (expr_type.IsPointer){
7151 Error (23, "The `.' operator can not be applied to pointer operands (" +
7152 TypeManager.CSharpName (expr_type) + ")");
7154 } else if (expr_type == TypeManager.void_type) {
7155 Error (23, "The `.' operator can not be applied to operands of type 'void'");
7157 } else if (expr_type == TypeManager.anonymous_method_type){
7158 Error (23, "The `.' operator can not be applied to anonymous methods");
7162 Expression member_lookup;
7163 member_lookup = MemberLookup (
7164 ec.ContainerType, expr_type, expr_type, Identifier, loc);
7165 if ((member_lookup == null) && (args != null)) {
7166 string lookup_id = MemberName.MakeName (Identifier, args);
7167 member_lookup = MemberLookup (
7168 ec.ContainerType, expr_type, expr_type, lookup_id, loc);
7170 if (member_lookup == null) {
7171 MemberLookupFailed (
7172 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
7176 if (member_lookup is TypeExpr) {
7177 if (!(new_expr is TypeExpr) &&
7178 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
7179 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7180 Identifier, member_lookup.GetSignatureForError ());
7184 ConstructedType ct = new_expr as ConstructedType;
7187 // When looking up a nested type in a generic instance
7188 // via reflection, we always get a generic type definition
7189 // and not a generic instance - so we have to do this here.
7191 // See gtest-172-lib.cs and gtest-172.cs for an example.
7193 ct = new ConstructedType (
7194 member_lookup.Type, ct.TypeArguments, loc);
7196 return ct.ResolveAsTypeStep (ec, false);
7199 return member_lookup;
7202 MemberExpr me = (MemberExpr) member_lookup;
7203 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
7204 if (member_lookup == null)
7208 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
7210 throw new InternalErrorException ();
7212 return mg.ResolveGeneric (ec, args);
7215 if (original != null && !TypeManager.IsValueType (expr_type)) {
7216 me = member_lookup as MemberExpr;
7217 if (me != null && me.IsInstance) {
7218 LocalVariableReference var = new_expr as LocalVariableReference;
7219 if (var != null && !var.VerifyAssigned (ec))
7224 // The following DoResolve/DoResolveLValue will do the definite assignment
7227 if (right_side != null)
7228 return member_lookup.DoResolveLValue (ec, right_side);
7230 return member_lookup.DoResolve (ec);
7233 public override Expression DoResolve (EmitContext ec)
7235 return DoResolve (ec, null);
7238 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7240 return DoResolve (ec, right_side);
7243 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7245 return ResolveNamespaceOrType (ec, silent);
7248 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7250 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
7252 if (new_expr == null)
7255 string lookup_id = MemberName.MakeName (Identifier, args);
7257 if (new_expr is Namespace) {
7258 Namespace ns = (Namespace) new_expr;
7259 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, lookup_id, loc);
7260 if ((retval != null) && (args != null))
7261 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
7262 if (!silent && retval == null)
7263 ns.Error_NamespaceDoesNotExist (loc, Identifier);
7267 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
7268 if (tnew_expr == null)
7271 Type expr_type = tnew_expr.Type;
7273 if (expr_type.IsPointer){
7274 Error (23, "The `.' operator can not be applied to pointer operands (" +
7275 TypeManager.CSharpName (expr_type) + ")");
7279 Expression member_lookup = MemberLookup (
7280 rc.DeclContainer.TypeBuilder, expr_type, expr_type, lookup_id,
7281 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7282 if (member_lookup == null) {
7283 int errors = Report.Errors;
7284 MemberLookupFailed (rc.DeclContainer.TypeBuilder, expr_type, expr_type, lookup_id, null, false, loc);
7286 if (!silent && errors == Report.Errors) {
7287 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7288 Identifier, new_expr.GetSignatureForError ());
7293 if (!(member_lookup is TypeExpr)) {
7294 new_expr.Error_UnexpectedKind (rc.DeclContainer, "type", loc);
7298 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
7302 TypeArguments the_args = args;
7303 if (TypeManager.HasGenericArguments (expr_type)) {
7304 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
7306 TypeArguments new_args = new TypeArguments (loc);
7307 foreach (Type decl in decl_args)
7308 new_args.Add (new TypeExpression (decl, loc));
7311 new_args.Add (args);
7313 the_args = new_args;
7316 if (the_args != null) {
7317 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
7318 return ctype.ResolveAsTypeStep (rc, false);
7324 public override void Emit (EmitContext ec)
7326 throw new Exception ("Should not happen");
7329 public override string ToString ()
7331 return expr + "." + MemberName.MakeName (Identifier, args);
7334 public override string GetSignatureForError ()
7336 return expr.GetSignatureForError () + "." + Identifier;
7341 /// Implements checked expressions
7343 public class CheckedExpr : Expression {
7345 public Expression Expr;
7347 public CheckedExpr (Expression e, Location l)
7353 public override Expression DoResolve (EmitContext ec)
7355 using (ec.WithCheckState (true, true))
7356 Expr = Expr.Resolve (ec);
7361 if (Expr is Constant)
7364 eclass = Expr.eclass;
7369 public override void Emit (EmitContext ec)
7371 using (ec.WithCheckState (true, true))
7375 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7377 using (ec.WithCheckState (true, true))
7378 Expr.EmitBranchable (ec, target, onTrue);
7383 /// Implements the unchecked expression
7385 public class UnCheckedExpr : Expression {
7387 public Expression Expr;
7389 public UnCheckedExpr (Expression e, Location l)
7395 public override Expression DoResolve (EmitContext ec)
7397 using (ec.WithCheckState (false, false))
7398 Expr = Expr.Resolve (ec);
7403 if (Expr is Constant)
7406 eclass = Expr.eclass;
7411 public override void Emit (EmitContext ec)
7413 using (ec.WithCheckState (false, false))
7417 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7419 using (ec.WithCheckState (false, false))
7420 Expr.EmitBranchable (ec, target, onTrue);
7425 /// An Element Access expression.
7427 /// During semantic analysis these are transformed into
7428 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7430 public class ElementAccess : Expression {
7431 public ArrayList Arguments;
7432 public Expression Expr;
7434 public ElementAccess (Expression e, ArrayList e_list)
7443 Arguments = new ArrayList ();
7444 foreach (Expression tmp in e_list)
7445 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7449 bool CommonResolve (EmitContext ec)
7451 Expr = Expr.Resolve (ec);
7456 if (Arguments == null)
7459 foreach (Argument a in Arguments){
7460 if (!a.Resolve (ec, loc))
7467 Expression MakePointerAccess (EmitContext ec, Type t)
7469 if (t == TypeManager.void_ptr_type){
7470 Error (242, "The array index operation is not valid on void pointers");
7473 if (Arguments.Count != 1){
7474 Error (196, "A pointer must be indexed by only one value");
7479 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7482 return new Indirection (p, loc).Resolve (ec);
7485 public override Expression DoResolve (EmitContext ec)
7487 if (!CommonResolve (ec))
7491 // We perform some simple tests, and then to "split" the emit and store
7492 // code we create an instance of a different class, and return that.
7494 // I am experimenting with this pattern.
7498 if (t == TypeManager.array_type){
7499 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7504 return (new ArrayAccess (this, loc)).Resolve (ec);
7506 return MakePointerAccess (ec, Expr.Type);
7508 FieldExpr fe = Expr as FieldExpr;
7510 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7512 return MakePointerAccess (ec, ff.ElementType);
7515 return (new IndexerAccess (this, loc)).Resolve (ec);
7518 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7520 if (!CommonResolve (ec))
7525 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7528 return MakePointerAccess (ec, Expr.Type);
7530 FieldExpr fe = Expr as FieldExpr;
7532 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7534 if (!(fe.InstanceExpression is LocalVariableReference) &&
7535 !(fe.InstanceExpression is This)) {
7536 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7539 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7540 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7543 return MakePointerAccess (ec, ff.ElementType);
7546 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7549 public override void Emit (EmitContext ec)
7551 throw new Exception ("Should never be reached");
7556 /// Implements array access
7558 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7560 // Points to our "data" repository
7564 LocalTemporary temp;
7567 public ArrayAccess (ElementAccess ea_data, Location l)
7570 eclass = ExprClass.Variable;
7574 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7576 return DoResolve (ec);
7579 public override Expression DoResolve (EmitContext ec)
7582 ExprClass eclass = ea.Expr.eclass;
7584 // As long as the type is valid
7585 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7586 eclass == ExprClass.Value)) {
7587 ea.Expr.Error_UnexpectedKind ("variable or value");
7592 Type t = ea.Expr.Type;
7593 if (t.GetArrayRank () != ea.Arguments.Count){
7594 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7595 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7599 type = TypeManager.GetElementType (t);
7600 if (type.IsPointer && !ec.InUnsafe){
7601 UnsafeError (ea.Location);
7605 foreach (Argument a in ea.Arguments){
7606 Type argtype = a.Type;
7608 if (argtype == TypeManager.int32_type ||
7609 argtype == TypeManager.uint32_type ||
7610 argtype == TypeManager.int64_type ||
7611 argtype == TypeManager.uint64_type) {
7612 Constant c = a.Expr as Constant;
7613 if (c != null && c.IsNegative) {
7614 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7620 // Mhm. This is strage, because the Argument.Type is not the same as
7621 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7623 // Wonder if I will run into trouble for this.
7625 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7630 eclass = ExprClass.Variable;
7636 /// Emits the right opcode to load an object of Type `t'
7637 /// from an array of T
7639 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7641 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7642 ig.Emit (OpCodes.Ldelem_U1);
7643 else if (type == TypeManager.sbyte_type)
7644 ig.Emit (OpCodes.Ldelem_I1);
7645 else if (type == TypeManager.short_type)
7646 ig.Emit (OpCodes.Ldelem_I2);
7647 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7648 ig.Emit (OpCodes.Ldelem_U2);
7649 else if (type == TypeManager.int32_type)
7650 ig.Emit (OpCodes.Ldelem_I4);
7651 else if (type == TypeManager.uint32_type)
7652 ig.Emit (OpCodes.Ldelem_U4);
7653 else if (type == TypeManager.uint64_type)
7654 ig.Emit (OpCodes.Ldelem_I8);
7655 else if (type == TypeManager.int64_type)
7656 ig.Emit (OpCodes.Ldelem_I8);
7657 else if (type == TypeManager.float_type)
7658 ig.Emit (OpCodes.Ldelem_R4);
7659 else if (type == TypeManager.double_type)
7660 ig.Emit (OpCodes.Ldelem_R8);
7661 else if (type == TypeManager.intptr_type)
7662 ig.Emit (OpCodes.Ldelem_I);
7663 else if (TypeManager.IsEnumType (type)){
7664 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7665 } else if (type.IsValueType){
7666 ig.Emit (OpCodes.Ldelema, type);
7667 ig.Emit (OpCodes.Ldobj, type);
7668 } else if (type.IsGenericParameter)
7670 ig.Emit (OpCodes.Ldelem, type);
7672 ig.Emit (OpCodes.Ldelem_Any, type);
7674 else if (type.IsPointer)
7675 ig.Emit (OpCodes.Ldelem_I);
7677 ig.Emit (OpCodes.Ldelem_Ref);
7681 /// Returns the right opcode to store an object of Type `t'
7682 /// from an array of T.
7684 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7686 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7687 has_type_arg = false; is_stobj = false;
7688 t = TypeManager.TypeToCoreType (t);
7689 if (TypeManager.IsEnumType (t))
7690 t = TypeManager.EnumToUnderlying (t);
7691 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7692 t == TypeManager.bool_type)
7693 return OpCodes.Stelem_I1;
7694 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7695 t == TypeManager.char_type)
7696 return OpCodes.Stelem_I2;
7697 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7698 return OpCodes.Stelem_I4;
7699 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7700 return OpCodes.Stelem_I8;
7701 else if (t == TypeManager.float_type)
7702 return OpCodes.Stelem_R4;
7703 else if (t == TypeManager.double_type)
7704 return OpCodes.Stelem_R8;
7705 else if (t == TypeManager.intptr_type) {
7706 has_type_arg = true;
7708 return OpCodes.Stobj;
7709 } else if (t.IsValueType) {
7710 has_type_arg = true;
7712 return OpCodes.Stobj;
7713 } else if (t.IsGenericParameter) {
7714 has_type_arg = true;
7716 return OpCodes.Stelem;
7718 return OpCodes.Stelem_Any;
7721 } else if (t.IsPointer)
7722 return OpCodes.Stelem_I;
7724 return OpCodes.Stelem_Ref;
7727 MethodInfo FetchGetMethod ()
7729 ModuleBuilder mb = CodeGen.Module.Builder;
7730 int arg_count = ea.Arguments.Count;
7731 Type [] args = new Type [arg_count];
7734 for (int i = 0; i < arg_count; i++){
7735 //args [i++] = a.Type;
7736 args [i] = TypeManager.int32_type;
7739 get = mb.GetArrayMethod (
7740 ea.Expr.Type, "Get",
7741 CallingConventions.HasThis |
7742 CallingConventions.Standard,
7748 MethodInfo FetchAddressMethod ()
7750 ModuleBuilder mb = CodeGen.Module.Builder;
7751 int arg_count = ea.Arguments.Count;
7752 Type [] args = new Type [arg_count];
7756 ret_type = TypeManager.GetReferenceType (type);
7758 for (int i = 0; i < arg_count; i++){
7759 //args [i++] = a.Type;
7760 args [i] = TypeManager.int32_type;
7763 address = mb.GetArrayMethod (
7764 ea.Expr.Type, "Address",
7765 CallingConventions.HasThis |
7766 CallingConventions.Standard,
7773 // Load the array arguments into the stack.
7775 // If we have been requested to cache the values (cached_locations array
7776 // initialized), then load the arguments the first time and store them
7777 // in locals. otherwise load from local variables.
7779 void LoadArrayAndArguments (EmitContext ec)
7781 ILGenerator ig = ec.ig;
7784 foreach (Argument a in ea.Arguments){
7785 Type argtype = a.Expr.Type;
7789 if (argtype == TypeManager.int64_type)
7790 ig.Emit (OpCodes.Conv_Ovf_I);
7791 else if (argtype == TypeManager.uint64_type)
7792 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7796 public void Emit (EmitContext ec, bool leave_copy)
7798 int rank = ea.Expr.Type.GetArrayRank ();
7799 ILGenerator ig = ec.ig;
7802 LoadArrayAndArguments (ec);
7805 EmitLoadOpcode (ig, type);
7809 method = FetchGetMethod ();
7810 ig.Emit (OpCodes.Call, method);
7813 LoadFromPtr (ec.ig, this.type);
7816 ec.ig.Emit (OpCodes.Dup);
7817 temp = new LocalTemporary (this.type);
7822 public override void Emit (EmitContext ec)
7827 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7829 int rank = ea.Expr.Type.GetArrayRank ();
7830 ILGenerator ig = ec.ig;
7831 Type t = source.Type;
7832 prepared = prepare_for_load;
7834 if (prepare_for_load) {
7835 AddressOf (ec, AddressOp.LoadStore);
7836 ec.ig.Emit (OpCodes.Dup);
7839 ec.ig.Emit (OpCodes.Dup);
7840 temp = new LocalTemporary (this.type);
7843 StoreFromPtr (ec.ig, t);
7851 LoadArrayAndArguments (ec);
7854 bool is_stobj, has_type_arg;
7855 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7858 // The stobj opcode used by value types will need
7859 // an address on the stack, not really an array/array
7863 ig.Emit (OpCodes.Ldelema, t);
7867 ec.ig.Emit (OpCodes.Dup);
7868 temp = new LocalTemporary (this.type);
7873 ig.Emit (OpCodes.Stobj, t);
7874 else if (has_type_arg)
7879 ModuleBuilder mb = CodeGen.Module.Builder;
7880 int arg_count = ea.Arguments.Count;
7881 Type [] args = new Type [arg_count + 1];
7886 ec.ig.Emit (OpCodes.Dup);
7887 temp = new LocalTemporary (this.type);
7891 for (int i = 0; i < arg_count; i++){
7892 //args [i++] = a.Type;
7893 args [i] = TypeManager.int32_type;
7896 args [arg_count] = type;
7898 set = mb.GetArrayMethod (
7899 ea.Expr.Type, "Set",
7900 CallingConventions.HasThis |
7901 CallingConventions.Standard,
7902 TypeManager.void_type, args);
7904 ig.Emit (OpCodes.Call, set);
7911 public void AddressOf (EmitContext ec, AddressOp mode)
7913 int rank = ea.Expr.Type.GetArrayRank ();
7914 ILGenerator ig = ec.ig;
7916 LoadArrayAndArguments (ec);
7919 ig.Emit (OpCodes.Ldelema, type);
7921 MethodInfo address = FetchAddressMethod ();
7922 ig.Emit (OpCodes.Call, address);
7926 public void EmitGetLength (EmitContext ec, int dim)
7928 int rank = ea.Expr.Type.GetArrayRank ();
7929 ILGenerator ig = ec.ig;
7933 ig.Emit (OpCodes.Ldlen);
7934 ig.Emit (OpCodes.Conv_I4);
7936 IntLiteral.EmitInt (ig, dim);
7937 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7943 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7944 public readonly ArrayList Properties;
7945 static Indexers empty;
7947 public struct Indexer {
7948 public readonly PropertyInfo PropertyInfo;
7949 public readonly MethodInfo Getter, Setter;
7951 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7953 this.PropertyInfo = property_info;
7961 empty = new Indexers (null);
7964 Indexers (ArrayList array)
7969 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
7974 foreach (PropertyInfo property in mi){
7975 MethodInfo get, set;
7977 get = property.GetGetMethod (true);
7978 set = property.GetSetMethod (true);
7979 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
7981 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
7983 if (get != null || set != null) {
7985 ix = new Indexers (new ArrayList ());
7986 ix.Properties.Add (new Indexer (property, get, set));
7991 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
7993 string p_name = TypeManager.IndexerPropertyName (lookup_type);
7995 return TypeManager.MemberLookup (
7996 caller_type, caller_type, lookup_type, MemberTypes.Property,
7997 BindingFlags.Public | BindingFlags.Instance |
7998 BindingFlags.DeclaredOnly, p_name, null);
8001 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
8003 Indexers ix = empty;
8005 if (lookup_type.IsGenericParameter) {
8006 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
8010 if (gc.HasClassConstraint)
8011 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
8013 Type[] ifaces = gc.InterfaceConstraints;
8014 foreach (Type itype in ifaces)
8015 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8020 Type copy = lookup_type;
8021 while (copy != TypeManager.object_type && copy != null){
8022 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
8023 copy = copy.BaseType;
8026 if (lookup_type.IsInterface) {
8027 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
8028 if (ifaces != null) {
8029 foreach (Type itype in ifaces)
8030 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8039 /// Expressions that represent an indexer call.
8041 public class IndexerAccess : Expression, IAssignMethod {
8043 // Points to our "data" repository
8045 MethodInfo get, set;
8046 ArrayList set_arguments;
8047 bool is_base_indexer;
8049 protected Type indexer_type;
8050 protected Type current_type;
8051 protected Expression instance_expr;
8052 protected ArrayList arguments;
8054 public IndexerAccess (ElementAccess ea, Location loc)
8055 : this (ea.Expr, false, loc)
8057 this.arguments = ea.Arguments;
8060 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
8063 this.instance_expr = instance_expr;
8064 this.is_base_indexer = is_base_indexer;
8065 this.eclass = ExprClass.Value;
8069 protected virtual bool CommonResolve (EmitContext ec)
8071 indexer_type = instance_expr.Type;
8072 current_type = ec.ContainerType;
8077 public override Expression DoResolve (EmitContext ec)
8079 ArrayList AllGetters = new ArrayList();
8080 if (!CommonResolve (ec))
8084 // Step 1: Query for all `Item' *properties*. Notice
8085 // that the actual methods are pointed from here.
8087 // This is a group of properties, piles of them.
8089 bool found_any = false, found_any_getters = false;
8090 Type lookup_type = indexer_type;
8092 Indexers ilist = Indexers.GetIndexersForType (current_type, lookup_type);
8093 if (ilist.Properties != null) {
8095 foreach (Indexers.Indexer ix in ilist.Properties) {
8096 if (ix.Getter != null)
8097 AllGetters.Add (ix.Getter);
8101 if (AllGetters.Count > 0) {
8102 found_any_getters = true;
8103 get = (MethodInfo) Invocation.OverloadResolve (
8104 ec, new MethodGroupExpr (AllGetters, loc),
8105 arguments, false, loc);
8109 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8110 TypeManager.CSharpName (indexer_type));
8114 if (!found_any_getters) {
8115 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
8121 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8126 // Only base will allow this invocation to happen.
8128 if (get.IsAbstract && this is BaseIndexerAccess){
8129 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
8133 type = get.ReturnType;
8134 if (type.IsPointer && !ec.InUnsafe){
8139 instance_expr.CheckMarshalByRefAccess ();
8141 eclass = ExprClass.IndexerAccess;
8145 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8147 if (right_side == EmptyExpression.OutAccess) {
8148 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
8149 GetSignatureForError ());
8153 // if the indexer returns a value type, and we try to set a field in it
8154 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
8155 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
8156 GetSignatureForError ());
8160 ArrayList AllSetters = new ArrayList();
8161 if (!CommonResolve (ec))
8164 bool found_any = false, found_any_setters = false;
8166 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8167 if (ilist.Properties != null) {
8169 foreach (Indexers.Indexer ix in ilist.Properties) {
8170 if (ix.Setter != null)
8171 AllSetters.Add (ix.Setter);
8174 if (AllSetters.Count > 0) {
8175 found_any_setters = true;
8176 set_arguments = (ArrayList) arguments.Clone ();
8177 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
8178 set = (MethodInfo) Invocation.OverloadResolve (
8179 ec, new MethodGroupExpr (AllSetters, loc),
8180 set_arguments, false, loc);
8184 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8185 TypeManager.CSharpName (indexer_type));
8189 if (!found_any_setters) {
8190 Error (154, "indexer can not be used in this context, because " +
8191 "it lacks a `set' accessor");
8196 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8201 // Only base will allow this invocation to happen.
8203 if (set.IsAbstract && this is BaseIndexerAccess){
8204 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
8209 // Now look for the actual match in the list of indexers to set our "return" type
8211 type = TypeManager.void_type; // default value
8212 foreach (Indexers.Indexer ix in ilist.Properties){
8213 if (ix.Setter == set){
8214 type = ix.PropertyInfo.PropertyType;
8219 instance_expr.CheckMarshalByRefAccess ();
8221 eclass = ExprClass.IndexerAccess;
8225 bool prepared = false;
8226 LocalTemporary temp;
8228 public void Emit (EmitContext ec, bool leave_copy)
8230 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
8232 ec.ig.Emit (OpCodes.Dup);
8233 temp = new LocalTemporary (Type);
8239 // source is ignored, because we already have a copy of it from the
8240 // LValue resolution and we have already constructed a pre-cached
8241 // version of the arguments (ea.set_arguments);
8243 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8245 prepared = prepare_for_load;
8246 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
8251 ec.ig.Emit (OpCodes.Dup);
8252 temp = new LocalTemporary (Type);
8255 } else if (leave_copy) {
8256 temp = new LocalTemporary (Type);
8262 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
8269 public override void Emit (EmitContext ec)
8274 public override string GetSignatureForError ()
8276 // FIXME: print the argument list of the indexer
8277 return instance_expr.GetSignatureForError () + ".this[...]";
8282 /// The base operator for method names
8284 public class BaseAccess : Expression {
8285 public readonly string Identifier;
8288 public BaseAccess (string member, TypeArguments args, Location l)
8290 this.Identifier = member;
8295 public override Expression DoResolve (EmitContext ec)
8297 Expression c = CommonResolve (ec);
8303 // MethodGroups use this opportunity to flag an error on lacking ()
8305 if (!(c is MethodGroupExpr))
8306 return c.Resolve (ec);
8310 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8312 Expression c = CommonResolve (ec);
8318 // MethodGroups use this opportunity to flag an error on lacking ()
8320 if (! (c is MethodGroupExpr))
8321 return c.DoResolveLValue (ec, right_side);
8326 Expression CommonResolve (EmitContext ec)
8328 Expression member_lookup;
8329 Type current_type = ec.ContainerType;
8330 Type base_type = current_type.BaseType;
8333 Error (1511, "Keyword `base' is not available in a static method");
8337 if (ec.IsFieldInitializer){
8338 Error (1512, "Keyword `base' is not available in the current context");
8342 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8343 AllMemberTypes, AllBindingFlags, loc);
8344 if (member_lookup == null) {
8345 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8352 left = new TypeExpression (base_type, loc);
8354 left = ec.GetThis (loc);
8356 MemberExpr me = (MemberExpr) member_lookup;
8358 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8360 if (e is PropertyExpr) {
8361 PropertyExpr pe = (PropertyExpr) e;
8366 MethodGroupExpr mg = e as MethodGroupExpr;
8372 return mg.ResolveGeneric (ec, args);
8374 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8382 public override void Emit (EmitContext ec)
8384 throw new Exception ("Should never be called");
8389 /// The base indexer operator
8391 public class BaseIndexerAccess : IndexerAccess {
8392 public BaseIndexerAccess (ArrayList args, Location loc)
8393 : base (null, true, loc)
8395 arguments = new ArrayList ();
8396 foreach (Expression tmp in args)
8397 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8400 protected override bool CommonResolve (EmitContext ec)
8402 instance_expr = ec.GetThis (loc);
8404 current_type = ec.ContainerType.BaseType;
8405 indexer_type = current_type;
8407 foreach (Argument a in arguments){
8408 if (!a.Resolve (ec, loc))
8417 /// This class exists solely to pass the Type around and to be a dummy
8418 /// that can be passed to the conversion functions (this is used by
8419 /// foreach implementation to typecast the object return value from
8420 /// get_Current into the proper type. All code has been generated and
8421 /// we only care about the side effect conversions to be performed
8423 /// This is also now used as a placeholder where a no-action expression
8424 /// is needed (the `New' class).
8426 public class EmptyExpression : Expression {
8427 public static readonly EmptyExpression Null = new EmptyExpression ();
8429 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8430 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8431 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8433 static EmptyExpression temp = new EmptyExpression ();
8434 public static EmptyExpression Grab ()
8437 throw new InternalErrorException ("Nested Grab");
8438 EmptyExpression retval = temp;
8443 public static void Release (EmptyExpression e)
8446 throw new InternalErrorException ("Already released");
8450 // TODO: should be protected
8451 public EmptyExpression ()
8453 type = TypeManager.object_type;
8454 eclass = ExprClass.Value;
8455 loc = Location.Null;
8458 public EmptyExpression (Type t)
8461 eclass = ExprClass.Value;
8462 loc = Location.Null;
8465 public override Expression DoResolve (EmitContext ec)
8470 public override void Emit (EmitContext ec)
8472 // nothing, as we only exist to not do anything.
8476 // This is just because we might want to reuse this bad boy
8477 // instead of creating gazillions of EmptyExpressions.
8478 // (CanImplicitConversion uses it)
8480 public void SetType (Type t)
8486 public class UserCast : Expression {
8490 public UserCast (MethodInfo method, Expression source, Location l)
8492 this.method = method;
8493 this.source = source;
8494 type = method.ReturnType;
8495 eclass = ExprClass.Value;
8499 public Expression Source {
8505 public override Expression DoResolve (EmitContext ec)
8508 // We are born fully resolved
8513 public override void Emit (EmitContext ec)
8515 ILGenerator ig = ec.ig;
8519 if (method is MethodInfo)
8520 ig.Emit (OpCodes.Call, (MethodInfo) method);
8522 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8528 // This class is used to "construct" the type during a typecast
8529 // operation. Since the Type.GetType class in .NET can parse
8530 // the type specification, we just use this to construct the type
8531 // one bit at a time.
8533 public class ComposedCast : TypeExpr {
8537 public ComposedCast (Expression left, string dim)
8538 : this (left, dim, left.Location)
8542 public ComposedCast (Expression left, string dim, Location l)
8549 public Expression RemoveNullable ()
8551 if (dim.EndsWith ("?")) {
8552 dim = dim.Substring (0, dim.Length - 1);
8560 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8562 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8566 Type ltype = lexpr.Type;
8567 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8568 Report.Error (1547, Location,
8569 "Keyword 'void' cannot be used in this context");
8573 if ((dim.Length > 0) && (dim [0] == '?')) {
8574 TypeExpr nullable = new NullableType (left, loc);
8576 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8577 return nullable.ResolveAsTypeTerminal (ec, false);
8580 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc)) {
8585 type = TypeManager.GetConstructedType (ltype, dim);
8590 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8593 if (type.IsPointer && !ec.IsInUnsafeScope){
8598 if (type.IsArray && (type.GetElementType () == TypeManager.arg_iterator_type ||
8599 type.GetElementType () == TypeManager.typed_reference_type)) {
8600 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (type.GetElementType ()));
8604 eclass = ExprClass.Type;
8608 public override string Name {
8609 get { return left + dim; }
8612 public override string FullName {
8613 get { return type.FullName; }
8616 public override string GetSignatureForError ()
8618 return left.GetSignatureForError () + dim;
8622 public class FixedBufferPtr : Expression {
8625 public FixedBufferPtr (Expression array, Type array_type, Location l)
8630 type = TypeManager.GetPointerType (array_type);
8631 eclass = ExprClass.Value;
8634 public override void Emit(EmitContext ec)
8639 public override Expression DoResolve (EmitContext ec)
8642 // We are born fully resolved
8650 // This class is used to represent the address of an array, used
8651 // only by the Fixed statement, this generates "&a [0]" construct
8652 // for fixed (char *pa = a)
8654 public class ArrayPtr : FixedBufferPtr {
8657 public ArrayPtr (Expression array, Type array_type, Location l):
8658 base (array, array_type, l)
8660 this.array_type = array_type;
8663 public override void Emit (EmitContext ec)
8667 ILGenerator ig = ec.ig;
8668 IntLiteral.EmitInt (ig, 0);
8669 ig.Emit (OpCodes.Ldelema, array_type);
8674 // Used by the fixed statement
8676 public class StringPtr : Expression {
8679 public StringPtr (LocalBuilder b, Location l)
8682 eclass = ExprClass.Value;
8683 type = TypeManager.char_ptr_type;
8687 public override Expression DoResolve (EmitContext ec)
8689 // This should never be invoked, we are born in fully
8690 // initialized state.
8695 public override void Emit (EmitContext ec)
8697 ILGenerator ig = ec.ig;
8699 ig.Emit (OpCodes.Ldloc, b);
8700 ig.Emit (OpCodes.Conv_I);
8701 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8702 ig.Emit (OpCodes.Add);
8707 // Implements the `stackalloc' keyword
8709 public class StackAlloc : Expression {
8714 public StackAlloc (Expression type, Expression count, Location l)
8721 public override Expression DoResolve (EmitContext ec)
8723 count = count.Resolve (ec);
8727 if (count.Type != TypeManager.int32_type){
8728 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8733 Constant c = count as Constant;
8734 if (c != null && c.IsNegative) {
8735 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8739 if (ec.InCatch || ec.InFinally) {
8740 Error (255, "Cannot use stackalloc in finally or catch");
8744 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8750 if (!TypeManager.VerifyUnManaged (otype, loc))
8753 type = TypeManager.GetPointerType (otype);
8754 eclass = ExprClass.Value;
8759 public override void Emit (EmitContext ec)
8761 int size = GetTypeSize (otype);
8762 ILGenerator ig = ec.ig;
8765 ig.Emit (OpCodes.Sizeof, otype);
8767 IntConstant.EmitInt (ig, size);
8769 ig.Emit (OpCodes.Mul);
8770 ig.Emit (OpCodes.Localloc);