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), TypeManager.CSharpName (left.Type),
1619 TypeManager.CSharpName(right.Type));
1622 static bool is_unsigned (Type t)
1624 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1625 t == TypeManager.short_type || t == TypeManager.byte_type);
1628 Expression Make32or64 (EmitContext ec, Expression e)
1632 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1633 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1635 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1638 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1641 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1644 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1650 Expression CheckShiftArguments (EmitContext ec)
1652 Expression new_left = Make32or64 (ec, left);
1653 Expression new_right = ForceConversion (ec, right, TypeManager.int32_type);
1654 if (new_left == null || new_right == null) {
1655 Error_OperatorCannotBeApplied ();
1658 type = new_left.Type;
1659 int shiftmask = (type == TypeManager.int32_type || type == TypeManager.uint32_type) ? 31 : 63;
1661 right = new Binary (Binary.Operator.BitwiseAnd, new_right, new IntConstant (shiftmask, loc)).DoResolve (ec);
1666 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1667 // i.e., not invoke op_Equality.
1669 static bool EqualsNullIsReferenceEquals (Type t)
1671 return t == TypeManager.object_type || t == TypeManager.string_type ||
1672 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1675 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1677 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1678 "Possible unintended reference comparison; to get a value comparison, " +
1679 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1682 Expression ResolveOperator (EmitContext ec)
1685 Type r = right.Type;
1687 if (oper == Operator.Equality || oper == Operator.Inequality){
1688 if (l.IsGenericParameter && (right is NullLiteral)) {
1689 if (l.BaseType == TypeManager.value_type) {
1690 Error_OperatorCannotBeApplied ();
1694 left = new BoxedCast (left, TypeManager.object_type);
1695 Type = TypeManager.bool_type;
1699 if (r.IsGenericParameter && (left is NullLiteral)) {
1700 if (r.BaseType == TypeManager.value_type) {
1701 Error_OperatorCannotBeApplied ();
1705 right = new BoxedCast (right, TypeManager.object_type);
1706 Type = TypeManager.bool_type;
1711 // Optimize out call to op_Equality in a few cases.
1713 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1714 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1715 Type = TypeManager.bool_type;
1720 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1721 Type = TypeManager.bool_type;
1728 // Do not perform operator overload resolution when both sides are
1731 Expression left_operators = null, right_operators = null;
1732 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))) {
1734 // Step 1: Perform Operator Overload location
1736 string op = oper_names [(int) oper];
1738 MethodGroupExpr union;
1739 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc);
1741 right_operators = MemberLookup (
1742 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc);
1743 union = Invocation.MakeUnionSet (left_operators, right_operators, loc);
1745 union = (MethodGroupExpr) left_operators;
1747 if (union != null) {
1748 ArrayList args = new ArrayList (2);
1749 args.Add (new Argument (left, Argument.AType.Expression));
1750 args.Add (new Argument (right, Argument.AType.Expression));
1752 MethodBase method = Invocation.OverloadResolve (ec, union, args, true, Location.Null);
1754 if (method != null) {
1755 MethodInfo mi = (MethodInfo) method;
1756 return new BinaryMethod (mi.ReturnType, method, args);
1762 // Step 0: String concatenation (because overloading will get this wrong)
1764 if (oper == Operator.Addition){
1766 // If any of the arguments is a string, cast to string
1769 // Simple constant folding
1770 if (left is StringConstant && right is StringConstant)
1771 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1773 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1775 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1776 Error_OperatorCannotBeApplied ();
1780 // try to fold it in on the left
1781 if (left is StringConcat) {
1784 // We have to test here for not-null, since we can be doubly-resolved
1785 // take care of not appending twice
1788 type = TypeManager.string_type;
1789 ((StringConcat) left).Append (ec, right);
1790 return left.Resolve (ec);
1796 // Otherwise, start a new concat expression
1797 return new StringConcat (ec, loc, left, right).Resolve (ec);
1801 // Transform a + ( - b) into a - b
1803 if (right is Unary){
1804 Unary right_unary = (Unary) right;
1806 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1807 oper = Operator.Subtraction;
1808 right = right_unary.Expr;
1814 if (oper == Operator.Equality || oper == Operator.Inequality){
1815 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1816 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1817 Error_OperatorCannotBeApplied ();
1821 type = TypeManager.bool_type;
1825 if (l.IsPointer || r.IsPointer) {
1826 if (l.IsPointer && r.IsPointer) {
1827 type = TypeManager.bool_type;
1831 if (l.IsPointer && r == TypeManager.null_type) {
1832 right = new EmptyCast (NullPointer.Null, l);
1833 type = TypeManager.bool_type;
1837 if (r.IsPointer && l == TypeManager.null_type) {
1838 left = new EmptyCast (NullPointer.Null, r);
1839 type = TypeManager.bool_type;
1844 if (l.IsGenericParameter && r.IsGenericParameter) {
1845 GenericConstraints l_gc, r_gc;
1847 l_gc = TypeManager.GetTypeParameterConstraints (l);
1848 r_gc = TypeManager.GetTypeParameterConstraints (r);
1850 if ((l_gc == null) || (r_gc == null) ||
1851 !(l_gc.HasReferenceTypeConstraint || l_gc.HasClassConstraint) ||
1852 !(r_gc.HasReferenceTypeConstraint || r_gc.HasClassConstraint)) {
1853 Error_OperatorCannotBeApplied ();
1860 // operator != (object a, object b)
1861 // operator == (object a, object b)
1863 // For this to be used, both arguments have to be reference-types.
1864 // Read the rationale on the spec (14.9.6)
1866 if (!(l.IsValueType || r.IsValueType)){
1867 type = TypeManager.bool_type;
1873 // Also, a standard conversion must exist from either one
1875 bool left_to_right =
1876 Convert.ImplicitStandardConversionExists (left, r);
1877 bool right_to_left = !left_to_right &&
1878 Convert.ImplicitStandardConversionExists (right, l);
1880 if (!left_to_right && !right_to_left) {
1881 Error_OperatorCannotBeApplied ();
1885 if (left_to_right && left_operators != null &&
1886 RootContext.WarningLevel >= 2) {
1887 ArrayList args = new ArrayList (2);
1888 args.Add (new Argument (left, Argument.AType.Expression));
1889 args.Add (new Argument (left, Argument.AType.Expression));
1890 MethodBase method = Invocation.OverloadResolve (
1891 ec, (MethodGroupExpr) left_operators, args, true, Location.Null);
1893 Warning_UnintendedReferenceComparison (loc, "right", l);
1896 if (right_to_left && right_operators != null &&
1897 RootContext.WarningLevel >= 2) {
1898 ArrayList args = new ArrayList (2);
1899 args.Add (new Argument (right, Argument.AType.Expression));
1900 args.Add (new Argument (right, Argument.AType.Expression));
1901 MethodBase method = Invocation.OverloadResolve (
1902 ec, (MethodGroupExpr) right_operators, args, true, Location.Null);
1904 Warning_UnintendedReferenceComparison (loc, "left", r);
1908 // We are going to have to convert to an object to compare
1910 if (l != TypeManager.object_type)
1911 left = new EmptyCast (left, TypeManager.object_type);
1912 if (r != TypeManager.object_type)
1913 right = new EmptyCast (right, TypeManager.object_type);
1919 // Only perform numeric promotions on:
1920 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
1922 if (oper == Operator.Addition || oper == Operator.Subtraction) {
1923 if (TypeManager.IsDelegateType (l)){
1924 if (((right.eclass == ExprClass.MethodGroup) ||
1925 (r == TypeManager.anonymous_method_type))){
1926 if ((RootContext.Version != LanguageVersion.ISO_1)){
1927 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
1935 if (TypeManager.IsDelegateType (r) || right is NullLiteral){
1937 ArrayList args = new ArrayList (2);
1939 args = new ArrayList (2);
1940 args.Add (new Argument (left, Argument.AType.Expression));
1941 args.Add (new Argument (right, Argument.AType.Expression));
1943 if (oper == Operator.Addition)
1944 method = TypeManager.delegate_combine_delegate_delegate;
1946 method = TypeManager.delegate_remove_delegate_delegate;
1948 if (!TypeManager.IsEqual (l, r) && !(right is NullLiteral)) {
1949 Error_OperatorCannotBeApplied ();
1953 return new BinaryDelegate (l, method, args);
1958 // Pointer arithmetic:
1960 // T* operator + (T* x, int y);
1961 // T* operator + (T* x, uint y);
1962 // T* operator + (T* x, long y);
1963 // T* operator + (T* x, ulong y);
1965 // T* operator + (int y, T* x);
1966 // T* operator + (uint y, T *x);
1967 // T* operator + (long y, T *x);
1968 // T* operator + (ulong y, T *x);
1970 // T* operator - (T* x, int y);
1971 // T* operator - (T* x, uint y);
1972 // T* operator - (T* x, long y);
1973 // T* operator - (T* x, ulong y);
1975 // long operator - (T* x, T *y)
1978 if (r.IsPointer && oper == Operator.Subtraction){
1980 return new PointerArithmetic (
1981 false, left, right, TypeManager.int64_type,
1984 Expression t = Make32or64 (ec, right);
1986 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
1988 } else if (r.IsPointer && oper == Operator.Addition){
1989 Expression t = Make32or64 (ec, left);
1991 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
1996 // Enumeration operators
1998 bool lie = TypeManager.IsEnumType (l);
1999 bool rie = TypeManager.IsEnumType (r);
2003 // U operator - (E e, E f)
2005 if (oper == Operator.Subtraction){
2007 type = TypeManager.EnumToUnderlying (l);
2010 Error_OperatorCannotBeApplied ();
2016 // operator + (E e, U x)
2017 // operator - (E e, U x)
2019 if (oper == Operator.Addition || oper == Operator.Subtraction){
2020 Type enum_type = lie ? l : r;
2021 Type other_type = lie ? r : l;
2022 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2024 if (underlying_type != other_type){
2025 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2035 Error_OperatorCannotBeApplied ();
2044 temp = Convert.ImplicitConversion (ec, right, l, loc);
2048 Error_OperatorCannotBeApplied ();
2052 temp = Convert.ImplicitConversion (ec, left, r, loc);
2057 Error_OperatorCannotBeApplied ();
2062 if (oper == Operator.Equality || oper == Operator.Inequality ||
2063 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2064 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2065 if (left.Type != right.Type){
2066 Error_OperatorCannotBeApplied ();
2069 type = TypeManager.bool_type;
2073 if (oper == Operator.BitwiseAnd ||
2074 oper == Operator.BitwiseOr ||
2075 oper == Operator.ExclusiveOr){
2076 if (left.Type != right.Type){
2077 Error_OperatorCannotBeApplied ();
2083 Error_OperatorCannotBeApplied ();
2087 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2088 return CheckShiftArguments (ec);
2090 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2091 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2092 type = TypeManager.bool_type;
2097 Error_OperatorCannotBeApplied ();
2101 Expression e = new ConditionalLogicalOperator (
2102 oper == Operator.LogicalAnd, left, right, l, loc);
2103 return e.Resolve (ec);
2106 Expression orig_left = left;
2107 Expression orig_right = right;
2110 // operator & (bool x, bool y)
2111 // operator | (bool x, bool y)
2112 // operator ^ (bool x, bool y)
2114 if (oper == Operator.BitwiseAnd ||
2115 oper == Operator.BitwiseOr ||
2116 oper == Operator.ExclusiveOr) {
2117 if (OverloadResolve_PredefinedIntegral (ec)) {
2118 if (IsConvertible (ec, orig_left, orig_right, TypeManager.bool_type)) {
2119 Error_OperatorAmbiguous (loc, oper, l, r);
2122 } else if (!VerifyApplicable_Predefined (ec, TypeManager.bool_type)) {
2123 Error_OperatorCannotBeApplied ();
2130 // Pointer comparison
2132 if (l.IsPointer && r.IsPointer){
2133 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2134 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2135 type = TypeManager.bool_type;
2140 if (OverloadResolve_PredefinedIntegral (ec)) {
2141 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2142 Error_OperatorAmbiguous (loc, oper, l, r);
2145 } else if (OverloadResolve_PredefinedFloating (ec)) {
2146 if (IsConvertible (ec, orig_left, orig_right, TypeManager.decimal_type) ||
2147 IsApplicable_String (ec, orig_left, orig_right, oper)) {
2148 Error_OperatorAmbiguous (loc, oper, l, r);
2151 } else if (VerifyApplicable_Predefined (ec, TypeManager.decimal_type)) {
2152 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2153 Error_OperatorAmbiguous (loc, oper, l, r);
2156 } else if (!OverloadResolve_PredefinedString (ec, oper)) {
2157 Error_OperatorCannotBeApplied ();
2161 if (oper == Operator.Equality ||
2162 oper == Operator.Inequality ||
2163 oper == Operator.LessThanOrEqual ||
2164 oper == Operator.LessThan ||
2165 oper == Operator.GreaterThanOrEqual ||
2166 oper == Operator.GreaterThan)
2167 type = TypeManager.bool_type;
2172 if (l == TypeManager.decimal_type || l == TypeManager.string_type || r == TypeManager.string_type) {
2174 if (r == TypeManager.string_type)
2176 MethodGroupExpr ops = (MethodGroupExpr) MemberLookup (
2177 ec.ContainerType, lookup, oper_names [(int) oper],
2178 MemberTypes.Method, AllBindingFlags, loc);
2179 ArrayList args = new ArrayList (2);
2180 args.Add (new Argument (left, Argument.AType.Expression));
2181 args.Add (new Argument (right, Argument.AType.Expression));
2182 MethodBase method = Invocation.OverloadResolve (ec, ops, args, true, Location.Null);
2183 return new BinaryMethod (type, method, args);
2189 Constant EnumLiftUp (Constant left, Constant right)
2192 case Operator.BitwiseOr:
2193 case Operator.BitwiseAnd:
2194 case Operator.ExclusiveOr:
2195 case Operator.Equality:
2196 case Operator.Inequality:
2197 case Operator.LessThan:
2198 case Operator.LessThanOrEqual:
2199 case Operator.GreaterThan:
2200 case Operator.GreaterThanOrEqual:
2201 if (left is EnumConstant)
2204 if (left.IsZeroInteger)
2205 return new EnumConstant (left, right.Type);
2209 case Operator.Addition:
2210 case Operator.Subtraction:
2213 case Operator.Multiply:
2214 case Operator.Division:
2215 case Operator.Modulus:
2216 case Operator.LeftShift:
2217 case Operator.RightShift:
2218 if (right is EnumConstant || left is EnumConstant)
2222 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2226 public override Expression DoResolve (EmitContext ec)
2231 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2232 left = ((ParenthesizedExpression) left).Expr;
2233 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2237 if (left.eclass == ExprClass.Type) {
2238 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2242 left = left.Resolve (ec);
2247 Constant lc = left as Constant;
2248 if (lc != null && lc.Type == TypeManager.bool_type &&
2249 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2250 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2252 // TODO: make a sense to resolve unreachable expression as we do for statement
2253 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2257 right = right.Resolve (ec);
2261 eclass = ExprClass.Value;
2262 Constant rc = right as Constant;
2264 // The conversion rules are ignored in enum context but why
2265 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2266 left = lc = EnumLiftUp (lc, rc);
2270 right = rc = EnumLiftUp (rc, lc);
2275 if (oper == Operator.BitwiseAnd) {
2276 if (rc != null && rc.IsZeroInteger) {
2277 return lc is EnumConstant ?
2278 new EnumConstant (rc, lc.Type):
2282 if (lc != null && lc.IsZeroInteger) {
2283 return rc is EnumConstant ?
2284 new EnumConstant (lc, rc.Type):
2288 else if (oper == Operator.BitwiseOr) {
2289 if (lc is EnumConstant &&
2290 rc != null && rc.IsZeroInteger)
2292 if (rc is EnumConstant &&
2293 lc != null && lc.IsZeroInteger)
2295 } else if (oper == Operator.LogicalAnd) {
2296 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2298 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2302 if (rc != null && lc != null){
2303 int prev_e = Report.Errors;
2304 Expression e = ConstantFold.BinaryFold (
2305 ec, oper, lc, rc, loc);
2306 if (e != null || Report.Errors != prev_e)
2310 Type ltype = left.Type, rtype = right.Type;
2311 if ((left is NullLiteral || ltype.IsValueType) &&
2312 (right is NullLiteral || rtype.IsValueType) &&
2313 !(left is NullLiteral && right is NullLiteral) &&
2314 (TypeManager.IsNullableType (ltype) || TypeManager.IsNullableType (rtype)))
2315 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2317 // Comparison warnings
2318 if (oper == Operator.Equality || oper == Operator.Inequality ||
2319 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2320 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2321 if (left.Equals (right)) {
2322 Report.Warning (1718, 3, loc, "Comparison made to same variable; did you mean to compare something else?");
2324 CheckUselessComparison (lc, right.Type);
2325 CheckUselessComparison (rc, left.Type);
2328 return ResolveOperator (ec);
2331 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2336 private void CheckUselessComparison (Constant c, Type type)
2338 if (c == null || !IsTypeIntegral (type)
2339 || c is StringConstant
2340 || c is BoolConstant
2341 || c is CharConstant
2342 || c is FloatConstant
2343 || c is DoubleConstant
2344 || c is DecimalConstant
2350 if (c is ULongConstant) {
2351 ulong uvalue = ((ULongConstant) c).Value;
2352 if (uvalue > long.MaxValue) {
2353 if (type == TypeManager.byte_type ||
2354 type == TypeManager.sbyte_type ||
2355 type == TypeManager.short_type ||
2356 type == TypeManager.ushort_type ||
2357 type == TypeManager.int32_type ||
2358 type == TypeManager.uint32_type ||
2359 type == TypeManager.int64_type)
2360 WarnUselessComparison (type);
2363 value = (long) uvalue;
2365 else if (c is ByteConstant)
2366 value = ((ByteConstant) c).Value;
2367 else if (c is SByteConstant)
2368 value = ((SByteConstant) c).Value;
2369 else if (c is ShortConstant)
2370 value = ((ShortConstant) c).Value;
2371 else if (c is UShortConstant)
2372 value = ((UShortConstant) c).Value;
2373 else if (c is IntConstant)
2374 value = ((IntConstant) c).Value;
2375 else if (c is UIntConstant)
2376 value = ((UIntConstant) c).Value;
2377 else if (c is LongConstant)
2378 value = ((LongConstant) c).Value;
2381 if (IsValueOutOfRange (value, type))
2382 WarnUselessComparison (type);
2387 private bool IsValueOutOfRange (long value, Type type)
2389 if (IsTypeUnsigned (type) && value < 0)
2391 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2392 type == TypeManager.byte_type && value >= 0x100 ||
2393 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2394 type == TypeManager.ushort_type && value >= 0x10000 ||
2395 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2396 type == TypeManager.uint32_type && value >= 0x100000000;
2399 private static bool IsTypeIntegral (Type type)
2401 return type == TypeManager.uint64_type ||
2402 type == TypeManager.int64_type ||
2403 type == TypeManager.uint32_type ||
2404 type == TypeManager.int32_type ||
2405 type == TypeManager.ushort_type ||
2406 type == TypeManager.short_type ||
2407 type == TypeManager.sbyte_type ||
2408 type == TypeManager.byte_type;
2411 private static bool IsTypeUnsigned (Type type)
2413 return type == TypeManager.uint64_type ||
2414 type == TypeManager.uint32_type ||
2415 type == TypeManager.ushort_type ||
2416 type == TypeManager.byte_type;
2419 private void WarnUselessComparison (Type type)
2421 Report.Warning (652, 2, loc, "Comparison to integral constant is useless; the constant is outside the range of type `{0}'",
2422 TypeManager.CSharpName (type));
2426 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2427 /// context of a conditional bool expression. This function will return
2428 /// false if it is was possible to use EmitBranchable, or true if it was.
2430 /// The expression's code is generated, and we will generate a branch to `target'
2431 /// if the resulting expression value is equal to isTrue
2433 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2435 ILGenerator ig = ec.ig;
2438 // This is more complicated than it looks, but its just to avoid
2439 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2440 // but on top of that we want for == and != to use a special path
2441 // if we are comparing against null
2443 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2444 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2447 // put the constant on the rhs, for simplicity
2449 if (left is Constant) {
2450 Expression swap = right;
2455 if (((Constant) right).IsZeroInteger) {
2458 ig.Emit (OpCodes.Brtrue, target);
2460 ig.Emit (OpCodes.Brfalse, target);
2463 } else if (right is BoolConstant) {
2465 if (my_on_true != ((BoolConstant) right).Value)
2466 ig.Emit (OpCodes.Brtrue, target);
2468 ig.Emit (OpCodes.Brfalse, target);
2473 } else if (oper == Operator.LogicalAnd) {
2476 Label tests_end = ig.DefineLabel ();
2478 left.EmitBranchable (ec, tests_end, false);
2479 right.EmitBranchable (ec, target, true);
2480 ig.MarkLabel (tests_end);
2482 left.EmitBranchable (ec, target, false);
2483 right.EmitBranchable (ec, target, false);
2488 } else if (oper == Operator.LogicalOr){
2490 left.EmitBranchable (ec, target, true);
2491 right.EmitBranchable (ec, target, true);
2494 Label tests_end = ig.DefineLabel ();
2495 left.EmitBranchable (ec, tests_end, true);
2496 right.EmitBranchable (ec, target, false);
2497 ig.MarkLabel (tests_end);
2502 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2503 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2504 oper == Operator.Equality || oper == Operator.Inequality)) {
2505 base.EmitBranchable (ec, target, onTrue);
2513 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2516 case Operator.Equality:
2518 ig.Emit (OpCodes.Beq, target);
2520 ig.Emit (OpCodes.Bne_Un, target);
2523 case Operator.Inequality:
2525 ig.Emit (OpCodes.Bne_Un, target);
2527 ig.Emit (OpCodes.Beq, target);
2530 case Operator.LessThan:
2533 ig.Emit (OpCodes.Blt_Un, target);
2535 ig.Emit (OpCodes.Blt, target);
2538 ig.Emit (OpCodes.Bge_Un, target);
2540 ig.Emit (OpCodes.Bge, target);
2543 case Operator.GreaterThan:
2546 ig.Emit (OpCodes.Bgt_Un, target);
2548 ig.Emit (OpCodes.Bgt, target);
2551 ig.Emit (OpCodes.Ble_Un, target);
2553 ig.Emit (OpCodes.Ble, target);
2556 case Operator.LessThanOrEqual:
2559 ig.Emit (OpCodes.Ble_Un, target);
2561 ig.Emit (OpCodes.Ble, target);
2564 ig.Emit (OpCodes.Bgt_Un, target);
2566 ig.Emit (OpCodes.Bgt, target);
2570 case Operator.GreaterThanOrEqual:
2573 ig.Emit (OpCodes.Bge_Un, target);
2575 ig.Emit (OpCodes.Bge, target);
2578 ig.Emit (OpCodes.Blt_Un, target);
2580 ig.Emit (OpCodes.Blt, target);
2583 Console.WriteLine (oper);
2584 throw new Exception ("what is THAT");
2588 public override void Emit (EmitContext ec)
2590 ILGenerator ig = ec.ig;
2595 // Handle short-circuit operators differently
2598 if (oper == Operator.LogicalAnd) {
2599 Label load_zero = ig.DefineLabel ();
2600 Label end = ig.DefineLabel ();
2602 left.EmitBranchable (ec, load_zero, false);
2604 ig.Emit (OpCodes.Br, end);
2606 ig.MarkLabel (load_zero);
2607 ig.Emit (OpCodes.Ldc_I4_0);
2610 } else if (oper == Operator.LogicalOr) {
2611 Label load_one = ig.DefineLabel ();
2612 Label end = ig.DefineLabel ();
2614 left.EmitBranchable (ec, load_one, true);
2616 ig.Emit (OpCodes.Br, end);
2618 ig.MarkLabel (load_one);
2619 ig.Emit (OpCodes.Ldc_I4_1);
2627 bool isUnsigned = is_unsigned (left.Type);
2630 case Operator.Multiply:
2632 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2633 opcode = OpCodes.Mul_Ovf;
2634 else if (isUnsigned)
2635 opcode = OpCodes.Mul_Ovf_Un;
2637 opcode = OpCodes.Mul;
2639 opcode = OpCodes.Mul;
2643 case Operator.Division:
2645 opcode = OpCodes.Div_Un;
2647 opcode = OpCodes.Div;
2650 case Operator.Modulus:
2652 opcode = OpCodes.Rem_Un;
2654 opcode = OpCodes.Rem;
2657 case Operator.Addition:
2659 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2660 opcode = OpCodes.Add_Ovf;
2661 else if (isUnsigned)
2662 opcode = OpCodes.Add_Ovf_Un;
2664 opcode = OpCodes.Add;
2666 opcode = OpCodes.Add;
2669 case Operator.Subtraction:
2671 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2672 opcode = OpCodes.Sub_Ovf;
2673 else if (isUnsigned)
2674 opcode = OpCodes.Sub_Ovf_Un;
2676 opcode = OpCodes.Sub;
2678 opcode = OpCodes.Sub;
2681 case Operator.RightShift:
2683 opcode = OpCodes.Shr_Un;
2685 opcode = OpCodes.Shr;
2688 case Operator.LeftShift:
2689 opcode = OpCodes.Shl;
2692 case Operator.Equality:
2693 opcode = OpCodes.Ceq;
2696 case Operator.Inequality:
2697 ig.Emit (OpCodes.Ceq);
2698 ig.Emit (OpCodes.Ldc_I4_0);
2700 opcode = OpCodes.Ceq;
2703 case Operator.LessThan:
2705 opcode = OpCodes.Clt_Un;
2707 opcode = OpCodes.Clt;
2710 case Operator.GreaterThan:
2712 opcode = OpCodes.Cgt_Un;
2714 opcode = OpCodes.Cgt;
2717 case Operator.LessThanOrEqual:
2718 Type lt = left.Type;
2720 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2721 ig.Emit (OpCodes.Cgt_Un);
2723 ig.Emit (OpCodes.Cgt);
2724 ig.Emit (OpCodes.Ldc_I4_0);
2726 opcode = OpCodes.Ceq;
2729 case Operator.GreaterThanOrEqual:
2730 Type le = left.Type;
2732 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2733 ig.Emit (OpCodes.Clt_Un);
2735 ig.Emit (OpCodes.Clt);
2737 ig.Emit (OpCodes.Ldc_I4_0);
2739 opcode = OpCodes.Ceq;
2742 case Operator.BitwiseOr:
2743 opcode = OpCodes.Or;
2746 case Operator.BitwiseAnd:
2747 opcode = OpCodes.And;
2750 case Operator.ExclusiveOr:
2751 opcode = OpCodes.Xor;
2755 throw new Exception ("This should not happen: Operator = "
2756 + oper.ToString ());
2764 // Object created by Binary when the binary operator uses an method instead of being
2765 // a binary operation that maps to a CIL binary operation.
2767 public class BinaryMethod : Expression {
2768 public MethodBase method;
2769 public ArrayList Arguments;
2771 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2776 eclass = ExprClass.Value;
2779 public override Expression DoResolve (EmitContext ec)
2784 public override void Emit (EmitContext ec)
2786 ILGenerator ig = ec.ig;
2788 if (Arguments != null)
2789 Invocation.EmitArguments (ec, method, Arguments, false, null);
2791 if (method is MethodInfo)
2792 ig.Emit (OpCodes.Call, (MethodInfo) method);
2794 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2799 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2800 // b, c, d... may be strings or objects.
2802 public class StringConcat : Expression {
2804 bool invalid = false;
2805 bool emit_conv_done = false;
2807 // Are we also concating objects?
2809 bool is_strings_only = true;
2811 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2814 type = TypeManager.string_type;
2815 eclass = ExprClass.Value;
2817 operands = new ArrayList (2);
2822 public override Expression DoResolve (EmitContext ec)
2830 public void Append (EmitContext ec, Expression operand)
2835 StringConstant sc = operand as StringConstant;
2837 // TODO: it will be better to do this silently as an optimalization
2839 // string s = "" + i;
2840 // because this code has poor performace
2841 // if (sc.Value.Length == 0)
2842 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2844 if (operands.Count != 0) {
2845 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2846 if (last_operand != null) {
2847 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2854 // Conversion to object
2856 if (operand.Type != TypeManager.string_type) {
2857 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2860 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2866 operands.Add (operand);
2869 public override void Emit (EmitContext ec)
2871 MethodInfo concat_method = null;
2874 // Do conversion to arguments; check for strings only
2877 // This can get called multiple times, so we have to deal with that.
2878 if (!emit_conv_done) {
2879 emit_conv_done = true;
2880 for (int i = 0; i < operands.Count; i ++) {
2881 Expression e = (Expression) operands [i];
2882 is_strings_only &= e.Type == TypeManager.string_type;
2885 for (int i = 0; i < operands.Count; i ++) {
2886 Expression e = (Expression) operands [i];
2888 if (! is_strings_only && e.Type == TypeManager.string_type) {
2889 // need to make sure this is an object, because the EmitParams
2890 // method might look at the type of this expression, see it is a
2891 // string and emit a string [] when we want an object [];
2893 e = new EmptyCast (e, TypeManager.object_type);
2895 operands [i] = new Argument (e, Argument.AType.Expression);
2900 // Find the right method
2902 switch (operands.Count) {
2905 // This should not be possible, because simple constant folding
2906 // is taken care of in the Binary code.
2908 throw new Exception ("how did you get here?");
2911 concat_method = is_strings_only ?
2912 TypeManager.string_concat_string_string :
2913 TypeManager.string_concat_object_object ;
2916 concat_method = is_strings_only ?
2917 TypeManager.string_concat_string_string_string :
2918 TypeManager.string_concat_object_object_object ;
2922 // There is not a 4 param overlaod for object (the one that there is
2923 // is actually a varargs methods, and is only in corlib because it was
2924 // introduced there before.).
2926 if (!is_strings_only)
2929 concat_method = TypeManager.string_concat_string_string_string_string;
2932 concat_method = is_strings_only ?
2933 TypeManager.string_concat_string_dot_dot_dot :
2934 TypeManager.string_concat_object_dot_dot_dot ;
2938 Invocation.EmitArguments (ec, concat_method, operands, false, null);
2939 ec.ig.Emit (OpCodes.Call, concat_method);
2944 // Object created with +/= on delegates
2946 public class BinaryDelegate : Expression {
2950 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
2955 eclass = ExprClass.Value;
2958 public override Expression DoResolve (EmitContext ec)
2963 public override void Emit (EmitContext ec)
2965 ILGenerator ig = ec.ig;
2967 Invocation.EmitArguments (ec, method, args, false, null);
2969 ig.Emit (OpCodes.Call, (MethodInfo) method);
2970 ig.Emit (OpCodes.Castclass, type);
2973 public Expression Right {
2975 Argument arg = (Argument) args [1];
2980 public bool IsAddition {
2982 return method == TypeManager.delegate_combine_delegate_delegate;
2988 // User-defined conditional logical operator
2989 public class ConditionalLogicalOperator : Expression {
2990 Expression left, right;
2993 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
2996 eclass = ExprClass.Value;
3000 this.is_and = is_and;
3003 protected void Error19 ()
3005 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3008 protected void Error218 ()
3010 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3011 "declarations of operator true and operator false");
3014 Expression op_true, op_false, op;
3015 LocalTemporary left_temp;
3017 public override Expression DoResolve (EmitContext ec)
3020 Expression operator_group;
3022 operator_group = MethodLookup (ec, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
3023 if (operator_group == null) {
3028 left_temp = new LocalTemporary (type);
3030 ArrayList arguments = new ArrayList ();
3031 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3032 arguments.Add (new Argument (right, Argument.AType.Expression));
3033 method = Invocation.OverloadResolve (
3034 ec, (MethodGroupExpr) operator_group, arguments, false, loc)
3036 if (method == null) {
3041 if (method.ReturnType != type) {
3042 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3043 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3047 op = new StaticCallExpr (method, arguments, loc);
3049 op_true = GetOperatorTrue (ec, left_temp, loc);
3050 op_false = GetOperatorFalse (ec, left_temp, loc);
3051 if ((op_true == null) || (op_false == null)) {
3059 public override void Emit (EmitContext ec)
3061 ILGenerator ig = ec.ig;
3062 Label false_target = ig.DefineLabel ();
3063 Label end_target = ig.DefineLabel ();
3066 left_temp.Store (ec);
3068 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3069 left_temp.Emit (ec);
3070 ig.Emit (OpCodes.Br, end_target);
3071 ig.MarkLabel (false_target);
3073 ig.MarkLabel (end_target);
3075 // We release 'left_temp' here since 'op' may refer to it too
3076 left_temp.Release (ec);
3080 public class PointerArithmetic : Expression {
3081 Expression left, right;
3085 // We assume that `l' is always a pointer
3087 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3093 is_add = is_addition;
3096 public override Expression DoResolve (EmitContext ec)
3098 eclass = ExprClass.Variable;
3100 if (left.Type == TypeManager.void_ptr_type) {
3101 Error (242, "The operation in question is undefined on void pointers");
3108 public override void Emit (EmitContext ec)
3110 Type op_type = left.Type;
3111 ILGenerator ig = ec.ig;
3113 // It must be either array or fixed buffer
3114 Type element = TypeManager.HasElementType (op_type) ?
3115 element = TypeManager.GetElementType (op_type) :
3116 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3118 int size = GetTypeSize (element);
3119 Type rtype = right.Type;
3121 if (rtype.IsPointer){
3123 // handle (pointer - pointer)
3127 ig.Emit (OpCodes.Sub);
3131 ig.Emit (OpCodes.Sizeof, element);
3133 IntLiteral.EmitInt (ig, size);
3134 ig.Emit (OpCodes.Div);
3136 ig.Emit (OpCodes.Conv_I8);
3139 // handle + and - on (pointer op int)
3142 ig.Emit (OpCodes.Conv_I);
3144 Constant right_const = right as Constant;
3145 if (right_const != null && size != 0) {
3146 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3154 ig.Emit (OpCodes.Sizeof, element);
3156 IntLiteral.EmitInt (ig, size);
3157 if (rtype == TypeManager.int64_type)
3158 ig.Emit (OpCodes.Conv_I8);
3159 else if (rtype == TypeManager.uint64_type)
3160 ig.Emit (OpCodes.Conv_U8);
3161 ig.Emit (OpCodes.Mul);
3165 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3166 ig.Emit (OpCodes.Conv_I);
3169 ig.Emit (OpCodes.Add);
3171 ig.Emit (OpCodes.Sub);
3177 /// Implements the ternary conditional operator (?:)
3179 public class Conditional : Expression {
3180 Expression expr, trueExpr, falseExpr;
3182 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3185 this.trueExpr = trueExpr;
3186 this.falseExpr = falseExpr;
3187 this.loc = expr.Location;
3190 public Expression Expr {
3196 public Expression TrueExpr {
3202 public Expression FalseExpr {
3208 public override Expression DoResolve (EmitContext ec)
3210 expr = expr.Resolve (ec);
3215 if (TypeManager.IsNullableValueType (expr.Type))
3216 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
3218 if (expr.Type != TypeManager.bool_type){
3219 expr = Expression.ResolveBoolean (
3226 Assign ass = expr as Assign;
3227 if (ass != null && ass.Source is Constant) {
3228 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3231 trueExpr = trueExpr.Resolve (ec);
3232 falseExpr = falseExpr.Resolve (ec);
3234 if (trueExpr == null || falseExpr == null)
3237 eclass = ExprClass.Value;
3238 if (trueExpr.Type == falseExpr.Type)
3239 type = trueExpr.Type;
3242 Type true_type = trueExpr.Type;
3243 Type false_type = falseExpr.Type;
3246 // First, if an implicit conversion exists from trueExpr
3247 // to falseExpr, then the result type is of type falseExpr.Type
3249 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3252 // Check if both can convert implicitl to each other's type
3254 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3256 "Can not compute type of conditional expression " +
3257 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3258 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3259 "' convert implicitly to each other");
3264 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3268 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3269 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3274 // Dead code optimalization
3275 if (expr is BoolConstant){
3276 BoolConstant bc = (BoolConstant) expr;
3278 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3279 return bc.Value ? trueExpr : falseExpr;
3285 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3290 public override void Emit (EmitContext ec)
3292 ILGenerator ig = ec.ig;
3293 Label false_target = ig.DefineLabel ();
3294 Label end_target = ig.DefineLabel ();
3296 expr.EmitBranchable (ec, false_target, false);
3298 ig.Emit (OpCodes.Br, end_target);
3299 ig.MarkLabel (false_target);
3300 falseExpr.Emit (ec);
3301 ig.MarkLabel (end_target);
3309 public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3310 public readonly string Name;
3311 public readonly Block Block;
3312 public LocalInfo local_info;
3315 LocalTemporary temp;
3317 public LocalVariableReference (Block block, string name, Location l)
3322 eclass = ExprClass.Variable;
3326 // Setting `is_readonly' to false will allow you to create a writable
3327 // reference to a read-only variable. This is used by foreach and using.
3329 public LocalVariableReference (Block block, string name, Location l,
3330 LocalInfo local_info, bool is_readonly)
3331 : this (block, name, l)
3333 this.local_info = local_info;
3334 this.is_readonly = is_readonly;
3337 public VariableInfo VariableInfo {
3338 get { return local_info.VariableInfo; }
3341 public bool IsReadOnly {
3342 get { return is_readonly; }
3345 public bool VerifyAssigned (EmitContext ec)
3347 VariableInfo variable_info = local_info.VariableInfo;
3348 return variable_info == null || variable_info.IsAssigned (ec, loc);
3351 void ResolveLocalInfo ()
3353 if (local_info == null) {
3354 local_info = Block.GetLocalInfo (Name);
3355 is_readonly = local_info.ReadOnly;
3359 protected Expression DoResolveBase (EmitContext ec)
3361 type = local_info.VariableType;
3363 Expression e = Block.GetConstantExpression (Name);
3365 return e.Resolve (ec);
3367 if (!VerifyAssigned (ec))
3370 if (ec.CurrentAnonymousMethod != null){
3372 // If we are referencing a variable from the external block
3373 // flag it for capturing
3375 if ((local_info.Block.Toplevel != ec.CurrentBlock.Toplevel) ||
3376 ec.CurrentAnonymousMethod.IsIterator)
3378 if (local_info.AddressTaken){
3379 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3382 ec.CaptureVariable (local_info);
3389 public override Expression DoResolve (EmitContext ec)
3391 ResolveLocalInfo ();
3392 local_info.Used = true;
3393 return DoResolveBase (ec);
3396 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3398 ResolveLocalInfo ();
3403 if (right_side == EmptyExpression.OutAccess) {
3404 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3405 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3406 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3407 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3408 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3410 Error_CannotAssign (Name, local_info.GetReadOnlyContext ());
3413 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3418 if (right_side == EmptyExpression.OutAccess)
3419 local_info.Used = true;
3421 if (VariableInfo != null)
3422 VariableInfo.SetAssigned (ec);
3424 return DoResolveBase (ec);
3427 public bool VerifyFixed ()
3429 // A local Variable is always fixed.
3433 public override int GetHashCode ()
3435 return Name.GetHashCode ();
3438 public override bool Equals (object obj)
3440 LocalVariableReference lvr = obj as LocalVariableReference;
3444 return Name == lvr.Name && Block == lvr.Block;
3447 public override void Emit (EmitContext ec)
3449 ILGenerator ig = ec.ig;
3451 if (local_info.FieldBuilder == null){
3453 // A local variable on the local CLR stack
3455 ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
3458 // A local variable captured by anonymous methods.
3461 ec.EmitCapturedVariableInstance (local_info);
3463 ig.Emit (OpCodes.Ldfld, local_info.FieldBuilder);
3467 public void Emit (EmitContext ec, bool leave_copy)
3471 ec.ig.Emit (OpCodes.Dup);
3472 if (local_info.FieldBuilder != null){
3473 temp = new LocalTemporary (Type);
3479 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3481 ILGenerator ig = ec.ig;
3482 prepared = prepare_for_load;
3484 if (local_info.FieldBuilder == null){
3486 // A local variable on the local CLR stack
3488 if (local_info.LocalBuilder == null)
3489 throw new Exception ("This should not happen: both Field and Local are null");
3493 ec.ig.Emit (OpCodes.Dup);
3494 ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
3497 // A local variable captured by anonymous methods or itereators.
3499 ec.EmitCapturedVariableInstance (local_info);
3501 if (prepare_for_load)
3502 ig.Emit (OpCodes.Dup);
3505 ig.Emit (OpCodes.Dup);
3506 temp = new LocalTemporary (Type);
3509 ig.Emit (OpCodes.Stfld, local_info.FieldBuilder);
3517 public void AddressOf (EmitContext ec, AddressOp mode)
3519 ILGenerator ig = ec.ig;
3521 if (local_info.FieldBuilder == null){
3523 // A local variable on the local CLR stack
3525 ig.Emit (OpCodes.Ldloca, local_info.LocalBuilder);
3528 // A local variable captured by anonymous methods or iterators
3530 ec.EmitCapturedVariableInstance (local_info);
3531 ig.Emit (OpCodes.Ldflda, local_info.FieldBuilder);
3535 public override string ToString ()
3537 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3542 /// This represents a reference to a parameter in the intermediate
3545 public class ParameterReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3551 public bool is_ref, is_out, prepared;
3565 public string Name {
3571 LocalTemporary temp;
3573 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3576 this.name = par.Name;
3580 eclass = ExprClass.Variable;
3583 public VariableInfo VariableInfo {
3587 public bool VerifyFixed ()
3589 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3590 return par.ModFlags == Parameter.Modifier.NONE;
3593 public bool IsAssigned (EmitContext ec, Location loc)
3595 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3598 Report.Error (269, loc,
3599 "Use of unassigned out parameter `{0}'", par.Name);
3603 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3605 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3608 Report.Error (170, loc,
3609 "Use of possibly unassigned field `" + field_name + "'");
3613 public void SetAssigned (EmitContext ec)
3615 if (is_out && ec.DoFlowAnalysis)
3616 ec.CurrentBranching.SetAssigned (vi);
3619 public void SetFieldAssigned (EmitContext ec, string field_name)
3621 if (is_out && ec.DoFlowAnalysis)
3622 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3625 protected bool DoResolveBase (EmitContext ec)
3627 if (!par.Resolve (ec)) {
3631 type = par.ParameterType;
3632 Parameter.Modifier mod = par.ModFlags;
3633 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3634 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3635 eclass = ExprClass.Variable;
3638 vi = block.ParameterMap [idx];
3640 if (ec.CurrentAnonymousMethod != null){
3641 if (is_ref && !block.Toplevel.IsLocalParameter (name)){
3642 Report.Error (1628, Location, "Cannot use ref or out parameter `{0}' inside an anonymous method block",
3648 // If we are referencing the parameter from the external block
3649 // flag it for capturing
3651 //Console.WriteLine ("Is parameter `{0}' local? {1}", name, block.IsLocalParameter (name));
3652 if (!block.Toplevel.IsLocalParameter (name)){
3653 ec.CaptureParameter (name, type, idx);
3660 public override int GetHashCode()
3662 return name.GetHashCode ();
3665 public override bool Equals (object obj)
3667 ParameterReference pr = obj as ParameterReference;
3671 return name == pr.name && block == pr.block;
3675 // Notice that for ref/out parameters, the type exposed is not the
3676 // same type exposed externally.
3679 // externally we expose "int&"
3680 // here we expose "int".
3682 // We record this in "is_ref". This means that the type system can treat
3683 // the type as it is expected, but when we generate the code, we generate
3684 // the alternate kind of code.
3686 public override Expression DoResolve (EmitContext ec)
3688 if (!DoResolveBase (ec))
3691 if (is_out && ec.DoFlowAnalysis && (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3697 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3699 if (!DoResolveBase (ec))
3707 static public void EmitLdArg (ILGenerator ig, int x)
3711 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3712 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3713 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3714 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3715 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3718 ig.Emit (OpCodes.Ldarg, x);
3722 // This method is used by parameters that are references, that are
3723 // being passed as references: we only want to pass the pointer (that
3724 // is already stored in the parameter, not the address of the pointer,
3725 // and not the value of the variable).
3727 public void EmitLoad (EmitContext ec)
3729 ILGenerator ig = ec.ig;
3732 if (!ec.MethodIsStatic)
3735 EmitLdArg (ig, arg_idx);
3738 // FIXME: Review for anonymous methods
3742 public override void Emit (EmitContext ec)
3747 public void Emit (EmitContext ec, bool leave_copy)
3749 ILGenerator ig = ec.ig;
3752 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3753 ec.EmitParameter (name, leave_copy, prepared, ref temp);
3757 if (!ec.MethodIsStatic)
3760 EmitLdArg (ig, arg_idx);
3764 ec.ig.Emit (OpCodes.Dup);
3767 // If we are a reference, we loaded on the stack a pointer
3768 // Now lets load the real value
3770 LoadFromPtr (ig, type);
3774 ec.ig.Emit (OpCodes.Dup);
3777 temp = new LocalTemporary (type);
3783 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3785 prepared = prepare_for_load;
3786 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3787 ec.EmitAssignParameter (name, source, leave_copy, prepare_for_load, ref temp);
3791 ILGenerator ig = ec.ig;
3796 if (!ec.MethodIsStatic)
3799 if (is_ref && !prepared)
3800 EmitLdArg (ig, arg_idx);
3805 ec.ig.Emit (OpCodes.Dup);
3809 temp = new LocalTemporary (type);
3813 StoreFromPtr (ig, type);
3821 ig.Emit (OpCodes.Starg_S, (byte) arg_idx);
3823 ig.Emit (OpCodes.Starg, arg_idx);
3827 public void AddressOf (EmitContext ec, AddressOp mode)
3829 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3830 ec.EmitAddressOfParameter (name);
3836 if (!ec.MethodIsStatic)
3841 ec.ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx);
3843 ec.ig.Emit (OpCodes.Ldarg, arg_idx);
3846 ec.ig.Emit (OpCodes.Ldarga_S, (byte) arg_idx);
3848 ec.ig.Emit (OpCodes.Ldarga, arg_idx);
3852 public override string ToString ()
3854 return "ParameterReference[" + name + "]";
3859 /// Used for arguments to New(), Invocation()
3861 public class Argument {
3862 public enum AType : byte {
3869 public readonly AType ArgType;
3870 public Expression Expr;
3872 public Argument (Expression expr, AType type)
3875 this.ArgType = type;
3878 public Argument (Expression expr)
3881 this.ArgType = AType.Expression;
3886 if (ArgType == AType.Ref || ArgType == AType.Out)
3887 return TypeManager.GetReferenceType (Expr.Type);
3893 public Parameter.Modifier Modifier
3898 return Parameter.Modifier.OUT;
3901 return Parameter.Modifier.REF;
3904 return Parameter.Modifier.NONE;
3909 public static string FullDesc (Argument a)
3911 if (a.ArgType == AType.ArgList)
3914 return (a.ArgType == AType.Ref ? "ref " :
3915 (a.ArgType == AType.Out ? "out " : "")) +
3916 TypeManager.CSharpName (a.Expr.Type);
3919 public bool ResolveMethodGroup (EmitContext ec)
3921 SimpleName sn = Expr as SimpleName;
3923 Expr = sn.GetMethodGroup ();
3925 // FIXME: csc doesn't report any error if you try to use `ref' or
3926 // `out' in a delegate creation expression.
3927 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3934 public bool Resolve (EmitContext ec, Location loc)
3936 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3937 // Verify that the argument is readable
3938 if (ArgType != AType.Out)
3939 Expr = Expr.Resolve (ec);
3941 // Verify that the argument is writeable
3942 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3943 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3945 return Expr != null;
3949 public void Emit (EmitContext ec)
3951 if (ArgType != AType.Ref && ArgType != AType.Out) {
3956 AddressOp mode = AddressOp.Store;
3957 if (ArgType == AType.Ref)
3958 mode |= AddressOp.Load;
3960 IMemoryLocation ml = (IMemoryLocation) Expr;
3961 ParameterReference pr = ml as ParameterReference;
3964 // ParameterReferences might already be references, so we want
3965 // to pass just the value
3967 if (pr != null && pr.IsRef)
3970 ml.AddressOf (ec, mode);
3975 /// Invocation of methods or delegates.
3977 public class Invocation : ExpressionStatement {
3978 public readonly ArrayList Arguments;
3981 MethodBase method = null;
3984 // arguments is an ArrayList, but we do not want to typecast,
3985 // as it might be null.
3987 // FIXME: only allow expr to be a method invocation or a
3988 // delegate invocation (7.5.5)
3990 public Invocation (Expression expr, ArrayList arguments)
3993 Arguments = arguments;
3994 loc = expr.Location;
3997 public Expression Expr {
4004 /// Determines "better conversion" as specified in 14.4.2.3
4006 /// Returns : p if a->p is better,
4007 /// q if a->q is better,
4008 /// null if neither is better
4010 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q)
4012 Type argument_type = TypeManager.TypeToCoreType (a.Type);
4013 Expression argument_expr = a.Expr;
4015 // p = TypeManager.TypeToCoreType (p);
4016 // q = TypeManager.TypeToCoreType (q);
4018 if (argument_type == null)
4019 throw new Exception ("Expression of type " + a.Expr +
4020 " does not resolve its type");
4022 if (p == null || q == null)
4023 throw new InternalErrorException ("BetterConversion Got a null conversion");
4028 if (argument_expr is NullLiteral) {
4030 // If the argument is null and one of the types to compare is 'object' and
4031 // the other is a reference type, we prefer the other.
4033 // This follows from the usual rules:
4034 // * There is an implicit conversion from 'null' to type 'object'
4035 // * There is an implicit conversion from 'null' to any reference type
4036 // * There is an implicit conversion from any reference type to type 'object'
4037 // * There is no implicit conversion from type 'object' to other reference types
4038 // => Conversion of 'null' to a reference type is better than conversion to 'object'
4040 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
4041 // null type. I think it used to be 'object' and thus needed a special
4042 // case to avoid the immediately following two checks.
4044 if (!p.IsValueType && q == TypeManager.object_type)
4046 if (!q.IsValueType && p == TypeManager.object_type)
4050 if (argument_type == p)
4053 if (argument_type == q)
4056 Expression p_tmp = new EmptyExpression (p);
4057 Expression q_tmp = new EmptyExpression (q);
4059 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
4060 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
4062 if (p_to_q && !q_to_p)
4065 if (q_to_p && !p_to_q)
4068 if (p == TypeManager.sbyte_type)
4069 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
4070 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4072 if (q == TypeManager.sbyte_type)
4073 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
4074 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4077 if (p == TypeManager.short_type)
4078 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
4079 q == TypeManager.uint64_type)
4081 if (q == TypeManager.short_type)
4082 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
4083 p == TypeManager.uint64_type)
4086 if (p == TypeManager.int32_type)
4087 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4089 if (q == TypeManager.int32_type)
4090 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4093 if (p == TypeManager.int64_type)
4094 if (q == TypeManager.uint64_type)
4096 if (q == TypeManager.int64_type)
4097 if (p == TypeManager.uint64_type)
4103 static Type MoreSpecific (Type p, Type q)
4105 if (p.IsGenericParameter && !q.IsGenericParameter)
4107 if (!p.IsGenericParameter && q.IsGenericParameter)
4110 if (TypeManager.HasElementType (p)) {
4111 Type pe = TypeManager.GetElementType (p);
4112 Type qe = TypeManager.GetElementType (q);
4113 Type specific = MoreSpecific (pe, qe);
4118 } else if (p.IsGenericType) {
4119 Type[] pargs = TypeManager.GetTypeArguments (p);
4120 Type[] qargs = TypeManager.GetTypeArguments (q);
4122 bool p_specific_at_least_once = false;
4123 bool q_specific_at_least_once = false;
4125 for (int i = 0; i < pargs.Length; i++) {
4126 Type specific = MoreSpecific (pargs [i], qargs [i]);
4127 if (specific == pargs [i])
4128 p_specific_at_least_once = true;
4129 if (specific == qargs [i])
4130 q_specific_at_least_once = true;
4133 if (p_specific_at_least_once && !q_specific_at_least_once)
4135 if (!p_specific_at_least_once && q_specific_at_least_once)
4143 /// Determines "Better function" between candidate
4144 /// and the current best match
4147 /// Returns a boolean indicating :
4148 /// false if candidate ain't better
4149 /// true if candidate is better than the current best match
4151 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4152 MethodBase candidate, bool candidate_params,
4153 MethodBase best, bool best_params)
4155 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4156 ParameterData best_pd = TypeManager.GetParameterData (best);
4158 bool better_at_least_one = false;
4160 for (int j = 0; j < argument_count; ++j) {
4161 Argument a = (Argument) args [j];
4163 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
4164 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
4166 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4167 if (candidate_params)
4168 ct = TypeManager.GetElementType (ct);
4170 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4172 bt = TypeManager.GetElementType (bt);
4178 Type better = BetterConversion (ec, a, ct, bt);
4180 // for each argument, the conversion to 'ct' should be no worse than
4181 // the conversion to 'bt'.
4185 // for at least one argument, the conversion to 'ct' should be better than
4186 // the conversion to 'bt'.
4188 better_at_least_one = true;
4191 if (better_at_least_one)
4195 // This handles the case
4197 // Add (float f1, float f2, float f3);
4198 // Add (params decimal [] foo);
4200 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4201 // first candidate would've chosen as better.
4207 // The two methods have equal parameter types. Now apply tie-breaking rules
4209 if (TypeManager.IsGenericMethod (best) && !TypeManager.IsGenericMethod (candidate))
4211 if (!TypeManager.IsGenericMethod (best) && TypeManager.IsGenericMethod (candidate))
4215 // This handles the following cases:
4217 // Trim () is better than Trim (params char[] chars)
4218 // Concat (string s1, string s2, string s3) is better than
4219 // Concat (string s1, params string [] srest)
4220 // Foo (int, params int [] rest) is better than Foo (params int [] rest)
4222 if (!candidate_params && best_params)
4224 if (candidate_params && !best_params)
4227 int candidate_param_count = candidate_pd.Count;
4228 int best_param_count = best_pd.Count;
4230 if (candidate_param_count != best_param_count)
4231 // can only happen if (candidate_params && best_params)
4232 return candidate_param_count > best_param_count;
4235 // now, both methods have the same number of parameters, and the parameters have the same types
4236 // Pick the "more specific" signature
4239 MethodBase orig_candidate = TypeManager.DropGenericMethodArguments (candidate);
4240 MethodBase orig_best = TypeManager.DropGenericMethodArguments (best);
4242 ParameterData orig_candidate_pd = TypeManager.GetParameterData (orig_candidate);
4243 ParameterData orig_best_pd = TypeManager.GetParameterData (orig_best);
4245 bool specific_at_least_once = false;
4246 for (int j = 0; j < candidate_param_count; ++j) {
4247 Type ct = TypeManager.TypeToCoreType (orig_candidate_pd.ParameterType (j));
4248 Type bt = TypeManager.TypeToCoreType (orig_best_pd.ParameterType (j));
4251 Type specific = MoreSpecific (ct, bt);
4255 specific_at_least_once = true;
4258 if (specific_at_least_once)
4261 // FIXME: handle lifted operators
4267 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4269 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4272 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4273 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4275 if (cand_pd.Count != base_pd.Count)
4278 for (int j = 0; j < cand_pd.Count; ++j) {
4279 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4280 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4281 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4282 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4284 if (cm != bm || ct != bt)
4291 public static string FullMethodDesc (MethodBase mb)
4297 if (mb is MethodInfo) {
4298 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4302 sb = new StringBuilder ();
4304 sb.Append (TypeManager.CSharpSignature (mb));
4305 return sb.ToString ();
4308 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4310 MemberInfo [] miset;
4311 MethodGroupExpr union;
4316 return (MethodGroupExpr) mg2;
4319 return (MethodGroupExpr) mg1;
4322 MethodGroupExpr left_set = null, right_set = null;
4323 int length1 = 0, length2 = 0;
4325 left_set = (MethodGroupExpr) mg1;
4326 length1 = left_set.Methods.Length;
4328 right_set = (MethodGroupExpr) mg2;
4329 length2 = right_set.Methods.Length;
4331 ArrayList common = new ArrayList ();
4333 foreach (MethodBase r in right_set.Methods){
4334 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4338 miset = new MemberInfo [length1 + length2 - common.Count];
4339 left_set.Methods.CopyTo (miset, 0);
4343 foreach (MethodBase r in right_set.Methods) {
4344 if (!common.Contains (r))
4348 union = new MethodGroupExpr (miset, loc);
4353 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4354 ArrayList arguments, int arg_count,
4355 ref MethodBase candidate)
4357 return IsParamsMethodApplicable (
4358 ec, me, arguments, arg_count, false, ref candidate) ||
4359 IsParamsMethodApplicable (
4360 ec, me, arguments, arg_count, true, ref candidate);
4365 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4366 ArrayList arguments, int arg_count,
4367 bool do_varargs, ref MethodBase candidate)
4369 if (!me.HasTypeArguments &&
4370 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4373 if (TypeManager.IsGenericMethodDefinition (candidate))
4374 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4376 return IsParamsMethodApplicable (
4377 ec, arguments, arg_count, candidate, do_varargs);
4381 /// Determines if the candidate method, if a params method, is applicable
4382 /// in its expanded form to the given set of arguments
4384 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4385 int arg_count, MethodBase candidate,
4388 ParameterData pd = TypeManager.GetParameterData (candidate);
4390 int pd_count = pd.Count;
4394 int count = pd_count - 1;
4396 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4398 if (pd_count != arg_count)
4405 if (count > arg_count)
4408 if (pd_count == 1 && arg_count == 0)
4412 // If we have come this far, the case which
4413 // remains is when the number of parameters is
4414 // less than or equal to the argument count.
4416 for (int i = 0; i < count; ++i) {
4418 Argument a = (Argument) arguments [i];
4420 Parameter.Modifier a_mod = a.Modifier &
4421 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4422 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4423 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4425 if (a_mod == p_mod) {
4427 if (a_mod == Parameter.Modifier.NONE)
4428 if (!Convert.ImplicitConversionExists (ec,
4430 pd.ParameterType (i)))
4433 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4434 Type pt = pd.ParameterType (i);
4437 pt = TypeManager.GetReferenceType (pt);
4448 Argument a = (Argument) arguments [count];
4449 if (!(a.Expr is Arglist))
4455 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
4457 for (int i = pd_count - 1; i < arg_count; i++) {
4458 Argument a = (Argument) arguments [i];
4460 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4467 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4468 ArrayList arguments, int arg_count,
4469 ref MethodBase candidate)
4471 if (!me.HasTypeArguments &&
4472 !TypeManager.InferTypeArguments (arguments, ref candidate))
4475 if (TypeManager.IsGenericMethodDefinition (candidate))
4476 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4478 return IsApplicable (ec, arguments, arg_count, candidate);
4482 /// Determines if the candidate method is applicable (section 14.4.2.1)
4483 /// to the given set of arguments
4485 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4486 MethodBase candidate)
4488 ParameterData pd = TypeManager.GetParameterData (candidate);
4490 if (arg_count != pd.Count)
4493 for (int i = arg_count; i > 0; ) {
4496 Argument a = (Argument) arguments [i];
4498 Parameter.Modifier a_mod = a.Modifier &
4499 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4501 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4502 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4504 if (a_mod == p_mod) {
4505 Type pt = pd.ParameterType (i);
4507 if (a_mod == Parameter.Modifier.NONE) {
4508 if (!TypeManager.IsEqual (a.Type, pt) &&
4509 !Convert.ImplicitConversionExists (ec, a.Expr, pt))
4523 static internal bool IsAncestralType (Type first_type, Type second_type)
4525 return first_type != second_type &&
4526 (TypeManager.IsSubclassOf (second_type, first_type) ||
4527 TypeManager.ImplementsInterface (second_type, first_type));
4531 /// Find the Applicable Function Members (7.4.2.1)
4533 /// me: Method Group expression with the members to select.
4534 /// it might contain constructors or methods (or anything
4535 /// that maps to a method).
4537 /// Arguments: ArrayList containing resolved Argument objects.
4539 /// loc: The location if we want an error to be reported, or a Null
4540 /// location for "probing" purposes.
4542 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4543 /// that is the best match of me on Arguments.
4546 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4547 ArrayList Arguments, bool may_fail,
4550 MethodBase method = null;
4551 bool method_params = false;
4552 Type applicable_type = null;
4554 ArrayList candidates = new ArrayList (2);
4555 ArrayList candidate_overrides = null;
4558 // Used to keep a map between the candidate
4559 // and whether it is being considered in its
4560 // normal or expanded form
4562 // false is normal form, true is expanded form
4564 Hashtable candidate_to_form = null;
4566 if (Arguments != null)
4567 arg_count = Arguments.Count;
4569 if ((me.Name == "Invoke") &&
4570 TypeManager.IsDelegateType (me.DeclaringType)) {
4571 Error_InvokeOnDelegate (loc);
4575 MethodBase[] methods = me.Methods;
4577 int nmethods = methods.Length;
4581 // Methods marked 'override' don't take part in 'applicable_type'
4582 // computation, nor in the actual overload resolution.
4583 // However, they still need to be emitted instead of a base virtual method.
4584 // So, we salt them away into the 'candidate_overrides' array.
4586 // In case of reflected methods, we replace each overriding method with
4587 // its corresponding base virtual method. This is to improve compatibility
4588 // with non-C# libraries which change the visibility of overrides (#75636)
4591 for (int i = 0; i < methods.Length; ++i) {
4592 MethodBase m = methods [i];
4593 Type [] gen_args = m.IsGenericMethod && !m.IsGenericMethodDefinition ? m.GetGenericArguments () : null;
4594 if (TypeManager.IsOverride (m)) {
4595 if (candidate_overrides == null)
4596 candidate_overrides = new ArrayList ();
4597 candidate_overrides.Add (m);
4598 m = TypeManager.TryGetBaseDefinition (m);
4599 if (m != null && gen_args != null) {
4600 if (!m.IsGenericMethodDefinition)
4601 throw new InternalErrorException ("GetBaseDefinition didn't return a GenericMethodDefinition");
4602 m = ((MethodInfo) m).MakeGenericMethod (gen_args);
4611 int applicable_errors = Report.Errors;
4614 // First we construct the set of applicable methods
4616 bool is_sorted = true;
4617 for (int i = 0; i < nmethods; i++){
4618 Type decl_type = methods [i].DeclaringType;
4621 // If we have already found an applicable method
4622 // we eliminate all base types (Section 14.5.5.1)
4624 if (applicable_type != null && IsAncestralType (decl_type, applicable_type))
4628 // Check if candidate is applicable (section 14.4.2.1)
4629 // Is candidate applicable in normal form?
4631 bool is_applicable = IsApplicable (ec, me, Arguments, arg_count, ref methods [i]);
4633 if (!is_applicable && IsParamsMethodApplicable (ec, me, Arguments, arg_count, ref methods [i])) {
4634 MethodBase candidate = methods [i];
4635 if (candidate_to_form == null)
4636 candidate_to_form = new PtrHashtable ();
4637 candidate_to_form [candidate] = candidate;
4638 // Candidate is applicable in expanded form
4639 is_applicable = true;
4645 candidates.Add (methods [i]);
4647 if (applicable_type == null)
4648 applicable_type = decl_type;
4649 else if (applicable_type != decl_type) {
4651 if (IsAncestralType (applicable_type, decl_type))
4652 applicable_type = decl_type;
4656 if (applicable_errors != Report.Errors)
4659 int candidate_top = candidates.Count;
4661 if (applicable_type == null) {
4663 // Okay so we have failed to find anything so we
4664 // return by providing info about the closest match
4666 int errors = Report.Errors;
4667 for (int i = 0; i < nmethods; ++i) {
4668 MethodBase c = (MethodBase) methods [i];
4669 ParameterData pd = TypeManager.GetParameterData (c);
4671 if (pd.Count != arg_count)
4674 if (!TypeManager.InferTypeArguments (Arguments, ref c))
4677 if (TypeManager.IsGenericMethodDefinition (c))
4680 VerifyArgumentsCompat (ec, Arguments, arg_count,
4681 c, false, null, may_fail, loc);
4683 if (!may_fail && errors == Report.Errors)
4684 throw new InternalErrorException (
4685 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4686 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4691 if (!may_fail && errors == Report.Errors) {
4692 string report_name = me.Name;
4693 if (report_name == ".ctor")
4694 report_name = me.DeclaringType.ToString ();
4696 for (int i = 0; i < methods.Length; ++i) {
4697 MethodBase c = methods [i];
4698 ParameterData pd = TypeManager.GetParameterData (c);
4700 if (pd.Count != arg_count)
4703 if (TypeManager.InferTypeArguments (Arguments, ref c))
4707 411, loc, "The type arguments for " +
4708 "method `{0}' cannot be infered from " +
4709 "the usage. Try specifying the type " +
4710 "arguments explicitly.", report_name);
4714 Error_WrongNumArguments (loc, report_name, arg_count);
4722 // At this point, applicable_type is _one_ of the most derived types
4723 // in the set of types containing the methods in this MethodGroup.
4724 // Filter the candidates so that they only contain methods from the
4725 // most derived types.
4728 int finalized = 0; // Number of finalized candidates
4731 // Invariant: applicable_type is a most derived type
4733 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4734 // eliminating all it's base types. At the same time, we'll also move
4735 // every unrelated type to the end of the array, and pick the next
4736 // 'applicable_type'.
4738 Type next_applicable_type = null;
4739 int j = finalized; // where to put the next finalized candidate
4740 int k = finalized; // where to put the next undiscarded candidate
4741 for (int i = finalized; i < candidate_top; ++i) {
4742 MethodBase candidate = (MethodBase) candidates [i];
4743 Type decl_type = candidate.DeclaringType;
4745 if (decl_type == applicable_type) {
4746 candidates [k++] = candidates [j];
4747 candidates [j++] = candidates [i];
4751 if (IsAncestralType (decl_type, applicable_type))
4754 if (next_applicable_type != null &&
4755 IsAncestralType (decl_type, next_applicable_type))
4758 candidates [k++] = candidates [i];
4760 if (next_applicable_type == null ||
4761 IsAncestralType (next_applicable_type, decl_type))
4762 next_applicable_type = decl_type;
4765 applicable_type = next_applicable_type;
4768 } while (applicable_type != null);
4772 // Now we actually find the best method
4775 method = (MethodBase) candidates [0];
4776 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4777 for (int ix = 1; ix < candidate_top; ix++){
4778 MethodBase candidate = (MethodBase) candidates [ix];
4780 if (candidate == method)
4783 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4785 if (BetterFunction (ec, Arguments, arg_count,
4786 candidate, cand_params,
4787 method, method_params)) {
4789 method_params = cand_params;
4793 // Now check that there are no ambiguities i.e the selected method
4794 // should be better than all the others
4796 MethodBase ambiguous = null;
4797 for (int ix = 0; ix < candidate_top; ix++){
4798 MethodBase candidate = (MethodBase) candidates [ix];
4800 if (candidate == method)
4803 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4804 if (!BetterFunction (ec, Arguments, arg_count,
4805 method, method_params,
4806 candidate, cand_params)) {
4807 Report.SymbolRelatedToPreviousError (candidate);
4808 ambiguous = candidate;
4812 if (ambiguous != null) {
4813 Report.SymbolRelatedToPreviousError (method);
4814 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4815 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4820 // If the method is a virtual function, pick an override closer to the LHS type.
4822 if (!me.IsBase && method.IsVirtual) {
4823 if (TypeManager.IsOverride (method))
4824 throw new InternalErrorException (
4825 "Should not happen. An 'override' method took part in overload resolution: " + method);
4827 if (candidate_overrides != null)
4828 foreach (MethodBase candidate in candidate_overrides) {
4829 if (IsOverride (candidate, method))
4835 // And now check if the arguments are all
4836 // compatible, perform conversions if
4837 // necessary etc. and return if everything is
4840 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4841 method_params, null, may_fail, loc))
4847 MethodBase the_method = TypeManager.DropGenericMethodArguments (method);
4848 if (the_method.IsGenericMethodDefinition &&
4849 !ConstraintChecker.CheckConstraints (ec, the_method, method, loc))
4852 IMethodData data = TypeManager.GetMethod (the_method);
4854 data.SetMemberIsUsed ();
4859 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4861 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4862 name, arg_count.ToString ());
4865 static void Error_InvokeOnDelegate (Location loc)
4867 Report.Error (1533, loc,
4868 "Invoke cannot be called directly on a delegate");
4871 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4872 Type delegate_type, Argument a, ParameterData expected_par)
4874 if (delegate_type == null)
4875 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4876 TypeManager.CSharpSignature (method));
4878 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4879 TypeManager.CSharpName (delegate_type));
4881 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4883 string index = (idx + 1).ToString ();
4884 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4885 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4886 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4887 index, Parameter.GetModifierSignature (a.Modifier));
4889 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4890 index, Parameter.GetModifierSignature (mod));
4892 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4893 index, Argument.FullDesc (a), expected_par.ParameterDesc (idx));
4897 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4898 int arg_count, MethodBase method,
4899 bool chose_params_expanded,
4900 Type delegate_type, bool may_fail,
4903 ParameterData pd = TypeManager.GetParameterData (method);
4905 for (j = 0; j < arg_count; j++) {
4906 Argument a = (Argument) Arguments [j];
4907 Expression a_expr = a.Expr;
4908 Type parameter_type = pd.ParameterType (j);
4909 Parameter.Modifier pm = pd.ParameterModifier (j);
4910 Parameter.Modifier am = a.Modifier;
4912 if (pm == Parameter.Modifier.ARGLIST) {
4913 if (!(a.Expr is Arglist))
4918 if (pm == Parameter.Modifier.PARAMS) {
4919 pm = Parameter.Modifier.NONE;
4920 if (chose_params_expanded)
4921 parameter_type = TypeManager.GetElementType (parameter_type);
4927 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4928 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4931 Expression conv = Convert.ImplicitConversion (ec, a_expr, parameter_type, loc);
4935 // Update the argument with the implicit conversion
4940 if (parameter_type.IsPointer && !ec.InUnsafe) {
4950 Error_InvalidArguments (loc, j, method, delegate_type, (Argument) Arguments [j], pd);
4954 private bool resolved = false;
4955 public override Expression DoResolve (EmitContext ec)
4958 return this.method == null ? null : this;
4962 // First, resolve the expression that is used to
4963 // trigger the invocation
4965 SimpleName sn = expr as SimpleName;
4967 expr = sn.GetMethodGroup ();
4969 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4973 if (!(expr is MethodGroupExpr)) {
4974 Type expr_type = expr.Type;
4976 if (expr_type != null){
4977 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4979 return (new DelegateInvocation (
4980 this.expr, Arguments, loc)).Resolve (ec);
4984 if (!(expr is MethodGroupExpr)){
4985 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4990 // Next, evaluate all the expressions in the argument list
4992 if (Arguments != null){
4993 foreach (Argument a in Arguments){
4994 if (!a.Resolve (ec, loc))
4999 MethodGroupExpr mg = (MethodGroupExpr) expr;
5000 MethodBase method = OverloadResolve (ec, mg, Arguments, false, loc);
5005 MethodInfo mi = method as MethodInfo;
5007 type = TypeManager.TypeToCoreType (mi.ReturnType);
5008 Expression iexpr = mg.InstanceExpression;
5010 if (iexpr == null ||
5011 iexpr is This || iexpr is EmptyExpression ||
5012 mg.IdenticalTypeName) {
5013 mg.InstanceExpression = null;
5015 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
5019 if (iexpr == null || iexpr is EmptyExpression) {
5020 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
5026 if (type.IsPointer){
5034 // Only base will allow this invocation to happen.
5036 if (mg.IsBase && method.IsAbstract){
5037 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
5041 if (Arguments == null && method.Name == "Finalize") {
5043 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
5045 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
5049 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
5053 if (mg.InstanceExpression != null)
5054 mg.InstanceExpression.CheckMarshalByRefAccess ();
5056 eclass = ExprClass.Value;
5057 this.method = method;
5061 bool IsSpecialMethodInvocation (MethodBase method)
5063 IMethodData md = TypeManager.GetMethod (method);
5065 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
5068 if (!TypeManager.IsSpecialMethod (method))
5071 int args = TypeManager.GetParameterData (method).Count;
5072 if (method.Name.StartsWith ("get_") && args > 0)
5074 else if (method.Name.StartsWith ("set_") && args > 2)
5077 // TODO: check operators and events as well ?
5080 Report.SymbolRelatedToPreviousError (method);
5081 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
5082 TypeManager.CSharpSignature (method, true));
5088 // Emits the list of arguments as an array
5090 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
5092 ILGenerator ig = ec.ig;
5093 int count = arguments.Count - idx;
5094 Argument a = (Argument) arguments [idx];
5095 Type t = a.Expr.Type;
5097 IntConstant.EmitInt (ig, count);
5098 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
5100 int top = arguments.Count;
5101 for (int j = idx; j < top; j++){
5102 a = (Argument) arguments [j];
5104 ig.Emit (OpCodes.Dup);
5105 IntConstant.EmitInt (ig, j - idx);
5107 bool is_stobj, has_type_arg;
5108 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
5110 ig.Emit (OpCodes.Ldelema, t);
5122 /// Emits a list of resolved Arguments that are in the arguments
5125 /// The MethodBase argument might be null if the
5126 /// emission of the arguments is known not to contain
5127 /// a `params' field (for example in constructors or other routines
5128 /// that keep their arguments in this structure)
5130 /// if `dup_args' is true, a copy of the arguments will be left
5131 /// on the stack. If `dup_args' is true, you can specify `this_arg'
5132 /// which will be duplicated before any other args. Only EmitCall
5133 /// should be using this interface.
5135 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
5137 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
5138 int top = arguments == null ? 0 : arguments.Count;
5139 LocalTemporary [] temps = null;
5141 if (dup_args && top != 0)
5142 temps = new LocalTemporary [top];
5144 for (int i = 0; i < top; i++){
5145 Argument a = (Argument) arguments [i];
5148 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
5150 // Special case if we are passing the same data as the
5151 // params argument, do not put it in an array.
5153 if (pd.ParameterType (i) == a.Type)
5156 EmitParams (ec, i, arguments);
5163 ec.ig.Emit (OpCodes.Dup);
5164 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
5169 if (this_arg != null)
5172 for (int i = 0; i < top; i ++) {
5173 temps [i].Emit (ec);
5174 temps [i].Release (ec);
5178 if (pd != null && pd.Count > top &&
5179 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
5180 ILGenerator ig = ec.ig;
5182 IntConstant.EmitInt (ig, 0);
5183 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
5187 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
5189 ParameterData pd = TypeManager.GetParameterData (mb);
5191 if (arguments == null)
5192 return new Type [0];
5194 Argument a = (Argument) arguments [pd.Count - 1];
5195 Arglist list = (Arglist) a.Expr;
5197 return list.ArgumentTypes;
5201 /// This checks the ConditionalAttribute on the method
5203 static bool IsMethodExcluded (MethodBase method)
5205 if (method.IsConstructor)
5208 IMethodData md = TypeManager.GetMethod (method);
5210 return md.IsExcluded ();
5212 // For some methods (generated by delegate class) GetMethod returns null
5213 // because they are not included in builder_to_method table
5214 if (method.DeclaringType is TypeBuilder)
5217 return AttributeTester.IsConditionalMethodExcluded (method);
5221 /// is_base tells whether we want to force the use of the `call'
5222 /// opcode instead of using callvirt. Call is required to call
5223 /// a specific method, while callvirt will always use the most
5224 /// recent method in the vtable.
5226 /// is_static tells whether this is an invocation on a static method
5228 /// instance_expr is an expression that represents the instance
5229 /// it must be non-null if is_static is false.
5231 /// method is the method to invoke.
5233 /// Arguments is the list of arguments to pass to the method or constructor.
5235 public static void EmitCall (EmitContext ec, bool is_base,
5236 bool is_static, Expression instance_expr,
5237 MethodBase method, ArrayList Arguments, Location loc)
5239 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
5242 // `dup_args' leaves an extra copy of the arguments on the stack
5243 // `omit_args' does not leave any arguments at all.
5244 // So, basically, you could make one call with `dup_args' set to true,
5245 // and then another with `omit_args' set to true, and the two calls
5246 // would have the same set of arguments. However, each argument would
5247 // only have been evaluated once.
5248 public static void EmitCall (EmitContext ec, bool is_base,
5249 bool is_static, Expression instance_expr,
5250 MethodBase method, ArrayList Arguments, Location loc,
5251 bool dup_args, bool omit_args)
5253 ILGenerator ig = ec.ig;
5254 bool struct_call = false;
5255 bool this_call = false;
5256 LocalTemporary this_arg = null;
5258 Type decl_type = method.DeclaringType;
5260 if (!RootContext.StdLib) {
5261 // Replace any calls to the system's System.Array type with calls to
5262 // the newly created one.
5263 if (method == TypeManager.system_int_array_get_length)
5264 method = TypeManager.int_array_get_length;
5265 else if (method == TypeManager.system_int_array_get_rank)
5266 method = TypeManager.int_array_get_rank;
5267 else if (method == TypeManager.system_object_array_clone)
5268 method = TypeManager.object_array_clone;
5269 else if (method == TypeManager.system_int_array_get_length_int)
5270 method = TypeManager.int_array_get_length_int;
5271 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5272 method = TypeManager.int_array_get_lower_bound_int;
5273 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5274 method = TypeManager.int_array_get_upper_bound_int;
5275 else if (method == TypeManager.system_void_array_copyto_array_int)
5276 method = TypeManager.void_array_copyto_array_int;
5279 if (!ec.IsInObsoleteScope) {
5281 // This checks ObsoleteAttribute on the method and on the declaring type
5283 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5285 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5287 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5289 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5293 if (IsMethodExcluded (method))
5297 if (instance_expr == EmptyExpression.Null) {
5298 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5302 this_call = instance_expr is This;
5303 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5307 // If this is ourselves, push "this"
5311 Type iexpr_type = instance_expr.Type;
5314 // Push the instance expression
5316 if (TypeManager.IsValueType (iexpr_type)) {
5318 // Special case: calls to a function declared in a
5319 // reference-type with a value-type argument need
5320 // to have their value boxed.
5321 if (decl_type.IsValueType ||
5322 iexpr_type.IsGenericParameter) {
5324 // If the expression implements IMemoryLocation, then
5325 // we can optimize and use AddressOf on the
5328 // If not we have to use some temporary storage for
5330 if (instance_expr is IMemoryLocation) {
5331 ((IMemoryLocation)instance_expr).
5332 AddressOf (ec, AddressOp.LoadStore);
5334 LocalTemporary temp = new LocalTemporary (iexpr_type);
5335 instance_expr.Emit (ec);
5337 temp.AddressOf (ec, AddressOp.Load);
5340 // avoid the overhead of doing this all the time.
5342 t = TypeManager.GetReferenceType (iexpr_type);
5344 instance_expr.Emit (ec);
5345 ig.Emit (OpCodes.Box, instance_expr.Type);
5346 t = TypeManager.object_type;
5349 instance_expr.Emit (ec);
5350 t = instance_expr.Type;
5354 ig.Emit (OpCodes.Dup);
5355 if (Arguments != null && Arguments.Count != 0) {
5356 this_arg = new LocalTemporary (t);
5357 this_arg.Store (ec);
5364 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5366 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
5367 ig.Emit (OpCodes.Constrained, instance_expr.Type);
5370 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5371 call_op = OpCodes.Call;
5373 call_op = OpCodes.Callvirt;
5375 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5376 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5377 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5384 // and DoFoo is not virtual, you can omit the callvirt,
5385 // because you don't need the null checking behavior.
5387 if (method is MethodInfo)
5388 ig.Emit (call_op, (MethodInfo) method);
5390 ig.Emit (call_op, (ConstructorInfo) method);
5393 public override void Emit (EmitContext ec)
5395 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5397 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5400 public override void EmitStatement (EmitContext ec)
5405 // Pop the return value if there is one
5407 if (method is MethodInfo){
5408 Type ret = ((MethodInfo)method).ReturnType;
5409 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5410 ec.ig.Emit (OpCodes.Pop);
5415 public class InvocationOrCast : ExpressionStatement
5418 Expression argument;
5420 public InvocationOrCast (Expression expr, Expression argument)
5423 this.argument = argument;
5424 this.loc = expr.Location;
5427 public override Expression DoResolve (EmitContext ec)
5430 // First try to resolve it as a cast.
5432 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5433 if ((te != null) && (te.eclass == ExprClass.Type)) {
5434 Cast cast = new Cast (te, argument, loc);
5435 return cast.Resolve (ec);
5439 // This can either be a type or a delegate invocation.
5440 // Let's just resolve it and see what we'll get.
5442 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5447 // Ok, so it's a Cast.
5449 if (expr.eclass == ExprClass.Type) {
5450 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5451 return cast.Resolve (ec);
5455 // It's a delegate invocation.
5457 if (!TypeManager.IsDelegateType (expr.Type)) {
5458 Error (149, "Method name expected");
5462 ArrayList args = new ArrayList ();
5463 args.Add (new Argument (argument, Argument.AType.Expression));
5464 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5465 return invocation.Resolve (ec);
5470 Error (201, "Only assignment, call, increment, decrement and new object " +
5471 "expressions can be used as a statement");
5474 public override ExpressionStatement ResolveStatement (EmitContext ec)
5477 // First try to resolve it as a cast.
5479 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5480 if ((te != null) && (te.eclass == ExprClass.Type)) {
5486 // This can either be a type or a delegate invocation.
5487 // Let's just resolve it and see what we'll get.
5489 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5490 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5496 // It's a delegate invocation.
5498 if (!TypeManager.IsDelegateType (expr.Type)) {
5499 Error (149, "Method name expected");
5503 ArrayList args = new ArrayList ();
5504 args.Add (new Argument (argument, Argument.AType.Expression));
5505 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5506 return invocation.ResolveStatement (ec);
5509 public override void Emit (EmitContext ec)
5511 throw new Exception ("Cannot happen");
5514 public override void EmitStatement (EmitContext ec)
5516 throw new Exception ("Cannot happen");
5521 // This class is used to "disable" the code generation for the
5522 // temporary variable when initializing value types.
5524 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5525 public void AddressOf (EmitContext ec, AddressOp Mode)
5532 /// Implements the new expression
5534 public class New : ExpressionStatement, IMemoryLocation {
5535 public readonly ArrayList Arguments;
5538 // During bootstrap, it contains the RequestedType,
5539 // but if `type' is not null, it *might* contain a NewDelegate
5540 // (because of field multi-initialization)
5542 public Expression RequestedType;
5544 MethodBase method = null;
5547 // If set, the new expression is for a value_target, and
5548 // we will not leave anything on the stack.
5550 Expression value_target;
5551 bool value_target_set = false;
5552 bool is_type_parameter = false;
5554 public New (Expression requested_type, ArrayList arguments, Location l)
5556 RequestedType = requested_type;
5557 Arguments = arguments;
5561 public bool SetValueTypeVariable (Expression value)
5563 value_target = value;
5564 value_target_set = true;
5565 if (!(value_target is IMemoryLocation)){
5566 Error_UnexpectedKind (null, "variable", loc);
5573 // This function is used to disable the following code sequence for
5574 // value type initialization:
5576 // AddressOf (temporary)
5580 // Instead the provide will have provided us with the address on the
5581 // stack to store the results.
5583 static Expression MyEmptyExpression;
5585 public void DisableTemporaryValueType ()
5587 if (MyEmptyExpression == null)
5588 MyEmptyExpression = new EmptyAddressOf ();
5591 // To enable this, look into:
5592 // test-34 and test-89 and self bootstrapping.
5594 // For instance, we can avoid a copy by using `newobj'
5595 // instead of Call + Push-temp on value types.
5596 // value_target = MyEmptyExpression;
5601 /// Converts complex core type syntax like 'new int ()' to simple constant
5603 public static Constant Constantify (Type t)
5605 if (t == TypeManager.int32_type)
5606 return new IntConstant (0, Location.Null);
5607 if (t == TypeManager.uint32_type)
5608 return new UIntConstant (0, Location.Null);
5609 if (t == TypeManager.int64_type)
5610 return new LongConstant (0, Location.Null);
5611 if (t == TypeManager.uint64_type)
5612 return new ULongConstant (0, Location.Null);
5613 if (t == TypeManager.float_type)
5614 return new FloatConstant (0, Location.Null);
5615 if (t == TypeManager.double_type)
5616 return new DoubleConstant (0, Location.Null);
5617 if (t == TypeManager.short_type)
5618 return new ShortConstant (0, Location.Null);
5619 if (t == TypeManager.ushort_type)
5620 return new UShortConstant (0, Location.Null);
5621 if (t == TypeManager.sbyte_type)
5622 return new SByteConstant (0, Location.Null);
5623 if (t == TypeManager.byte_type)
5624 return new ByteConstant (0, Location.Null);
5625 if (t == TypeManager.char_type)
5626 return new CharConstant ('\0', Location.Null);
5627 if (t == TypeManager.bool_type)
5628 return new BoolConstant (false, Location.Null);
5629 if (t == TypeManager.decimal_type)
5630 return new DecimalConstant (0, Location.Null);
5636 // Checks whether the type is an interface that has the
5637 // [ComImport, CoClass] attributes and must be treated
5640 public Expression CheckComImport (EmitContext ec)
5642 if (!type.IsInterface)
5646 // Turn the call into:
5647 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5649 Type real_class = AttributeTester.GetCoClassAttribute (type);
5650 if (real_class == null)
5653 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5654 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5655 return cast.Resolve (ec);
5658 public override Expression DoResolve (EmitContext ec)
5661 // The New DoResolve might be called twice when initializing field
5662 // expressions (see EmitFieldInitializers, the call to
5663 // GetInitializerExpression will perform a resolve on the expression,
5664 // and later the assign will trigger another resolution
5666 // This leads to bugs (#37014)
5669 if (RequestedType is NewDelegate)
5670 return RequestedType;
5674 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5680 if (Arguments == null) {
5681 Expression c = Constantify (type);
5686 if (TypeManager.IsDelegateType (type)) {
5687 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5688 if (RequestedType != null)
5689 if (!(RequestedType is DelegateCreation))
5690 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5691 return RequestedType;
5694 if (type.IsGenericParameter) {
5695 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5697 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5698 Error (304, String.Format (
5699 "Cannot create an instance of the " +
5700 "variable type '{0}' because it " +
5701 "doesn't have the new() constraint",
5706 if ((Arguments != null) && (Arguments.Count != 0)) {
5707 Error (417, String.Format (
5708 "`{0}': cannot provide arguments " +
5709 "when creating an instance of a " +
5710 "variable type.", type));
5714 is_type_parameter = true;
5715 eclass = ExprClass.Value;
5719 if (type.IsAbstract && type.IsSealed) {
5720 Report.SymbolRelatedToPreviousError (type);
5721 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5725 if (type.IsInterface || type.IsAbstract){
5726 RequestedType = CheckComImport (ec);
5727 if (RequestedType != null)
5728 return RequestedType;
5730 Report.SymbolRelatedToPreviousError (type);
5731 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5735 bool is_struct = type.IsValueType;
5736 eclass = ExprClass.Value;
5739 // SRE returns a match for .ctor () on structs (the object constructor),
5740 // so we have to manually ignore it.
5742 if (is_struct && Arguments == null)
5745 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5746 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5747 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5752 MethodGroupExpr mg = ml as MethodGroupExpr;
5755 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5759 if (Arguments != null){
5760 foreach (Argument a in Arguments){
5761 if (!a.Resolve (ec, loc))
5766 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5767 if (method == null) {
5768 if (almostMatchedMembers.Count != 0)
5769 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5776 bool DoEmitTypeParameter (EmitContext ec)
5778 ILGenerator ig = ec.ig;
5780 ig.Emit (OpCodes.Ldtoken, type);
5781 ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
5782 ig.Emit (OpCodes.Call, TypeManager.activator_create_instance);
5783 ig.Emit (OpCodes.Unbox_Any, type);
5789 // This DoEmit can be invoked in two contexts:
5790 // * As a mechanism that will leave a value on the stack (new object)
5791 // * As one that wont (init struct)
5793 // You can control whether a value is required on the stack by passing
5794 // need_value_on_stack. The code *might* leave a value on the stack
5795 // so it must be popped manually
5797 // If we are dealing with a ValueType, we have a few
5798 // situations to deal with:
5800 // * The target is a ValueType, and we have been provided
5801 // the instance (this is easy, we are being assigned).
5803 // * The target of New is being passed as an argument,
5804 // to a boxing operation or a function that takes a
5807 // In this case, we need to create a temporary variable
5808 // that is the argument of New.
5810 // Returns whether a value is left on the stack
5812 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5814 bool is_value_type = TypeManager.IsValueType (type);
5815 ILGenerator ig = ec.ig;
5820 // Allow DoEmit() to be called multiple times.
5821 // We need to create a new LocalTemporary each time since
5822 // you can't share LocalBuilders among ILGeneators.
5823 if (!value_target_set)
5824 value_target = new LocalTemporary (type);
5826 ml = (IMemoryLocation) value_target;
5827 ml.AddressOf (ec, AddressOp.Store);
5831 Invocation.EmitArguments (ec, method, Arguments, false, null);
5835 ig.Emit (OpCodes.Initobj, type);
5837 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5838 if (need_value_on_stack){
5839 value_target.Emit (ec);
5844 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5849 public override void Emit (EmitContext ec)
5851 if (is_type_parameter)
5852 DoEmitTypeParameter (ec);
5857 public override void EmitStatement (EmitContext ec)
5859 if (is_type_parameter)
5860 throw new InvalidOperationException ();
5862 if (DoEmit (ec, false))
5863 ec.ig.Emit (OpCodes.Pop);
5866 public void AddressOf (EmitContext ec, AddressOp Mode)
5868 if (is_type_parameter)
5869 throw new InvalidOperationException ();
5871 if (!type.IsValueType){
5873 // We throw an exception. So far, I believe we only need to support
5875 // foreach (int j in new StructType ())
5878 throw new Exception ("AddressOf should not be used for classes");
5881 if (!value_target_set)
5882 value_target = new LocalTemporary (type);
5884 IMemoryLocation ml = (IMemoryLocation) value_target;
5885 ml.AddressOf (ec, AddressOp.Store);
5887 Invocation.EmitArguments (ec, method, Arguments, false, null);
5890 ec.ig.Emit (OpCodes.Initobj, type);
5892 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5894 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5899 /// 14.5.10.2: Represents an array creation expression.
5903 /// There are two possible scenarios here: one is an array creation
5904 /// expression that specifies the dimensions and optionally the
5905 /// initialization data and the other which does not need dimensions
5906 /// specified but where initialization data is mandatory.
5908 public class ArrayCreation : Expression {
5909 Expression requested_base_type;
5910 ArrayList initializers;
5913 // The list of Argument types.
5914 // This is used to construct the `newarray' or constructor signature
5916 ArrayList arguments;
5919 // Method used to create the array object.
5921 MethodBase new_method = null;
5923 Type array_element_type;
5924 Type underlying_type;
5925 bool is_one_dimensional = false;
5926 bool is_builtin_type = false;
5927 bool expect_initializers = false;
5928 int num_arguments = 0;
5932 ArrayList array_data;
5936 // The number of constants in array initializers
5937 int const_initializers_count;
5939 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5941 this.requested_base_type = requested_base_type;
5942 this.initializers = initializers;
5946 arguments = new ArrayList ();
5948 foreach (Expression e in exprs) {
5949 arguments.Add (new Argument (e, Argument.AType.Expression));
5954 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5956 this.requested_base_type = requested_base_type;
5957 this.initializers = initializers;
5961 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5963 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5965 //dimensions = tmp.Length - 1;
5966 expect_initializers = true;
5969 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5971 StringBuilder sb = new StringBuilder (rank);
5974 for (int i = 1; i < idx_count; i++)
5979 return new ComposedCast (base_type, sb.ToString (), loc);
5982 void Error_IncorrectArrayInitializer ()
5984 Error (178, "Invalid rank specifier: expected `,' or `]'");
5987 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5989 if (specified_dims) {
5990 Argument a = (Argument) arguments [idx];
5992 if (!a.Resolve (ec, loc))
5995 Constant c = a.Expr as Constant;
5997 c = c.ToType (TypeManager.int32_type, a.Expr.Location);
6001 Report.Error (150, a.Expr.Location, "A constant value is expected");
6005 int value = (int) c.GetValue ();
6007 if (value != probe.Count) {
6008 Error_IncorrectArrayInitializer ();
6012 bounds [idx] = value;
6015 int child_bounds = -1;
6016 for (int i = 0; i < probe.Count; ++i) {
6017 object o = probe [i];
6018 if (o is ArrayList) {
6019 ArrayList sub_probe = o as ArrayList;
6020 int current_bounds = sub_probe.Count;
6022 if (child_bounds == -1)
6023 child_bounds = current_bounds;
6025 else if (child_bounds != current_bounds){
6026 Error_IncorrectArrayInitializer ();
6029 if (idx + 1 >= dimensions){
6030 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
6034 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
6038 if (child_bounds != -1){
6039 Error_IncorrectArrayInitializer ();
6043 Expression tmp = (Expression) o;
6044 tmp = tmp.Resolve (ec);
6048 Expression conv = Convert.ImplicitConversionRequired (
6049 ec, tmp, underlying_type, loc);
6054 // Initializers with the default values can be ignored
6055 Constant c = tmp as Constant;
6057 if (c.IsDefaultInitializer (array_element_type)) {
6061 ++const_initializers_count;
6064 // Used to invalidate static initializer
6065 const_initializers_count = int.MinValue;
6068 array_data.Add (conv);
6075 public void UpdateIndices ()
6078 for (ArrayList probe = initializers; probe != null;) {
6079 if (probe.Count > 0 && probe [0] is ArrayList) {
6080 Expression e = new IntConstant (probe.Count, Location.Null);
6081 arguments.Add (new Argument (e, Argument.AType.Expression));
6083 bounds [i++] = probe.Count;
6085 probe = (ArrayList) probe [0];
6088 Expression e = new IntConstant (probe.Count, Location.Null);
6089 arguments.Add (new Argument (e, Argument.AType.Expression));
6091 bounds [i++] = probe.Count;
6098 bool ResolveInitializers (EmitContext ec)
6100 if (initializers == null) {
6101 return !expect_initializers;
6104 if (underlying_type == null)
6108 // We use this to store all the date values in the order in which we
6109 // will need to store them in the byte blob later
6111 array_data = new ArrayList ();
6112 bounds = new System.Collections.Specialized.HybridDictionary ();
6114 if (arguments != null)
6115 return CheckIndices (ec, initializers, 0, true);
6117 arguments = new ArrayList ();
6119 if (!CheckIndices (ec, initializers, 0, false))
6124 if (arguments.Count != dimensions) {
6125 Error_IncorrectArrayInitializer ();
6133 // Creates the type of the array
6135 bool LookupType (EmitContext ec)
6137 StringBuilder array_qualifier = new StringBuilder (rank);
6140 // `In the first form allocates an array instace of the type that results
6141 // from deleting each of the individual expression from the expression list'
6143 if (num_arguments > 0) {
6144 array_qualifier.Append ("[");
6145 for (int i = num_arguments-1; i > 0; i--)
6146 array_qualifier.Append (",");
6147 array_qualifier.Append ("]");
6153 TypeExpr array_type_expr;
6154 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
6155 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
6156 if (array_type_expr == null)
6159 type = array_type_expr.Type;
6160 underlying_type = TypeManager.GetElementType (type);
6161 dimensions = type.GetArrayRank ();
6166 public override Expression DoResolve (EmitContext ec)
6171 if (!LookupType (ec))
6174 array_element_type = TypeManager.GetElementType (type);
6175 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
6176 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
6181 // First step is to validate the initializers and fill
6182 // in any missing bits
6184 if (!ResolveInitializers (ec))
6188 if (arguments == null)
6191 arg_count = arguments.Count;
6192 foreach (Argument a in arguments){
6193 if (!a.Resolve (ec, loc))
6196 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
6197 if (real_arg == null)
6204 if (arg_count == 1) {
6205 is_one_dimensional = true;
6206 eclass = ExprClass.Value;
6210 is_builtin_type = TypeManager.IsBuiltinType (type);
6212 if (is_builtin_type) {
6215 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
6216 AllBindingFlags, loc);
6218 if (!(ml is MethodGroupExpr)) {
6219 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
6224 Error (-6, "New invocation: Can not find a constructor for " +
6225 "this argument list");
6229 new_method = Invocation.OverloadResolve (
6230 ec, (MethodGroupExpr) ml, arguments, false, loc);
6232 if (new_method == null) {
6233 Error (-6, "New invocation: Can not find a constructor for " +
6234 "this argument list");
6238 eclass = ExprClass.Value;
6241 ModuleBuilder mb = CodeGen.Module.Builder;
6242 ArrayList args = new ArrayList ();
6244 if (arguments != null) {
6245 for (int i = 0; i < arg_count; i++)
6246 args.Add (TypeManager.int32_type);
6249 Type [] arg_types = null;
6252 arg_types = new Type [args.Count];
6254 args.CopyTo (arg_types, 0);
6256 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6259 if (new_method == null) {
6260 Error (-6, "New invocation: Can not find a constructor for " +
6261 "this argument list");
6265 eclass = ExprClass.Value;
6270 byte [] MakeByteBlob ()
6275 int count = array_data.Count;
6277 if (underlying_type.IsEnum)
6278 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
6280 factor = GetTypeSize (underlying_type);
6282 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
6284 data = new byte [(count * factor + 4) & ~3];
6287 for (int i = 0; i < count; ++i) {
6288 object v = array_data [i];
6290 if (v is EnumConstant)
6291 v = ((EnumConstant) v).Child;
6293 if (v is Constant && !(v is StringConstant))
6294 v = ((Constant) v).GetValue ();
6300 if (underlying_type == TypeManager.int64_type){
6301 if (!(v is Expression)){
6302 long val = (long) v;
6304 for (int j = 0; j < factor; ++j) {
6305 data [idx + j] = (byte) (val & 0xFF);
6309 } else if (underlying_type == TypeManager.uint64_type){
6310 if (!(v is Expression)){
6311 ulong val = (ulong) v;
6313 for (int j = 0; j < factor; ++j) {
6314 data [idx + j] = (byte) (val & 0xFF);
6318 } else if (underlying_type == TypeManager.float_type) {
6319 if (!(v is Expression)){
6320 element = BitConverter.GetBytes ((float) v);
6322 for (int j = 0; j < factor; ++j)
6323 data [idx + j] = element [j];
6325 } else if (underlying_type == TypeManager.double_type) {
6326 if (!(v is Expression)){
6327 element = BitConverter.GetBytes ((double) v);
6329 for (int j = 0; j < factor; ++j)
6330 data [idx + j] = element [j];
6332 } else if (underlying_type == TypeManager.char_type){
6333 if (!(v is Expression)){
6334 int val = (int) ((char) v);
6336 data [idx] = (byte) (val & 0xff);
6337 data [idx+1] = (byte) (val >> 8);
6339 } else if (underlying_type == TypeManager.short_type){
6340 if (!(v is Expression)){
6341 int val = (int) ((short) v);
6343 data [idx] = (byte) (val & 0xff);
6344 data [idx+1] = (byte) (val >> 8);
6346 } else if (underlying_type == TypeManager.ushort_type){
6347 if (!(v is Expression)){
6348 int val = (int) ((ushort) v);
6350 data [idx] = (byte) (val & 0xff);
6351 data [idx+1] = (byte) (val >> 8);
6353 } else if (underlying_type == TypeManager.int32_type) {
6354 if (!(v is Expression)){
6357 data [idx] = (byte) (val & 0xff);
6358 data [idx+1] = (byte) ((val >> 8) & 0xff);
6359 data [idx+2] = (byte) ((val >> 16) & 0xff);
6360 data [idx+3] = (byte) (val >> 24);
6362 } else if (underlying_type == TypeManager.uint32_type) {
6363 if (!(v is Expression)){
6364 uint val = (uint) v;
6366 data [idx] = (byte) (val & 0xff);
6367 data [idx+1] = (byte) ((val >> 8) & 0xff);
6368 data [idx+2] = (byte) ((val >> 16) & 0xff);
6369 data [idx+3] = (byte) (val >> 24);
6371 } else if (underlying_type == TypeManager.sbyte_type) {
6372 if (!(v is Expression)){
6373 sbyte val = (sbyte) v;
6374 data [idx] = (byte) val;
6376 } else if (underlying_type == TypeManager.byte_type) {
6377 if (!(v is Expression)){
6378 byte val = (byte) v;
6379 data [idx] = (byte) val;
6381 } else if (underlying_type == TypeManager.bool_type) {
6382 if (!(v is Expression)){
6383 bool val = (bool) v;
6384 data [idx] = (byte) (val ? 1 : 0);
6386 } else if (underlying_type == TypeManager.decimal_type){
6387 if (!(v is Expression)){
6388 int [] bits = Decimal.GetBits ((decimal) v);
6391 // FIXME: For some reason, this doesn't work on the MS runtime.
6392 int [] nbits = new int [4];
6393 nbits [0] = bits [3];
6394 nbits [1] = bits [2];
6395 nbits [2] = bits [0];
6396 nbits [3] = bits [1];
6398 for (int j = 0; j < 4; j++){
6399 data [p++] = (byte) (nbits [j] & 0xff);
6400 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6401 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6402 data [p++] = (byte) (nbits [j] >> 24);
6406 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6415 // Emits the initializers for the array
6417 void EmitStaticInitializers (EmitContext ec)
6420 // First, the static data
6423 ILGenerator ig = ec.ig;
6425 byte [] data = MakeByteBlob ();
6427 fb = RootContext.MakeStaticData (data);
6429 ig.Emit (OpCodes.Dup);
6430 ig.Emit (OpCodes.Ldtoken, fb);
6431 ig.Emit (OpCodes.Call,
6432 TypeManager.void_initializearray_array_fieldhandle);
6436 // Emits pieces of the array that can not be computed at compile
6437 // time (variables and string locations).
6439 // This always expect the top value on the stack to be the array
6441 void EmitDynamicInitializers (EmitContext ec)
6443 ILGenerator ig = ec.ig;
6444 int dims = bounds.Count;
6445 int [] current_pos = new int [dims];
6447 MethodInfo set = null;
6450 Type [] args = new Type [dims + 1];
6452 for (int j = 0; j < dims; j++)
6453 args [j] = TypeManager.int32_type;
6454 args [dims] = array_element_type;
6456 set = CodeGen.Module.Builder.GetArrayMethod (
6458 CallingConventions.HasThis | CallingConventions.Standard,
6459 TypeManager.void_type, args);
6462 for (int i = 0; i < array_data.Count; i++){
6464 Expression e = (Expression)array_data [i];
6467 Type etype = e.Type;
6469 ig.Emit (OpCodes.Dup);
6471 for (int idx = 0; idx < dims; idx++)
6472 IntConstant.EmitInt (ig, current_pos [idx]);
6475 // If we are dealing with a struct, get the
6476 // address of it, so we can store it.
6479 TypeManager.IsValueType (etype) &&
6480 (!TypeManager.IsBuiltinOrEnum (etype) ||
6481 etype == TypeManager.decimal_type)) {
6486 // Let new know that we are providing
6487 // the address where to store the results
6489 n.DisableTemporaryValueType ();
6492 ig.Emit (OpCodes.Ldelema, etype);
6498 bool is_stobj, has_type_arg;
6499 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
6501 ig.Emit (OpCodes.Stobj, etype);
6502 else if (has_type_arg)
6503 ig.Emit (op, etype);
6507 ig.Emit (OpCodes.Call, set);
6514 for (int j = dims - 1; j >= 0; j--){
6516 if (current_pos [j] < (int) bounds [j])
6518 current_pos [j] = 0;
6523 void EmitArrayArguments (EmitContext ec)
6525 ILGenerator ig = ec.ig;
6527 foreach (Argument a in arguments) {
6528 Type atype = a.Type;
6531 if (atype == TypeManager.uint64_type)
6532 ig.Emit (OpCodes.Conv_Ovf_U4);
6533 else if (atype == TypeManager.int64_type)
6534 ig.Emit (OpCodes.Conv_Ovf_I4);
6538 public override void Emit (EmitContext ec)
6540 ILGenerator ig = ec.ig;
6542 EmitArrayArguments (ec);
6543 if (is_one_dimensional)
6544 ig.Emit (OpCodes.Newarr, array_element_type);
6546 if (is_builtin_type)
6547 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6549 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6552 if (initializers == null)
6555 // This is a treshold for static initializers
6556 // I tried to make more accurate but it seems to me that Array.Initialize is
6557 // always slower (managed -> unmanaged switch?)
6558 const int max_automatic_initializers = 200;
6560 if (const_initializers_count > max_automatic_initializers && TypeManager.IsPrimitiveType (array_element_type)) {
6561 EmitStaticInitializers (ec);
6565 EmitDynamicInitializers (ec);
6568 public override bool GetAttributableValue (Type valueType, out object value)
6570 if (!is_one_dimensional){
6571 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6572 return base.GetAttributableValue (null, out value);
6575 if (array_data == null) {
6576 Constant c = (Constant)((Argument)arguments [0]).Expr;
6577 if (c.IsDefaultValue) {
6578 value = Array.CreateInstance (array_element_type, 0);
6581 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6582 return base.GetAttributableValue (null, out value);
6585 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6586 object element_value;
6587 for (int i = 0; i < ret.Length; ++i)
6589 Expression e = (Expression)array_data [i];
6590 if (e == null) // Is null when initializer is optimized away
6591 e = (Expression)initializers [i];
6593 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6597 ret.SetValue (element_value, i);
6604 public sealed class CompilerGeneratedThis : This
6606 public static This Instance = new CompilerGeneratedThis ();
6608 private CompilerGeneratedThis ()
6609 : base (Location.Null)
6613 public override Expression DoResolve (EmitContext ec)
6615 eclass = ExprClass.Variable;
6616 type = ec.ContainerType;
6622 /// Represents the `this' construct
6625 public class This : Expression, IAssignMethod, IMemoryLocation, IVariable {
6628 VariableInfo variable_info;
6630 public This (Block block, Location loc)
6636 public This (Location loc)
6641 public VariableInfo VariableInfo {
6642 get { return variable_info; }
6645 public bool VerifyFixed ()
6647 return !TypeManager.IsValueType (Type);
6650 public bool ResolveBase (EmitContext ec)
6652 eclass = ExprClass.Variable;
6654 if (ec.TypeContainer.CurrentType != null)
6655 type = ec.TypeContainer.CurrentType;
6657 type = ec.ContainerType;
6660 Error (26, "Keyword `this' is not valid in a static property, static method, or static field initializer");
6664 if (block != null && block.Toplevel.ThisVariable != null)
6665 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6667 if (ec.CurrentAnonymousMethod != null)
6673 public override Expression DoResolve (EmitContext ec)
6675 if (!ResolveBase (ec))
6678 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) && !variable_info.IsAssigned (ec)) {
6679 Error (188, "The `this' object cannot be used before all of its fields are assigned to");
6680 variable_info.SetAssigned (ec);
6684 if (ec.IsFieldInitializer) {
6685 Error (27, "Keyword `this' is not available in the current context");
6692 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6694 if (!ResolveBase (ec))
6697 if (variable_info != null)
6698 variable_info.SetAssigned (ec);
6700 if (ec.TypeContainer is Class){
6701 Error (1604, "Cannot assign to 'this' because it is read-only");
6708 public void Emit (EmitContext ec, bool leave_copy)
6712 ec.ig.Emit (OpCodes.Dup);
6715 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
6717 ILGenerator ig = ec.ig;
6719 if (ec.TypeContainer is Struct){
6720 ec.EmitThis (false);
6723 LocalTemporary t = null;
6725 t = new LocalTemporary (type);
6726 ec.ig.Emit (OpCodes.Dup);
6730 ig.Emit (OpCodes.Stobj, type);
6737 throw new Exception ("how did you get here");
6741 public override void Emit (EmitContext ec)
6743 ILGenerator ig = ec.ig;
6745 ec.EmitThis (false);
6746 if (ec.TypeContainer is Struct)
6747 ig.Emit (OpCodes.Ldobj, type);
6750 public override int GetHashCode()
6752 return block.GetHashCode ();
6755 public override bool Equals (object obj)
6757 This t = obj as This;
6761 return block == t.block;
6764 public void AddressOf (EmitContext ec, AddressOp mode)
6769 // FIGURE OUT WHY LDARG_S does not work
6771 // consider: struct X { int val; int P { set { val = value; }}}
6773 // Yes, this looks very bad. Look at `NOTAS' for
6775 // ec.ig.Emit (OpCodes.Ldarga_S, (byte) 0);
6780 /// Represents the `__arglist' construct
6782 public class ArglistAccess : Expression
6784 public ArglistAccess (Location loc)
6789 public override Expression DoResolve (EmitContext ec)
6791 eclass = ExprClass.Variable;
6792 type = TypeManager.runtime_argument_handle_type;
6794 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6796 Error (190, "The __arglist construct is valid only within " +
6797 "a variable argument method");
6804 public override void Emit (EmitContext ec)
6806 ec.ig.Emit (OpCodes.Arglist);
6811 /// Represents the `__arglist (....)' construct
6813 public class Arglist : Expression
6815 public readonly Argument[] Arguments;
6817 public Arglist (Argument[] args, Location l)
6823 public Type[] ArgumentTypes {
6825 Type[] retval = new Type [Arguments.Length];
6826 for (int i = 0; i < Arguments.Length; i++)
6827 retval [i] = Arguments [i].Type;
6832 public override Expression DoResolve (EmitContext ec)
6834 eclass = ExprClass.Variable;
6835 type = TypeManager.runtime_argument_handle_type;
6837 foreach (Argument arg in Arguments) {
6838 if (!arg.Resolve (ec, loc))
6845 public override void Emit (EmitContext ec)
6847 foreach (Argument arg in Arguments)
6853 // This produces the value that renders an instance, used by the iterators code
6855 public class ProxyInstance : Expression, IMemoryLocation {
6856 public override Expression DoResolve (EmitContext ec)
6858 eclass = ExprClass.Variable;
6859 type = ec.ContainerType;
6863 public override void Emit (EmitContext ec)
6865 ec.ig.Emit (OpCodes.Ldarg_0);
6869 public void AddressOf (EmitContext ec, AddressOp mode)
6871 ec.ig.Emit (OpCodes.Ldarg_0);
6876 /// Implements the typeof operator
6878 public class TypeOf : Expression {
6879 readonly Expression QueriedType;
6880 protected Type typearg;
6882 public TypeOf (Expression queried_type, Location l)
6884 QueriedType = queried_type;
6888 public override Expression DoResolve (EmitContext ec)
6890 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6894 typearg = texpr.Type;
6896 if (typearg == TypeManager.void_type) {
6897 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6901 if (typearg.IsPointer && !ec.InUnsafe){
6906 type = TypeManager.type_type;
6907 // Even though what is returned is a type object, it's treated as a value by the compiler.
6908 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6909 eclass = ExprClass.Value;
6913 public override void Emit (EmitContext ec)
6915 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6916 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6919 public override bool GetAttributableValue (Type valueType, out object value)
6921 if (typearg.ContainsGenericParameters) {
6922 Report.SymbolRelatedToPreviousError(typearg);
6923 Report.Error(416, loc, "`{0}': an attribute argument cannot use type parameters",
6924 TypeManager.CSharpName(typearg));
6929 if (valueType == TypeManager.object_type) {
6930 value = (object)typearg;
6937 public Type TypeArgument
6947 /// Implements the `typeof (void)' operator
6949 public class TypeOfVoid : TypeOf {
6950 public TypeOfVoid (Location l) : base (null, l)
6955 public override Expression DoResolve (EmitContext ec)
6957 type = TypeManager.type_type;
6958 typearg = TypeManager.void_type;
6959 // See description in TypeOf.
6960 eclass = ExprClass.Value;
6966 /// Implements the sizeof expression
6968 public class SizeOf : Expression {
6969 public Expression QueriedType;
6972 public SizeOf (Expression queried_type, Location l)
6974 this.QueriedType = queried_type;
6978 public override Expression DoResolve (EmitContext ec)
6980 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6984 if (texpr is TypeParameterExpr){
6985 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
6989 type_queried = texpr.Type;
6990 if (type_queried.IsEnum)
6991 type_queried = TypeManager.EnumToUnderlying (type_queried);
6993 if (type_queried == TypeManager.void_type) {
6994 Expression.Error_VoidInvalidInTheContext (loc);
6998 int size_of = GetTypeSize (type_queried);
7000 return new IntConstant (size_of, loc);
7004 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)",
7005 TypeManager.CSharpName (type_queried));
7009 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
7013 type = TypeManager.int32_type;
7014 eclass = ExprClass.Value;
7018 public override void Emit (EmitContext ec)
7020 int size = GetTypeSize (type_queried);
7023 ec.ig.Emit (OpCodes.Sizeof, type_queried);
7025 IntConstant.EmitInt (ec.ig, size);
7030 /// Implements the qualified-alias-member (::) expression.
7032 public class QualifiedAliasMember : Expression
7034 string alias, identifier;
7036 public QualifiedAliasMember (string alias, string identifier, Location l)
7039 this.identifier = identifier;
7043 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7045 if (alias == "global")
7046 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
7048 int errors = Report.Errors;
7049 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7051 if (errors == Report.Errors)
7052 Report.Error (432, loc, "Alias `{0}' not found", alias);
7055 if (fne.eclass != ExprClass.Namespace) {
7057 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7060 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
7063 public override Expression DoResolve (EmitContext ec)
7065 FullNamedExpression fne;
7066 if (alias == "global") {
7067 fne = RootNamespace.Global;
7069 int errors = Report.Errors;
7070 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7072 if (errors == Report.Errors)
7073 Report.Error (432, loc, "Alias `{0}' not found", alias);
7078 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
7082 if (!(retval is FullNamedExpression)) {
7083 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
7087 // We defer this check till the end to match the behaviour of CSC
7088 if (fne.eclass != ExprClass.Namespace) {
7089 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7095 public override void Emit (EmitContext ec)
7097 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
7101 public override string ToString ()
7103 return alias + "::" + identifier;
7106 public override string GetSignatureForError ()
7113 /// Implements the member access expression
7115 public class MemberAccess : Expression {
7116 public readonly string Identifier;
7120 public MemberAccess (Expression expr, string id)
7121 : this (expr, id, expr.Location)
7125 public MemberAccess (Expression expr, string identifier, Location loc)
7128 Identifier = identifier;
7132 public MemberAccess (Expression expr, string id, TypeArguments args)
7138 public Expression Expr {
7139 get { return expr; }
7142 // TODO: this method has very poor performace for Enum fields and
7143 // probably for other constants as well
7144 Expression DoResolve (EmitContext ec, Expression right_side)
7147 throw new Exception ();
7150 // Resolve the expression with flow analysis turned off, we'll do the definite
7151 // assignment checks later. This is because we don't know yet what the expression
7152 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7153 // definite assignment check on the actual field and not on the whole struct.
7156 SimpleName original = expr as SimpleName;
7157 Expression new_expr = expr.Resolve (ec,
7158 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7159 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7161 if (new_expr == null)
7164 if (new_expr is Namespace) {
7165 Namespace ns = (Namespace) new_expr;
7166 string lookup_id = MemberName.MakeName (Identifier, args);
7167 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, lookup_id, loc);
7168 if ((retval != null) && (args != null))
7169 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
7171 ns.Error_NamespaceDoesNotExist (loc, Identifier);
7175 Type expr_type = new_expr.Type;
7176 if (expr_type.IsPointer){
7177 Error (23, "The `.' operator can not be applied to pointer operands (" +
7178 TypeManager.CSharpName (expr_type) + ")");
7180 } else if (expr_type == TypeManager.void_type) {
7181 Error (23, "The `.' operator can not be applied to operands of type 'void'");
7183 } else if (expr_type == TypeManager.anonymous_method_type){
7184 Error (23, "The `.' operator can not be applied to anonymous methods");
7188 Expression member_lookup;
7189 member_lookup = MemberLookup (
7190 ec.ContainerType, expr_type, expr_type, Identifier, loc);
7191 if ((member_lookup == null) && (args != null)) {
7192 string lookup_id = MemberName.MakeName (Identifier, args);
7193 member_lookup = MemberLookup (
7194 ec.ContainerType, expr_type, expr_type, lookup_id, loc);
7196 if (member_lookup == null) {
7197 MemberLookupFailed (
7198 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
7202 if (member_lookup is TypeExpr) {
7203 if (!(new_expr is TypeExpr) &&
7204 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
7205 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7206 Identifier, member_lookup.GetSignatureForError ());
7210 ConstructedType ct = new_expr as ConstructedType;
7213 // When looking up a nested type in a generic instance
7214 // via reflection, we always get a generic type definition
7215 // and not a generic instance - so we have to do this here.
7217 // See gtest-172-lib.cs and gtest-172.cs for an example.
7219 ct = new ConstructedType (
7220 member_lookup.Type, ct.TypeArguments, loc);
7222 return ct.ResolveAsTypeStep (ec, false);
7225 return member_lookup;
7228 MemberExpr me = (MemberExpr) member_lookup;
7229 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
7230 if (member_lookup == null)
7234 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
7236 throw new InternalErrorException ();
7238 return mg.ResolveGeneric (ec, args);
7241 if (original != null && !TypeManager.IsValueType (expr_type)) {
7242 me = member_lookup as MemberExpr;
7243 if (me != null && me.IsInstance) {
7244 LocalVariableReference var = new_expr as LocalVariableReference;
7245 if (var != null && !var.VerifyAssigned (ec))
7250 // The following DoResolve/DoResolveLValue will do the definite assignment
7253 if (right_side != null)
7254 return member_lookup.DoResolveLValue (ec, right_side);
7256 return member_lookup.DoResolve (ec);
7259 public override Expression DoResolve (EmitContext ec)
7261 return DoResolve (ec, null);
7264 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7266 return DoResolve (ec, right_side);
7269 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7271 return ResolveNamespaceOrType (ec, silent);
7274 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7276 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
7278 if (new_expr == null)
7281 string lookup_id = MemberName.MakeName (Identifier, args);
7283 if (new_expr is Namespace) {
7284 Namespace ns = (Namespace) new_expr;
7285 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, lookup_id, loc);
7286 if ((retval != null) && (args != null))
7287 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
7288 if (!silent && retval == null)
7289 ns.Error_NamespaceDoesNotExist (loc, Identifier);
7293 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
7294 if (tnew_expr == null)
7297 Type expr_type = tnew_expr.Type;
7299 if (expr_type.IsPointer){
7300 Error (23, "The `.' operator can not be applied to pointer operands (" +
7301 TypeManager.CSharpName (expr_type) + ")");
7305 Expression member_lookup = MemberLookup (
7306 rc.DeclContainer.TypeBuilder, expr_type, expr_type, lookup_id,
7307 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7308 if (member_lookup == null) {
7309 int errors = Report.Errors;
7310 MemberLookupFailed (rc.DeclContainer.TypeBuilder, expr_type, expr_type, lookup_id, null, false, loc);
7312 if (!silent && errors == Report.Errors) {
7313 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7314 Identifier, new_expr.GetSignatureForError ());
7319 if (!(member_lookup is TypeExpr)) {
7320 new_expr.Error_UnexpectedKind (rc.DeclContainer, "type", loc);
7324 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
7328 TypeArguments the_args = args;
7329 if (TypeManager.HasGenericArguments (expr_type)) {
7330 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
7332 TypeArguments new_args = new TypeArguments (loc);
7333 foreach (Type decl in decl_args)
7334 new_args.Add (new TypeExpression (decl, loc));
7337 new_args.Add (args);
7339 the_args = new_args;
7342 if (the_args != null) {
7343 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
7344 return ctype.ResolveAsTypeStep (rc, false);
7350 public override void Emit (EmitContext ec)
7352 throw new Exception ("Should not happen");
7355 public override string ToString ()
7357 return expr + "." + MemberName.MakeName (Identifier, args);
7360 public override string GetSignatureForError ()
7362 return expr.GetSignatureForError () + "." + Identifier;
7367 /// Implements checked expressions
7369 public class CheckedExpr : Expression {
7371 public Expression Expr;
7373 public CheckedExpr (Expression e, Location l)
7379 public override Expression DoResolve (EmitContext ec)
7381 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7382 Expr = Expr.Resolve (ec);
7387 if (Expr is Constant)
7390 eclass = Expr.eclass;
7395 public override void Emit (EmitContext ec)
7397 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7401 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7403 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7404 Expr.EmitBranchable (ec, target, onTrue);
7409 /// Implements the unchecked expression
7411 public class UnCheckedExpr : Expression {
7413 public Expression Expr;
7415 public UnCheckedExpr (Expression e, Location l)
7421 public override Expression DoResolve (EmitContext ec)
7423 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7424 Expr = Expr.Resolve (ec);
7429 if (Expr is Constant)
7432 eclass = Expr.eclass;
7437 public override void Emit (EmitContext ec)
7439 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7443 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7445 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7446 Expr.EmitBranchable (ec, target, onTrue);
7451 /// An Element Access expression.
7453 /// During semantic analysis these are transformed into
7454 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7456 public class ElementAccess : Expression {
7457 public ArrayList Arguments;
7458 public Expression Expr;
7460 public ElementAccess (Expression e, ArrayList e_list)
7469 Arguments = new ArrayList ();
7470 foreach (Expression tmp in e_list)
7471 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7475 bool CommonResolve (EmitContext ec)
7477 Expr = Expr.Resolve (ec);
7482 if (Arguments == null)
7485 foreach (Argument a in Arguments){
7486 if (!a.Resolve (ec, loc))
7493 Expression MakePointerAccess (EmitContext ec, Type t)
7495 if (t == TypeManager.void_ptr_type){
7496 Error (242, "The array index operation is not valid on void pointers");
7499 if (Arguments.Count != 1){
7500 Error (196, "A pointer must be indexed by only one value");
7505 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7508 return new Indirection (p, loc).Resolve (ec);
7511 public override Expression DoResolve (EmitContext ec)
7513 if (!CommonResolve (ec))
7517 // We perform some simple tests, and then to "split" the emit and store
7518 // code we create an instance of a different class, and return that.
7520 // I am experimenting with this pattern.
7524 if (t == TypeManager.array_type){
7525 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7530 return (new ArrayAccess (this, loc)).Resolve (ec);
7532 return MakePointerAccess (ec, Expr.Type);
7534 FieldExpr fe = Expr as FieldExpr;
7536 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7538 return MakePointerAccess (ec, ff.ElementType);
7541 return (new IndexerAccess (this, loc)).Resolve (ec);
7544 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7546 if (!CommonResolve (ec))
7551 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7554 return MakePointerAccess (ec, Expr.Type);
7556 FieldExpr fe = Expr as FieldExpr;
7558 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7560 if (!(fe.InstanceExpression is LocalVariableReference) &&
7561 !(fe.InstanceExpression is This)) {
7562 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7565 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7566 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7569 return MakePointerAccess (ec, ff.ElementType);
7572 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7575 public override void Emit (EmitContext ec)
7577 throw new Exception ("Should never be reached");
7582 /// Implements array access
7584 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7586 // Points to our "data" repository
7590 LocalTemporary temp;
7593 public ArrayAccess (ElementAccess ea_data, Location l)
7596 eclass = ExprClass.Variable;
7600 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7602 return DoResolve (ec);
7605 public override Expression DoResolve (EmitContext ec)
7608 ExprClass eclass = ea.Expr.eclass;
7610 // As long as the type is valid
7611 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7612 eclass == ExprClass.Value)) {
7613 ea.Expr.Error_UnexpectedKind ("variable or value");
7618 Type t = ea.Expr.Type;
7619 if (t.GetArrayRank () != ea.Arguments.Count){
7620 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7621 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7625 type = TypeManager.GetElementType (t);
7626 if (type.IsPointer && !ec.InUnsafe){
7627 UnsafeError (ea.Location);
7631 foreach (Argument a in ea.Arguments){
7632 Type argtype = a.Type;
7634 if (argtype == TypeManager.int32_type ||
7635 argtype == TypeManager.uint32_type ||
7636 argtype == TypeManager.int64_type ||
7637 argtype == TypeManager.uint64_type) {
7638 Constant c = a.Expr as Constant;
7639 if (c != null && c.IsNegative) {
7640 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7646 // Mhm. This is strage, because the Argument.Type is not the same as
7647 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7649 // Wonder if I will run into trouble for this.
7651 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7656 eclass = ExprClass.Variable;
7662 /// Emits the right opcode to load an object of Type `t'
7663 /// from an array of T
7665 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7667 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7668 ig.Emit (OpCodes.Ldelem_U1);
7669 else if (type == TypeManager.sbyte_type)
7670 ig.Emit (OpCodes.Ldelem_I1);
7671 else if (type == TypeManager.short_type)
7672 ig.Emit (OpCodes.Ldelem_I2);
7673 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7674 ig.Emit (OpCodes.Ldelem_U2);
7675 else if (type == TypeManager.int32_type)
7676 ig.Emit (OpCodes.Ldelem_I4);
7677 else if (type == TypeManager.uint32_type)
7678 ig.Emit (OpCodes.Ldelem_U4);
7679 else if (type == TypeManager.uint64_type)
7680 ig.Emit (OpCodes.Ldelem_I8);
7681 else if (type == TypeManager.int64_type)
7682 ig.Emit (OpCodes.Ldelem_I8);
7683 else if (type == TypeManager.float_type)
7684 ig.Emit (OpCodes.Ldelem_R4);
7685 else if (type == TypeManager.double_type)
7686 ig.Emit (OpCodes.Ldelem_R8);
7687 else if (type == TypeManager.intptr_type)
7688 ig.Emit (OpCodes.Ldelem_I);
7689 else if (TypeManager.IsEnumType (type)){
7690 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7691 } else if (type.IsValueType){
7692 ig.Emit (OpCodes.Ldelema, type);
7693 ig.Emit (OpCodes.Ldobj, type);
7694 } else if (type.IsGenericParameter)
7696 ig.Emit (OpCodes.Ldelem, type);
7698 ig.Emit (OpCodes.Ldelem_Any, type);
7700 else if (type.IsPointer)
7701 ig.Emit (OpCodes.Ldelem_I);
7703 ig.Emit (OpCodes.Ldelem_Ref);
7707 /// Returns the right opcode to store an object of Type `t'
7708 /// from an array of T.
7710 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7712 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7713 has_type_arg = false; is_stobj = false;
7714 t = TypeManager.TypeToCoreType (t);
7715 if (TypeManager.IsEnumType (t))
7716 t = TypeManager.EnumToUnderlying (t);
7717 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7718 t == TypeManager.bool_type)
7719 return OpCodes.Stelem_I1;
7720 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7721 t == TypeManager.char_type)
7722 return OpCodes.Stelem_I2;
7723 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7724 return OpCodes.Stelem_I4;
7725 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7726 return OpCodes.Stelem_I8;
7727 else if (t == TypeManager.float_type)
7728 return OpCodes.Stelem_R4;
7729 else if (t == TypeManager.double_type)
7730 return OpCodes.Stelem_R8;
7731 else if (t == TypeManager.intptr_type) {
7732 has_type_arg = true;
7734 return OpCodes.Stobj;
7735 } else if (t.IsValueType) {
7736 has_type_arg = true;
7738 return OpCodes.Stobj;
7739 } else if (t.IsGenericParameter) {
7740 has_type_arg = true;
7742 return OpCodes.Stelem;
7744 return OpCodes.Stelem_Any;
7747 } else if (t.IsPointer)
7748 return OpCodes.Stelem_I;
7750 return OpCodes.Stelem_Ref;
7753 MethodInfo FetchGetMethod ()
7755 ModuleBuilder mb = CodeGen.Module.Builder;
7756 int arg_count = ea.Arguments.Count;
7757 Type [] args = new Type [arg_count];
7760 for (int i = 0; i < arg_count; i++){
7761 //args [i++] = a.Type;
7762 args [i] = TypeManager.int32_type;
7765 get = mb.GetArrayMethod (
7766 ea.Expr.Type, "Get",
7767 CallingConventions.HasThis |
7768 CallingConventions.Standard,
7774 MethodInfo FetchAddressMethod ()
7776 ModuleBuilder mb = CodeGen.Module.Builder;
7777 int arg_count = ea.Arguments.Count;
7778 Type [] args = new Type [arg_count];
7782 ret_type = TypeManager.GetReferenceType (type);
7784 for (int i = 0; i < arg_count; i++){
7785 //args [i++] = a.Type;
7786 args [i] = TypeManager.int32_type;
7789 address = mb.GetArrayMethod (
7790 ea.Expr.Type, "Address",
7791 CallingConventions.HasThis |
7792 CallingConventions.Standard,
7799 // Load the array arguments into the stack.
7801 // If we have been requested to cache the values (cached_locations array
7802 // initialized), then load the arguments the first time and store them
7803 // in locals. otherwise load from local variables.
7805 void LoadArrayAndArguments (EmitContext ec)
7807 ILGenerator ig = ec.ig;
7810 foreach (Argument a in ea.Arguments){
7811 Type argtype = a.Expr.Type;
7815 if (argtype == TypeManager.int64_type)
7816 ig.Emit (OpCodes.Conv_Ovf_I);
7817 else if (argtype == TypeManager.uint64_type)
7818 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7822 public void Emit (EmitContext ec, bool leave_copy)
7824 int rank = ea.Expr.Type.GetArrayRank ();
7825 ILGenerator ig = ec.ig;
7828 LoadArrayAndArguments (ec);
7831 EmitLoadOpcode (ig, type);
7835 method = FetchGetMethod ();
7836 ig.Emit (OpCodes.Call, method);
7839 LoadFromPtr (ec.ig, this.type);
7842 ec.ig.Emit (OpCodes.Dup);
7843 temp = new LocalTemporary (this.type);
7848 public override void Emit (EmitContext ec)
7853 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7855 int rank = ea.Expr.Type.GetArrayRank ();
7856 ILGenerator ig = ec.ig;
7857 Type t = source.Type;
7858 prepared = prepare_for_load;
7860 if (prepare_for_load) {
7861 AddressOf (ec, AddressOp.LoadStore);
7862 ec.ig.Emit (OpCodes.Dup);
7865 ec.ig.Emit (OpCodes.Dup);
7866 temp = new LocalTemporary (this.type);
7869 StoreFromPtr (ec.ig, t);
7879 LoadArrayAndArguments (ec);
7882 bool is_stobj, has_type_arg;
7883 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7886 // The stobj opcode used by value types will need
7887 // an address on the stack, not really an array/array
7891 ig.Emit (OpCodes.Ldelema, t);
7895 ec.ig.Emit (OpCodes.Dup);
7896 temp = new LocalTemporary (this.type);
7901 ig.Emit (OpCodes.Stobj, t);
7902 else if (has_type_arg)
7907 ModuleBuilder mb = CodeGen.Module.Builder;
7908 int arg_count = ea.Arguments.Count;
7909 Type [] args = new Type [arg_count + 1];
7914 ec.ig.Emit (OpCodes.Dup);
7915 temp = new LocalTemporary (this.type);
7919 for (int i = 0; i < arg_count; i++){
7920 //args [i++] = a.Type;
7921 args [i] = TypeManager.int32_type;
7924 args [arg_count] = type;
7926 set = mb.GetArrayMethod (
7927 ea.Expr.Type, "Set",
7928 CallingConventions.HasThis |
7929 CallingConventions.Standard,
7930 TypeManager.void_type, args);
7932 ig.Emit (OpCodes.Call, set);
7941 public void AddressOf (EmitContext ec, AddressOp mode)
7943 int rank = ea.Expr.Type.GetArrayRank ();
7944 ILGenerator ig = ec.ig;
7946 LoadArrayAndArguments (ec);
7949 ig.Emit (OpCodes.Ldelema, type);
7951 MethodInfo address = FetchAddressMethod ();
7952 ig.Emit (OpCodes.Call, address);
7956 public void EmitGetLength (EmitContext ec, int dim)
7958 int rank = ea.Expr.Type.GetArrayRank ();
7959 ILGenerator ig = ec.ig;
7963 ig.Emit (OpCodes.Ldlen);
7964 ig.Emit (OpCodes.Conv_I4);
7966 IntLiteral.EmitInt (ig, dim);
7967 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7973 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7974 public readonly ArrayList Properties;
7975 static Indexers empty;
7977 public struct Indexer {
7978 public readonly PropertyInfo PropertyInfo;
7979 public readonly MethodInfo Getter, Setter;
7981 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7983 this.PropertyInfo = property_info;
7991 empty = new Indexers (null);
7994 Indexers (ArrayList array)
7999 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
8004 foreach (PropertyInfo property in mi){
8005 MethodInfo get, set;
8007 get = property.GetGetMethod (true);
8008 set = property.GetSetMethod (true);
8009 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
8011 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
8013 if (get != null || set != null) {
8015 ix = new Indexers (new ArrayList ());
8016 ix.Properties.Add (new Indexer (property, get, set));
8021 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
8023 string p_name = TypeManager.IndexerPropertyName (lookup_type);
8025 return TypeManager.MemberLookup (
8026 caller_type, caller_type, lookup_type, MemberTypes.Property,
8027 BindingFlags.Public | BindingFlags.Instance |
8028 BindingFlags.DeclaredOnly, p_name, null);
8031 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
8033 Indexers ix = empty;
8035 if (lookup_type.IsGenericParameter) {
8036 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
8040 if (gc.HasClassConstraint)
8041 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
8043 Type[] ifaces = gc.InterfaceConstraints;
8044 foreach (Type itype in ifaces)
8045 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8050 Type copy = lookup_type;
8051 while (copy != TypeManager.object_type && copy != null){
8052 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
8053 copy = copy.BaseType;
8056 if (lookup_type.IsInterface) {
8057 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
8058 if (ifaces != null) {
8059 foreach (Type itype in ifaces)
8060 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8069 /// Expressions that represent an indexer call.
8071 public class IndexerAccess : Expression, IAssignMethod {
8073 // Points to our "data" repository
8075 MethodInfo get, set;
8076 ArrayList set_arguments;
8077 bool is_base_indexer;
8079 protected Type indexer_type;
8080 protected Type current_type;
8081 protected Expression instance_expr;
8082 protected ArrayList arguments;
8084 public IndexerAccess (ElementAccess ea, Location loc)
8085 : this (ea.Expr, false, loc)
8087 this.arguments = ea.Arguments;
8090 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
8093 this.instance_expr = instance_expr;
8094 this.is_base_indexer = is_base_indexer;
8095 this.eclass = ExprClass.Value;
8099 protected virtual bool CommonResolve (EmitContext ec)
8101 indexer_type = instance_expr.Type;
8102 current_type = ec.ContainerType;
8107 public override Expression DoResolve (EmitContext ec)
8109 if (!CommonResolve (ec))
8113 // Step 1: Query for all `Item' *properties*. Notice
8114 // that the actual methods are pointed from here.
8116 // This is a group of properties, piles of them.
8118 ArrayList AllGetters = null;
8120 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8121 if (ilist.Properties != null) {
8122 AllGetters = new ArrayList(ilist.Properties.Count);
8123 foreach (Indexers.Indexer ix in ilist.Properties) {
8124 if (ix.Getter != null)
8125 AllGetters.Add (ix.Getter);
8129 if (AllGetters == null) {
8130 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8131 TypeManager.CSharpName (indexer_type));
8135 if (AllGetters.Count == 0) {
8136 // FIXME: we cannot simply select first one as the error message is missleading when
8137 // multiple indexers exist
8138 Indexers.Indexer first_indexer = (Indexers.Indexer)ilist.Properties[ilist.Properties.Count - 1];
8139 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
8140 TypeManager.GetFullNameSignature (first_indexer.PropertyInfo));
8144 get = (MethodInfo)Invocation.OverloadResolve (ec, new MethodGroupExpr (AllGetters, loc),
8145 arguments, false, loc);
8148 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8153 // Only base will allow this invocation to happen.
8155 if (get.IsAbstract && this is BaseIndexerAccess){
8156 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
8160 type = get.ReturnType;
8161 if (type.IsPointer && !ec.InUnsafe){
8166 instance_expr.CheckMarshalByRefAccess ();
8168 eclass = ExprClass.IndexerAccess;
8172 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8174 if (right_side == EmptyExpression.OutAccess) {
8175 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
8176 GetSignatureForError ());
8180 // if the indexer returns a value type, and we try to set a field in it
8181 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
8182 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
8183 GetSignatureForError ());
8187 ArrayList AllSetters = new ArrayList();
8188 if (!CommonResolve (ec))
8191 bool found_any = false, found_any_setters = false;
8193 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8194 if (ilist.Properties != null) {
8196 foreach (Indexers.Indexer ix in ilist.Properties) {
8197 if (ix.Setter != null)
8198 AllSetters.Add (ix.Setter);
8201 if (AllSetters.Count > 0) {
8202 found_any_setters = true;
8203 set_arguments = (ArrayList) arguments.Clone ();
8204 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
8205 set = (MethodInfo) Invocation.OverloadResolve (
8206 ec, new MethodGroupExpr (AllSetters, loc),
8207 set_arguments, false, loc);
8211 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8212 TypeManager.CSharpName (indexer_type));
8216 if (!found_any_setters) {
8217 Error (154, "indexer can not be used in this context, because " +
8218 "it lacks a `set' accessor");
8223 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8228 // Only base will allow this invocation to happen.
8230 if (set.IsAbstract && this is BaseIndexerAccess){
8231 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
8236 // Now look for the actual match in the list of indexers to set our "return" type
8238 type = TypeManager.void_type; // default value
8239 foreach (Indexers.Indexer ix in ilist.Properties){
8240 if (ix.Setter == set){
8241 type = ix.PropertyInfo.PropertyType;
8246 instance_expr.CheckMarshalByRefAccess ();
8248 eclass = ExprClass.IndexerAccess;
8252 bool prepared = false;
8253 LocalTemporary temp;
8255 public void Emit (EmitContext ec, bool leave_copy)
8257 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
8259 ec.ig.Emit (OpCodes.Dup);
8260 temp = new LocalTemporary (Type);
8266 // source is ignored, because we already have a copy of it from the
8267 // LValue resolution and we have already constructed a pre-cached
8268 // version of the arguments (ea.set_arguments);
8270 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8272 prepared = prepare_for_load;
8273 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
8278 ec.ig.Emit (OpCodes.Dup);
8279 temp = new LocalTemporary (Type);
8282 } else if (leave_copy) {
8283 temp = new LocalTemporary (Type);
8289 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
8298 public override void Emit (EmitContext ec)
8303 public override string GetSignatureForError ()
8305 // FIXME: print the argument list of the indexer
8306 return instance_expr.GetSignatureForError () + ".this[...]";
8311 /// The base operator for method names
8313 public class BaseAccess : Expression {
8314 public readonly string Identifier;
8317 public BaseAccess (string member, TypeArguments args, Location l)
8319 this.Identifier = member;
8324 public override Expression DoResolve (EmitContext ec)
8326 Expression c = CommonResolve (ec);
8332 // MethodGroups use this opportunity to flag an error on lacking ()
8334 if (!(c is MethodGroupExpr))
8335 return c.Resolve (ec);
8339 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8341 Expression c = CommonResolve (ec);
8347 // MethodGroups use this opportunity to flag an error on lacking ()
8349 if (! (c is MethodGroupExpr))
8350 return c.DoResolveLValue (ec, right_side);
8355 Expression CommonResolve (EmitContext ec)
8357 Expression member_lookup;
8358 Type current_type = ec.ContainerType;
8359 Type base_type = current_type.BaseType;
8362 Error (1511, "Keyword `base' is not available in a static method");
8366 if (ec.IsFieldInitializer){
8367 Error (1512, "Keyword `base' is not available in the current context");
8371 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8372 AllMemberTypes, AllBindingFlags, loc);
8373 if (member_lookup == null) {
8374 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8381 left = new TypeExpression (base_type, loc);
8383 left = ec.GetThis (loc);
8385 MemberExpr me = (MemberExpr) member_lookup;
8387 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8389 if (e is PropertyExpr) {
8390 PropertyExpr pe = (PropertyExpr) e;
8395 MethodGroupExpr mg = e as MethodGroupExpr;
8401 return mg.ResolveGeneric (ec, args);
8403 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8411 public override void Emit (EmitContext ec)
8413 throw new Exception ("Should never be called");
8418 /// The base indexer operator
8420 public class BaseIndexerAccess : IndexerAccess {
8421 public BaseIndexerAccess (ArrayList args, Location loc)
8422 : base (null, true, loc)
8424 arguments = new ArrayList ();
8425 foreach (Expression tmp in args)
8426 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8429 protected override bool CommonResolve (EmitContext ec)
8431 instance_expr = ec.GetThis (loc);
8433 current_type = ec.ContainerType.BaseType;
8434 indexer_type = current_type;
8436 foreach (Argument a in arguments){
8437 if (!a.Resolve (ec, loc))
8446 /// This class exists solely to pass the Type around and to be a dummy
8447 /// that can be passed to the conversion functions (this is used by
8448 /// foreach implementation to typecast the object return value from
8449 /// get_Current into the proper type. All code has been generated and
8450 /// we only care about the side effect conversions to be performed
8452 /// This is also now used as a placeholder where a no-action expression
8453 /// is needed (the `New' class).
8455 public class EmptyExpression : Expression {
8456 public static readonly EmptyExpression Null = new EmptyExpression ();
8458 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8459 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8460 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8462 static EmptyExpression temp = new EmptyExpression ();
8463 public static EmptyExpression Grab ()
8466 throw new InternalErrorException ("Nested Grab");
8467 EmptyExpression retval = temp;
8472 public static void Release (EmptyExpression e)
8475 throw new InternalErrorException ("Already released");
8479 // TODO: should be protected
8480 public EmptyExpression ()
8482 type = TypeManager.object_type;
8483 eclass = ExprClass.Value;
8484 loc = Location.Null;
8487 public EmptyExpression (Type t)
8490 eclass = ExprClass.Value;
8491 loc = Location.Null;
8494 public override Expression DoResolve (EmitContext ec)
8499 public override void Emit (EmitContext ec)
8501 // nothing, as we only exist to not do anything.
8505 // This is just because we might want to reuse this bad boy
8506 // instead of creating gazillions of EmptyExpressions.
8507 // (CanImplicitConversion uses it)
8509 public void SetType (Type t)
8515 public class UserCast : Expression {
8519 public UserCast (MethodInfo method, Expression source, Location l)
8521 this.method = method;
8522 this.source = source;
8523 type = method.ReturnType;
8524 eclass = ExprClass.Value;
8528 public Expression Source {
8534 public override Expression DoResolve (EmitContext ec)
8537 // We are born fully resolved
8542 public override void Emit (EmitContext ec)
8544 ILGenerator ig = ec.ig;
8548 if (method is MethodInfo)
8549 ig.Emit (OpCodes.Call, (MethodInfo) method);
8551 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8557 // This class is used to "construct" the type during a typecast
8558 // operation. Since the Type.GetType class in .NET can parse
8559 // the type specification, we just use this to construct the type
8560 // one bit at a time.
8562 public class ComposedCast : TypeExpr {
8566 public ComposedCast (Expression left, string dim)
8567 : this (left, dim, left.Location)
8571 public ComposedCast (Expression left, string dim, Location l)
8578 public Expression RemoveNullable ()
8580 if (dim.EndsWith ("?")) {
8581 dim = dim.Substring (0, dim.Length - 1);
8589 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8591 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8595 Type ltype = lexpr.Type;
8596 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8597 Error_VoidInvalidInTheContext (loc);
8601 if ((dim.Length > 0) && (dim [0] == '?')) {
8602 TypeExpr nullable = new NullableType (left, loc);
8604 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8605 return nullable.ResolveAsTypeTerminal (ec, false);
8608 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc)) {
8613 type = TypeManager.GetConstructedType (ltype, dim);
8618 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8621 if (type.IsPointer && !ec.IsInUnsafeScope){
8626 if (type.IsArray && (type.GetElementType () == TypeManager.arg_iterator_type ||
8627 type.GetElementType () == TypeManager.typed_reference_type)) {
8628 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (type.GetElementType ()));
8632 eclass = ExprClass.Type;
8636 public override string Name {
8637 get { return left + dim; }
8640 public override string FullName {
8641 get { return type.FullName; }
8644 public override string GetSignatureForError ()
8646 return left.GetSignatureForError () + dim;
8650 public class FixedBufferPtr : Expression {
8653 public FixedBufferPtr (Expression array, Type array_type, Location l)
8658 type = TypeManager.GetPointerType (array_type);
8659 eclass = ExprClass.Value;
8662 public override void Emit(EmitContext ec)
8667 public override Expression DoResolve (EmitContext ec)
8670 // We are born fully resolved
8678 // This class is used to represent the address of an array, used
8679 // only by the Fixed statement, this generates "&a [0]" construct
8680 // for fixed (char *pa = a)
8682 public class ArrayPtr : FixedBufferPtr {
8685 public ArrayPtr (Expression array, Type array_type, Location l):
8686 base (array, array_type, l)
8688 this.array_type = array_type;
8691 public override void Emit (EmitContext ec)
8695 ILGenerator ig = ec.ig;
8696 IntLiteral.EmitInt (ig, 0);
8697 ig.Emit (OpCodes.Ldelema, array_type);
8702 // Used by the fixed statement
8704 public class StringPtr : Expression {
8707 public StringPtr (LocalBuilder b, Location l)
8710 eclass = ExprClass.Value;
8711 type = TypeManager.char_ptr_type;
8715 public override Expression DoResolve (EmitContext ec)
8717 // This should never be invoked, we are born in fully
8718 // initialized state.
8723 public override void Emit (EmitContext ec)
8725 ILGenerator ig = ec.ig;
8727 ig.Emit (OpCodes.Ldloc, b);
8728 ig.Emit (OpCodes.Conv_I);
8729 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8730 ig.Emit (OpCodes.Add);
8735 // Implements the `stackalloc' keyword
8737 public class StackAlloc : Expression {
8742 public StackAlloc (Expression type, Expression count, Location l)
8749 public override Expression DoResolve (EmitContext ec)
8751 count = count.Resolve (ec);
8755 if (count.Type != TypeManager.int32_type){
8756 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8761 Constant c = count as Constant;
8762 if (c != null && c.IsNegative) {
8763 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8767 if (ec.InCatch || ec.InFinally) {
8768 Error (255, "Cannot use stackalloc in finally or catch");
8772 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8778 if (!TypeManager.VerifyUnManaged (otype, loc))
8781 type = TypeManager.GetPointerType (otype);
8782 eclass = ExprClass.Value;
8787 public override void Emit (EmitContext ec)
8789 int size = GetTypeSize (otype);
8790 ILGenerator ig = ec.ig;
8793 ig.Emit (OpCodes.Sizeof, otype);
8795 IntConstant.EmitInt (ig, size);
8797 ig.Emit (OpCodes.Mul);
8798 ig.Emit (OpCodes.Localloc);