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) {
731 temporary.Release (ec);
735 public void AddressOf (EmitContext ec, AddressOp Mode)
740 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
742 return DoResolve (ec);
745 public override Expression DoResolve (EmitContext ec)
748 // Born fully resolved
753 public override string ToString ()
755 return "*(" + expr + ")";
758 #region IVariable Members
760 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 Error_VoidInvalidInTheContext (loc);
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);
3074 // We release 'left_temp' here since 'op' may refer to it too
3075 left_temp.Release (ec);
3079 public class PointerArithmetic : Expression {
3080 Expression left, right;
3084 // We assume that `l' is always a pointer
3086 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3092 is_add = is_addition;
3095 public override Expression DoResolve (EmitContext ec)
3097 eclass = ExprClass.Variable;
3099 if (left.Type == TypeManager.void_ptr_type) {
3100 Error (242, "The operation in question is undefined on void pointers");
3107 public override void Emit (EmitContext ec)
3109 Type op_type = left.Type;
3110 ILGenerator ig = ec.ig;
3112 // It must be either array or fixed buffer
3113 Type element = TypeManager.HasElementType (op_type) ?
3114 element = TypeManager.GetElementType (op_type) :
3115 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3117 int size = GetTypeSize (element);
3118 Type rtype = right.Type;
3120 if (rtype.IsPointer){
3122 // handle (pointer - pointer)
3126 ig.Emit (OpCodes.Sub);
3130 ig.Emit (OpCodes.Sizeof, element);
3132 IntLiteral.EmitInt (ig, size);
3133 ig.Emit (OpCodes.Div);
3135 ig.Emit (OpCodes.Conv_I8);
3138 // handle + and - on (pointer op int)
3141 ig.Emit (OpCodes.Conv_I);
3143 Constant right_const = right as Constant;
3144 if (right_const != null && size != 0) {
3145 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3153 ig.Emit (OpCodes.Sizeof, element);
3155 IntLiteral.EmitInt (ig, size);
3156 if (rtype == TypeManager.int64_type)
3157 ig.Emit (OpCodes.Conv_I8);
3158 else if (rtype == TypeManager.uint64_type)
3159 ig.Emit (OpCodes.Conv_U8);
3160 ig.Emit (OpCodes.Mul);
3164 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3165 ig.Emit (OpCodes.Conv_I);
3168 ig.Emit (OpCodes.Add);
3170 ig.Emit (OpCodes.Sub);
3176 /// Implements the ternary conditional operator (?:)
3178 public class Conditional : Expression {
3179 Expression expr, trueExpr, falseExpr;
3181 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3184 this.trueExpr = trueExpr;
3185 this.falseExpr = falseExpr;
3186 this.loc = expr.Location;
3189 public Expression Expr {
3195 public Expression TrueExpr {
3201 public Expression FalseExpr {
3207 public override Expression DoResolve (EmitContext ec)
3209 expr = expr.Resolve (ec);
3214 if (TypeManager.IsNullableValueType (expr.Type))
3215 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
3217 if (expr.Type != TypeManager.bool_type){
3218 expr = Expression.ResolveBoolean (
3225 Assign ass = expr as Assign;
3226 if (ass != null && ass.Source is Constant) {
3227 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3230 trueExpr = trueExpr.Resolve (ec);
3231 falseExpr = falseExpr.Resolve (ec);
3233 if (trueExpr == null || falseExpr == null)
3236 eclass = ExprClass.Value;
3237 if (trueExpr.Type == falseExpr.Type)
3238 type = trueExpr.Type;
3241 Type true_type = trueExpr.Type;
3242 Type false_type = falseExpr.Type;
3245 // First, if an implicit conversion exists from trueExpr
3246 // to falseExpr, then the result type is of type falseExpr.Type
3248 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3251 // Check if both can convert implicitl to each other's type
3253 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3255 "Can not compute type of conditional expression " +
3256 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3257 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3258 "' convert implicitly to each other");
3263 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3267 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3268 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3273 // Dead code optimalization
3274 if (expr is BoolConstant){
3275 BoolConstant bc = (BoolConstant) expr;
3277 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3278 return bc.Value ? trueExpr : falseExpr;
3284 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3289 public override void Emit (EmitContext ec)
3291 ILGenerator ig = ec.ig;
3292 Label false_target = ig.DefineLabel ();
3293 Label end_target = ig.DefineLabel ();
3295 expr.EmitBranchable (ec, false_target, false);
3297 ig.Emit (OpCodes.Br, end_target);
3298 ig.MarkLabel (false_target);
3299 falseExpr.Emit (ec);
3300 ig.MarkLabel (end_target);
3308 public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3309 public readonly string Name;
3310 public readonly Block Block;
3311 public LocalInfo local_info;
3314 LocalTemporary temp;
3316 public LocalVariableReference (Block block, string name, Location l)
3321 eclass = ExprClass.Variable;
3325 // Setting `is_readonly' to false will allow you to create a writable
3326 // reference to a read-only variable. This is used by foreach and using.
3328 public LocalVariableReference (Block block, string name, Location l,
3329 LocalInfo local_info, bool is_readonly)
3330 : this (block, name, l)
3332 this.local_info = local_info;
3333 this.is_readonly = is_readonly;
3336 public VariableInfo VariableInfo {
3337 get { return local_info.VariableInfo; }
3340 public bool IsReadOnly {
3341 get { return is_readonly; }
3344 public bool VerifyAssigned (EmitContext ec)
3346 VariableInfo variable_info = local_info.VariableInfo;
3347 return variable_info == null || variable_info.IsAssigned (ec, loc);
3350 void ResolveLocalInfo ()
3352 if (local_info == null) {
3353 local_info = Block.GetLocalInfo (Name);
3354 is_readonly = local_info.ReadOnly;
3358 protected Expression DoResolveBase (EmitContext ec)
3360 type = local_info.VariableType;
3362 Expression e = Block.GetConstantExpression (Name);
3364 return e.Resolve (ec);
3366 if (!VerifyAssigned (ec))
3369 if (ec.CurrentAnonymousMethod != null){
3371 // If we are referencing a variable from the external block
3372 // flag it for capturing
3374 if ((local_info.Block.Toplevel != ec.CurrentBlock.Toplevel) ||
3375 ec.CurrentAnonymousMethod.IsIterator)
3377 if (local_info.AddressTaken){
3378 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3381 ec.CaptureVariable (local_info);
3388 public override Expression DoResolve (EmitContext ec)
3390 ResolveLocalInfo ();
3391 local_info.Used = true;
3392 return DoResolveBase (ec);
3395 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3397 ResolveLocalInfo ();
3402 if (right_side == EmptyExpression.OutAccess) {
3403 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3404 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3405 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3406 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3407 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3409 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
3411 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3416 if (right_side == EmptyExpression.OutAccess)
3417 local_info.Used = true;
3419 if (VariableInfo != null)
3420 VariableInfo.SetAssigned (ec);
3422 return DoResolveBase (ec);
3425 public bool VerifyFixed ()
3427 // A local Variable is always fixed.
3431 public override int GetHashCode ()
3433 return Name.GetHashCode ();
3436 public override bool Equals (object obj)
3438 LocalVariableReference lvr = obj as LocalVariableReference;
3442 return Name == lvr.Name && Block == lvr.Block;
3445 public override void Emit (EmitContext ec)
3447 ILGenerator ig = ec.ig;
3449 if (local_info.FieldBuilder == null){
3451 // A local variable on the local CLR stack
3453 ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
3456 // A local variable captured by anonymous methods.
3459 ec.EmitCapturedVariableInstance (local_info);
3461 ig.Emit (OpCodes.Ldfld, local_info.FieldBuilder);
3465 public void Emit (EmitContext ec, bool leave_copy)
3469 ec.ig.Emit (OpCodes.Dup);
3470 if (local_info.FieldBuilder != null){
3471 temp = new LocalTemporary (Type);
3477 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3479 ILGenerator ig = ec.ig;
3480 prepared = prepare_for_load;
3482 if (local_info.FieldBuilder == null){
3484 // A local variable on the local CLR stack
3486 if (local_info.LocalBuilder == null)
3487 throw new Exception ("This should not happen: both Field and Local are null");
3491 ec.ig.Emit (OpCodes.Dup);
3492 ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
3495 // A local variable captured by anonymous methods or itereators.
3497 ec.EmitCapturedVariableInstance (local_info);
3499 if (prepare_for_load)
3500 ig.Emit (OpCodes.Dup);
3503 ig.Emit (OpCodes.Dup);
3504 temp = new LocalTemporary (Type);
3507 ig.Emit (OpCodes.Stfld, local_info.FieldBuilder);
3515 public void AddressOf (EmitContext ec, AddressOp mode)
3517 ILGenerator ig = ec.ig;
3519 if (local_info.FieldBuilder == null){
3521 // A local variable on the local CLR stack
3523 ig.Emit (OpCodes.Ldloca, local_info.LocalBuilder);
3526 // A local variable captured by anonymous methods or iterators
3528 ec.EmitCapturedVariableInstance (local_info);
3529 ig.Emit (OpCodes.Ldflda, local_info.FieldBuilder);
3533 public override string ToString ()
3535 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3540 /// This represents a reference to a parameter in the intermediate
3543 public class ParameterReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3549 public bool is_ref, is_out, prepared;
3563 public string Name {
3569 LocalTemporary temp;
3571 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3574 this.name = par.Name;
3578 eclass = ExprClass.Variable;
3581 public VariableInfo VariableInfo {
3585 public bool VerifyFixed ()
3587 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3588 return par.ModFlags == Parameter.Modifier.NONE;
3591 public bool IsAssigned (EmitContext ec, Location loc)
3593 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3596 Report.Error (269, loc,
3597 "Use of unassigned out parameter `{0}'", par.Name);
3601 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3603 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3606 Report.Error (170, loc,
3607 "Use of possibly unassigned field `" + field_name + "'");
3611 public void SetAssigned (EmitContext ec)
3613 if (is_out && ec.DoFlowAnalysis)
3614 ec.CurrentBranching.SetAssigned (vi);
3617 public void SetFieldAssigned (EmitContext ec, string field_name)
3619 if (is_out && ec.DoFlowAnalysis)
3620 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3623 protected bool DoResolveBase (EmitContext ec)
3625 if (!par.Resolve (ec)) {
3629 type = par.ParameterType;
3630 Parameter.Modifier mod = par.ModFlags;
3631 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3632 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3633 eclass = ExprClass.Variable;
3636 vi = block.ParameterMap [idx];
3638 if (ec.CurrentAnonymousMethod != null){
3639 if (is_ref && !block.Toplevel.IsLocalParameter (name)){
3640 Report.Error (1628, Location, "Cannot use ref or out parameter `{0}' inside an anonymous method block",
3646 // If we are referencing the parameter from the external block
3647 // flag it for capturing
3649 //Console.WriteLine ("Is parameter `{0}' local? {1}", name, block.IsLocalParameter (name));
3650 if (!block.Toplevel.IsLocalParameter (name)){
3651 ec.CaptureParameter (name, type, idx);
3658 public override int GetHashCode()
3660 return name.GetHashCode ();
3663 public override bool Equals (object obj)
3665 ParameterReference pr = obj as ParameterReference;
3669 return name == pr.name && block == pr.block;
3673 // Notice that for ref/out parameters, the type exposed is not the
3674 // same type exposed externally.
3677 // externally we expose "int&"
3678 // here we expose "int".
3680 // We record this in "is_ref". This means that the type system can treat
3681 // the type as it is expected, but when we generate the code, we generate
3682 // the alternate kind of code.
3684 public override Expression DoResolve (EmitContext ec)
3686 if (!DoResolveBase (ec))
3689 if (is_out && ec.DoFlowAnalysis && (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3695 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3697 if (!DoResolveBase (ec))
3705 static public void EmitLdArg (ILGenerator ig, int x)
3709 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3710 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3711 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3712 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3713 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3716 ig.Emit (OpCodes.Ldarg, x);
3720 // This method is used by parameters that are references, that are
3721 // being passed as references: we only want to pass the pointer (that
3722 // is already stored in the parameter, not the address of the pointer,
3723 // and not the value of the variable).
3725 public void EmitLoad (EmitContext ec)
3727 ILGenerator ig = ec.ig;
3730 if (!ec.MethodIsStatic)
3733 EmitLdArg (ig, arg_idx);
3736 // FIXME: Review for anonymous methods
3740 public override void Emit (EmitContext ec)
3745 public void Emit (EmitContext ec, bool leave_copy)
3747 ILGenerator ig = ec.ig;
3750 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3751 ec.EmitParameter (name, leave_copy, prepared, ref temp);
3755 if (!ec.MethodIsStatic)
3758 EmitLdArg (ig, arg_idx);
3762 ec.ig.Emit (OpCodes.Dup);
3765 // If we are a reference, we loaded on the stack a pointer
3766 // Now lets load the real value
3768 LoadFromPtr (ig, type);
3772 ec.ig.Emit (OpCodes.Dup);
3775 temp = new LocalTemporary (type);
3781 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3783 prepared = prepare_for_load;
3784 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3785 ec.EmitAssignParameter (name, source, leave_copy, prepare_for_load, ref temp);
3789 ILGenerator ig = ec.ig;
3794 if (!ec.MethodIsStatic)
3797 if (is_ref && !prepared)
3798 EmitLdArg (ig, arg_idx);
3803 ec.ig.Emit (OpCodes.Dup);
3807 temp = new LocalTemporary (type);
3811 StoreFromPtr (ig, type);
3819 ig.Emit (OpCodes.Starg_S, (byte) arg_idx);
3821 ig.Emit (OpCodes.Starg, arg_idx);
3825 public void AddressOf (EmitContext ec, AddressOp mode)
3827 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3828 ec.EmitAddressOfParameter (name);
3834 if (!ec.MethodIsStatic)
3839 ec.ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx);
3841 ec.ig.Emit (OpCodes.Ldarg, arg_idx);
3844 ec.ig.Emit (OpCodes.Ldarga_S, (byte) arg_idx);
3846 ec.ig.Emit (OpCodes.Ldarga, arg_idx);
3850 public override string ToString ()
3852 return "ParameterReference[" + name + "]";
3857 /// Used for arguments to New(), Invocation()
3859 public class Argument {
3860 public enum AType : byte {
3867 public readonly AType ArgType;
3868 public Expression Expr;
3870 public Argument (Expression expr, AType type)
3873 this.ArgType = type;
3876 public Argument (Expression expr)
3879 this.ArgType = AType.Expression;
3884 if (ArgType == AType.Ref || ArgType == AType.Out)
3885 return TypeManager.GetReferenceType (Expr.Type);
3891 public Parameter.Modifier Modifier
3896 return Parameter.Modifier.OUT;
3899 return Parameter.Modifier.REF;
3902 return Parameter.Modifier.NONE;
3907 public static string FullDesc (Argument a)
3909 if (a.ArgType == AType.ArgList)
3912 return (a.ArgType == AType.Ref ? "ref " :
3913 (a.ArgType == AType.Out ? "out " : "")) +
3914 TypeManager.CSharpName (a.Expr.Type);
3917 public bool ResolveMethodGroup (EmitContext ec)
3919 SimpleName sn = Expr as SimpleName;
3921 Expr = sn.GetMethodGroup ();
3923 // FIXME: csc doesn't report any error if you try to use `ref' or
3924 // `out' in a delegate creation expression.
3925 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3932 public bool Resolve (EmitContext ec, Location loc)
3934 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3935 // Verify that the argument is readable
3936 if (ArgType != AType.Out)
3937 Expr = Expr.Resolve (ec);
3939 // Verify that the argument is writeable
3940 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3941 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3943 return Expr != null;
3947 public void Emit (EmitContext ec)
3949 if (ArgType != AType.Ref && ArgType != AType.Out) {
3954 AddressOp mode = AddressOp.Store;
3955 if (ArgType == AType.Ref)
3956 mode |= AddressOp.Load;
3958 IMemoryLocation ml = (IMemoryLocation) Expr;
3959 ParameterReference pr = ml as ParameterReference;
3962 // ParameterReferences might already be references, so we want
3963 // to pass just the value
3965 if (pr != null && pr.IsRef)
3968 ml.AddressOf (ec, mode);
3973 /// Invocation of methods or delegates.
3975 public class Invocation : ExpressionStatement {
3976 public readonly ArrayList Arguments;
3979 MethodBase method = null;
3982 // arguments is an ArrayList, but we do not want to typecast,
3983 // as it might be null.
3985 // FIXME: only allow expr to be a method invocation or a
3986 // delegate invocation (7.5.5)
3988 public Invocation (Expression expr, ArrayList arguments)
3991 Arguments = arguments;
3992 loc = expr.Location;
3995 public Expression Expr {
4002 /// Determines "better conversion" as specified in 14.4.2.3
4004 /// Returns : p if a->p is better,
4005 /// q if a->q is better,
4006 /// null if neither is better
4008 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q)
4010 Type argument_type = TypeManager.TypeToCoreType (a.Type);
4011 Expression argument_expr = a.Expr;
4013 // p = TypeManager.TypeToCoreType (p);
4014 // q = TypeManager.TypeToCoreType (q);
4016 if (argument_type == null)
4017 throw new Exception ("Expression of type " + a.Expr +
4018 " does not resolve its type");
4020 if (p == null || q == null)
4021 throw new InternalErrorException ("BetterConversion Got a null conversion");
4026 if (argument_expr is NullLiteral) {
4028 // If the argument is null and one of the types to compare is 'object' and
4029 // the other is a reference type, we prefer the other.
4031 // This follows from the usual rules:
4032 // * There is an implicit conversion from 'null' to type 'object'
4033 // * There is an implicit conversion from 'null' to any reference type
4034 // * There is an implicit conversion from any reference type to type 'object'
4035 // * There is no implicit conversion from type 'object' to other reference types
4036 // => Conversion of 'null' to a reference type is better than conversion to 'object'
4038 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
4039 // null type. I think it used to be 'object' and thus needed a special
4040 // case to avoid the immediately following two checks.
4042 if (!p.IsValueType && q == TypeManager.object_type)
4044 if (!q.IsValueType && p == TypeManager.object_type)
4048 if (argument_type == p)
4051 if (argument_type == q)
4054 Expression p_tmp = new EmptyExpression (p);
4055 Expression q_tmp = new EmptyExpression (q);
4057 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
4058 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
4060 if (p_to_q && !q_to_p)
4063 if (q_to_p && !p_to_q)
4066 if (p == TypeManager.sbyte_type)
4067 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
4068 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4070 if (q == TypeManager.sbyte_type)
4071 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
4072 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4075 if (p == TypeManager.short_type)
4076 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
4077 q == TypeManager.uint64_type)
4079 if (q == TypeManager.short_type)
4080 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
4081 p == TypeManager.uint64_type)
4084 if (p == TypeManager.int32_type)
4085 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4087 if (q == TypeManager.int32_type)
4088 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4091 if (p == TypeManager.int64_type)
4092 if (q == TypeManager.uint64_type)
4094 if (q == TypeManager.int64_type)
4095 if (p == TypeManager.uint64_type)
4101 static Type MoreSpecific (Type p, Type q)
4103 if (p.IsGenericParameter && !q.IsGenericParameter)
4105 if (!p.IsGenericParameter && q.IsGenericParameter)
4108 if (TypeManager.HasElementType (p)) {
4109 Type pe = TypeManager.GetElementType (p);
4110 Type qe = TypeManager.GetElementType (q);
4111 Type specific = MoreSpecific (pe, qe);
4116 } else if (p.IsGenericType) {
4117 Type[] pargs = TypeManager.GetTypeArguments (p);
4118 Type[] qargs = TypeManager.GetTypeArguments (q);
4120 bool p_specific_at_least_once = false;
4121 bool q_specific_at_least_once = false;
4123 for (int i = 0; i < pargs.Length; i++) {
4124 Type specific = MoreSpecific (pargs [i], qargs [i]);
4125 if (specific == pargs [i])
4126 p_specific_at_least_once = true;
4127 if (specific == qargs [i])
4128 q_specific_at_least_once = true;
4131 if (p_specific_at_least_once && !q_specific_at_least_once)
4133 if (!p_specific_at_least_once && q_specific_at_least_once)
4141 /// Determines "Better function" between candidate
4142 /// and the current best match
4145 /// Returns a boolean indicating :
4146 /// false if candidate ain't better
4147 /// true if candidate is better than the current best match
4149 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4150 MethodBase candidate, bool candidate_params,
4151 MethodBase best, bool best_params)
4153 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4154 ParameterData best_pd = TypeManager.GetParameterData (best);
4156 bool better_at_least_one = false;
4158 for (int j = 0; j < argument_count; ++j) {
4159 Argument a = (Argument) args [j];
4161 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
4162 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
4164 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4165 if (candidate_params)
4166 ct = TypeManager.GetElementType (ct);
4168 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4170 bt = TypeManager.GetElementType (bt);
4176 Type better = BetterConversion (ec, a, ct, bt);
4178 // for each argument, the conversion to 'ct' should be no worse than
4179 // the conversion to 'bt'.
4183 // for at least one argument, the conversion to 'ct' should be better than
4184 // the conversion to 'bt'.
4186 better_at_least_one = true;
4189 if (better_at_least_one)
4193 // This handles the case
4195 // Add (float f1, float f2, float f3);
4196 // Add (params decimal [] foo);
4198 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4199 // first candidate would've chosen as better.
4205 // The two methods have equal parameter types. Now apply tie-breaking rules
4207 if (TypeManager.IsGenericMethod (best) && !TypeManager.IsGenericMethod (candidate))
4209 if (!TypeManager.IsGenericMethod (best) && TypeManager.IsGenericMethod (candidate))
4213 // This handles the following cases:
4215 // Trim () is better than Trim (params char[] chars)
4216 // Concat (string s1, string s2, string s3) is better than
4217 // Concat (string s1, params string [] srest)
4218 // Foo (int, params int [] rest) is better than Foo (params int [] rest)
4220 if (!candidate_params && best_params)
4222 if (candidate_params && !best_params)
4225 int candidate_param_count = candidate_pd.Count;
4226 int best_param_count = best_pd.Count;
4228 if (candidate_param_count != best_param_count)
4229 // can only happen if (candidate_params && best_params)
4230 return candidate_param_count > best_param_count;
4233 // now, both methods have the same number of parameters, and the parameters have the same types
4234 // Pick the "more specific" signature
4237 MethodBase orig_candidate = TypeManager.DropGenericMethodArguments (candidate);
4238 MethodBase orig_best = TypeManager.DropGenericMethodArguments (best);
4240 ParameterData orig_candidate_pd = TypeManager.GetParameterData (orig_candidate);
4241 ParameterData orig_best_pd = TypeManager.GetParameterData (orig_best);
4243 bool specific_at_least_once = false;
4244 for (int j = 0; j < candidate_param_count; ++j) {
4245 Type ct = TypeManager.TypeToCoreType (orig_candidate_pd.ParameterType (j));
4246 Type bt = TypeManager.TypeToCoreType (orig_best_pd.ParameterType (j));
4249 Type specific = MoreSpecific (ct, bt);
4253 specific_at_least_once = true;
4256 if (specific_at_least_once)
4259 // FIXME: handle lifted operators
4265 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4267 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4270 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4271 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4273 if (cand_pd.Count != base_pd.Count)
4276 for (int j = 0; j < cand_pd.Count; ++j) {
4277 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4278 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4279 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4280 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4282 if (cm != bm || ct != bt)
4289 public static string FullMethodDesc (MethodBase mb)
4295 if (mb is MethodInfo) {
4296 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4300 sb = new StringBuilder ();
4302 sb.Append (TypeManager.CSharpSignature (mb));
4303 return sb.ToString ();
4306 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4308 MemberInfo [] miset;
4309 MethodGroupExpr union;
4314 return (MethodGroupExpr) mg2;
4317 return (MethodGroupExpr) mg1;
4320 MethodGroupExpr left_set = null, right_set = null;
4321 int length1 = 0, length2 = 0;
4323 left_set = (MethodGroupExpr) mg1;
4324 length1 = left_set.Methods.Length;
4326 right_set = (MethodGroupExpr) mg2;
4327 length2 = right_set.Methods.Length;
4329 ArrayList common = new ArrayList ();
4331 foreach (MethodBase r in right_set.Methods){
4332 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4336 miset = new MemberInfo [length1 + length2 - common.Count];
4337 left_set.Methods.CopyTo (miset, 0);
4341 foreach (MethodBase r in right_set.Methods) {
4342 if (!common.Contains (r))
4346 union = new MethodGroupExpr (miset, loc);
4351 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4352 ArrayList arguments, int arg_count,
4353 ref MethodBase candidate)
4355 return IsParamsMethodApplicable (
4356 ec, me, arguments, arg_count, false, ref candidate) ||
4357 IsParamsMethodApplicable (
4358 ec, me, arguments, arg_count, true, ref candidate);
4363 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4364 ArrayList arguments, int arg_count,
4365 bool do_varargs, ref MethodBase candidate)
4367 if (!me.HasTypeArguments &&
4368 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4371 if (TypeManager.IsGenericMethodDefinition (candidate))
4372 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4374 return IsParamsMethodApplicable (
4375 ec, arguments, arg_count, candidate, do_varargs);
4379 /// Determines if the candidate method, if a params method, is applicable
4380 /// in its expanded form to the given set of arguments
4382 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4383 int arg_count, MethodBase candidate,
4386 ParameterData pd = TypeManager.GetParameterData (candidate);
4388 int pd_count = pd.Count;
4392 int count = pd_count - 1;
4394 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4396 if (pd_count != arg_count)
4403 if (count > arg_count)
4406 if (pd_count == 1 && arg_count == 0)
4410 // If we have come this far, the case which
4411 // remains is when the number of parameters is
4412 // less than or equal to the argument count.
4414 for (int i = 0; i < count; ++i) {
4416 Argument a = (Argument) arguments [i];
4418 Parameter.Modifier a_mod = a.Modifier &
4419 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4420 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4421 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4423 if (a_mod == p_mod) {
4425 if (a_mod == Parameter.Modifier.NONE)
4426 if (!Convert.ImplicitConversionExists (ec,
4428 pd.ParameterType (i)))
4431 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4432 Type pt = pd.ParameterType (i);
4435 pt = TypeManager.GetReferenceType (pt);
4446 Argument a = (Argument) arguments [count];
4447 if (!(a.Expr is Arglist))
4453 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
4455 for (int i = pd_count - 1; i < arg_count; i++) {
4456 Argument a = (Argument) arguments [i];
4458 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4465 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4466 ArrayList arguments, int arg_count,
4467 ref MethodBase candidate)
4469 if (!me.HasTypeArguments &&
4470 !TypeManager.InferTypeArguments (arguments, ref candidate))
4473 if (TypeManager.IsGenericMethodDefinition (candidate))
4474 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4476 return IsApplicable (ec, arguments, arg_count, candidate);
4480 /// Determines if the candidate method is applicable (section 14.4.2.1)
4481 /// to the given set of arguments
4483 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4484 MethodBase candidate)
4486 ParameterData pd = TypeManager.GetParameterData (candidate);
4488 if (arg_count != pd.Count)
4491 for (int i = arg_count; i > 0; ) {
4494 Argument a = (Argument) arguments [i];
4496 Parameter.Modifier a_mod = a.Modifier &
4497 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4499 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4500 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4502 if (a_mod == p_mod) {
4503 Type pt = pd.ParameterType (i);
4505 if (a_mod == Parameter.Modifier.NONE) {
4506 if (!TypeManager.IsEqual (a.Type, pt) &&
4507 !Convert.ImplicitConversionExists (ec, a.Expr, pt))
4521 static internal bool IsAncestralType (Type first_type, Type second_type)
4523 return first_type != second_type &&
4524 (TypeManager.IsSubclassOf (second_type, first_type) ||
4525 TypeManager.ImplementsInterface (second_type, first_type));
4529 /// Find the Applicable Function Members (7.4.2.1)
4531 /// me: Method Group expression with the members to select.
4532 /// it might contain constructors or methods (or anything
4533 /// that maps to a method).
4535 /// Arguments: ArrayList containing resolved Argument objects.
4537 /// loc: The location if we want an error to be reported, or a Null
4538 /// location for "probing" purposes.
4540 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4541 /// that is the best match of me on Arguments.
4544 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4545 ArrayList Arguments, bool may_fail,
4548 MethodBase method = null;
4549 bool method_params = false;
4550 Type applicable_type = null;
4552 ArrayList candidates = new ArrayList (2);
4553 ArrayList candidate_overrides = null;
4556 // Used to keep a map between the candidate
4557 // and whether it is being considered in its
4558 // normal or expanded form
4560 // false is normal form, true is expanded form
4562 Hashtable candidate_to_form = null;
4564 if (Arguments != null)
4565 arg_count = Arguments.Count;
4567 if ((me.Name == "Invoke") &&
4568 TypeManager.IsDelegateType (me.DeclaringType)) {
4569 Error_InvokeOnDelegate (loc);
4573 MethodBase[] methods = me.Methods;
4575 int nmethods = methods.Length;
4579 // Methods marked 'override' don't take part in 'applicable_type'
4580 // computation, nor in the actual overload resolution.
4581 // However, they still need to be emitted instead of a base virtual method.
4582 // So, we salt them away into the 'candidate_overrides' array.
4584 // In case of reflected methods, we replace each overriding method with
4585 // its corresponding base virtual method. This is to improve compatibility
4586 // with non-C# libraries which change the visibility of overrides (#75636)
4589 for (int i = 0; i < methods.Length; ++i) {
4590 MethodBase m = methods [i];
4591 Type [] gen_args = m.IsGenericMethod && !m.IsGenericMethodDefinition ? m.GetGenericArguments () : null;
4592 if (TypeManager.IsOverride (m)) {
4593 if (candidate_overrides == null)
4594 candidate_overrides = new ArrayList ();
4595 candidate_overrides.Add (m);
4596 m = TypeManager.TryGetBaseDefinition (m);
4597 if (m != null && gen_args != null) {
4598 if (!m.IsGenericMethodDefinition)
4599 throw new InternalErrorException ("GetBaseDefinition didn't return a GenericMethodDefinition");
4600 m = ((MethodInfo) m).MakeGenericMethod (gen_args);
4609 int applicable_errors = Report.Errors;
4612 // First we construct the set of applicable methods
4614 bool is_sorted = true;
4615 for (int i = 0; i < nmethods; i++){
4616 Type decl_type = methods [i].DeclaringType;
4619 // If we have already found an applicable method
4620 // we eliminate all base types (Section 14.5.5.1)
4622 if (applicable_type != null && IsAncestralType (decl_type, applicable_type))
4626 // Check if candidate is applicable (section 14.4.2.1)
4627 // Is candidate applicable in normal form?
4629 bool is_applicable = IsApplicable (ec, me, Arguments, arg_count, ref methods [i]);
4631 if (!is_applicable && IsParamsMethodApplicable (ec, me, Arguments, arg_count, ref methods [i])) {
4632 MethodBase candidate = methods [i];
4633 if (candidate_to_form == null)
4634 candidate_to_form = new PtrHashtable ();
4635 candidate_to_form [candidate] = candidate;
4636 // Candidate is applicable in expanded form
4637 is_applicable = true;
4643 candidates.Add (methods [i]);
4645 if (applicable_type == null)
4646 applicable_type = decl_type;
4647 else if (applicable_type != decl_type) {
4649 if (IsAncestralType (applicable_type, decl_type))
4650 applicable_type = decl_type;
4654 if (applicable_errors != Report.Errors)
4657 int candidate_top = candidates.Count;
4659 if (applicable_type == null) {
4661 // Okay so we have failed to find anything so we
4662 // return by providing info about the closest match
4664 int errors = Report.Errors;
4665 for (int i = 0; i < nmethods; ++i) {
4666 MethodBase c = (MethodBase) methods [i];
4667 ParameterData pd = TypeManager.GetParameterData (c);
4669 if (pd.Count != arg_count)
4672 if (!TypeManager.InferTypeArguments (Arguments, ref c))
4675 if (TypeManager.IsGenericMethodDefinition (c))
4678 VerifyArgumentsCompat (ec, Arguments, arg_count,
4679 c, false, null, may_fail, loc);
4681 if (!may_fail && errors == Report.Errors)
4682 throw new InternalErrorException (
4683 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4684 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4689 if (!may_fail && errors == Report.Errors) {
4690 string report_name = me.Name;
4691 if (report_name == ".ctor")
4692 report_name = me.DeclaringType.ToString ();
4694 for (int i = 0; i < methods.Length; ++i) {
4695 MethodBase c = methods [i];
4696 ParameterData pd = TypeManager.GetParameterData (c);
4698 if (pd.Count != arg_count)
4701 if (TypeManager.InferTypeArguments (Arguments, ref c))
4705 411, loc, "The type arguments for " +
4706 "method `{0}' cannot be infered from " +
4707 "the usage. Try specifying the type " +
4708 "arguments explicitly.", report_name);
4712 Error_WrongNumArguments (loc, report_name, arg_count);
4720 // At this point, applicable_type is _one_ of the most derived types
4721 // in the set of types containing the methods in this MethodGroup.
4722 // Filter the candidates so that they only contain methods from the
4723 // most derived types.
4726 int finalized = 0; // Number of finalized candidates
4729 // Invariant: applicable_type is a most derived type
4731 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4732 // eliminating all it's base types. At the same time, we'll also move
4733 // every unrelated type to the end of the array, and pick the next
4734 // 'applicable_type'.
4736 Type next_applicable_type = null;
4737 int j = finalized; // where to put the next finalized candidate
4738 int k = finalized; // where to put the next undiscarded candidate
4739 for (int i = finalized; i < candidate_top; ++i) {
4740 MethodBase candidate = (MethodBase) candidates [i];
4741 Type decl_type = candidate.DeclaringType;
4743 if (decl_type == applicable_type) {
4744 candidates [k++] = candidates [j];
4745 candidates [j++] = candidates [i];
4749 if (IsAncestralType (decl_type, applicable_type))
4752 if (next_applicable_type != null &&
4753 IsAncestralType (decl_type, next_applicable_type))
4756 candidates [k++] = candidates [i];
4758 if (next_applicable_type == null ||
4759 IsAncestralType (next_applicable_type, decl_type))
4760 next_applicable_type = decl_type;
4763 applicable_type = next_applicable_type;
4766 } while (applicable_type != null);
4770 // Now we actually find the best method
4773 method = (MethodBase) candidates [0];
4774 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4775 for (int ix = 1; ix < candidate_top; ix++){
4776 MethodBase candidate = (MethodBase) candidates [ix];
4778 if (candidate == method)
4781 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4783 if (BetterFunction (ec, Arguments, arg_count,
4784 candidate, cand_params,
4785 method, method_params)) {
4787 method_params = cand_params;
4791 // Now check that there are no ambiguities i.e the selected method
4792 // should be better than all the others
4794 MethodBase ambiguous = null;
4795 for (int ix = 0; ix < candidate_top; ix++){
4796 MethodBase candidate = (MethodBase) candidates [ix];
4798 if (candidate == method)
4801 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4802 if (!BetterFunction (ec, Arguments, arg_count,
4803 method, method_params,
4804 candidate, cand_params)) {
4805 Report.SymbolRelatedToPreviousError (candidate);
4806 ambiguous = candidate;
4810 if (ambiguous != null) {
4811 Report.SymbolRelatedToPreviousError (method);
4812 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4813 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4818 // If the method is a virtual function, pick an override closer to the LHS type.
4820 if (!me.IsBase && method.IsVirtual) {
4821 if (TypeManager.IsOverride (method))
4822 throw new InternalErrorException (
4823 "Should not happen. An 'override' method took part in overload resolution: " + method);
4825 if (candidate_overrides != null)
4826 foreach (MethodBase candidate in candidate_overrides) {
4827 if (IsOverride (candidate, method))
4833 // And now check if the arguments are all
4834 // compatible, perform conversions if
4835 // necessary etc. and return if everything is
4838 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4839 method_params, null, may_fail, loc))
4845 MethodBase the_method = TypeManager.DropGenericMethodArguments (method);
4846 if (the_method.IsGenericMethodDefinition &&
4847 !ConstraintChecker.CheckConstraints (ec, the_method, method, loc))
4850 IMethodData data = TypeManager.GetMethod (the_method);
4852 data.SetMemberIsUsed ();
4857 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4859 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4860 name, arg_count.ToString ());
4863 static void Error_InvokeOnDelegate (Location loc)
4865 Report.Error (1533, loc,
4866 "Invoke cannot be called directly on a delegate");
4869 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4870 Type delegate_type, Argument a, ParameterData expected_par)
4872 if (delegate_type == null)
4873 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4874 TypeManager.CSharpSignature (method));
4876 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4877 TypeManager.CSharpName (delegate_type));
4879 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4881 string index = (idx + 1).ToString ();
4882 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4883 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4884 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4885 index, Parameter.GetModifierSignature (a.Modifier));
4887 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4888 index, Parameter.GetModifierSignature (mod));
4890 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4891 index, Argument.FullDesc (a), expected_par.ParameterDesc (idx));
4895 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4896 int arg_count, MethodBase method,
4897 bool chose_params_expanded,
4898 Type delegate_type, bool may_fail,
4901 ParameterData pd = TypeManager.GetParameterData (method);
4903 for (j = 0; j < arg_count; j++) {
4904 Argument a = (Argument) Arguments [j];
4905 Expression a_expr = a.Expr;
4906 Type parameter_type = pd.ParameterType (j);
4907 Parameter.Modifier pm = pd.ParameterModifier (j);
4908 Parameter.Modifier am = a.Modifier;
4910 if (pm == Parameter.Modifier.ARGLIST) {
4911 if (!(a.Expr is Arglist))
4916 if (pm == Parameter.Modifier.PARAMS) {
4917 pm = Parameter.Modifier.NONE;
4918 if (chose_params_expanded)
4919 parameter_type = TypeManager.GetElementType (parameter_type);
4925 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4926 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4929 Expression conv = Convert.ImplicitConversion (ec, a_expr, parameter_type, loc);
4933 // Update the argument with the implicit conversion
4938 if (parameter_type.IsPointer && !ec.InUnsafe) {
4948 Error_InvalidArguments (loc, j, method, delegate_type, (Argument) Arguments [j], pd);
4952 private bool resolved = false;
4953 public override Expression DoResolve (EmitContext ec)
4956 return this.method == null ? null : this;
4960 // First, resolve the expression that is used to
4961 // trigger the invocation
4963 SimpleName sn = expr as SimpleName;
4965 expr = sn.GetMethodGroup ();
4967 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4971 if (!(expr is MethodGroupExpr)) {
4972 Type expr_type = expr.Type;
4974 if (expr_type != null){
4975 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4977 return (new DelegateInvocation (
4978 this.expr, Arguments, loc)).Resolve (ec);
4982 if (!(expr is MethodGroupExpr)){
4983 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4988 // Next, evaluate all the expressions in the argument list
4990 if (Arguments != null){
4991 foreach (Argument a in Arguments){
4992 if (!a.Resolve (ec, loc))
4997 MethodGroupExpr mg = (MethodGroupExpr) expr;
4998 MethodBase method = OverloadResolve (ec, mg, Arguments, false, loc);
5003 MethodInfo mi = method as MethodInfo;
5005 type = TypeManager.TypeToCoreType (mi.ReturnType);
5006 Expression iexpr = mg.InstanceExpression;
5008 if (iexpr == null ||
5009 iexpr is This || iexpr is EmptyExpression ||
5010 mg.IdenticalTypeName) {
5011 mg.InstanceExpression = null;
5013 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
5017 if (iexpr == null || iexpr is EmptyExpression) {
5018 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
5024 if (type.IsPointer){
5032 // Only base will allow this invocation to happen.
5034 if (mg.IsBase && method.IsAbstract){
5035 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
5039 if (Arguments == null && method.Name == "Finalize") {
5041 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
5043 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
5047 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
5051 if (mg.InstanceExpression != null)
5052 mg.InstanceExpression.CheckMarshalByRefAccess ();
5054 eclass = ExprClass.Value;
5055 this.method = method;
5059 bool IsSpecialMethodInvocation (MethodBase method)
5061 IMethodData md = TypeManager.GetMethod (method);
5063 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
5066 if (!TypeManager.IsSpecialMethod (method))
5069 int args = TypeManager.GetParameterData (method).Count;
5070 if (method.Name.StartsWith ("get_") && args > 0)
5072 else if (method.Name.StartsWith ("set_") && args > 2)
5075 // TODO: check operators and events as well ?
5078 Report.SymbolRelatedToPreviousError (method);
5079 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
5080 TypeManager.CSharpSignature (method, true));
5086 // Emits the list of arguments as an array
5088 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
5090 ILGenerator ig = ec.ig;
5091 int count = arguments.Count - idx;
5092 Argument a = (Argument) arguments [idx];
5093 Type t = a.Expr.Type;
5095 IntConstant.EmitInt (ig, count);
5096 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
5098 int top = arguments.Count;
5099 for (int j = idx; j < top; j++){
5100 a = (Argument) arguments [j];
5102 ig.Emit (OpCodes.Dup);
5103 IntConstant.EmitInt (ig, j - idx);
5105 bool is_stobj, has_type_arg;
5106 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
5108 ig.Emit (OpCodes.Ldelema, t);
5120 /// Emits a list of resolved Arguments that are in the arguments
5123 /// The MethodBase argument might be null if the
5124 /// emission of the arguments is known not to contain
5125 /// a `params' field (for example in constructors or other routines
5126 /// that keep their arguments in this structure)
5128 /// if `dup_args' is true, a copy of the arguments will be left
5129 /// on the stack. If `dup_args' is true, you can specify `this_arg'
5130 /// which will be duplicated before any other args. Only EmitCall
5131 /// should be using this interface.
5133 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
5135 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
5136 int top = arguments == null ? 0 : arguments.Count;
5137 LocalTemporary [] temps = null;
5139 if (dup_args && top != 0)
5140 temps = new LocalTemporary [top];
5142 for (int i = 0; i < top; i++){
5143 Argument a = (Argument) arguments [i];
5146 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
5148 // Special case if we are passing the same data as the
5149 // params argument, do not put it in an array.
5151 if (pd.ParameterType (i) == a.Type)
5154 EmitParams (ec, i, arguments);
5161 ec.ig.Emit (OpCodes.Dup);
5162 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
5167 if (this_arg != null)
5170 for (int i = 0; i < top; i ++) {
5171 temps [i].Emit (ec);
5172 temps [i].Release (ec);
5176 if (pd != null && pd.Count > top &&
5177 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
5178 ILGenerator ig = ec.ig;
5180 IntConstant.EmitInt (ig, 0);
5181 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
5185 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
5187 ParameterData pd = TypeManager.GetParameterData (mb);
5189 if (arguments == null)
5190 return new Type [0];
5192 Argument a = (Argument) arguments [pd.Count - 1];
5193 Arglist list = (Arglist) a.Expr;
5195 return list.ArgumentTypes;
5199 /// This checks the ConditionalAttribute on the method
5201 static bool IsMethodExcluded (MethodBase method)
5203 if (method.IsConstructor)
5206 IMethodData md = TypeManager.GetMethod (method);
5208 return md.IsExcluded ();
5210 // For some methods (generated by delegate class) GetMethod returns null
5211 // because they are not included in builder_to_method table
5212 if (method.DeclaringType is TypeBuilder)
5215 return AttributeTester.IsConditionalMethodExcluded (method);
5219 /// is_base tells whether we want to force the use of the `call'
5220 /// opcode instead of using callvirt. Call is required to call
5221 /// a specific method, while callvirt will always use the most
5222 /// recent method in the vtable.
5224 /// is_static tells whether this is an invocation on a static method
5226 /// instance_expr is an expression that represents the instance
5227 /// it must be non-null if is_static is false.
5229 /// method is the method to invoke.
5231 /// Arguments is the list of arguments to pass to the method or constructor.
5233 public static void EmitCall (EmitContext ec, bool is_base,
5234 bool is_static, Expression instance_expr,
5235 MethodBase method, ArrayList Arguments, Location loc)
5237 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
5240 // `dup_args' leaves an extra copy of the arguments on the stack
5241 // `omit_args' does not leave any arguments at all.
5242 // So, basically, you could make one call with `dup_args' set to true,
5243 // and then another with `omit_args' set to true, and the two calls
5244 // would have the same set of arguments. However, each argument would
5245 // only have been evaluated once.
5246 public static void EmitCall (EmitContext ec, bool is_base,
5247 bool is_static, Expression instance_expr,
5248 MethodBase method, ArrayList Arguments, Location loc,
5249 bool dup_args, bool omit_args)
5251 ILGenerator ig = ec.ig;
5252 bool struct_call = false;
5253 bool this_call = false;
5254 LocalTemporary this_arg = null;
5256 Type decl_type = method.DeclaringType;
5258 if (!RootContext.StdLib) {
5259 // Replace any calls to the system's System.Array type with calls to
5260 // the newly created one.
5261 if (method == TypeManager.system_int_array_get_length)
5262 method = TypeManager.int_array_get_length;
5263 else if (method == TypeManager.system_int_array_get_rank)
5264 method = TypeManager.int_array_get_rank;
5265 else if (method == TypeManager.system_object_array_clone)
5266 method = TypeManager.object_array_clone;
5267 else if (method == TypeManager.system_int_array_get_length_int)
5268 method = TypeManager.int_array_get_length_int;
5269 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5270 method = TypeManager.int_array_get_lower_bound_int;
5271 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5272 method = TypeManager.int_array_get_upper_bound_int;
5273 else if (method == TypeManager.system_void_array_copyto_array_int)
5274 method = TypeManager.void_array_copyto_array_int;
5277 if (!ec.IsInObsoleteScope) {
5279 // This checks ObsoleteAttribute on the method and on the declaring type
5281 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5283 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5285 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5287 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5291 if (IsMethodExcluded (method))
5295 if (instance_expr == EmptyExpression.Null) {
5296 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5300 this_call = instance_expr is This;
5301 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5305 // If this is ourselves, push "this"
5309 Type iexpr_type = instance_expr.Type;
5312 // Push the instance expression
5314 if (TypeManager.IsValueType (iexpr_type)) {
5316 // Special case: calls to a function declared in a
5317 // reference-type with a value-type argument need
5318 // to have their value boxed.
5319 if (decl_type.IsValueType ||
5320 iexpr_type.IsGenericParameter) {
5322 // If the expression implements IMemoryLocation, then
5323 // we can optimize and use AddressOf on the
5326 // If not we have to use some temporary storage for
5328 if (instance_expr is IMemoryLocation) {
5329 ((IMemoryLocation)instance_expr).
5330 AddressOf (ec, AddressOp.LoadStore);
5332 LocalTemporary temp = new LocalTemporary (iexpr_type);
5333 instance_expr.Emit (ec);
5335 temp.AddressOf (ec, AddressOp.Load);
5338 // avoid the overhead of doing this all the time.
5340 t = TypeManager.GetReferenceType (iexpr_type);
5342 instance_expr.Emit (ec);
5343 ig.Emit (OpCodes.Box, instance_expr.Type);
5344 t = TypeManager.object_type;
5347 instance_expr.Emit (ec);
5348 t = instance_expr.Type;
5352 ig.Emit (OpCodes.Dup);
5353 if (Arguments != null && Arguments.Count != 0) {
5354 this_arg = new LocalTemporary (t);
5355 this_arg.Store (ec);
5362 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5364 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
5365 ig.Emit (OpCodes.Constrained, instance_expr.Type);
5368 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5369 call_op = OpCodes.Call;
5371 call_op = OpCodes.Callvirt;
5373 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5374 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5375 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5382 // and DoFoo is not virtual, you can omit the callvirt,
5383 // because you don't need the null checking behavior.
5385 if (method is MethodInfo)
5386 ig.Emit (call_op, (MethodInfo) method);
5388 ig.Emit (call_op, (ConstructorInfo) method);
5391 public override void Emit (EmitContext ec)
5393 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5395 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5398 public override void EmitStatement (EmitContext ec)
5403 // Pop the return value if there is one
5405 if (method is MethodInfo){
5406 Type ret = ((MethodInfo)method).ReturnType;
5407 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5408 ec.ig.Emit (OpCodes.Pop);
5413 public class InvocationOrCast : ExpressionStatement
5416 Expression argument;
5418 public InvocationOrCast (Expression expr, Expression argument)
5421 this.argument = argument;
5422 this.loc = expr.Location;
5425 public override Expression DoResolve (EmitContext ec)
5428 // First try to resolve it as a cast.
5430 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5431 if ((te != null) && (te.eclass == ExprClass.Type)) {
5432 Cast cast = new Cast (te, argument, loc);
5433 return cast.Resolve (ec);
5437 // This can either be a type or a delegate invocation.
5438 // Let's just resolve it and see what we'll get.
5440 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5445 // Ok, so it's a Cast.
5447 if (expr.eclass == ExprClass.Type) {
5448 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5449 return cast.Resolve (ec);
5453 // It's a delegate invocation.
5455 if (!TypeManager.IsDelegateType (expr.Type)) {
5456 Error (149, "Method name expected");
5460 ArrayList args = new ArrayList ();
5461 args.Add (new Argument (argument, Argument.AType.Expression));
5462 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5463 return invocation.Resolve (ec);
5468 Error (201, "Only assignment, call, increment, decrement and new object " +
5469 "expressions can be used as a statement");
5472 public override ExpressionStatement ResolveStatement (EmitContext ec)
5475 // First try to resolve it as a cast.
5477 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5478 if ((te != null) && (te.eclass == ExprClass.Type)) {
5484 // This can either be a type or a delegate invocation.
5485 // Let's just resolve it and see what we'll get.
5487 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5488 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5494 // It's a delegate invocation.
5496 if (!TypeManager.IsDelegateType (expr.Type)) {
5497 Error (149, "Method name expected");
5501 ArrayList args = new ArrayList ();
5502 args.Add (new Argument (argument, Argument.AType.Expression));
5503 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5504 return invocation.ResolveStatement (ec);
5507 public override void Emit (EmitContext ec)
5509 throw new Exception ("Cannot happen");
5512 public override void EmitStatement (EmitContext ec)
5514 throw new Exception ("Cannot happen");
5519 // This class is used to "disable" the code generation for the
5520 // temporary variable when initializing value types.
5522 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5523 public void AddressOf (EmitContext ec, AddressOp Mode)
5530 /// Implements the new expression
5532 public class New : ExpressionStatement, IMemoryLocation {
5533 public readonly ArrayList Arguments;
5536 // During bootstrap, it contains the RequestedType,
5537 // but if `type' is not null, it *might* contain a NewDelegate
5538 // (because of field multi-initialization)
5540 public Expression RequestedType;
5542 MethodBase method = null;
5545 // If set, the new expression is for a value_target, and
5546 // we will not leave anything on the stack.
5548 Expression value_target;
5549 bool value_target_set = false;
5550 bool is_type_parameter = false;
5552 public New (Expression requested_type, ArrayList arguments, Location l)
5554 RequestedType = requested_type;
5555 Arguments = arguments;
5559 public bool SetValueTypeVariable (Expression value)
5561 value_target = value;
5562 value_target_set = true;
5563 if (!(value_target is IMemoryLocation)){
5564 Error_UnexpectedKind (null, "variable", loc);
5571 // This function is used to disable the following code sequence for
5572 // value type initialization:
5574 // AddressOf (temporary)
5578 // Instead the provide will have provided us with the address on the
5579 // stack to store the results.
5581 static Expression MyEmptyExpression;
5583 public void DisableTemporaryValueType ()
5585 if (MyEmptyExpression == null)
5586 MyEmptyExpression = new EmptyAddressOf ();
5589 // To enable this, look into:
5590 // test-34 and test-89 and self bootstrapping.
5592 // For instance, we can avoid a copy by using `newobj'
5593 // instead of Call + Push-temp on value types.
5594 // value_target = MyEmptyExpression;
5599 /// Converts complex core type syntax like 'new int ()' to simple constant
5601 public static Constant Constantify (Type t)
5603 if (t == TypeManager.int32_type)
5604 return new IntConstant (0, Location.Null);
5605 if (t == TypeManager.uint32_type)
5606 return new UIntConstant (0, Location.Null);
5607 if (t == TypeManager.int64_type)
5608 return new LongConstant (0, Location.Null);
5609 if (t == TypeManager.uint64_type)
5610 return new ULongConstant (0, Location.Null);
5611 if (t == TypeManager.float_type)
5612 return new FloatConstant (0, Location.Null);
5613 if (t == TypeManager.double_type)
5614 return new DoubleConstant (0, Location.Null);
5615 if (t == TypeManager.short_type)
5616 return new ShortConstant (0, Location.Null);
5617 if (t == TypeManager.ushort_type)
5618 return new UShortConstant (0, Location.Null);
5619 if (t == TypeManager.sbyte_type)
5620 return new SByteConstant (0, Location.Null);
5621 if (t == TypeManager.byte_type)
5622 return new ByteConstant (0, Location.Null);
5623 if (t == TypeManager.char_type)
5624 return new CharConstant ('\0', Location.Null);
5625 if (t == TypeManager.bool_type)
5626 return new BoolConstant (false, Location.Null);
5627 if (t == TypeManager.decimal_type)
5628 return new DecimalConstant (0, Location.Null);
5634 // Checks whether the type is an interface that has the
5635 // [ComImport, CoClass] attributes and must be treated
5638 public Expression CheckComImport (EmitContext ec)
5640 if (!type.IsInterface)
5644 // Turn the call into:
5645 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5647 Type real_class = AttributeTester.GetCoClassAttribute (type);
5648 if (real_class == null)
5651 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5652 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5653 return cast.Resolve (ec);
5656 public override Expression DoResolve (EmitContext ec)
5659 // The New DoResolve might be called twice when initializing field
5660 // expressions (see EmitFieldInitializers, the call to
5661 // GetInitializerExpression will perform a resolve on the expression,
5662 // and later the assign will trigger another resolution
5664 // This leads to bugs (#37014)
5667 if (RequestedType is NewDelegate)
5668 return RequestedType;
5672 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5678 if (Arguments == null) {
5679 Expression c = Constantify (type);
5684 if (TypeManager.IsDelegateType (type)) {
5685 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5686 if (RequestedType != null)
5687 if (!(RequestedType is DelegateCreation))
5688 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5689 return RequestedType;
5692 if (type.IsGenericParameter) {
5693 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5695 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5696 Error (304, String.Format (
5697 "Cannot create an instance of the " +
5698 "variable type '{0}' because it " +
5699 "doesn't have the new() constraint",
5704 if ((Arguments != null) && (Arguments.Count != 0)) {
5705 Error (417, String.Format (
5706 "`{0}': cannot provide arguments " +
5707 "when creating an instance of a " +
5708 "variable type.", type));
5712 is_type_parameter = true;
5713 eclass = ExprClass.Value;
5717 if (type.IsAbstract && type.IsSealed) {
5718 Report.SymbolRelatedToPreviousError (type);
5719 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5723 if (type.IsInterface || type.IsAbstract){
5724 RequestedType = CheckComImport (ec);
5725 if (RequestedType != null)
5726 return RequestedType;
5728 Report.SymbolRelatedToPreviousError (type);
5729 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5733 bool is_struct = type.IsValueType;
5734 eclass = ExprClass.Value;
5737 // SRE returns a match for .ctor () on structs (the object constructor),
5738 // so we have to manually ignore it.
5740 if (is_struct && Arguments == null)
5743 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5744 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5745 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5750 MethodGroupExpr mg = ml as MethodGroupExpr;
5753 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5757 if (Arguments != null){
5758 foreach (Argument a in Arguments){
5759 if (!a.Resolve (ec, loc))
5764 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5765 if (method == null) {
5766 if (almostMatchedMembers.Count != 0)
5767 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5774 bool DoEmitTypeParameter (EmitContext ec)
5776 ILGenerator ig = ec.ig;
5778 ig.Emit (OpCodes.Ldtoken, type);
5779 ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
5780 ig.Emit (OpCodes.Call, TypeManager.activator_create_instance);
5781 ig.Emit (OpCodes.Unbox_Any, type);
5787 // This DoEmit can be invoked in two contexts:
5788 // * As a mechanism that will leave a value on the stack (new object)
5789 // * As one that wont (init struct)
5791 // You can control whether a value is required on the stack by passing
5792 // need_value_on_stack. The code *might* leave a value on the stack
5793 // so it must be popped manually
5795 // If we are dealing with a ValueType, we have a few
5796 // situations to deal with:
5798 // * The target is a ValueType, and we have been provided
5799 // the instance (this is easy, we are being assigned).
5801 // * The target of New is being passed as an argument,
5802 // to a boxing operation or a function that takes a
5805 // In this case, we need to create a temporary variable
5806 // that is the argument of New.
5808 // Returns whether a value is left on the stack
5810 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5812 bool is_value_type = TypeManager.IsValueType (type);
5813 ILGenerator ig = ec.ig;
5818 // Allow DoEmit() to be called multiple times.
5819 // We need to create a new LocalTemporary each time since
5820 // you can't share LocalBuilders among ILGeneators.
5821 if (!value_target_set)
5822 value_target = new LocalTemporary (type);
5824 ml = (IMemoryLocation) value_target;
5825 ml.AddressOf (ec, AddressOp.Store);
5829 Invocation.EmitArguments (ec, method, Arguments, false, null);
5833 ig.Emit (OpCodes.Initobj, type);
5835 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5836 if (need_value_on_stack){
5837 value_target.Emit (ec);
5842 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5847 public override void Emit (EmitContext ec)
5849 if (is_type_parameter)
5850 DoEmitTypeParameter (ec);
5855 public override void EmitStatement (EmitContext ec)
5857 if (is_type_parameter)
5858 throw new InvalidOperationException ();
5860 if (DoEmit (ec, false))
5861 ec.ig.Emit (OpCodes.Pop);
5864 public void AddressOf (EmitContext ec, AddressOp Mode)
5866 if (is_type_parameter)
5867 throw new InvalidOperationException ();
5869 if (!type.IsValueType){
5871 // We throw an exception. So far, I believe we only need to support
5873 // foreach (int j in new StructType ())
5876 throw new Exception ("AddressOf should not be used for classes");
5879 if (!value_target_set)
5880 value_target = new LocalTemporary (type);
5882 IMemoryLocation ml = (IMemoryLocation) value_target;
5883 ml.AddressOf (ec, AddressOp.Store);
5885 Invocation.EmitArguments (ec, method, Arguments, false, null);
5888 ec.ig.Emit (OpCodes.Initobj, type);
5890 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5892 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5897 /// 14.5.10.2: Represents an array creation expression.
5901 /// There are two possible scenarios here: one is an array creation
5902 /// expression that specifies the dimensions and optionally the
5903 /// initialization data and the other which does not need dimensions
5904 /// specified but where initialization data is mandatory.
5906 public class ArrayCreation : Expression {
5907 Expression requested_base_type;
5908 ArrayList initializers;
5911 // The list of Argument types.
5912 // This is used to construct the `newarray' or constructor signature
5914 ArrayList arguments;
5917 // Method used to create the array object.
5919 MethodBase new_method = null;
5921 Type array_element_type;
5922 Type underlying_type;
5923 bool is_one_dimensional = false;
5924 bool is_builtin_type = false;
5925 bool expect_initializers = false;
5926 int num_arguments = 0;
5930 ArrayList array_data;
5934 // The number of constants in array initializers
5935 int const_initializers_count;
5937 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5939 this.requested_base_type = requested_base_type;
5940 this.initializers = initializers;
5944 arguments = new ArrayList ();
5946 foreach (Expression e in exprs) {
5947 arguments.Add (new Argument (e, Argument.AType.Expression));
5952 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5954 this.requested_base_type = requested_base_type;
5955 this.initializers = initializers;
5959 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5961 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5963 //dimensions = tmp.Length - 1;
5964 expect_initializers = true;
5967 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5969 StringBuilder sb = new StringBuilder (rank);
5972 for (int i = 1; i < idx_count; i++)
5977 return new ComposedCast (base_type, sb.ToString (), loc);
5980 void Error_IncorrectArrayInitializer ()
5982 Error (178, "Invalid rank specifier: expected `,' or `]'");
5985 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5987 if (specified_dims) {
5988 Argument a = (Argument) arguments [idx];
5990 if (!a.Resolve (ec, loc))
5993 Constant c = a.Expr as Constant;
5995 c = c.ToType (TypeManager.int32_type, a.Expr.Location);
5999 Report.Error (150, a.Expr.Location, "A constant value is expected");
6003 int value = (int) c.GetValue ();
6005 if (value != probe.Count) {
6006 Error_IncorrectArrayInitializer ();
6010 bounds [idx] = value;
6013 int child_bounds = -1;
6014 for (int i = 0; i < probe.Count; ++i) {
6015 object o = probe [i];
6016 if (o is ArrayList) {
6017 ArrayList sub_probe = o as ArrayList;
6018 int current_bounds = sub_probe.Count;
6020 if (child_bounds == -1)
6021 child_bounds = current_bounds;
6023 else if (child_bounds != current_bounds){
6024 Error_IncorrectArrayInitializer ();
6027 if (idx + 1 >= dimensions){
6028 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
6032 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
6036 if (child_bounds != -1){
6037 Error_IncorrectArrayInitializer ();
6041 Expression tmp = (Expression) o;
6042 tmp = tmp.Resolve (ec);
6046 Expression conv = Convert.ImplicitConversionRequired (
6047 ec, tmp, underlying_type, loc);
6052 // Initializers with the default values can be ignored
6053 Constant c = tmp as Constant;
6055 if (c.IsDefaultInitializer (array_element_type)) {
6059 ++const_initializers_count;
6062 // Used to invalidate static initializer
6063 const_initializers_count = int.MinValue;
6066 array_data.Add (conv);
6073 public void UpdateIndices ()
6076 for (ArrayList probe = initializers; probe != null;) {
6077 if (probe.Count > 0 && probe [0] is ArrayList) {
6078 Expression e = new IntConstant (probe.Count, Location.Null);
6079 arguments.Add (new Argument (e, Argument.AType.Expression));
6081 bounds [i++] = probe.Count;
6083 probe = (ArrayList) probe [0];
6086 Expression e = new IntConstant (probe.Count, Location.Null);
6087 arguments.Add (new Argument (e, Argument.AType.Expression));
6089 bounds [i++] = probe.Count;
6096 bool ResolveInitializers (EmitContext ec)
6098 if (initializers == null) {
6099 return !expect_initializers;
6102 if (underlying_type == null)
6106 // We use this to store all the date values in the order in which we
6107 // will need to store them in the byte blob later
6109 array_data = new ArrayList ();
6110 bounds = new System.Collections.Specialized.HybridDictionary ();
6112 if (arguments != null)
6113 return CheckIndices (ec, initializers, 0, true);
6115 arguments = new ArrayList ();
6117 if (!CheckIndices (ec, initializers, 0, false))
6122 if (arguments.Count != dimensions) {
6123 Error_IncorrectArrayInitializer ();
6131 // Creates the type of the array
6133 bool LookupType (EmitContext ec)
6135 StringBuilder array_qualifier = new StringBuilder (rank);
6138 // `In the first form allocates an array instace of the type that results
6139 // from deleting each of the individual expression from the expression list'
6141 if (num_arguments > 0) {
6142 array_qualifier.Append ("[");
6143 for (int i = num_arguments-1; i > 0; i--)
6144 array_qualifier.Append (",");
6145 array_qualifier.Append ("]");
6151 TypeExpr array_type_expr;
6152 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
6153 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
6154 if (array_type_expr == null)
6157 type = array_type_expr.Type;
6158 underlying_type = TypeManager.GetElementType (type);
6159 dimensions = type.GetArrayRank ();
6164 public override Expression DoResolve (EmitContext ec)
6169 if (!LookupType (ec))
6172 array_element_type = TypeManager.GetElementType (type);
6173 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
6174 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
6179 // First step is to validate the initializers and fill
6180 // in any missing bits
6182 if (!ResolveInitializers (ec))
6186 if (arguments == null)
6189 arg_count = arguments.Count;
6190 foreach (Argument a in arguments){
6191 if (!a.Resolve (ec, loc))
6194 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
6195 if (real_arg == null)
6202 if (arg_count == 1) {
6203 is_one_dimensional = true;
6204 eclass = ExprClass.Value;
6208 is_builtin_type = TypeManager.IsBuiltinType (type);
6210 if (is_builtin_type) {
6213 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
6214 AllBindingFlags, loc);
6216 if (!(ml is MethodGroupExpr)) {
6217 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
6222 Error (-6, "New invocation: Can not find a constructor for " +
6223 "this argument list");
6227 new_method = Invocation.OverloadResolve (
6228 ec, (MethodGroupExpr) ml, arguments, false, loc);
6230 if (new_method == null) {
6231 Error (-6, "New invocation: Can not find a constructor for " +
6232 "this argument list");
6236 eclass = ExprClass.Value;
6239 ModuleBuilder mb = CodeGen.Module.Builder;
6240 ArrayList args = new ArrayList ();
6242 if (arguments != null) {
6243 for (int i = 0; i < arg_count; i++)
6244 args.Add (TypeManager.int32_type);
6247 Type [] arg_types = null;
6250 arg_types = new Type [args.Count];
6252 args.CopyTo (arg_types, 0);
6254 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6257 if (new_method == null) {
6258 Error (-6, "New invocation: Can not find a constructor for " +
6259 "this argument list");
6263 eclass = ExprClass.Value;
6268 byte [] MakeByteBlob ()
6273 int count = array_data.Count;
6275 if (underlying_type.IsEnum)
6276 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
6278 factor = GetTypeSize (underlying_type);
6280 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
6282 data = new byte [(count * factor + 4) & ~3];
6285 for (int i = 0; i < count; ++i) {
6286 object v = array_data [i];
6288 if (v is EnumConstant)
6289 v = ((EnumConstant) v).Child;
6291 if (v is Constant && !(v is StringConstant))
6292 v = ((Constant) v).GetValue ();
6298 if (underlying_type == TypeManager.int64_type){
6299 if (!(v is Expression)){
6300 long val = (long) v;
6302 for (int j = 0; j < factor; ++j) {
6303 data [idx + j] = (byte) (val & 0xFF);
6307 } else if (underlying_type == TypeManager.uint64_type){
6308 if (!(v is Expression)){
6309 ulong val = (ulong) v;
6311 for (int j = 0; j < factor; ++j) {
6312 data [idx + j] = (byte) (val & 0xFF);
6316 } else if (underlying_type == TypeManager.float_type) {
6317 if (!(v is Expression)){
6318 element = BitConverter.GetBytes ((float) v);
6320 for (int j = 0; j < factor; ++j)
6321 data [idx + j] = element [j];
6323 } else if (underlying_type == TypeManager.double_type) {
6324 if (!(v is Expression)){
6325 element = BitConverter.GetBytes ((double) v);
6327 for (int j = 0; j < factor; ++j)
6328 data [idx + j] = element [j];
6330 } else if (underlying_type == TypeManager.char_type){
6331 if (!(v is Expression)){
6332 int val = (int) ((char) v);
6334 data [idx] = (byte) (val & 0xff);
6335 data [idx+1] = (byte) (val >> 8);
6337 } else if (underlying_type == TypeManager.short_type){
6338 if (!(v is Expression)){
6339 int val = (int) ((short) v);
6341 data [idx] = (byte) (val & 0xff);
6342 data [idx+1] = (byte) (val >> 8);
6344 } else if (underlying_type == TypeManager.ushort_type){
6345 if (!(v is Expression)){
6346 int val = (int) ((ushort) v);
6348 data [idx] = (byte) (val & 0xff);
6349 data [idx+1] = (byte) (val >> 8);
6351 } else if (underlying_type == TypeManager.int32_type) {
6352 if (!(v is Expression)){
6355 data [idx] = (byte) (val & 0xff);
6356 data [idx+1] = (byte) ((val >> 8) & 0xff);
6357 data [idx+2] = (byte) ((val >> 16) & 0xff);
6358 data [idx+3] = (byte) (val >> 24);
6360 } else if (underlying_type == TypeManager.uint32_type) {
6361 if (!(v is Expression)){
6362 uint val = (uint) v;
6364 data [idx] = (byte) (val & 0xff);
6365 data [idx+1] = (byte) ((val >> 8) & 0xff);
6366 data [idx+2] = (byte) ((val >> 16) & 0xff);
6367 data [idx+3] = (byte) (val >> 24);
6369 } else if (underlying_type == TypeManager.sbyte_type) {
6370 if (!(v is Expression)){
6371 sbyte val = (sbyte) v;
6372 data [idx] = (byte) val;
6374 } else if (underlying_type == TypeManager.byte_type) {
6375 if (!(v is Expression)){
6376 byte val = (byte) v;
6377 data [idx] = (byte) val;
6379 } else if (underlying_type == TypeManager.bool_type) {
6380 if (!(v is Expression)){
6381 bool val = (bool) v;
6382 data [idx] = (byte) (val ? 1 : 0);
6384 } else if (underlying_type == TypeManager.decimal_type){
6385 if (!(v is Expression)){
6386 int [] bits = Decimal.GetBits ((decimal) v);
6389 // FIXME: For some reason, this doesn't work on the MS runtime.
6390 int [] nbits = new int [4];
6391 nbits [0] = bits [3];
6392 nbits [1] = bits [2];
6393 nbits [2] = bits [0];
6394 nbits [3] = bits [1];
6396 for (int j = 0; j < 4; j++){
6397 data [p++] = (byte) (nbits [j] & 0xff);
6398 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6399 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6400 data [p++] = (byte) (nbits [j] >> 24);
6404 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6413 // Emits the initializers for the array
6415 void EmitStaticInitializers (EmitContext ec)
6418 // First, the static data
6421 ILGenerator ig = ec.ig;
6423 byte [] data = MakeByteBlob ();
6425 fb = RootContext.MakeStaticData (data);
6427 ig.Emit (OpCodes.Dup);
6428 ig.Emit (OpCodes.Ldtoken, fb);
6429 ig.Emit (OpCodes.Call,
6430 TypeManager.void_initializearray_array_fieldhandle);
6434 // Emits pieces of the array that can not be computed at compile
6435 // time (variables and string locations).
6437 // This always expect the top value on the stack to be the array
6439 void EmitDynamicInitializers (EmitContext ec)
6441 ILGenerator ig = ec.ig;
6442 int dims = bounds.Count;
6443 int [] current_pos = new int [dims];
6445 MethodInfo set = null;
6448 Type [] args = new Type [dims + 1];
6450 for (int j = 0; j < dims; j++)
6451 args [j] = TypeManager.int32_type;
6452 args [dims] = array_element_type;
6454 set = CodeGen.Module.Builder.GetArrayMethod (
6456 CallingConventions.HasThis | CallingConventions.Standard,
6457 TypeManager.void_type, args);
6460 for (int i = 0; i < array_data.Count; i++){
6462 Expression e = (Expression)array_data [i];
6465 Type etype = e.Type;
6467 ig.Emit (OpCodes.Dup);
6469 for (int idx = 0; idx < dims; idx++)
6470 IntConstant.EmitInt (ig, current_pos [idx]);
6473 // If we are dealing with a struct, get the
6474 // address of it, so we can store it.
6477 TypeManager.IsValueType (etype) &&
6478 (!TypeManager.IsBuiltinOrEnum (etype) ||
6479 etype == TypeManager.decimal_type)) {
6484 // Let new know that we are providing
6485 // the address where to store the results
6487 n.DisableTemporaryValueType ();
6490 ig.Emit (OpCodes.Ldelema, etype);
6496 bool is_stobj, has_type_arg;
6497 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
6499 ig.Emit (OpCodes.Stobj, etype);
6500 else if (has_type_arg)
6501 ig.Emit (op, etype);
6505 ig.Emit (OpCodes.Call, set);
6512 for (int j = dims - 1; j >= 0; j--){
6514 if (current_pos [j] < (int) bounds [j])
6516 current_pos [j] = 0;
6521 void EmitArrayArguments (EmitContext ec)
6523 ILGenerator ig = ec.ig;
6525 foreach (Argument a in arguments) {
6526 Type atype = a.Type;
6529 if (atype == TypeManager.uint64_type)
6530 ig.Emit (OpCodes.Conv_Ovf_U4);
6531 else if (atype == TypeManager.int64_type)
6532 ig.Emit (OpCodes.Conv_Ovf_I4);
6536 public override void Emit (EmitContext ec)
6538 ILGenerator ig = ec.ig;
6540 EmitArrayArguments (ec);
6541 if (is_one_dimensional)
6542 ig.Emit (OpCodes.Newarr, array_element_type);
6544 if (is_builtin_type)
6545 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6547 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6550 if (initializers == null)
6553 // This is a treshold for static initializers
6554 // I tried to make more accurate but it seems to me that Array.Initialize is
6555 // always slower (managed -> unmanaged switch?)
6556 const int max_automatic_initializers = 200;
6558 if (const_initializers_count > max_automatic_initializers && TypeManager.IsPrimitiveType (array_element_type)) {
6559 EmitStaticInitializers (ec);
6563 EmitDynamicInitializers (ec);
6566 public override bool GetAttributableValue (Type valueType, out object value)
6568 if (!is_one_dimensional){
6569 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6570 return base.GetAttributableValue (null, out value);
6573 if (array_data == null) {
6574 Constant c = (Constant)((Argument)arguments [0]).Expr;
6575 if (c.IsDefaultValue) {
6576 value = Array.CreateInstance (array_element_type, 0);
6579 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6580 return base.GetAttributableValue (null, out value);
6583 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6584 object element_value;
6585 for (int i = 0; i < ret.Length; ++i)
6587 Expression e = (Expression)array_data [i];
6588 if (e == null) // Is null when initializer is optimized away
6589 e = (Expression)initializers [i];
6591 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6595 ret.SetValue (element_value, i);
6602 public sealed class CompilerGeneratedThis : This
6604 public static This Instance = new CompilerGeneratedThis ();
6606 private CompilerGeneratedThis ()
6607 : base (Location.Null)
6611 public override Expression DoResolve (EmitContext ec)
6613 eclass = ExprClass.Variable;
6614 type = ec.ContainerType;
6620 /// Represents the `this' construct
6623 public class This : Expression, IAssignMethod, IMemoryLocation, IVariable {
6626 VariableInfo variable_info;
6628 public This (Block block, Location loc)
6634 public This (Location loc)
6639 public VariableInfo VariableInfo {
6640 get { return variable_info; }
6643 public bool VerifyFixed ()
6645 return !TypeManager.IsValueType (Type);
6648 public bool ResolveBase (EmitContext ec)
6650 eclass = ExprClass.Variable;
6652 if (ec.TypeContainer.CurrentType != null)
6653 type = ec.TypeContainer.CurrentType;
6655 type = ec.ContainerType;
6658 Error (26, "Keyword `this' is not valid in a static property, static method, or static field initializer");
6662 if (block != null && block.Toplevel.ThisVariable != null)
6663 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6665 if (ec.CurrentAnonymousMethod != null)
6671 public override Expression DoResolve (EmitContext ec)
6673 if (!ResolveBase (ec))
6676 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) && !variable_info.IsAssigned (ec)) {
6677 Error (188, "The `this' object cannot be used before all of its fields are assigned to");
6678 variable_info.SetAssigned (ec);
6682 if (ec.IsFieldInitializer) {
6683 Error (27, "Keyword `this' is not available in the current context");
6690 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6692 if (!ResolveBase (ec))
6695 if (variable_info != null)
6696 variable_info.SetAssigned (ec);
6698 if (ec.TypeContainer is Class){
6699 Error (1604, "Cannot assign to 'this' because it is read-only");
6706 public void Emit (EmitContext ec, bool leave_copy)
6710 ec.ig.Emit (OpCodes.Dup);
6713 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
6715 ILGenerator ig = ec.ig;
6717 if (ec.TypeContainer is Struct){
6718 ec.EmitThis (false);
6721 LocalTemporary t = null;
6723 t = new LocalTemporary (type);
6724 ec.ig.Emit (OpCodes.Dup);
6728 ig.Emit (OpCodes.Stobj, type);
6735 throw new Exception ("how did you get here");
6739 public override void Emit (EmitContext ec)
6741 ILGenerator ig = ec.ig;
6743 ec.EmitThis (false);
6744 if (ec.TypeContainer is Struct)
6745 ig.Emit (OpCodes.Ldobj, type);
6748 public override int GetHashCode()
6750 return block.GetHashCode ();
6753 public override bool Equals (object obj)
6755 This t = obj as This;
6759 return block == t.block;
6762 public void AddressOf (EmitContext ec, AddressOp mode)
6767 // FIGURE OUT WHY LDARG_S does not work
6769 // consider: struct X { int val; int P { set { val = value; }}}
6771 // Yes, this looks very bad. Look at `NOTAS' for
6773 // ec.ig.Emit (OpCodes.Ldarga_S, (byte) 0);
6778 /// Represents the `__arglist' construct
6780 public class ArglistAccess : Expression
6782 public ArglistAccess (Location loc)
6787 public override Expression DoResolve (EmitContext ec)
6789 eclass = ExprClass.Variable;
6790 type = TypeManager.runtime_argument_handle_type;
6792 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6794 Error (190, "The __arglist construct is valid only within " +
6795 "a variable argument method");
6802 public override void Emit (EmitContext ec)
6804 ec.ig.Emit (OpCodes.Arglist);
6809 /// Represents the `__arglist (....)' construct
6811 public class Arglist : Expression
6813 public readonly Argument[] Arguments;
6815 public Arglist (Argument[] args, Location l)
6821 public Type[] ArgumentTypes {
6823 Type[] retval = new Type [Arguments.Length];
6824 for (int i = 0; i < Arguments.Length; i++)
6825 retval [i] = Arguments [i].Type;
6830 public override Expression DoResolve (EmitContext ec)
6832 eclass = ExprClass.Variable;
6833 type = TypeManager.runtime_argument_handle_type;
6835 foreach (Argument arg in Arguments) {
6836 if (!arg.Resolve (ec, loc))
6843 public override void Emit (EmitContext ec)
6845 foreach (Argument arg in Arguments)
6851 // This produces the value that renders an instance, used by the iterators code
6853 public class ProxyInstance : Expression, IMemoryLocation {
6854 public override Expression DoResolve (EmitContext ec)
6856 eclass = ExprClass.Variable;
6857 type = ec.ContainerType;
6861 public override void Emit (EmitContext ec)
6863 ec.ig.Emit (OpCodes.Ldarg_0);
6867 public void AddressOf (EmitContext ec, AddressOp mode)
6869 ec.ig.Emit (OpCodes.Ldarg_0);
6874 /// Implements the typeof operator
6876 public class TypeOf : Expression {
6877 readonly Expression QueriedType;
6878 protected Type typearg;
6880 public TypeOf (Expression queried_type, Location l)
6882 QueriedType = queried_type;
6886 public override Expression DoResolve (EmitContext ec)
6888 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6892 typearg = texpr.Type;
6894 if (typearg == TypeManager.void_type) {
6895 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6899 if (typearg.IsPointer && !ec.InUnsafe){
6904 type = TypeManager.type_type;
6905 // Even though what is returned is a type object, it's treated as a value by the compiler.
6906 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6907 eclass = ExprClass.Value;
6911 public override void Emit (EmitContext ec)
6913 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6914 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6917 public override bool GetAttributableValue (Type valueType, out object value)
6919 if (valueType == TypeManager.object_type) {
6920 value = (object)typearg;
6927 public Type TypeArgument
6937 /// Implements the `typeof (void)' operator
6939 public class TypeOfVoid : TypeOf {
6940 public TypeOfVoid (Location l) : base (null, l)
6945 public override Expression DoResolve (EmitContext ec)
6947 type = TypeManager.type_type;
6948 typearg = TypeManager.void_type;
6949 // See description in TypeOf.
6950 eclass = ExprClass.Value;
6956 /// Implements the sizeof expression
6958 public class SizeOf : Expression {
6959 public Expression QueriedType;
6962 public SizeOf (Expression queried_type, Location l)
6964 this.QueriedType = queried_type;
6968 public override Expression DoResolve (EmitContext ec)
6970 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6974 if (texpr is TypeParameterExpr){
6975 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
6979 type_queried = texpr.Type;
6980 if (type_queried.IsEnum)
6981 type_queried = TypeManager.EnumToUnderlying (type_queried);
6983 if (type_queried == TypeManager.void_type) {
6984 Expression.Error_VoidInvalidInTheContext (loc);
6988 int size_of = GetTypeSize (type_queried);
6990 return new IntConstant (size_of, loc);
6994 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)",
6995 TypeManager.CSharpName (type_queried));
6999 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
7003 type = TypeManager.int32_type;
7004 eclass = ExprClass.Value;
7008 public override void Emit (EmitContext ec)
7010 int size = GetTypeSize (type_queried);
7013 ec.ig.Emit (OpCodes.Sizeof, type_queried);
7015 IntConstant.EmitInt (ec.ig, size);
7020 /// Implements the qualified-alias-member (::) expression.
7022 public class QualifiedAliasMember : Expression
7024 string alias, identifier;
7026 public QualifiedAliasMember (string alias, string identifier, Location l)
7029 this.identifier = identifier;
7033 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7035 if (alias == "global")
7036 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
7038 int errors = Report.Errors;
7039 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7041 if (errors == Report.Errors)
7042 Report.Error (432, loc, "Alias `{0}' not found", alias);
7045 if (fne.eclass != ExprClass.Namespace) {
7047 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7050 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
7053 public override Expression DoResolve (EmitContext ec)
7055 FullNamedExpression fne;
7056 if (alias == "global") {
7057 fne = RootNamespace.Global;
7059 int errors = Report.Errors;
7060 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7062 if (errors == Report.Errors)
7063 Report.Error (432, loc, "Alias `{0}' not found", alias);
7068 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
7072 if (!(retval is FullNamedExpression)) {
7073 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
7077 // We defer this check till the end to match the behaviour of CSC
7078 if (fne.eclass != ExprClass.Namespace) {
7079 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7085 public override void Emit (EmitContext ec)
7087 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
7091 public override string ToString ()
7093 return alias + "::" + identifier;
7096 public override string GetSignatureForError ()
7103 /// Implements the member access expression
7105 public class MemberAccess : Expression {
7106 public readonly string Identifier;
7110 public MemberAccess (Expression expr, string id)
7111 : this (expr, id, expr.Location)
7115 public MemberAccess (Expression expr, string identifier, Location loc)
7118 Identifier = identifier;
7122 public MemberAccess (Expression expr, string id, TypeArguments args)
7128 public Expression Expr {
7129 get { return expr; }
7132 // TODO: this method has very poor performace for Enum fields and
7133 // probably for other constants as well
7134 Expression DoResolve (EmitContext ec, Expression right_side)
7137 throw new Exception ();
7140 // Resolve the expression with flow analysis turned off, we'll do the definite
7141 // assignment checks later. This is because we don't know yet what the expression
7142 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7143 // definite assignment check on the actual field and not on the whole struct.
7146 SimpleName original = expr as SimpleName;
7147 Expression new_expr = expr.Resolve (ec,
7148 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7149 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7151 if (new_expr == null)
7154 if (new_expr is Namespace) {
7155 Namespace ns = (Namespace) new_expr;
7156 string lookup_id = MemberName.MakeName (Identifier, args);
7157 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, lookup_id, loc);
7158 if ((retval != null) && (args != null))
7159 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
7161 ns.Error_NamespaceDoesNotExist (loc, Identifier);
7165 Type expr_type = new_expr.Type;
7166 if (expr_type.IsPointer){
7167 Error (23, "The `.' operator can not be applied to pointer operands (" +
7168 TypeManager.CSharpName (expr_type) + ")");
7170 } else if (expr_type == TypeManager.void_type) {
7171 Error (23, "The `.' operator can not be applied to operands of type 'void'");
7173 } else if (expr_type == TypeManager.anonymous_method_type){
7174 Error (23, "The `.' operator can not be applied to anonymous methods");
7178 Expression member_lookup;
7179 member_lookup = MemberLookup (
7180 ec.ContainerType, expr_type, expr_type, Identifier, loc);
7181 if ((member_lookup == null) && (args != null)) {
7182 string lookup_id = MemberName.MakeName (Identifier, args);
7183 member_lookup = MemberLookup (
7184 ec.ContainerType, expr_type, expr_type, lookup_id, loc);
7186 if (member_lookup == null) {
7187 MemberLookupFailed (
7188 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
7192 if (member_lookup is TypeExpr) {
7193 if (!(new_expr is TypeExpr) &&
7194 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
7195 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7196 Identifier, member_lookup.GetSignatureForError ());
7200 ConstructedType ct = new_expr as ConstructedType;
7203 // When looking up a nested type in a generic instance
7204 // via reflection, we always get a generic type definition
7205 // and not a generic instance - so we have to do this here.
7207 // See gtest-172-lib.cs and gtest-172.cs for an example.
7209 ct = new ConstructedType (
7210 member_lookup.Type, ct.TypeArguments, loc);
7212 return ct.ResolveAsTypeStep (ec, false);
7215 return member_lookup;
7218 MemberExpr me = (MemberExpr) member_lookup;
7219 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
7220 if (member_lookup == null)
7224 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
7226 throw new InternalErrorException ();
7228 return mg.ResolveGeneric (ec, args);
7231 if (original != null && !TypeManager.IsValueType (expr_type)) {
7232 me = member_lookup as MemberExpr;
7233 if (me != null && me.IsInstance) {
7234 LocalVariableReference var = new_expr as LocalVariableReference;
7235 if (var != null && !var.VerifyAssigned (ec))
7240 // The following DoResolve/DoResolveLValue will do the definite assignment
7243 if (right_side != null)
7244 return member_lookup.DoResolveLValue (ec, right_side);
7246 return member_lookup.DoResolve (ec);
7249 public override Expression DoResolve (EmitContext ec)
7251 return DoResolve (ec, null);
7254 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7256 return DoResolve (ec, right_side);
7259 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7261 return ResolveNamespaceOrType (ec, silent);
7264 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7266 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
7268 if (new_expr == null)
7271 string lookup_id = MemberName.MakeName (Identifier, args);
7273 if (new_expr is Namespace) {
7274 Namespace ns = (Namespace) new_expr;
7275 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, lookup_id, loc);
7276 if ((retval != null) && (args != null))
7277 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
7278 if (!silent && retval == null)
7279 ns.Error_NamespaceDoesNotExist (loc, Identifier);
7283 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
7284 if (tnew_expr == null)
7287 Type expr_type = tnew_expr.Type;
7289 if (expr_type.IsPointer){
7290 Error (23, "The `.' operator can not be applied to pointer operands (" +
7291 TypeManager.CSharpName (expr_type) + ")");
7295 Expression member_lookup = MemberLookup (
7296 rc.DeclContainer.TypeBuilder, expr_type, expr_type, lookup_id,
7297 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7298 if (member_lookup == null) {
7299 int errors = Report.Errors;
7300 MemberLookupFailed (rc.DeclContainer.TypeBuilder, expr_type, expr_type, lookup_id, null, false, loc);
7302 if (!silent && errors == Report.Errors) {
7303 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7304 Identifier, new_expr.GetSignatureForError ());
7309 if (!(member_lookup is TypeExpr)) {
7310 new_expr.Error_UnexpectedKind (rc.DeclContainer, "type", loc);
7314 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
7318 TypeArguments the_args = args;
7319 if (TypeManager.HasGenericArguments (expr_type)) {
7320 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
7322 TypeArguments new_args = new TypeArguments (loc);
7323 foreach (Type decl in decl_args)
7324 new_args.Add (new TypeExpression (decl, loc));
7327 new_args.Add (args);
7329 the_args = new_args;
7332 if (the_args != null) {
7333 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
7334 return ctype.ResolveAsTypeStep (rc, false);
7340 public override void Emit (EmitContext ec)
7342 throw new Exception ("Should not happen");
7345 public override string ToString ()
7347 return expr + "." + MemberName.MakeName (Identifier, args);
7350 public override string GetSignatureForError ()
7352 return expr.GetSignatureForError () + "." + Identifier;
7357 /// Implements checked expressions
7359 public class CheckedExpr : Expression {
7361 public Expression Expr;
7363 public CheckedExpr (Expression e, Location l)
7369 public override Expression DoResolve (EmitContext ec)
7371 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7372 Expr = Expr.Resolve (ec);
7377 if (Expr is Constant)
7380 eclass = Expr.eclass;
7385 public override void Emit (EmitContext ec)
7387 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7391 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7393 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7394 Expr.EmitBranchable (ec, target, onTrue);
7399 /// Implements the unchecked expression
7401 public class UnCheckedExpr : Expression {
7403 public Expression Expr;
7405 public UnCheckedExpr (Expression e, Location l)
7411 public override Expression DoResolve (EmitContext ec)
7413 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7414 Expr = Expr.Resolve (ec);
7419 if (Expr is Constant)
7422 eclass = Expr.eclass;
7427 public override void Emit (EmitContext ec)
7429 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7433 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7435 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7436 Expr.EmitBranchable (ec, target, onTrue);
7441 /// An Element Access expression.
7443 /// During semantic analysis these are transformed into
7444 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7446 public class ElementAccess : Expression {
7447 public ArrayList Arguments;
7448 public Expression Expr;
7450 public ElementAccess (Expression e, ArrayList e_list)
7459 Arguments = new ArrayList ();
7460 foreach (Expression tmp in e_list)
7461 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7465 bool CommonResolve (EmitContext ec)
7467 Expr = Expr.Resolve (ec);
7472 if (Arguments == null)
7475 foreach (Argument a in Arguments){
7476 if (!a.Resolve (ec, loc))
7483 Expression MakePointerAccess (EmitContext ec, Type t)
7485 if (t == TypeManager.void_ptr_type){
7486 Error (242, "The array index operation is not valid on void pointers");
7489 if (Arguments.Count != 1){
7490 Error (196, "A pointer must be indexed by only one value");
7495 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7498 return new Indirection (p, loc).Resolve (ec);
7501 public override Expression DoResolve (EmitContext ec)
7503 if (!CommonResolve (ec))
7507 // We perform some simple tests, and then to "split" the emit and store
7508 // code we create an instance of a different class, and return that.
7510 // I am experimenting with this pattern.
7514 if (t == TypeManager.array_type){
7515 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7520 return (new ArrayAccess (this, loc)).Resolve (ec);
7522 return MakePointerAccess (ec, Expr.Type);
7524 FieldExpr fe = Expr as FieldExpr;
7526 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7528 return MakePointerAccess (ec, ff.ElementType);
7531 return (new IndexerAccess (this, loc)).Resolve (ec);
7534 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7536 if (!CommonResolve (ec))
7541 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7544 return MakePointerAccess (ec, Expr.Type);
7546 FieldExpr fe = Expr as FieldExpr;
7548 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7550 if (!(fe.InstanceExpression is LocalVariableReference) &&
7551 !(fe.InstanceExpression is This)) {
7552 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7555 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7556 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7559 return MakePointerAccess (ec, ff.ElementType);
7562 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7565 public override void Emit (EmitContext ec)
7567 throw new Exception ("Should never be reached");
7572 /// Implements array access
7574 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7576 // Points to our "data" repository
7580 LocalTemporary temp;
7583 public ArrayAccess (ElementAccess ea_data, Location l)
7586 eclass = ExprClass.Variable;
7590 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7592 return DoResolve (ec);
7595 public override Expression DoResolve (EmitContext ec)
7598 ExprClass eclass = ea.Expr.eclass;
7600 // As long as the type is valid
7601 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7602 eclass == ExprClass.Value)) {
7603 ea.Expr.Error_UnexpectedKind ("variable or value");
7608 Type t = ea.Expr.Type;
7609 if (t.GetArrayRank () != ea.Arguments.Count){
7610 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7611 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7615 type = TypeManager.GetElementType (t);
7616 if (type.IsPointer && !ec.InUnsafe){
7617 UnsafeError (ea.Location);
7621 foreach (Argument a in ea.Arguments){
7622 Type argtype = a.Type;
7624 if (argtype == TypeManager.int32_type ||
7625 argtype == TypeManager.uint32_type ||
7626 argtype == TypeManager.int64_type ||
7627 argtype == TypeManager.uint64_type) {
7628 Constant c = a.Expr as Constant;
7629 if (c != null && c.IsNegative) {
7630 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7636 // Mhm. This is strage, because the Argument.Type is not the same as
7637 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7639 // Wonder if I will run into trouble for this.
7641 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7646 eclass = ExprClass.Variable;
7652 /// Emits the right opcode to load an object of Type `t'
7653 /// from an array of T
7655 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7657 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7658 ig.Emit (OpCodes.Ldelem_U1);
7659 else if (type == TypeManager.sbyte_type)
7660 ig.Emit (OpCodes.Ldelem_I1);
7661 else if (type == TypeManager.short_type)
7662 ig.Emit (OpCodes.Ldelem_I2);
7663 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7664 ig.Emit (OpCodes.Ldelem_U2);
7665 else if (type == TypeManager.int32_type)
7666 ig.Emit (OpCodes.Ldelem_I4);
7667 else if (type == TypeManager.uint32_type)
7668 ig.Emit (OpCodes.Ldelem_U4);
7669 else if (type == TypeManager.uint64_type)
7670 ig.Emit (OpCodes.Ldelem_I8);
7671 else if (type == TypeManager.int64_type)
7672 ig.Emit (OpCodes.Ldelem_I8);
7673 else if (type == TypeManager.float_type)
7674 ig.Emit (OpCodes.Ldelem_R4);
7675 else if (type == TypeManager.double_type)
7676 ig.Emit (OpCodes.Ldelem_R8);
7677 else if (type == TypeManager.intptr_type)
7678 ig.Emit (OpCodes.Ldelem_I);
7679 else if (TypeManager.IsEnumType (type)){
7680 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7681 } else if (type.IsValueType){
7682 ig.Emit (OpCodes.Ldelema, type);
7683 ig.Emit (OpCodes.Ldobj, type);
7684 } else if (type.IsGenericParameter)
7686 ig.Emit (OpCodes.Ldelem, type);
7688 ig.Emit (OpCodes.Ldelem_Any, type);
7690 else if (type.IsPointer)
7691 ig.Emit (OpCodes.Ldelem_I);
7693 ig.Emit (OpCodes.Ldelem_Ref);
7697 /// Returns the right opcode to store an object of Type `t'
7698 /// from an array of T.
7700 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7702 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7703 has_type_arg = false; is_stobj = false;
7704 t = TypeManager.TypeToCoreType (t);
7705 if (TypeManager.IsEnumType (t))
7706 t = TypeManager.EnumToUnderlying (t);
7707 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7708 t == TypeManager.bool_type)
7709 return OpCodes.Stelem_I1;
7710 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7711 t == TypeManager.char_type)
7712 return OpCodes.Stelem_I2;
7713 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7714 return OpCodes.Stelem_I4;
7715 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7716 return OpCodes.Stelem_I8;
7717 else if (t == TypeManager.float_type)
7718 return OpCodes.Stelem_R4;
7719 else if (t == TypeManager.double_type)
7720 return OpCodes.Stelem_R8;
7721 else if (t == TypeManager.intptr_type) {
7722 has_type_arg = true;
7724 return OpCodes.Stobj;
7725 } else if (t.IsValueType) {
7726 has_type_arg = true;
7728 return OpCodes.Stobj;
7729 } else if (t.IsGenericParameter) {
7730 has_type_arg = true;
7732 return OpCodes.Stelem;
7734 return OpCodes.Stelem_Any;
7737 } else if (t.IsPointer)
7738 return OpCodes.Stelem_I;
7740 return OpCodes.Stelem_Ref;
7743 MethodInfo FetchGetMethod ()
7745 ModuleBuilder mb = CodeGen.Module.Builder;
7746 int arg_count = ea.Arguments.Count;
7747 Type [] args = new Type [arg_count];
7750 for (int i = 0; i < arg_count; i++){
7751 //args [i++] = a.Type;
7752 args [i] = TypeManager.int32_type;
7755 get = mb.GetArrayMethod (
7756 ea.Expr.Type, "Get",
7757 CallingConventions.HasThis |
7758 CallingConventions.Standard,
7764 MethodInfo FetchAddressMethod ()
7766 ModuleBuilder mb = CodeGen.Module.Builder;
7767 int arg_count = ea.Arguments.Count;
7768 Type [] args = new Type [arg_count];
7772 ret_type = TypeManager.GetReferenceType (type);
7774 for (int i = 0; i < arg_count; i++){
7775 //args [i++] = a.Type;
7776 args [i] = TypeManager.int32_type;
7779 address = mb.GetArrayMethod (
7780 ea.Expr.Type, "Address",
7781 CallingConventions.HasThis |
7782 CallingConventions.Standard,
7789 // Load the array arguments into the stack.
7791 // If we have been requested to cache the values (cached_locations array
7792 // initialized), then load the arguments the first time and store them
7793 // in locals. otherwise load from local variables.
7795 void LoadArrayAndArguments (EmitContext ec)
7797 ILGenerator ig = ec.ig;
7800 foreach (Argument a in ea.Arguments){
7801 Type argtype = a.Expr.Type;
7805 if (argtype == TypeManager.int64_type)
7806 ig.Emit (OpCodes.Conv_Ovf_I);
7807 else if (argtype == TypeManager.uint64_type)
7808 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7812 public void Emit (EmitContext ec, bool leave_copy)
7814 int rank = ea.Expr.Type.GetArrayRank ();
7815 ILGenerator ig = ec.ig;
7818 LoadArrayAndArguments (ec);
7821 EmitLoadOpcode (ig, type);
7825 method = FetchGetMethod ();
7826 ig.Emit (OpCodes.Call, method);
7829 LoadFromPtr (ec.ig, this.type);
7832 ec.ig.Emit (OpCodes.Dup);
7833 temp = new LocalTemporary (this.type);
7838 public override void Emit (EmitContext ec)
7843 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7845 int rank = ea.Expr.Type.GetArrayRank ();
7846 ILGenerator ig = ec.ig;
7847 Type t = source.Type;
7848 prepared = prepare_for_load;
7850 if (prepare_for_load) {
7851 AddressOf (ec, AddressOp.LoadStore);
7852 ec.ig.Emit (OpCodes.Dup);
7855 ec.ig.Emit (OpCodes.Dup);
7856 temp = new LocalTemporary (this.type);
7859 StoreFromPtr (ec.ig, t);
7869 LoadArrayAndArguments (ec);
7872 bool is_stobj, has_type_arg;
7873 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7876 // The stobj opcode used by value types will need
7877 // an address on the stack, not really an array/array
7881 ig.Emit (OpCodes.Ldelema, t);
7885 ec.ig.Emit (OpCodes.Dup);
7886 temp = new LocalTemporary (this.type);
7891 ig.Emit (OpCodes.Stobj, t);
7892 else if (has_type_arg)
7897 ModuleBuilder mb = CodeGen.Module.Builder;
7898 int arg_count = ea.Arguments.Count;
7899 Type [] args = new Type [arg_count + 1];
7904 ec.ig.Emit (OpCodes.Dup);
7905 temp = new LocalTemporary (this.type);
7909 for (int i = 0; i < arg_count; i++){
7910 //args [i++] = a.Type;
7911 args [i] = TypeManager.int32_type;
7914 args [arg_count] = type;
7916 set = mb.GetArrayMethod (
7917 ea.Expr.Type, "Set",
7918 CallingConventions.HasThis |
7919 CallingConventions.Standard,
7920 TypeManager.void_type, args);
7922 ig.Emit (OpCodes.Call, set);
7931 public void AddressOf (EmitContext ec, AddressOp mode)
7933 int rank = ea.Expr.Type.GetArrayRank ();
7934 ILGenerator ig = ec.ig;
7936 LoadArrayAndArguments (ec);
7939 ig.Emit (OpCodes.Ldelema, type);
7941 MethodInfo address = FetchAddressMethod ();
7942 ig.Emit (OpCodes.Call, address);
7946 public void EmitGetLength (EmitContext ec, int dim)
7948 int rank = ea.Expr.Type.GetArrayRank ();
7949 ILGenerator ig = ec.ig;
7953 ig.Emit (OpCodes.Ldlen);
7954 ig.Emit (OpCodes.Conv_I4);
7956 IntLiteral.EmitInt (ig, dim);
7957 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7963 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7964 public readonly ArrayList Properties;
7965 static Indexers empty;
7967 public struct Indexer {
7968 public readonly PropertyInfo PropertyInfo;
7969 public readonly MethodInfo Getter, Setter;
7971 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7973 this.PropertyInfo = property_info;
7981 empty = new Indexers (null);
7984 Indexers (ArrayList array)
7989 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
7994 foreach (PropertyInfo property in mi){
7995 MethodInfo get, set;
7997 get = property.GetGetMethod (true);
7998 set = property.GetSetMethod (true);
7999 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
8001 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
8003 if (get != null || set != null) {
8005 ix = new Indexers (new ArrayList ());
8006 ix.Properties.Add (new Indexer (property, get, set));
8011 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
8013 string p_name = TypeManager.IndexerPropertyName (lookup_type);
8015 return TypeManager.MemberLookup (
8016 caller_type, caller_type, lookup_type, MemberTypes.Property,
8017 BindingFlags.Public | BindingFlags.Instance |
8018 BindingFlags.DeclaredOnly, p_name, null);
8021 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
8023 Indexers ix = empty;
8025 if (lookup_type.IsGenericParameter) {
8026 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
8030 if (gc.HasClassConstraint)
8031 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
8033 Type[] ifaces = gc.InterfaceConstraints;
8034 foreach (Type itype in ifaces)
8035 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8040 Type copy = lookup_type;
8041 while (copy != TypeManager.object_type && copy != null){
8042 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
8043 copy = copy.BaseType;
8046 if (lookup_type.IsInterface) {
8047 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
8048 if (ifaces != null) {
8049 foreach (Type itype in ifaces)
8050 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8059 /// Expressions that represent an indexer call.
8061 public class IndexerAccess : Expression, IAssignMethod {
8063 // Points to our "data" repository
8065 MethodInfo get, set;
8066 ArrayList set_arguments;
8067 bool is_base_indexer;
8069 protected Type indexer_type;
8070 protected Type current_type;
8071 protected Expression instance_expr;
8072 protected ArrayList arguments;
8074 public IndexerAccess (ElementAccess ea, Location loc)
8075 : this (ea.Expr, false, loc)
8077 this.arguments = ea.Arguments;
8080 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
8083 this.instance_expr = instance_expr;
8084 this.is_base_indexer = is_base_indexer;
8085 this.eclass = ExprClass.Value;
8089 protected virtual bool CommonResolve (EmitContext ec)
8091 indexer_type = instance_expr.Type;
8092 current_type = ec.ContainerType;
8097 public override Expression DoResolve (EmitContext ec)
8099 if (!CommonResolve (ec))
8103 // Step 1: Query for all `Item' *properties*. Notice
8104 // that the actual methods are pointed from here.
8106 // This is a group of properties, piles of them.
8108 ArrayList AllGetters = null;
8110 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8111 if (ilist.Properties != null) {
8112 AllGetters = new ArrayList(ilist.Properties.Count);
8113 foreach (Indexers.Indexer ix in ilist.Properties) {
8114 if (ix.Getter != null)
8115 AllGetters.Add (ix.Getter);
8119 if (AllGetters == null) {
8120 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8121 TypeManager.CSharpName (indexer_type));
8125 if (AllGetters.Count == 0) {
8126 // FIXME: we cannot simply select first one as the error message is missleading when
8127 // multiple indexers exist
8128 Indexers.Indexer first_indexer = (Indexers.Indexer)ilist.Properties[ilist.Properties.Count - 1];
8129 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
8130 TypeManager.GetFullNameSignature (first_indexer.PropertyInfo));
8134 get = (MethodInfo)Invocation.OverloadResolve (ec, new MethodGroupExpr (AllGetters, loc),
8135 arguments, false, loc);
8138 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8143 // Only base will allow this invocation to happen.
8145 if (get.IsAbstract && this is BaseIndexerAccess){
8146 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
8150 type = get.ReturnType;
8151 if (type.IsPointer && !ec.InUnsafe){
8156 instance_expr.CheckMarshalByRefAccess ();
8158 eclass = ExprClass.IndexerAccess;
8162 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8164 if (right_side == EmptyExpression.OutAccess) {
8165 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
8166 GetSignatureForError ());
8170 // if the indexer returns a value type, and we try to set a field in it
8171 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
8172 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
8173 GetSignatureForError ());
8177 ArrayList AllSetters = new ArrayList();
8178 if (!CommonResolve (ec))
8181 bool found_any = false, found_any_setters = false;
8183 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8184 if (ilist.Properties != null) {
8186 foreach (Indexers.Indexer ix in ilist.Properties) {
8187 if (ix.Setter != null)
8188 AllSetters.Add (ix.Setter);
8191 if (AllSetters.Count > 0) {
8192 found_any_setters = true;
8193 set_arguments = (ArrayList) arguments.Clone ();
8194 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
8195 set = (MethodInfo) Invocation.OverloadResolve (
8196 ec, new MethodGroupExpr (AllSetters, loc),
8197 set_arguments, false, loc);
8201 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8202 TypeManager.CSharpName (indexer_type));
8206 if (!found_any_setters) {
8207 Error (154, "indexer can not be used in this context, because " +
8208 "it lacks a `set' accessor");
8213 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8218 // Only base will allow this invocation to happen.
8220 if (set.IsAbstract && this is BaseIndexerAccess){
8221 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
8226 // Now look for the actual match in the list of indexers to set our "return" type
8228 type = TypeManager.void_type; // default value
8229 foreach (Indexers.Indexer ix in ilist.Properties){
8230 if (ix.Setter == set){
8231 type = ix.PropertyInfo.PropertyType;
8236 instance_expr.CheckMarshalByRefAccess ();
8238 eclass = ExprClass.IndexerAccess;
8242 bool prepared = false;
8243 LocalTemporary temp;
8245 public void Emit (EmitContext ec, bool leave_copy)
8247 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
8249 ec.ig.Emit (OpCodes.Dup);
8250 temp = new LocalTemporary (Type);
8256 // source is ignored, because we already have a copy of it from the
8257 // LValue resolution and we have already constructed a pre-cached
8258 // version of the arguments (ea.set_arguments);
8260 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8262 prepared = prepare_for_load;
8263 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
8268 ec.ig.Emit (OpCodes.Dup);
8269 temp = new LocalTemporary (Type);
8272 } else if (leave_copy) {
8273 temp = new LocalTemporary (Type);
8279 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
8288 public override void Emit (EmitContext ec)
8293 public override string GetSignatureForError ()
8295 // FIXME: print the argument list of the indexer
8296 return instance_expr.GetSignatureForError () + ".this[...]";
8301 /// The base operator for method names
8303 public class BaseAccess : Expression {
8304 public readonly string Identifier;
8307 public BaseAccess (string member, TypeArguments args, Location l)
8309 this.Identifier = member;
8314 public override Expression DoResolve (EmitContext ec)
8316 Expression c = CommonResolve (ec);
8322 // MethodGroups use this opportunity to flag an error on lacking ()
8324 if (!(c is MethodGroupExpr))
8325 return c.Resolve (ec);
8329 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8331 Expression c = CommonResolve (ec);
8337 // MethodGroups use this opportunity to flag an error on lacking ()
8339 if (! (c is MethodGroupExpr))
8340 return c.DoResolveLValue (ec, right_side);
8345 Expression CommonResolve (EmitContext ec)
8347 Expression member_lookup;
8348 Type current_type = ec.ContainerType;
8349 Type base_type = current_type.BaseType;
8352 Error (1511, "Keyword `base' is not available in a static method");
8356 if (ec.IsFieldInitializer){
8357 Error (1512, "Keyword `base' is not available in the current context");
8361 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8362 AllMemberTypes, AllBindingFlags, loc);
8363 if (member_lookup == null) {
8364 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8371 left = new TypeExpression (base_type, loc);
8373 left = ec.GetThis (loc);
8375 MemberExpr me = (MemberExpr) member_lookup;
8377 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8379 if (e is PropertyExpr) {
8380 PropertyExpr pe = (PropertyExpr) e;
8385 MethodGroupExpr mg = e as MethodGroupExpr;
8391 return mg.ResolveGeneric (ec, args);
8393 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8401 public override void Emit (EmitContext ec)
8403 throw new Exception ("Should never be called");
8408 /// The base indexer operator
8410 public class BaseIndexerAccess : IndexerAccess {
8411 public BaseIndexerAccess (ArrayList args, Location loc)
8412 : base (null, true, loc)
8414 arguments = new ArrayList ();
8415 foreach (Expression tmp in args)
8416 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8419 protected override bool CommonResolve (EmitContext ec)
8421 instance_expr = ec.GetThis (loc);
8423 current_type = ec.ContainerType.BaseType;
8424 indexer_type = current_type;
8426 foreach (Argument a in arguments){
8427 if (!a.Resolve (ec, loc))
8436 /// This class exists solely to pass the Type around and to be a dummy
8437 /// that can be passed to the conversion functions (this is used by
8438 /// foreach implementation to typecast the object return value from
8439 /// get_Current into the proper type. All code has been generated and
8440 /// we only care about the side effect conversions to be performed
8442 /// This is also now used as a placeholder where a no-action expression
8443 /// is needed (the `New' class).
8445 public class EmptyExpression : Expression {
8446 public static readonly EmptyExpression Null = new EmptyExpression ();
8448 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8449 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8450 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8452 static EmptyExpression temp = new EmptyExpression ();
8453 public static EmptyExpression Grab ()
8456 throw new InternalErrorException ("Nested Grab");
8457 EmptyExpression retval = temp;
8462 public static void Release (EmptyExpression e)
8465 throw new InternalErrorException ("Already released");
8469 // TODO: should be protected
8470 public EmptyExpression ()
8472 type = TypeManager.object_type;
8473 eclass = ExprClass.Value;
8474 loc = Location.Null;
8477 public EmptyExpression (Type t)
8480 eclass = ExprClass.Value;
8481 loc = Location.Null;
8484 public override Expression DoResolve (EmitContext ec)
8489 public override void Emit (EmitContext ec)
8491 // nothing, as we only exist to not do anything.
8495 // This is just because we might want to reuse this bad boy
8496 // instead of creating gazillions of EmptyExpressions.
8497 // (CanImplicitConversion uses it)
8499 public void SetType (Type t)
8505 public class UserCast : Expression {
8509 public UserCast (MethodInfo method, Expression source, Location l)
8511 this.method = method;
8512 this.source = source;
8513 type = method.ReturnType;
8514 eclass = ExprClass.Value;
8518 public Expression Source {
8524 public override Expression DoResolve (EmitContext ec)
8527 // We are born fully resolved
8532 public override void Emit (EmitContext ec)
8534 ILGenerator ig = ec.ig;
8538 if (method is MethodInfo)
8539 ig.Emit (OpCodes.Call, (MethodInfo) method);
8541 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8547 // This class is used to "construct" the type during a typecast
8548 // operation. Since the Type.GetType class in .NET can parse
8549 // the type specification, we just use this to construct the type
8550 // one bit at a time.
8552 public class ComposedCast : TypeExpr {
8556 public ComposedCast (Expression left, string dim)
8557 : this (left, dim, left.Location)
8561 public ComposedCast (Expression left, string dim, Location l)
8568 public Expression RemoveNullable ()
8570 if (dim.EndsWith ("?")) {
8571 dim = dim.Substring (0, dim.Length - 1);
8579 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8581 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8585 Type ltype = lexpr.Type;
8586 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8587 Error_VoidInvalidInTheContext (loc);
8591 if ((dim.Length > 0) && (dim [0] == '?')) {
8592 TypeExpr nullable = new NullableType (left, loc);
8594 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8595 return nullable.ResolveAsTypeTerminal (ec, false);
8598 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc)) {
8603 type = TypeManager.GetConstructedType (ltype, dim);
8608 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8611 if (type.IsPointer && !ec.IsInUnsafeScope){
8616 if (type.IsArray && (type.GetElementType () == TypeManager.arg_iterator_type ||
8617 type.GetElementType () == TypeManager.typed_reference_type)) {
8618 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (type.GetElementType ()));
8622 eclass = ExprClass.Type;
8626 public override string Name {
8627 get { return left + dim; }
8630 public override string FullName {
8631 get { return type.FullName; }
8634 public override string GetSignatureForError ()
8636 return left.GetSignatureForError () + dim;
8640 public class FixedBufferPtr : Expression {
8643 public FixedBufferPtr (Expression array, Type array_type, Location l)
8648 type = TypeManager.GetPointerType (array_type);
8649 eclass = ExprClass.Value;
8652 public override void Emit(EmitContext ec)
8657 public override Expression DoResolve (EmitContext ec)
8660 // We are born fully resolved
8668 // This class is used to represent the address of an array, used
8669 // only by the Fixed statement, this generates "&a [0]" construct
8670 // for fixed (char *pa = a)
8672 public class ArrayPtr : FixedBufferPtr {
8675 public ArrayPtr (Expression array, Type array_type, Location l):
8676 base (array, array_type, l)
8678 this.array_type = array_type;
8681 public override void Emit (EmitContext ec)
8685 ILGenerator ig = ec.ig;
8686 IntLiteral.EmitInt (ig, 0);
8687 ig.Emit (OpCodes.Ldelema, array_type);
8692 // Used by the fixed statement
8694 public class StringPtr : Expression {
8697 public StringPtr (LocalBuilder b, Location l)
8700 eclass = ExprClass.Value;
8701 type = TypeManager.char_ptr_type;
8705 public override Expression DoResolve (EmitContext ec)
8707 // This should never be invoked, we are born in fully
8708 // initialized state.
8713 public override void Emit (EmitContext ec)
8715 ILGenerator ig = ec.ig;
8717 ig.Emit (OpCodes.Ldloc, b);
8718 ig.Emit (OpCodes.Conv_I);
8719 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8720 ig.Emit (OpCodes.Add);
8725 // Implements the `stackalloc' keyword
8727 public class StackAlloc : Expression {
8732 public StackAlloc (Expression type, Expression count, Location l)
8739 public override Expression DoResolve (EmitContext ec)
8741 count = count.Resolve (ec);
8745 if (count.Type != TypeManager.int32_type){
8746 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8751 Constant c = count as Constant;
8752 if (c != null && c.IsNegative) {
8753 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8757 if (ec.InCatch || ec.InFinally) {
8758 Error (255, "Cannot use stackalloc in finally or catch");
8762 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8768 if (!TypeManager.VerifyUnManaged (otype, loc))
8771 type = TypeManager.GetPointerType (otype);
8772 eclass = ExprClass.Value;
8777 public override void Emit (EmitContext ec)
8779 int size = GetTypeSize (otype);
8780 ILGenerator ig = ec.ig;
8783 ig.Emit (OpCodes.Sizeof, otype);
8785 IntConstant.EmitInt (ig, size);
8787 ig.Emit (OpCodes.Mul);
8788 ig.Emit (OpCodes.Localloc);