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 eclass = ExprClass.Value;
603 return ResolveOperator (ec);
606 public override Expression DoResolveLValue (EmitContext ec, Expression right)
608 if (Oper == Operator.Indirection)
609 return DoResolve (ec);
614 public override void Emit (EmitContext ec)
616 ILGenerator ig = ec.ig;
619 case Operator.UnaryPlus:
620 throw new Exception ("This should be caught by Resolve");
622 case Operator.UnaryNegation:
623 if (ec.CheckState && type != TypeManager.float_type && type != TypeManager.double_type) {
624 ig.Emit (OpCodes.Ldc_I4_0);
625 if (type == TypeManager.int64_type)
626 ig.Emit (OpCodes.Conv_U8);
628 ig.Emit (OpCodes.Sub_Ovf);
631 ig.Emit (OpCodes.Neg);
636 case Operator.LogicalNot:
638 ig.Emit (OpCodes.Ldc_I4_0);
639 ig.Emit (OpCodes.Ceq);
642 case Operator.OnesComplement:
644 ig.Emit (OpCodes.Not);
647 case Operator.AddressOf:
648 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
652 throw new Exception ("This should not happen: Operator = "
657 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
659 if (Oper == Operator.LogicalNot)
660 Expr.EmitBranchable (ec, target, !onTrue);
662 base.EmitBranchable (ec, target, onTrue);
665 public override string ToString ()
667 return "Unary (" + Oper + ", " + Expr + ")";
673 // Unary operators are turned into Indirection expressions
674 // after semantic analysis (this is so we can take the address
675 // of an indirection).
677 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
679 LocalTemporary temporary;
682 public Indirection (Expression expr, Location l)
685 type = TypeManager.HasElementType (expr.Type) ? TypeManager.GetElementType (expr.Type) : expr.Type;
686 eclass = ExprClass.Variable;
690 public override void Emit (EmitContext ec)
695 LoadFromPtr (ec.ig, Type);
698 public void Emit (EmitContext ec, bool leave_copy)
702 ec.ig.Emit (OpCodes.Dup);
703 temporary = new LocalTemporary (expr.Type);
704 temporary.Store (ec);
708 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
710 prepared = prepare_for_load;
714 if (prepare_for_load)
715 ec.ig.Emit (OpCodes.Dup);
719 ec.ig.Emit (OpCodes.Dup);
720 temporary = new LocalTemporary (expr.Type);
721 temporary.Store (ec);
724 StoreFromPtr (ec.ig, type);
726 if (temporary != null) {
728 temporary.Release (ec);
732 public void AddressOf (EmitContext ec, AddressOp Mode)
737 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
739 return DoResolve (ec);
742 public override Expression DoResolve (EmitContext ec)
745 // Born fully resolved
750 public override string ToString ()
752 return "*(" + expr + ")";
755 #region IVariable Members
757 public VariableInfo VariableInfo {
761 public bool VerifyFixed ()
763 // A pointer-indirection is always fixed.
771 /// Unary Mutator expressions (pre and post ++ and --)
775 /// UnaryMutator implements ++ and -- expressions. It derives from
776 /// ExpressionStatement becuase the pre/post increment/decrement
777 /// operators can be used in a statement context.
779 /// FIXME: Idea, we could split this up in two classes, one simpler
780 /// for the common case, and one with the extra fields for more complex
781 /// classes (indexers require temporary access; overloaded require method)
784 public class UnaryMutator : ExpressionStatement {
786 public enum Mode : byte {
793 PreDecrement = IsDecrement,
794 PostIncrement = IsPost,
795 PostDecrement = IsPost | IsDecrement
799 bool is_expr = false;
800 bool recurse = false;
805 // This is expensive for the simplest case.
807 StaticCallExpr method;
809 public UnaryMutator (Mode m, Expression e, Location l)
816 static string OperName (Mode mode)
818 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
823 /// Returns whether an object of type `t' can be incremented
824 /// or decremented with add/sub (ie, basically whether we can
825 /// use pre-post incr-decr operations on it, but it is not a
826 /// System.Decimal, which we require operator overloading to catch)
828 static bool IsIncrementableNumber (Type t)
830 return (t == TypeManager.sbyte_type) ||
831 (t == TypeManager.byte_type) ||
832 (t == TypeManager.short_type) ||
833 (t == TypeManager.ushort_type) ||
834 (t == TypeManager.int32_type) ||
835 (t == TypeManager.uint32_type) ||
836 (t == TypeManager.int64_type) ||
837 (t == TypeManager.uint64_type) ||
838 (t == TypeManager.char_type) ||
839 (t.IsSubclassOf (TypeManager.enum_type)) ||
840 (t == TypeManager.float_type) ||
841 (t == TypeManager.double_type) ||
842 (t.IsPointer && t != TypeManager.void_ptr_type);
845 Expression ResolveOperator (EmitContext ec)
847 Type expr_type = expr.Type;
850 // Step 1: Perform Operator Overload location
855 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
856 op_name = "op_Increment";
858 op_name = "op_Decrement";
860 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
863 method = StaticCallExpr.MakeSimpleCall (
864 ec, (MethodGroupExpr) mg, expr, loc);
867 } else if (!IsIncrementableNumber (expr_type)) {
868 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
869 TypeManager.CSharpName (expr_type) + "'");
874 // The operand of the prefix/postfix increment decrement operators
875 // should be an expression that is classified as a variable,
876 // a property access or an indexer access
879 if (expr.eclass == ExprClass.Variable){
880 LocalVariableReference var = expr as LocalVariableReference;
881 if ((var != null) && var.IsReadOnly) {
882 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
885 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
886 expr = expr.ResolveLValue (ec, this, Location);
890 expr.Error_UnexpectedKind (ec.DeclContainer, "variable, indexer or property access", loc);
897 public override Expression DoResolve (EmitContext ec)
899 expr = expr.Resolve (ec);
904 eclass = ExprClass.Value;
905 return ResolveOperator (ec);
908 static int PtrTypeSize (Type t)
910 return GetTypeSize (TypeManager.GetElementType (t));
914 // Loads the proper "1" into the stack based on the type, then it emits the
915 // opcode for the operation requested
917 void LoadOneAndEmitOp (EmitContext ec, Type t)
920 // Measure if getting the typecode and using that is more/less efficient
921 // that comparing types. t.GetTypeCode() is an internal call.
923 ILGenerator ig = ec.ig;
925 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
926 LongConstant.EmitLong (ig, 1);
927 else if (t == TypeManager.double_type)
928 ig.Emit (OpCodes.Ldc_R8, 1.0);
929 else if (t == TypeManager.float_type)
930 ig.Emit (OpCodes.Ldc_R4, 1.0F);
931 else if (t.IsPointer){
932 int n = PtrTypeSize (t);
935 ig.Emit (OpCodes.Sizeof, t);
937 IntConstant.EmitInt (ig, n);
939 ig.Emit (OpCodes.Ldc_I4_1);
942 // Now emit the operation
945 if (t == TypeManager.int32_type ||
946 t == TypeManager.int64_type){
947 if ((mode & Mode.IsDecrement) != 0)
948 ig.Emit (OpCodes.Sub_Ovf);
950 ig.Emit (OpCodes.Add_Ovf);
951 } else if (t == TypeManager.uint32_type ||
952 t == TypeManager.uint64_type){
953 if ((mode & Mode.IsDecrement) != 0)
954 ig.Emit (OpCodes.Sub_Ovf_Un);
956 ig.Emit (OpCodes.Add_Ovf_Un);
958 if ((mode & Mode.IsDecrement) != 0)
959 ig.Emit (OpCodes.Sub_Ovf);
961 ig.Emit (OpCodes.Add_Ovf);
964 if ((mode & Mode.IsDecrement) != 0)
965 ig.Emit (OpCodes.Sub);
967 ig.Emit (OpCodes.Add);
970 if (t == TypeManager.sbyte_type){
972 ig.Emit (OpCodes.Conv_Ovf_I1);
974 ig.Emit (OpCodes.Conv_I1);
975 } else if (t == TypeManager.byte_type){
977 ig.Emit (OpCodes.Conv_Ovf_U1);
979 ig.Emit (OpCodes.Conv_U1);
980 } else if (t == TypeManager.short_type){
982 ig.Emit (OpCodes.Conv_Ovf_I2);
984 ig.Emit (OpCodes.Conv_I2);
985 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
987 ig.Emit (OpCodes.Conv_Ovf_U2);
989 ig.Emit (OpCodes.Conv_U2);
994 void EmitCode (EmitContext ec, bool is_expr)
997 this.is_expr = is_expr;
998 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1001 public override void Emit (EmitContext ec)
1004 // We use recurse to allow ourselfs to be the source
1005 // of an assignment. This little hack prevents us from
1006 // having to allocate another expression
1009 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1011 LoadOneAndEmitOp (ec, expr.Type);
1013 ec.ig.Emit (OpCodes.Call, method.Method);
1018 EmitCode (ec, true);
1021 public override void EmitStatement (EmitContext ec)
1023 EmitCode (ec, false);
1028 /// Base class for the `Is' and `As' classes.
1032 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1035 public abstract class Probe : Expression {
1036 public Expression ProbeType;
1037 protected Expression expr;
1038 protected Type probe_type;
1040 public Probe (Expression expr, Expression probe_type, Location l)
1042 ProbeType = probe_type;
1047 public Expression Expr {
1053 public override Expression DoResolve (EmitContext ec)
1055 TypeExpr texpr = ProbeType.ResolveAsTypeTerminal (ec, false);
1059 probe_type = texpr.Type;
1061 expr = expr.Resolve (ec);
1065 if (expr.Type.IsPointer) {
1066 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1074 /// Implementation of the `is' operator.
1076 public class Is : Probe {
1077 public Is (Expression expr, Expression probe_type, Location l)
1078 : base (expr, probe_type, l)
1083 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1088 public override void Emit (EmitContext ec)
1090 ILGenerator ig = ec.ig;
1095 case Action.AlwaysFalse:
1096 ig.Emit (OpCodes.Pop);
1097 IntConstant.EmitInt (ig, 0);
1099 case Action.AlwaysTrue:
1100 ig.Emit (OpCodes.Pop);
1101 IntConstant.EmitInt (ig, 1);
1103 case Action.LeaveOnStack:
1104 // the `e != null' rule.
1105 ig.Emit (OpCodes.Ldnull);
1106 ig.Emit (OpCodes.Ceq);
1107 ig.Emit (OpCodes.Ldc_I4_0);
1108 ig.Emit (OpCodes.Ceq);
1111 ig.Emit (OpCodes.Isinst, probe_type);
1112 ig.Emit (OpCodes.Ldnull);
1113 ig.Emit (OpCodes.Cgt_Un);
1116 throw new Exception ("never reached");
1119 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1121 ILGenerator ig = ec.ig;
1124 case Action.AlwaysFalse:
1126 ig.Emit (OpCodes.Br, target);
1129 case Action.AlwaysTrue:
1131 ig.Emit (OpCodes.Br, target);
1134 case Action.LeaveOnStack:
1135 // the `e != null' rule.
1137 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1141 ig.Emit (OpCodes.Isinst, probe_type);
1142 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1145 throw new Exception ("never reached");
1148 public override Expression DoResolve (EmitContext ec)
1150 Expression e = base.DoResolve (ec);
1152 if ((e == null) || (expr == null))
1155 Type etype = expr.Type;
1156 bool warning_always_matches = false;
1157 bool warning_never_matches = false;
1159 type = TypeManager.bool_type;
1160 eclass = ExprClass.Value;
1163 // First case, if at compile time, there is an implicit conversion
1164 // then e != null (objects) or true (value types)
1166 e = Convert.ImplicitConversionStandard (ec, expr, probe_type, loc);
1167 if (e != null && !(e is NullCast)){
1169 if (etype.IsValueType)
1170 action = Action.AlwaysTrue;
1172 action = Action.LeaveOnStack;
1174 warning_always_matches = true;
1175 } else if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1177 // Second case: explicit reference convresion
1179 if (expr is NullLiteral)
1180 action = Action.AlwaysFalse;
1182 action = Action.Probe;
1184 action = Action.AlwaysFalse;
1185 warning_never_matches = true;
1188 if (warning_always_matches)
1189 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1190 else if (warning_never_matches){
1191 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1192 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1200 /// Implementation of the `as' operator.
1202 public class As : Probe {
1203 public As (Expression expr, Expression probe_type, Location l)
1204 : base (expr, probe_type, l)
1208 bool do_isinst = false;
1209 Expression resolved_type;
1211 public override void Emit (EmitContext ec)
1213 ILGenerator ig = ec.ig;
1218 ig.Emit (OpCodes.Isinst, probe_type);
1221 static void Error_CannotConvertType (Type source, Type target, Location loc)
1223 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1224 TypeManager.CSharpName (source),
1225 TypeManager.CSharpName (target));
1228 public override Expression DoResolve (EmitContext ec)
1230 if (resolved_type == null) {
1231 resolved_type = base.DoResolve (ec);
1233 if (resolved_type == null)
1238 eclass = ExprClass.Value;
1239 Type etype = expr.Type;
1241 if (TypeManager.IsValueType (probe_type)){
1242 Report.Error (77, loc, "The as operator must be used with a reference type (`" +
1243 TypeManager.CSharpName (probe_type) + "' is a value type)");
1248 Expression e = Convert.ImplicitConversion (ec, expr, probe_type, loc);
1255 if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1260 Error_CannotConvertType (etype, probe_type, loc);
1264 public override bool GetAttributableValue (Type valueType, out object value)
1266 return expr.GetAttributableValue (valueType, out value);
1271 /// This represents a typecast in the source language.
1273 /// FIXME: Cast expressions have an unusual set of parsing
1274 /// rules, we need to figure those out.
1276 public class Cast : Expression {
1277 Expression target_type;
1280 public Cast (Expression cast_type, Expression expr)
1281 : this (cast_type, expr, cast_type.Location)
1285 public Cast (Expression cast_type, Expression expr, Location loc)
1287 this.target_type = cast_type;
1291 if (target_type == TypeManager.system_void_expr)
1292 Error_VoidInvalidInTheContext (loc);
1295 public Expression TargetType {
1296 get { return target_type; }
1299 public Expression Expr {
1300 get { return expr; }
1301 set { expr = value; }
1304 public override Expression DoResolve (EmitContext ec)
1306 expr = expr.Resolve (ec);
1310 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1316 if (type.IsAbstract && type.IsSealed) {
1317 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1321 eclass = ExprClass.Value;
1323 Constant c = expr as Constant;
1326 c = c.TryReduce (ec, type, loc);
1330 catch (OverflowException) {
1335 if (type.IsPointer && !ec.InUnsafe) {
1339 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1343 public override void Emit (EmitContext ec)
1345 throw new Exception ("Should not happen");
1350 /// Binary operators
1352 public class Binary : Expression {
1353 public enum Operator : byte {
1354 Multiply, Division, Modulus,
1355 Addition, Subtraction,
1356 LeftShift, RightShift,
1357 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1358 Equality, Inequality,
1368 Expression left, right;
1370 // This must be kept in sync with Operator!!!
1371 public static readonly string [] oper_names;
1375 oper_names = new string [(int) Operator.TOP];
1377 oper_names [(int) Operator.Multiply] = "op_Multiply";
1378 oper_names [(int) Operator.Division] = "op_Division";
1379 oper_names [(int) Operator.Modulus] = "op_Modulus";
1380 oper_names [(int) Operator.Addition] = "op_Addition";
1381 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1382 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1383 oper_names [(int) Operator.RightShift] = "op_RightShift";
1384 oper_names [(int) Operator.LessThan] = "op_LessThan";
1385 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1386 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1387 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1388 oper_names [(int) Operator.Equality] = "op_Equality";
1389 oper_names [(int) Operator.Inequality] = "op_Inequality";
1390 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1391 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1392 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1393 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1394 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1397 public Binary (Operator oper, Expression left, Expression right)
1402 this.loc = left.Location;
1405 public Operator Oper {
1414 public Expression Left {
1423 public Expression Right {
1434 /// Returns a stringified representation of the Operator
1436 public static string OperName (Operator oper)
1439 case Operator.Multiply:
1441 case Operator.Division:
1443 case Operator.Modulus:
1445 case Operator.Addition:
1447 case Operator.Subtraction:
1449 case Operator.LeftShift:
1451 case Operator.RightShift:
1453 case Operator.LessThan:
1455 case Operator.GreaterThan:
1457 case Operator.LessThanOrEqual:
1459 case Operator.GreaterThanOrEqual:
1461 case Operator.Equality:
1463 case Operator.Inequality:
1465 case Operator.BitwiseAnd:
1467 case Operator.BitwiseOr:
1469 case Operator.ExclusiveOr:
1471 case Operator.LogicalOr:
1473 case Operator.LogicalAnd:
1477 return oper.ToString ();
1480 public override string ToString ()
1482 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1483 right.ToString () + ")";
1486 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1488 if (expr.Type == target_type)
1491 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1494 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1497 34, loc, "Operator `" + OperName (oper)
1498 + "' is ambiguous on operands of type `"
1499 + TypeManager.CSharpName (l) + "' "
1500 + "and `" + TypeManager.CSharpName (r)
1504 bool IsConvertible (EmitContext ec, Expression le, Expression re, Type t)
1506 return Convert.ImplicitConversionExists (ec, le, t) && Convert.ImplicitConversionExists (ec, re, t);
1509 bool VerifyApplicable_Predefined (EmitContext ec, Type t)
1511 if (!IsConvertible (ec, left, right, t))
1513 left = ForceConversion (ec, left, t);
1514 right = ForceConversion (ec, right, t);
1519 bool IsApplicable_String (EmitContext ec, Expression le, Expression re, Operator oper)
1521 bool l = Convert.ImplicitConversionExists (ec, le, TypeManager.string_type);
1522 bool r = Convert.ImplicitConversionExists (ec, re, TypeManager.string_type);
1524 if (oper == Operator.Equality || oper == Operator.Inequality)
1526 if (oper == Operator.Addition)
1531 bool OverloadResolve_PredefinedString (EmitContext ec, Operator oper)
1533 if (!IsApplicable_String (ec, left, right, oper))
1535 Type t = TypeManager.string_type;
1536 if (Convert.ImplicitConversionExists (ec, left, t))
1537 left = ForceConversion (ec, left, t);
1538 if (Convert.ImplicitConversionExists (ec, right, t))
1539 right = ForceConversion (ec, right, t);
1544 bool OverloadResolve_PredefinedIntegral (EmitContext ec)
1546 return VerifyApplicable_Predefined (ec, TypeManager.int32_type) ||
1547 VerifyApplicable_Predefined (ec, TypeManager.uint32_type) ||
1548 VerifyApplicable_Predefined (ec, TypeManager.int64_type) ||
1549 VerifyApplicable_Predefined (ec, TypeManager.uint64_type) ||
1553 bool OverloadResolve_PredefinedFloating (EmitContext ec)
1555 return VerifyApplicable_Predefined (ec, TypeManager.float_type) ||
1556 VerifyApplicable_Predefined (ec, TypeManager.double_type) ||
1560 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1562 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1565 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1567 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1571 void Error_OperatorCannotBeApplied ()
1573 Error_OperatorCannotBeApplied (Location, OperName (oper), left.GetSignatureForError (), right.GetSignatureForError ());
1576 static bool is_unsigned (Type t)
1578 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1579 t == TypeManager.short_type || t == TypeManager.byte_type);
1582 Expression Make32or64 (EmitContext ec, Expression e)
1586 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1587 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1589 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1592 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1595 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1598 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1604 Expression CheckShiftArguments (EmitContext ec)
1606 Expression new_left = Make32or64 (ec, left);
1607 Expression new_right = ForceConversion (ec, right, TypeManager.int32_type);
1608 if (new_left == null || new_right == null) {
1609 Error_OperatorCannotBeApplied ();
1612 type = new_left.Type;
1613 int shiftmask = (type == TypeManager.int32_type || type == TypeManager.uint32_type) ? 31 : 63;
1615 right = new Binary (Binary.Operator.BitwiseAnd, new_right, new IntConstant (shiftmask, loc)).DoResolve (ec);
1620 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1621 // i.e., not invoke op_Equality.
1623 static bool EqualsNullIsReferenceEquals (Type t)
1625 return t == TypeManager.object_type || t == TypeManager.string_type ||
1626 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1629 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1631 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1632 "Possible unintended reference comparison; to get a value comparison, " +
1633 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1636 Expression ResolveOperator (EmitContext ec)
1639 Type r = right.Type;
1641 if (oper == Operator.Equality || oper == Operator.Inequality){
1643 // Optimize out call to op_Equality in a few cases.
1645 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1646 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1647 Type = TypeManager.bool_type;
1652 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1653 Type = TypeManager.bool_type;
1659 // Do not perform operator overload resolution when both sides are
1662 Expression left_operators = null, right_operators = null;
1663 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))) {
1665 // Step 1: Perform Operator Overload location
1667 string op = oper_names [(int) oper];
1669 MethodGroupExpr union;
1670 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc);
1672 right_operators = MemberLookup (
1673 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc);
1674 union = Invocation.MakeUnionSet (left_operators, right_operators, loc);
1676 union = (MethodGroupExpr) left_operators;
1678 if (union != null) {
1679 ArrayList args = new ArrayList (2);
1680 args.Add (new Argument (left, Argument.AType.Expression));
1681 args.Add (new Argument (right, Argument.AType.Expression));
1683 MethodBase method = Invocation.OverloadResolve (ec, union, args, true, Location.Null);
1685 if (method != null) {
1686 MethodInfo mi = (MethodInfo) method;
1687 return new BinaryMethod (mi.ReturnType, method, args);
1693 // Step 0: String concatenation (because overloading will get this wrong)
1695 if (oper == Operator.Addition){
1697 // If any of the arguments is a string, cast to string
1700 // Simple constant folding
1701 if (left is StringConstant && right is StringConstant)
1702 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1704 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1706 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1707 Error_OperatorCannotBeApplied ();
1711 // try to fold it in on the left
1712 if (left is StringConcat) {
1715 // We have to test here for not-null, since we can be doubly-resolved
1716 // take care of not appending twice
1719 type = TypeManager.string_type;
1720 ((StringConcat) left).Append (ec, right);
1721 return left.Resolve (ec);
1727 // Otherwise, start a new concat expression
1728 return new StringConcat (ec, loc, left, right).Resolve (ec);
1732 // Transform a + ( - b) into a - b
1734 if (right is Unary){
1735 Unary right_unary = (Unary) right;
1737 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1738 oper = Operator.Subtraction;
1739 right = right_unary.Expr;
1745 if (oper == Operator.Equality || oper == Operator.Inequality){
1746 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1747 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1748 Error_OperatorCannotBeApplied ();
1752 type = TypeManager.bool_type;
1756 if (l.IsPointer || r.IsPointer) {
1757 if (l.IsPointer && r.IsPointer) {
1758 type = TypeManager.bool_type;
1762 if (l.IsPointer && r == TypeManager.null_type) {
1763 right = new EmptyCast (NullPointer.Null, l);
1764 type = TypeManager.bool_type;
1768 if (r.IsPointer && l == TypeManager.null_type) {
1769 left = new EmptyCast (NullPointer.Null, r);
1770 type = TypeManager.bool_type;
1776 // operator != (object a, object b)
1777 // operator == (object a, object b)
1779 // For this to be used, both arguments have to be reference-types.
1780 // Read the rationale on the spec (14.9.6)
1782 if (!(l.IsValueType || r.IsValueType)){
1783 type = TypeManager.bool_type;
1789 // Also, a standard conversion must exist from either one
1791 bool left_to_right =
1792 Convert.ImplicitStandardConversionExists (left, r);
1793 bool right_to_left = !left_to_right &&
1794 Convert.ImplicitStandardConversionExists (right, l);
1796 if (!left_to_right && !right_to_left) {
1797 Error_OperatorCannotBeApplied ();
1801 if (left_to_right && left_operators != null &&
1802 RootContext.WarningLevel >= 2) {
1803 ArrayList args = new ArrayList (2);
1804 args.Add (new Argument (left, Argument.AType.Expression));
1805 args.Add (new Argument (left, Argument.AType.Expression));
1806 MethodBase method = Invocation.OverloadResolve (
1807 ec, (MethodGroupExpr) left_operators, args, true, Location.Null);
1809 Warning_UnintendedReferenceComparison (loc, "right", l);
1812 if (right_to_left && right_operators != null &&
1813 RootContext.WarningLevel >= 2) {
1814 ArrayList args = new ArrayList (2);
1815 args.Add (new Argument (right, Argument.AType.Expression));
1816 args.Add (new Argument (right, Argument.AType.Expression));
1817 MethodBase method = Invocation.OverloadResolve (
1818 ec, (MethodGroupExpr) right_operators, args, true, Location.Null);
1820 Warning_UnintendedReferenceComparison (loc, "left", r);
1824 // We are going to have to convert to an object to compare
1826 if (l != TypeManager.object_type)
1827 left = new EmptyCast (left, TypeManager.object_type);
1828 if (r != TypeManager.object_type)
1829 right = new EmptyCast (right, TypeManager.object_type);
1835 // Only perform numeric promotions on:
1836 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
1838 if (oper == Operator.Addition || oper == Operator.Subtraction) {
1839 if (TypeManager.IsDelegateType (l)){
1840 if (((right.eclass == ExprClass.MethodGroup) ||
1841 (r == TypeManager.anonymous_method_type))){
1842 if ((RootContext.Version != LanguageVersion.ISO_1)){
1843 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
1851 if (TypeManager.IsDelegateType (r) || right is NullLiteral){
1853 ArrayList args = new ArrayList (2);
1855 args = new ArrayList (2);
1856 args.Add (new Argument (left, Argument.AType.Expression));
1857 args.Add (new Argument (right, Argument.AType.Expression));
1859 if (oper == Operator.Addition)
1860 method = TypeManager.delegate_combine_delegate_delegate;
1862 method = TypeManager.delegate_remove_delegate_delegate;
1864 if (l != r && !(right is NullLiteral)) {
1865 Error_OperatorCannotBeApplied ();
1869 return new BinaryDelegate (l, method, args);
1874 // Pointer arithmetic:
1876 // T* operator + (T* x, int y);
1877 // T* operator + (T* x, uint y);
1878 // T* operator + (T* x, long y);
1879 // T* operator + (T* x, ulong y);
1881 // T* operator + (int y, T* x);
1882 // T* operator + (uint y, T *x);
1883 // T* operator + (long y, T *x);
1884 // T* operator + (ulong y, T *x);
1886 // T* operator - (T* x, int y);
1887 // T* operator - (T* x, uint y);
1888 // T* operator - (T* x, long y);
1889 // T* operator - (T* x, ulong y);
1891 // long operator - (T* x, T *y)
1894 if (r.IsPointer && oper == Operator.Subtraction){
1896 return new PointerArithmetic (
1897 false, left, right, TypeManager.int64_type,
1900 Expression t = Make32or64 (ec, right);
1902 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
1904 } else if (r.IsPointer && oper == Operator.Addition){
1905 Expression t = Make32or64 (ec, left);
1907 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
1912 // Enumeration operators
1914 bool lie = TypeManager.IsEnumType (l);
1915 bool rie = TypeManager.IsEnumType (r);
1919 // U operator - (E e, E f)
1921 if (oper == Operator.Subtraction){
1923 type = TypeManager.EnumToUnderlying (l);
1926 Error_OperatorCannotBeApplied ();
1932 // operator + (E e, U x)
1933 // operator - (E e, U x)
1935 if (oper == Operator.Addition || oper == Operator.Subtraction){
1936 Type enum_type = lie ? l : r;
1937 Type other_type = lie ? r : l;
1938 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
1940 if (underlying_type != other_type){
1941 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
1951 Error_OperatorCannotBeApplied ();
1960 temp = Convert.ImplicitConversion (ec, right, l, loc);
1964 Error_OperatorCannotBeApplied ();
1968 temp = Convert.ImplicitConversion (ec, left, r, loc);
1973 Error_OperatorCannotBeApplied ();
1978 if (oper == Operator.Equality || oper == Operator.Inequality ||
1979 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
1980 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
1981 if (left.Type != right.Type){
1982 Error_OperatorCannotBeApplied ();
1985 type = TypeManager.bool_type;
1989 if (oper == Operator.BitwiseAnd ||
1990 oper == Operator.BitwiseOr ||
1991 oper == Operator.ExclusiveOr){
1992 if (left.Type != right.Type){
1993 Error_OperatorCannotBeApplied ();
1999 Error_OperatorCannotBeApplied ();
2003 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2004 return CheckShiftArguments (ec);
2006 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2007 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2008 type = TypeManager.bool_type;
2013 Error_OperatorCannotBeApplied ();
2017 Expression e = new ConditionalLogicalOperator (
2018 oper == Operator.LogicalAnd, left, right, l, loc);
2019 return e.Resolve (ec);
2022 Expression orig_left = left;
2023 Expression orig_right = right;
2026 // operator & (bool x, bool y)
2027 // operator | (bool x, bool y)
2028 // operator ^ (bool x, bool y)
2030 if (oper == Operator.BitwiseAnd ||
2031 oper == Operator.BitwiseOr ||
2032 oper == Operator.ExclusiveOr) {
2033 if (OverloadResolve_PredefinedIntegral (ec)) {
2034 if (IsConvertible (ec, orig_left, orig_right, TypeManager.bool_type)) {
2035 Error_OperatorAmbiguous (loc, oper, l, r);
2038 } else if (!VerifyApplicable_Predefined (ec, TypeManager.bool_type)) {
2039 Error_OperatorCannotBeApplied ();
2046 // Pointer comparison
2048 if (l.IsPointer && r.IsPointer){
2049 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2050 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2051 type = TypeManager.bool_type;
2056 if (OverloadResolve_PredefinedIntegral (ec)) {
2057 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2058 Error_OperatorAmbiguous (loc, oper, l, r);
2061 } else if (OverloadResolve_PredefinedFloating (ec)) {
2062 if (IsConvertible (ec, orig_left, orig_right, TypeManager.decimal_type) ||
2063 IsApplicable_String (ec, orig_left, orig_right, oper)) {
2064 Error_OperatorAmbiguous (loc, oper, l, r);
2067 } else if (VerifyApplicable_Predefined (ec, TypeManager.decimal_type)) {
2068 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2069 Error_OperatorAmbiguous (loc, oper, l, r);
2072 } else if (!OverloadResolve_PredefinedString (ec, oper)) {
2073 Error_OperatorCannotBeApplied ();
2077 if (oper == Operator.Equality ||
2078 oper == Operator.Inequality ||
2079 oper == Operator.LessThanOrEqual ||
2080 oper == Operator.LessThan ||
2081 oper == Operator.GreaterThanOrEqual ||
2082 oper == Operator.GreaterThan)
2083 type = TypeManager.bool_type;
2088 if (l == TypeManager.decimal_type || l == TypeManager.string_type || r == TypeManager.string_type) {
2090 if (r == TypeManager.string_type)
2092 MethodGroupExpr ops = (MethodGroupExpr) MemberLookup (
2093 ec.ContainerType, lookup, oper_names [(int) oper],
2094 MemberTypes.Method, AllBindingFlags, loc);
2095 ArrayList args = new ArrayList (2);
2096 args.Add (new Argument (left, Argument.AType.Expression));
2097 args.Add (new Argument (right, Argument.AType.Expression));
2098 MethodBase method = Invocation.OverloadResolve (ec, ops, args, true, Location.Null);
2099 return new BinaryMethod (type, method, args);
2105 Constant EnumLiftUp (Constant left, Constant right)
2108 case Operator.BitwiseOr:
2109 case Operator.BitwiseAnd:
2110 case Operator.ExclusiveOr:
2111 case Operator.Equality:
2112 case Operator.Inequality:
2113 case Operator.LessThan:
2114 case Operator.LessThanOrEqual:
2115 case Operator.GreaterThan:
2116 case Operator.GreaterThanOrEqual:
2117 if (left is EnumConstant)
2120 if (left.IsZeroInteger)
2121 return new EnumConstant (left, right.Type);
2125 case Operator.Addition:
2126 case Operator.Subtraction:
2129 case Operator.Multiply:
2130 case Operator.Division:
2131 case Operator.Modulus:
2132 case Operator.LeftShift:
2133 case Operator.RightShift:
2134 if (right is EnumConstant || left is EnumConstant)
2138 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2142 public override Expression DoResolve (EmitContext ec)
2147 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2148 left = ((ParenthesizedExpression) left).Expr;
2149 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2153 if (left.eclass == ExprClass.Type) {
2154 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2158 left = left.Resolve (ec);
2163 Constant lc = left as Constant;
2164 if (lc != null && lc.Type == TypeManager.bool_type &&
2165 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2166 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2168 // TODO: make a sense to resolve unreachable expression as we do for statement
2169 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2173 right = right.Resolve (ec);
2177 eclass = ExprClass.Value;
2178 Constant rc = right as Constant;
2180 // The conversion rules are ignored in enum context but why
2181 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2182 left = lc = EnumLiftUp (lc, rc);
2186 right = rc = EnumLiftUp (rc, lc);
2191 if (oper == Operator.BitwiseAnd) {
2192 if (rc != null && rc.IsZeroInteger) {
2193 return lc is EnumConstant ?
2194 new EnumConstant (rc, lc.Type):
2198 if (lc != null && lc.IsZeroInteger) {
2199 return rc is EnumConstant ?
2200 new EnumConstant (lc, rc.Type):
2204 else if (oper == Operator.BitwiseOr) {
2205 if (lc is EnumConstant &&
2206 rc != null && rc.IsZeroInteger)
2208 if (rc is EnumConstant &&
2209 lc != null && lc.IsZeroInteger)
2211 } else if (oper == Operator.LogicalAnd) {
2212 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2214 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2218 if (rc != null && lc != null){
2219 int prev_e = Report.Errors;
2220 Expression e = ConstantFold.BinaryFold (
2221 ec, oper, lc, rc, loc);
2222 if (e != null || Report.Errors != prev_e)
2226 // Comparison warnings
2227 if (oper == Operator.Equality || oper == Operator.Inequality ||
2228 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2229 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2230 if (left.Equals (right)) {
2231 Report.Warning (1718, 3, loc, "Comparison made to same variable; did you mean to compare something else?");
2233 CheckUselessComparison (lc, right.Type);
2234 CheckUselessComparison (rc, left.Type);
2237 return ResolveOperator (ec);
2240 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2245 private void CheckUselessComparison (Constant c, Type type)
2247 if (c == null || !IsTypeIntegral (type)
2248 || c is StringConstant
2249 || c is BoolConstant
2250 || c is CharConstant
2251 || c is FloatConstant
2252 || c is DoubleConstant
2253 || c is DecimalConstant
2259 if (c is ULongConstant) {
2260 ulong uvalue = ((ULongConstant) c).Value;
2261 if (uvalue > long.MaxValue) {
2262 if (type == TypeManager.byte_type ||
2263 type == TypeManager.sbyte_type ||
2264 type == TypeManager.short_type ||
2265 type == TypeManager.ushort_type ||
2266 type == TypeManager.int32_type ||
2267 type == TypeManager.uint32_type ||
2268 type == TypeManager.int64_type)
2269 WarnUselessComparison (type);
2272 value = (long) uvalue;
2274 else if (c is ByteConstant)
2275 value = ((ByteConstant) c).Value;
2276 else if (c is SByteConstant)
2277 value = ((SByteConstant) c).Value;
2278 else if (c is ShortConstant)
2279 value = ((ShortConstant) c).Value;
2280 else if (c is UShortConstant)
2281 value = ((UShortConstant) c).Value;
2282 else if (c is IntConstant)
2283 value = ((IntConstant) c).Value;
2284 else if (c is UIntConstant)
2285 value = ((UIntConstant) c).Value;
2286 else if (c is LongConstant)
2287 value = ((LongConstant) c).Value;
2290 if (IsValueOutOfRange (value, type))
2291 WarnUselessComparison (type);
2296 private bool IsValueOutOfRange (long value, Type type)
2298 if (IsTypeUnsigned (type) && value < 0)
2300 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2301 type == TypeManager.byte_type && value >= 0x100 ||
2302 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2303 type == TypeManager.ushort_type && value >= 0x10000 ||
2304 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2305 type == TypeManager.uint32_type && value >= 0x100000000;
2308 private static bool IsTypeIntegral (Type type)
2310 return type == TypeManager.uint64_type ||
2311 type == TypeManager.int64_type ||
2312 type == TypeManager.uint32_type ||
2313 type == TypeManager.int32_type ||
2314 type == TypeManager.ushort_type ||
2315 type == TypeManager.short_type ||
2316 type == TypeManager.sbyte_type ||
2317 type == TypeManager.byte_type;
2320 private static bool IsTypeUnsigned (Type type)
2322 return type == TypeManager.uint64_type ||
2323 type == TypeManager.uint32_type ||
2324 type == TypeManager.ushort_type ||
2325 type == TypeManager.byte_type;
2328 private void WarnUselessComparison (Type type)
2330 Report.Warning (652, 2, loc, "Comparison to integral constant is useless; the constant is outside the range of type `{0}'",
2331 TypeManager.CSharpName (type));
2335 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2336 /// context of a conditional bool expression. This function will return
2337 /// false if it is was possible to use EmitBranchable, or true if it was.
2339 /// The expression's code is generated, and we will generate a branch to `target'
2340 /// if the resulting expression value is equal to isTrue
2342 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2344 ILGenerator ig = ec.ig;
2347 // This is more complicated than it looks, but its just to avoid
2348 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2349 // but on top of that we want for == and != to use a special path
2350 // if we are comparing against null
2352 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2353 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2356 // put the constant on the rhs, for simplicity
2358 if (left is Constant) {
2359 Expression swap = right;
2364 if (((Constant) right).IsZeroInteger) {
2367 ig.Emit (OpCodes.Brtrue, target);
2369 ig.Emit (OpCodes.Brfalse, target);
2372 } else if (right is BoolConstant) {
2374 if (my_on_true != ((BoolConstant) right).Value)
2375 ig.Emit (OpCodes.Brtrue, target);
2377 ig.Emit (OpCodes.Brfalse, target);
2382 } else if (oper == Operator.LogicalAnd) {
2385 Label tests_end = ig.DefineLabel ();
2387 left.EmitBranchable (ec, tests_end, false);
2388 right.EmitBranchable (ec, target, true);
2389 ig.MarkLabel (tests_end);
2391 left.EmitBranchable (ec, target, false);
2392 right.EmitBranchable (ec, target, false);
2397 } else if (oper == Operator.LogicalOr){
2399 left.EmitBranchable (ec, target, true);
2400 right.EmitBranchable (ec, target, true);
2403 Label tests_end = ig.DefineLabel ();
2404 left.EmitBranchable (ec, tests_end, true);
2405 right.EmitBranchable (ec, target, false);
2406 ig.MarkLabel (tests_end);
2411 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2412 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2413 oper == Operator.Equality || oper == Operator.Inequality)) {
2414 base.EmitBranchable (ec, target, onTrue);
2422 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2425 case Operator.Equality:
2427 ig.Emit (OpCodes.Beq, target);
2429 ig.Emit (OpCodes.Bne_Un, target);
2432 case Operator.Inequality:
2434 ig.Emit (OpCodes.Bne_Un, target);
2436 ig.Emit (OpCodes.Beq, target);
2439 case Operator.LessThan:
2442 ig.Emit (OpCodes.Blt_Un, target);
2444 ig.Emit (OpCodes.Blt, target);
2447 ig.Emit (OpCodes.Bge_Un, target);
2449 ig.Emit (OpCodes.Bge, target);
2452 case Operator.GreaterThan:
2455 ig.Emit (OpCodes.Bgt_Un, target);
2457 ig.Emit (OpCodes.Bgt, target);
2460 ig.Emit (OpCodes.Ble_Un, target);
2462 ig.Emit (OpCodes.Ble, target);
2465 case Operator.LessThanOrEqual:
2468 ig.Emit (OpCodes.Ble_Un, target);
2470 ig.Emit (OpCodes.Ble, target);
2473 ig.Emit (OpCodes.Bgt_Un, target);
2475 ig.Emit (OpCodes.Bgt, target);
2479 case Operator.GreaterThanOrEqual:
2482 ig.Emit (OpCodes.Bge_Un, target);
2484 ig.Emit (OpCodes.Bge, target);
2487 ig.Emit (OpCodes.Blt_Un, target);
2489 ig.Emit (OpCodes.Blt, target);
2492 Console.WriteLine (oper);
2493 throw new Exception ("what is THAT");
2497 public override void Emit (EmitContext ec)
2499 ILGenerator ig = ec.ig;
2504 // Handle short-circuit operators differently
2507 if (oper == Operator.LogicalAnd) {
2508 Label load_zero = ig.DefineLabel ();
2509 Label end = ig.DefineLabel ();
2511 left.EmitBranchable (ec, load_zero, false);
2513 ig.Emit (OpCodes.Br, end);
2515 ig.MarkLabel (load_zero);
2516 ig.Emit (OpCodes.Ldc_I4_0);
2519 } else if (oper == Operator.LogicalOr) {
2520 Label load_one = ig.DefineLabel ();
2521 Label end = ig.DefineLabel ();
2523 left.EmitBranchable (ec, load_one, true);
2525 ig.Emit (OpCodes.Br, end);
2527 ig.MarkLabel (load_one);
2528 ig.Emit (OpCodes.Ldc_I4_1);
2536 bool isUnsigned = is_unsigned (left.Type);
2539 case Operator.Multiply:
2541 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2542 opcode = OpCodes.Mul_Ovf;
2543 else if (isUnsigned)
2544 opcode = OpCodes.Mul_Ovf_Un;
2546 opcode = OpCodes.Mul;
2548 opcode = OpCodes.Mul;
2552 case Operator.Division:
2554 opcode = OpCodes.Div_Un;
2556 opcode = OpCodes.Div;
2559 case Operator.Modulus:
2561 opcode = OpCodes.Rem_Un;
2563 opcode = OpCodes.Rem;
2566 case Operator.Addition:
2568 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2569 opcode = OpCodes.Add_Ovf;
2570 else if (isUnsigned)
2571 opcode = OpCodes.Add_Ovf_Un;
2573 opcode = OpCodes.Add;
2575 opcode = OpCodes.Add;
2578 case Operator.Subtraction:
2580 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2581 opcode = OpCodes.Sub_Ovf;
2582 else if (isUnsigned)
2583 opcode = OpCodes.Sub_Ovf_Un;
2585 opcode = OpCodes.Sub;
2587 opcode = OpCodes.Sub;
2590 case Operator.RightShift:
2592 opcode = OpCodes.Shr_Un;
2594 opcode = OpCodes.Shr;
2597 case Operator.LeftShift:
2598 opcode = OpCodes.Shl;
2601 case Operator.Equality:
2602 opcode = OpCodes.Ceq;
2605 case Operator.Inequality:
2606 ig.Emit (OpCodes.Ceq);
2607 ig.Emit (OpCodes.Ldc_I4_0);
2609 opcode = OpCodes.Ceq;
2612 case Operator.LessThan:
2614 opcode = OpCodes.Clt_Un;
2616 opcode = OpCodes.Clt;
2619 case Operator.GreaterThan:
2621 opcode = OpCodes.Cgt_Un;
2623 opcode = OpCodes.Cgt;
2626 case Operator.LessThanOrEqual:
2627 Type lt = left.Type;
2629 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2630 ig.Emit (OpCodes.Cgt_Un);
2632 ig.Emit (OpCodes.Cgt);
2633 ig.Emit (OpCodes.Ldc_I4_0);
2635 opcode = OpCodes.Ceq;
2638 case Operator.GreaterThanOrEqual:
2639 Type le = left.Type;
2641 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2642 ig.Emit (OpCodes.Clt_Un);
2644 ig.Emit (OpCodes.Clt);
2646 ig.Emit (OpCodes.Ldc_I4_0);
2648 opcode = OpCodes.Ceq;
2651 case Operator.BitwiseOr:
2652 opcode = OpCodes.Or;
2655 case Operator.BitwiseAnd:
2656 opcode = OpCodes.And;
2659 case Operator.ExclusiveOr:
2660 opcode = OpCodes.Xor;
2664 throw new Exception ("This should not happen: Operator = "
2665 + oper.ToString ());
2673 // Object created by Binary when the binary operator uses an method instead of being
2674 // a binary operation that maps to a CIL binary operation.
2676 public class BinaryMethod : Expression {
2677 public MethodBase method;
2678 public ArrayList Arguments;
2680 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2685 eclass = ExprClass.Value;
2688 public override Expression DoResolve (EmitContext ec)
2693 public override void Emit (EmitContext ec)
2695 ILGenerator ig = ec.ig;
2697 if (Arguments != null)
2698 Invocation.EmitArguments (ec, method, Arguments, false, null);
2700 if (method is MethodInfo)
2701 ig.Emit (OpCodes.Call, (MethodInfo) method);
2703 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2708 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2709 // b, c, d... may be strings or objects.
2711 public class StringConcat : Expression {
2713 bool invalid = false;
2714 bool emit_conv_done = false;
2716 // Are we also concating objects?
2718 bool is_strings_only = true;
2720 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2723 type = TypeManager.string_type;
2724 eclass = ExprClass.Value;
2726 operands = new ArrayList (2);
2731 public override Expression DoResolve (EmitContext ec)
2739 public void Append (EmitContext ec, Expression operand)
2744 StringConstant sc = operand as StringConstant;
2746 // TODO: it will be better to do this silently as an optimalization
2748 // string s = "" + i;
2749 // because this code has poor performace
2750 // if (sc.Value.Length == 0)
2751 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2753 if (operands.Count != 0) {
2754 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2755 if (last_operand != null) {
2756 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2763 // Conversion to object
2765 if (operand.Type != TypeManager.string_type) {
2766 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2769 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2775 operands.Add (operand);
2778 public override void Emit (EmitContext ec)
2780 MethodInfo concat_method = null;
2783 // Do conversion to arguments; check for strings only
2786 // This can get called multiple times, so we have to deal with that.
2787 if (!emit_conv_done) {
2788 emit_conv_done = true;
2789 for (int i = 0; i < operands.Count; i ++) {
2790 Expression e = (Expression) operands [i];
2791 is_strings_only &= e.Type == TypeManager.string_type;
2794 for (int i = 0; i < operands.Count; i ++) {
2795 Expression e = (Expression) operands [i];
2797 if (! is_strings_only && e.Type == TypeManager.string_type) {
2798 // need to make sure this is an object, because the EmitParams
2799 // method might look at the type of this expression, see it is a
2800 // string and emit a string [] when we want an object [];
2802 e = new EmptyCast (e, TypeManager.object_type);
2804 operands [i] = new Argument (e, Argument.AType.Expression);
2809 // Find the right method
2811 switch (operands.Count) {
2814 // This should not be possible, because simple constant folding
2815 // is taken care of in the Binary code.
2817 throw new Exception ("how did you get here?");
2820 concat_method = is_strings_only ?
2821 TypeManager.string_concat_string_string :
2822 TypeManager.string_concat_object_object ;
2825 concat_method = is_strings_only ?
2826 TypeManager.string_concat_string_string_string :
2827 TypeManager.string_concat_object_object_object ;
2831 // There is not a 4 param overlaod for object (the one that there is
2832 // is actually a varargs methods, and is only in corlib because it was
2833 // introduced there before.).
2835 if (!is_strings_only)
2838 concat_method = TypeManager.string_concat_string_string_string_string;
2841 concat_method = is_strings_only ?
2842 TypeManager.string_concat_string_dot_dot_dot :
2843 TypeManager.string_concat_object_dot_dot_dot ;
2847 Invocation.EmitArguments (ec, concat_method, operands, false, null);
2848 ec.ig.Emit (OpCodes.Call, concat_method);
2853 // Object created with +/= on delegates
2855 public class BinaryDelegate : Expression {
2859 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
2864 eclass = ExprClass.Value;
2867 public override Expression DoResolve (EmitContext ec)
2872 public override void Emit (EmitContext ec)
2874 ILGenerator ig = ec.ig;
2876 Invocation.EmitArguments (ec, method, args, false, null);
2878 ig.Emit (OpCodes.Call, (MethodInfo) method);
2879 ig.Emit (OpCodes.Castclass, type);
2882 public Expression Right {
2884 Argument arg = (Argument) args [1];
2889 public bool IsAddition {
2891 return method == TypeManager.delegate_combine_delegate_delegate;
2897 // User-defined conditional logical operator
2898 public class ConditionalLogicalOperator : Expression {
2899 Expression left, right;
2902 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
2905 eclass = ExprClass.Value;
2909 this.is_and = is_and;
2912 protected void Error19 ()
2914 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
2917 protected void Error218 ()
2919 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
2920 "declarations of operator true and operator false");
2923 Expression op_true, op_false, op;
2924 LocalTemporary left_temp;
2926 public override Expression DoResolve (EmitContext ec)
2929 Expression operator_group;
2931 operator_group = MethodLookup (ec, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
2932 if (operator_group == null) {
2937 left_temp = new LocalTemporary (type);
2939 ArrayList arguments = new ArrayList ();
2940 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
2941 arguments.Add (new Argument (right, Argument.AType.Expression));
2942 method = Invocation.OverloadResolve (
2943 ec, (MethodGroupExpr) operator_group, arguments, false, loc)
2945 if (method == null) {
2950 if (method.ReturnType != type) {
2951 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
2952 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
2956 op = new StaticCallExpr (method, arguments, loc);
2958 op_true = GetOperatorTrue (ec, left_temp, loc);
2959 op_false = GetOperatorFalse (ec, left_temp, loc);
2960 if ((op_true == null) || (op_false == null)) {
2968 public override void Emit (EmitContext ec)
2970 ILGenerator ig = ec.ig;
2971 Label false_target = ig.DefineLabel ();
2972 Label end_target = ig.DefineLabel ();
2975 left_temp.Store (ec);
2977 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
2978 left_temp.Emit (ec);
2979 ig.Emit (OpCodes.Br, end_target);
2980 ig.MarkLabel (false_target);
2982 ig.MarkLabel (end_target);
2984 // We release 'left_temp' here since 'op' may refer to it too
2985 left_temp.Release (ec);
2989 public class PointerArithmetic : Expression {
2990 Expression left, right;
2994 // We assume that `l' is always a pointer
2996 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3002 is_add = is_addition;
3005 public override Expression DoResolve (EmitContext ec)
3007 eclass = ExprClass.Variable;
3009 if (left.Type == TypeManager.void_ptr_type) {
3010 Error (242, "The operation in question is undefined on void pointers");
3017 public override void Emit (EmitContext ec)
3019 Type op_type = left.Type;
3020 ILGenerator ig = ec.ig;
3022 // It must be either array or fixed buffer
3023 Type element = TypeManager.HasElementType (op_type) ?
3024 element = TypeManager.GetElementType (op_type) :
3025 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3027 int size = GetTypeSize (element);
3028 Type rtype = right.Type;
3030 if (rtype.IsPointer){
3032 // handle (pointer - pointer)
3036 ig.Emit (OpCodes.Sub);
3040 ig.Emit (OpCodes.Sizeof, element);
3042 IntLiteral.EmitInt (ig, size);
3043 ig.Emit (OpCodes.Div);
3045 ig.Emit (OpCodes.Conv_I8);
3048 // handle + and - on (pointer op int)
3051 ig.Emit (OpCodes.Conv_I);
3053 Constant right_const = right as Constant;
3054 if (right_const != null && size != 0) {
3055 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3063 ig.Emit (OpCodes.Sizeof, element);
3065 IntLiteral.EmitInt (ig, size);
3066 if (rtype == TypeManager.int64_type)
3067 ig.Emit (OpCodes.Conv_I8);
3068 else if (rtype == TypeManager.uint64_type)
3069 ig.Emit (OpCodes.Conv_U8);
3070 ig.Emit (OpCodes.Mul);
3074 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3075 ig.Emit (OpCodes.Conv_I);
3078 ig.Emit (OpCodes.Add);
3080 ig.Emit (OpCodes.Sub);
3086 /// Implements the ternary conditional operator (?:)
3088 public class Conditional : Expression {
3089 Expression expr, trueExpr, falseExpr;
3091 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3094 this.trueExpr = trueExpr;
3095 this.falseExpr = falseExpr;
3096 this.loc = expr.Location;
3099 public Expression Expr {
3105 public Expression TrueExpr {
3111 public Expression FalseExpr {
3117 public override Expression DoResolve (EmitContext ec)
3119 expr = expr.Resolve (ec);
3124 if (expr.Type != TypeManager.bool_type){
3125 expr = Expression.ResolveBoolean (
3132 Assign ass = expr as Assign;
3133 if (ass != null && ass.Source is Constant) {
3134 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3137 trueExpr = trueExpr.Resolve (ec);
3138 falseExpr = falseExpr.Resolve (ec);
3140 if (trueExpr == null || falseExpr == null)
3143 eclass = ExprClass.Value;
3144 if (trueExpr.Type == falseExpr.Type)
3145 type = trueExpr.Type;
3148 Type true_type = trueExpr.Type;
3149 Type false_type = falseExpr.Type;
3152 // First, if an implicit conversion exists from trueExpr
3153 // to falseExpr, then the result type is of type falseExpr.Type
3155 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3158 // Check if both can convert implicitl to each other's type
3160 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3162 "Can not compute type of conditional expression " +
3163 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3164 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3165 "' convert implicitly to each other");
3170 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3174 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3175 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3180 // Dead code optimalization
3181 if (expr is BoolConstant){
3182 BoolConstant bc = (BoolConstant) expr;
3184 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3185 return bc.Value ? trueExpr : falseExpr;
3191 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3196 public override void Emit (EmitContext ec)
3198 ILGenerator ig = ec.ig;
3199 Label false_target = ig.DefineLabel ();
3200 Label end_target = ig.DefineLabel ();
3202 expr.EmitBranchable (ec, false_target, false);
3204 ig.Emit (OpCodes.Br, end_target);
3205 ig.MarkLabel (false_target);
3206 falseExpr.Emit (ec);
3207 ig.MarkLabel (end_target);
3215 public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3216 public readonly string Name;
3217 public readonly Block Block;
3218 public LocalInfo local_info;
3221 LocalTemporary temp;
3223 public LocalVariableReference (Block block, string name, Location l)
3228 eclass = ExprClass.Variable;
3232 // Setting `is_readonly' to false will allow you to create a writable
3233 // reference to a read-only variable. This is used by foreach and using.
3235 public LocalVariableReference (Block block, string name, Location l,
3236 LocalInfo local_info, bool is_readonly)
3237 : this (block, name, l)
3239 this.local_info = local_info;
3240 this.is_readonly = is_readonly;
3243 public VariableInfo VariableInfo {
3244 get { return local_info.VariableInfo; }
3247 public bool IsReadOnly {
3248 get { return is_readonly; }
3251 public bool VerifyAssigned (EmitContext ec)
3253 VariableInfo variable_info = local_info.VariableInfo;
3254 return variable_info == null || variable_info.IsAssigned (ec, loc);
3257 void ResolveLocalInfo ()
3259 if (local_info == null) {
3260 local_info = Block.GetLocalInfo (Name);
3261 is_readonly = local_info.ReadOnly;
3265 protected Expression DoResolveBase (EmitContext ec)
3267 type = local_info.VariableType;
3269 Expression e = Block.GetConstantExpression (Name);
3271 return e.Resolve (ec);
3273 if (!VerifyAssigned (ec))
3276 if (ec.CurrentAnonymousMethod != null){
3278 // If we are referencing a variable from the external block
3279 // flag it for capturing
3281 if ((local_info.Block.Toplevel != ec.CurrentBlock.Toplevel) ||
3282 ec.CurrentAnonymousMethod.IsIterator)
3284 if (local_info.AddressTaken){
3285 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3288 ec.CaptureVariable (local_info);
3295 public override Expression DoResolve (EmitContext ec)
3297 ResolveLocalInfo ();
3298 local_info.Used = true;
3299 return DoResolveBase (ec);
3302 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3304 ResolveLocalInfo ();
3309 if (right_side == EmptyExpression.OutAccess) {
3310 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3311 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3312 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3313 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3314 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3316 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
3318 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3323 if (right_side == EmptyExpression.OutAccess)
3324 local_info.Used = true;
3326 if (VariableInfo != null)
3327 VariableInfo.SetAssigned (ec);
3329 return DoResolveBase (ec);
3332 public bool VerifyFixed ()
3334 // A local Variable is always fixed.
3338 public override int GetHashCode ()
3340 return Name.GetHashCode ();
3343 public override bool Equals (object obj)
3345 LocalVariableReference lvr = obj as LocalVariableReference;
3349 return Name == lvr.Name && Block == lvr.Block;
3352 public override void Emit (EmitContext ec)
3354 ILGenerator ig = ec.ig;
3356 if (local_info.FieldBuilder == null){
3358 // A local variable on the local CLR stack
3360 ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
3363 // A local variable captured by anonymous methods.
3366 ec.EmitCapturedVariableInstance (local_info);
3368 ig.Emit (OpCodes.Ldfld, local_info.FieldBuilder);
3372 public void Emit (EmitContext ec, bool leave_copy)
3376 ec.ig.Emit (OpCodes.Dup);
3377 if (local_info.FieldBuilder != null){
3378 temp = new LocalTemporary (Type);
3384 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3386 ILGenerator ig = ec.ig;
3387 prepared = prepare_for_load;
3389 if (local_info.FieldBuilder == null){
3391 // A local variable on the local CLR stack
3393 if (local_info.LocalBuilder == null)
3394 throw new Exception ("This should not happen: both Field and Local are null");
3398 ec.ig.Emit (OpCodes.Dup);
3399 ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
3402 // A local variable captured by anonymous methods or itereators.
3404 ec.EmitCapturedVariableInstance (local_info);
3406 if (prepare_for_load)
3407 ig.Emit (OpCodes.Dup);
3410 ig.Emit (OpCodes.Dup);
3411 temp = new LocalTemporary (Type);
3414 ig.Emit (OpCodes.Stfld, local_info.FieldBuilder);
3422 public void AddressOf (EmitContext ec, AddressOp mode)
3424 ILGenerator ig = ec.ig;
3426 if (local_info.FieldBuilder == null){
3428 // A local variable on the local CLR stack
3430 ig.Emit (OpCodes.Ldloca, local_info.LocalBuilder);
3433 // A local variable captured by anonymous methods or iterators
3435 ec.EmitCapturedVariableInstance (local_info);
3436 ig.Emit (OpCodes.Ldflda, local_info.FieldBuilder);
3440 public override string ToString ()
3442 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3447 /// This represents a reference to a parameter in the intermediate
3450 public class ParameterReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3456 public bool is_ref, is_out, prepared;
3470 public string Name {
3476 LocalTemporary temp;
3478 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3481 this.name = par.Name;
3485 eclass = ExprClass.Variable;
3488 public VariableInfo VariableInfo {
3492 public bool VerifyFixed ()
3494 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3495 return par.ModFlags == Parameter.Modifier.NONE;
3498 public bool IsAssigned (EmitContext ec, Location loc)
3500 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3503 Report.Error (269, loc,
3504 "Use of unassigned out parameter `{0}'", par.Name);
3508 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3510 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3513 Report.Error (170, loc,
3514 "Use of possibly unassigned field `" + field_name + "'");
3518 public void SetAssigned (EmitContext ec)
3520 if (is_out && ec.DoFlowAnalysis)
3521 ec.CurrentBranching.SetAssigned (vi);
3524 public void SetFieldAssigned (EmitContext ec, string field_name)
3526 if (is_out && ec.DoFlowAnalysis)
3527 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3530 protected bool DoResolveBase (EmitContext ec)
3532 if (!par.Resolve (ec)) {
3536 type = par.ParameterType;
3537 Parameter.Modifier mod = par.ModFlags;
3538 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3539 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3540 eclass = ExprClass.Variable;
3543 vi = block.ParameterMap [idx];
3545 if (ec.CurrentAnonymousMethod != null){
3546 if (is_ref && !block.Toplevel.IsLocalParameter (name)){
3547 Report.Error (1628, Location, "Cannot use ref or out parameter `{0}' inside an anonymous method block",
3553 // If we are referencing the parameter from the external block
3554 // flag it for capturing
3556 //Console.WriteLine ("Is parameter `{0}' local? {1}", name, block.IsLocalParameter (name));
3557 if (!block.Toplevel.IsLocalParameter (name)){
3558 ec.CaptureParameter (name, type, idx);
3565 public override int GetHashCode()
3567 return name.GetHashCode ();
3570 public override bool Equals (object obj)
3572 ParameterReference pr = obj as ParameterReference;
3576 return name == pr.name && block == pr.block;
3580 // Notice that for ref/out parameters, the type exposed is not the
3581 // same type exposed externally.
3584 // externally we expose "int&"
3585 // here we expose "int".
3587 // We record this in "is_ref". This means that the type system can treat
3588 // the type as it is expected, but when we generate the code, we generate
3589 // the alternate kind of code.
3591 public override Expression DoResolve (EmitContext ec)
3593 if (!DoResolveBase (ec))
3596 if (is_out && ec.DoFlowAnalysis && (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3602 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3604 if (!DoResolveBase (ec))
3612 static public void EmitLdArg (ILGenerator ig, int x)
3616 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3617 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3618 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3619 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3620 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3623 ig.Emit (OpCodes.Ldarg, x);
3627 // This method is used by parameters that are references, that are
3628 // being passed as references: we only want to pass the pointer (that
3629 // is already stored in the parameter, not the address of the pointer,
3630 // and not the value of the variable).
3632 public void EmitLoad (EmitContext ec)
3634 ILGenerator ig = ec.ig;
3637 if (!ec.MethodIsStatic)
3640 EmitLdArg (ig, arg_idx);
3643 // FIXME: Review for anonymous methods
3647 public override void Emit (EmitContext ec)
3652 public void Emit (EmitContext ec, bool leave_copy)
3654 ILGenerator ig = ec.ig;
3657 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3658 ec.EmitParameter (name, leave_copy, prepared, ref temp);
3662 if (!ec.MethodIsStatic)
3665 EmitLdArg (ig, arg_idx);
3669 ec.ig.Emit (OpCodes.Dup);
3672 // If we are a reference, we loaded on the stack a pointer
3673 // Now lets load the real value
3675 LoadFromPtr (ig, type);
3679 ec.ig.Emit (OpCodes.Dup);
3682 temp = new LocalTemporary (type);
3688 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3690 prepared = prepare_for_load;
3691 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3692 ec.EmitAssignParameter (name, source, leave_copy, prepare_for_load, ref temp);
3696 ILGenerator ig = ec.ig;
3701 if (!ec.MethodIsStatic)
3704 if (is_ref && !prepared)
3705 EmitLdArg (ig, arg_idx);
3710 ec.ig.Emit (OpCodes.Dup);
3714 temp = new LocalTemporary (type);
3718 StoreFromPtr (ig, type);
3726 ig.Emit (OpCodes.Starg_S, (byte) arg_idx);
3728 ig.Emit (OpCodes.Starg, arg_idx);
3732 public void AddressOf (EmitContext ec, AddressOp mode)
3734 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3735 ec.EmitAddressOfParameter (name);
3741 if (!ec.MethodIsStatic)
3746 ec.ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx);
3748 ec.ig.Emit (OpCodes.Ldarg, arg_idx);
3751 ec.ig.Emit (OpCodes.Ldarga_S, (byte) arg_idx);
3753 ec.ig.Emit (OpCodes.Ldarga, arg_idx);
3757 public override string ToString ()
3759 return "ParameterReference[" + name + "]";
3764 /// Used for arguments to New(), Invocation()
3766 public class Argument {
3767 public enum AType : byte {
3774 public readonly AType ArgType;
3775 public Expression Expr;
3777 public Argument (Expression expr, AType type)
3780 this.ArgType = type;
3783 public Argument (Expression expr)
3786 this.ArgType = AType.Expression;
3791 if (ArgType == AType.Ref || ArgType == AType.Out)
3792 return TypeManager.GetReferenceType (Expr.Type);
3798 public Parameter.Modifier Modifier
3803 return Parameter.Modifier.OUT;
3806 return Parameter.Modifier.REF;
3809 return Parameter.Modifier.NONE;
3814 public static string FullDesc (Argument a)
3816 if (a.ArgType == AType.ArgList)
3819 return (a.ArgType == AType.Ref ? "ref " :
3820 (a.ArgType == AType.Out ? "out " : "")) +
3821 TypeManager.CSharpName (a.Expr.Type);
3824 public bool ResolveMethodGroup (EmitContext ec)
3826 // FIXME: csc doesn't report any error if you try to use `ref' or
3827 // `out' in a delegate creation expression.
3828 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3835 public bool Resolve (EmitContext ec, Location loc)
3837 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3838 // Verify that the argument is readable
3839 if (ArgType != AType.Out)
3840 Expr = Expr.Resolve (ec);
3842 // Verify that the argument is writeable
3843 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3844 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3846 return Expr != null;
3850 public void Emit (EmitContext ec)
3852 if (ArgType != AType.Ref && ArgType != AType.Out) {
3857 AddressOp mode = AddressOp.Store;
3858 if (ArgType == AType.Ref)
3859 mode |= AddressOp.Load;
3861 IMemoryLocation ml = (IMemoryLocation) Expr;
3862 ParameterReference pr = ml as ParameterReference;
3865 // ParameterReferences might already be references, so we want
3866 // to pass just the value
3868 if (pr != null && pr.IsRef)
3871 ml.AddressOf (ec, mode);
3876 /// Invocation of methods or delegates.
3878 public class Invocation : ExpressionStatement {
3879 public readonly ArrayList Arguments;
3882 MethodBase method = null;
3885 // arguments is an ArrayList, but we do not want to typecast,
3886 // as it might be null.
3888 // FIXME: only allow expr to be a method invocation or a
3889 // delegate invocation (7.5.5)
3891 public Invocation (Expression expr, ArrayList arguments)
3894 Arguments = arguments;
3895 loc = expr.Location;
3898 public Expression Expr {
3905 /// Determines "better conversion" as specified in 14.4.2.3
3907 /// Returns : p if a->p is better,
3908 /// q if a->q is better,
3909 /// null if neither is better
3911 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q)
3913 Type argument_type = a.Type;
3914 Expression argument_expr = a.Expr;
3916 if (argument_type == null)
3917 throw new Exception ("Expression of type " + a.Expr +
3918 " does not resolve its type");
3920 if (p == null || q == null)
3921 throw new InternalErrorException ("BetterConversion Got a null conversion");
3926 if (argument_expr is NullLiteral) {
3928 // If the argument is null and one of the types to compare is 'object' and
3929 // the other is a reference type, we prefer the other.
3931 // This follows from the usual rules:
3932 // * There is an implicit conversion from 'null' to type 'object'
3933 // * There is an implicit conversion from 'null' to any reference type
3934 // * There is an implicit conversion from any reference type to type 'object'
3935 // * There is no implicit conversion from type 'object' to other reference types
3936 // => Conversion of 'null' to a reference type is better than conversion to 'object'
3938 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
3939 // null type. I think it used to be 'object' and thus needed a special
3940 // case to avoid the immediately following two checks.
3942 if (!p.IsValueType && q == TypeManager.object_type)
3944 if (!q.IsValueType && p == TypeManager.object_type)
3948 if (argument_type == p)
3951 if (argument_type == q)
3954 Expression p_tmp = new EmptyExpression (p);
3955 Expression q_tmp = new EmptyExpression (q);
3957 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
3958 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
3960 if (p_to_q && !q_to_p)
3963 if (q_to_p && !p_to_q)
3966 if (p == TypeManager.sbyte_type)
3967 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
3968 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
3970 if (q == TypeManager.sbyte_type)
3971 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
3972 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
3975 if (p == TypeManager.short_type)
3976 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
3977 q == TypeManager.uint64_type)
3979 if (q == TypeManager.short_type)
3980 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
3981 p == TypeManager.uint64_type)
3984 if (p == TypeManager.int32_type)
3985 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
3987 if (q == TypeManager.int32_type)
3988 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
3991 if (p == TypeManager.int64_type)
3992 if (q == TypeManager.uint64_type)
3994 if (q == TypeManager.int64_type)
3995 if (p == TypeManager.uint64_type)
4002 /// Determines "Better function" between candidate
4003 /// and the current best match
4006 /// Returns a boolean indicating :
4007 /// false if candidate ain't better
4008 /// true if candidate is better than the current best match
4010 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4011 MethodBase candidate, bool candidate_params,
4012 MethodBase best, bool best_params)
4014 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4015 ParameterData best_pd = TypeManager.GetParameterData (best);
4017 bool better_at_least_one = false;
4019 for (int j = 0; j < argument_count; ++j) {
4020 Argument a = (Argument) args [j];
4022 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
4023 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
4025 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4026 if (candidate_params)
4027 ct = TypeManager.GetElementType (ct);
4029 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4031 bt = TypeManager.GetElementType (bt);
4037 Type better = BetterConversion (ec, a, ct, bt);
4039 // for each argument, the conversion to 'ct' should be no worse than
4040 // the conversion to 'bt'.
4044 // for at least one argument, the conversion to 'ct' should be better than
4045 // the conversion to 'bt'.
4047 better_at_least_one = true;
4050 if (better_at_least_one)
4054 // This handles the case
4056 // Add (float f1, float f2, float f3);
4057 // Add (params decimal [] foo);
4059 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4060 // first candidate would've chosen as better.
4066 // This handles the following cases:
4068 // Trim () is better than Trim (params char[] chars)
4069 // Concat (string s1, string s2, string s3) is better than
4070 // Concat (string s1, params string [] srest)
4072 return !candidate_params && best_params;
4075 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4077 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4080 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4081 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4083 if (cand_pd.Count != base_pd.Count)
4086 for (int j = 0; j < cand_pd.Count; ++j) {
4087 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4088 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4089 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4090 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4092 if (cm != bm || ct != bt)
4099 public static string FullMethodDesc (MethodBase mb)
4105 if (mb is MethodInfo) {
4106 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4110 sb = new StringBuilder ();
4112 sb.Append (TypeManager.CSharpSignature (mb));
4113 return sb.ToString ();
4116 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4118 MemberInfo [] miset;
4119 MethodGroupExpr union;
4124 return (MethodGroupExpr) mg2;
4127 return (MethodGroupExpr) mg1;
4130 MethodGroupExpr left_set = null, right_set = null;
4131 int length1 = 0, length2 = 0;
4133 left_set = (MethodGroupExpr) mg1;
4134 length1 = left_set.Methods.Length;
4136 right_set = (MethodGroupExpr) mg2;
4137 length2 = right_set.Methods.Length;
4139 ArrayList common = new ArrayList ();
4141 foreach (MethodBase r in right_set.Methods){
4142 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4146 miset = new MemberInfo [length1 + length2 - common.Count];
4147 left_set.Methods.CopyTo (miset, 0);
4151 foreach (MethodBase r in right_set.Methods) {
4152 if (!common.Contains (r))
4156 union = new MethodGroupExpr (miset, loc);
4161 public static bool IsParamsMethodApplicable (EmitContext ec,
4162 ArrayList arguments, int arg_count,
4163 MethodBase candidate)
4165 return IsParamsMethodApplicable (
4166 ec, arguments, arg_count, candidate, false) ||
4167 IsParamsMethodApplicable (
4168 ec, arguments, arg_count, candidate, true);
4174 /// Determines if the candidate method, if a params method, is applicable
4175 /// in its expanded form to the given set of arguments
4177 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4178 int arg_count, MethodBase candidate,
4181 ParameterData pd = TypeManager.GetParameterData (candidate);
4183 int pd_count = pd.Count;
4187 int count = pd_count - 1;
4189 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4191 if (pd_count != arg_count)
4198 if (count > arg_count)
4201 if (pd_count == 1 && arg_count == 0)
4205 // If we have come this far, the case which
4206 // remains is when the number of parameters is
4207 // less than or equal to the argument count.
4209 for (int i = 0; i < count; ++i) {
4211 Argument a = (Argument) arguments [i];
4213 Parameter.Modifier a_mod = a.Modifier &
4214 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4215 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4216 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4218 if (a_mod == p_mod) {
4220 if (a_mod == Parameter.Modifier.NONE)
4221 if (!Convert.ImplicitConversionExists (ec,
4223 pd.ParameterType (i)))
4226 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4227 Type pt = pd.ParameterType (i);
4230 pt = TypeManager.GetReferenceType (pt);
4241 Argument a = (Argument) arguments [count];
4242 if (!(a.Expr is Arglist))
4248 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
4250 for (int i = pd_count - 1; i < arg_count; i++) {
4251 Argument a = (Argument) arguments [i];
4253 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4261 /// Determines if the candidate method is applicable (section 14.4.2.1)
4262 /// to the given set of arguments
4264 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4265 MethodBase candidate)
4267 ParameterData pd = TypeManager.GetParameterData (candidate);
4269 if (arg_count != pd.Count)
4272 for (int i = arg_count; i > 0; ) {
4275 Argument a = (Argument) arguments [i];
4277 Parameter.Modifier a_mod = a.Modifier &
4278 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4280 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4281 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4283 if (a_mod == p_mod) {
4284 Type pt = pd.ParameterType (i);
4286 if (a_mod == Parameter.Modifier.NONE) {
4287 if (!Convert.ImplicitConversionExists (ec, a.Expr, pt))
4301 static internal bool IsAncestralType (Type first_type, Type second_type)
4303 return first_type != second_type &&
4304 (TypeManager.IsSubclassOf (second_type, first_type) ||
4305 TypeManager.ImplementsInterface (second_type, first_type));
4309 /// Find the Applicable Function Members (7.4.2.1)
4311 /// me: Method Group expression with the members to select.
4312 /// it might contain constructors or methods (or anything
4313 /// that maps to a method).
4315 /// Arguments: ArrayList containing resolved Argument objects.
4317 /// loc: The location if we want an error to be reported, or a Null
4318 /// location for "probing" purposes.
4320 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4321 /// that is the best match of me on Arguments.
4324 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4325 ArrayList Arguments, bool may_fail,
4328 MethodBase method = null;
4329 bool method_params = false;
4330 Type applicable_type = null;
4332 ArrayList candidates = new ArrayList (2);
4333 ArrayList candidate_overrides = null;
4336 // Used to keep a map between the candidate
4337 // and whether it is being considered in its
4338 // normal or expanded form
4340 // false is normal form, true is expanded form
4342 Hashtable candidate_to_form = null;
4344 if (Arguments != null)
4345 arg_count = Arguments.Count;
4347 if ((me.Name == "Invoke") &&
4348 TypeManager.IsDelegateType (me.DeclaringType)) {
4349 Error_InvokeOnDelegate (loc);
4353 MethodBase[] methods = me.Methods;
4355 int nmethods = methods.Length;
4359 // Methods marked 'override' don't take part in 'applicable_type'
4360 // computation, nor in the actual overload resolution.
4361 // However, they still need to be emitted instead of a base virtual method.
4362 // So, we salt them away into the 'candidate_overrides' array.
4364 // In case of reflected methods, we replace each overriding method with
4365 // its corresponding base virtual method. This is to improve compatibility
4366 // with non-C# libraries which change the visibility of overrides (#75636)
4369 for (int i = 0; i < methods.Length; ++i) {
4370 MethodBase m = methods [i];
4371 if (TypeManager.IsOverride (m)) {
4372 if (candidate_overrides == null)
4373 candidate_overrides = new ArrayList ();
4374 candidate_overrides.Add (m);
4375 m = TypeManager.TryGetBaseDefinition (m);
4383 int applicable_errors = Report.Errors;
4386 // First we construct the set of applicable methods
4388 bool is_sorted = true;
4389 for (int i = 0; i < nmethods; i++){
4390 Type decl_type = methods [i].DeclaringType;
4393 // If we have already found an applicable method
4394 // we eliminate all base types (Section 14.5.5.1)
4396 if (applicable_type != null && IsAncestralType (decl_type, applicable_type))
4400 // Check if candidate is applicable (section 14.4.2.1)
4401 // Is candidate applicable in normal form?
4403 bool is_applicable = IsApplicable (ec, Arguments, arg_count, methods [i]);
4405 if (!is_applicable && IsParamsMethodApplicable (ec, Arguments, arg_count, methods [i])) {
4406 MethodBase candidate = methods [i];
4407 if (candidate_to_form == null)
4408 candidate_to_form = new PtrHashtable ();
4409 candidate_to_form [candidate] = candidate;
4410 // Candidate is applicable in expanded form
4411 is_applicable = true;
4417 candidates.Add (methods [i]);
4419 if (applicable_type == null)
4420 applicable_type = decl_type;
4421 else if (applicable_type != decl_type) {
4423 if (IsAncestralType (applicable_type, decl_type))
4424 applicable_type = decl_type;
4428 if (applicable_errors != Report.Errors)
4431 int candidate_top = candidates.Count;
4433 if (applicable_type == null) {
4435 // Okay so we have failed to find anything so we
4436 // return by providing info about the closest match
4438 int errors = Report.Errors;
4439 for (int i = 0; i < nmethods; ++i) {
4440 MethodBase c = (MethodBase) methods [i];
4441 ParameterData pd = TypeManager.GetParameterData (c);
4443 if (pd.Count != arg_count)
4446 VerifyArgumentsCompat (ec, Arguments, arg_count,
4447 c, false, null, may_fail, loc);
4449 if (!may_fail && errors == Report.Errors)
4450 throw new InternalErrorException (
4451 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4452 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4457 if (!may_fail && errors == Report.Errors) {
4458 string report_name = me.Name;
4459 if (report_name == ".ctor")
4460 report_name = me.DeclaringType.ToString ();
4461 Error_WrongNumArguments (loc, report_name, arg_count);
4469 // At this point, applicable_type is _one_ of the most derived types
4470 // in the set of types containing the methods in this MethodGroup.
4471 // Filter the candidates so that they only contain methods from the
4472 // most derived types.
4475 int finalized = 0; // Number of finalized candidates
4478 // Invariant: applicable_type is a most derived type
4480 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4481 // eliminating all it's base types. At the same time, we'll also move
4482 // every unrelated type to the end of the array, and pick the next
4483 // 'applicable_type'.
4485 Type next_applicable_type = null;
4486 int j = finalized; // where to put the next finalized candidate
4487 int k = finalized; // where to put the next undiscarded candidate
4488 for (int i = finalized; i < candidate_top; ++i) {
4489 MethodBase candidate = (MethodBase) candidates [i];
4490 Type decl_type = candidate.DeclaringType;
4492 if (decl_type == applicable_type) {
4493 candidates [k++] = candidates [j];
4494 candidates [j++] = candidates [i];
4498 if (IsAncestralType (decl_type, applicable_type))
4501 if (next_applicable_type != null &&
4502 IsAncestralType (decl_type, next_applicable_type))
4505 candidates [k++] = candidates [i];
4507 if (next_applicable_type == null ||
4508 IsAncestralType (next_applicable_type, decl_type))
4509 next_applicable_type = decl_type;
4512 applicable_type = next_applicable_type;
4515 } while (applicable_type != null);
4519 // Now we actually find the best method
4522 method = (MethodBase) candidates [0];
4523 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4524 for (int ix = 1; ix < candidate_top; ix++){
4525 MethodBase candidate = (MethodBase) candidates [ix];
4527 if (candidate == method)
4530 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4532 if (BetterFunction (ec, Arguments, arg_count,
4533 candidate, cand_params,
4534 method, method_params)) {
4536 method_params = cand_params;
4540 // Now check that there are no ambiguities i.e the selected method
4541 // should be better than all the others
4543 MethodBase ambiguous = null;
4544 for (int ix = 0; ix < candidate_top; ix++){
4545 MethodBase candidate = (MethodBase) candidates [ix];
4547 if (candidate == method)
4550 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4551 if (!BetterFunction (ec, Arguments, arg_count,
4552 method, method_params,
4553 candidate, cand_params)) {
4554 Report.SymbolRelatedToPreviousError (candidate);
4555 ambiguous = candidate;
4559 if (ambiguous != null) {
4560 Report.SymbolRelatedToPreviousError (method);
4561 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4562 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4567 // If the method is a virtual function, pick an override closer to the LHS type.
4569 if (!me.IsBase && method.IsVirtual) {
4570 if (TypeManager.IsOverride (method))
4571 throw new InternalErrorException (
4572 "Should not happen. An 'override' method took part in overload resolution: " + method);
4574 if (candidate_overrides != null)
4575 foreach (MethodBase candidate in candidate_overrides) {
4576 if (IsOverride (candidate, method))
4582 // And now check if the arguments are all
4583 // compatible, perform conversions if
4584 // necessary etc. and return if everything is
4587 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4588 method_params, null, may_fail, loc))
4594 IMethodData data = TypeManager.GetMethod (method);
4596 data.SetMemberIsUsed ();
4601 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4603 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4604 name, arg_count.ToString ());
4607 static void Error_InvokeOnDelegate (Location loc)
4609 Report.Error (1533, loc,
4610 "Invoke cannot be called directly on a delegate");
4613 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4614 Type delegate_type, Argument a, ParameterData expected_par)
4616 if (delegate_type == null)
4617 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4618 TypeManager.CSharpSignature (method));
4620 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4621 TypeManager.CSharpName (delegate_type));
4623 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4625 string index = (idx + 1).ToString ();
4626 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4627 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4628 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4629 index, Parameter.GetModifierSignature (a.Modifier));
4631 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4632 index, Parameter.GetModifierSignature (mod));
4634 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4635 index, Argument.FullDesc (a), expected_par.ParameterDesc (idx));
4639 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4640 int arg_count, MethodBase method,
4641 bool chose_params_expanded,
4642 Type delegate_type, bool may_fail,
4645 ParameterData pd = TypeManager.GetParameterData (method);
4647 for (j = 0; j < arg_count; j++) {
4648 Argument a = (Argument) Arguments [j];
4649 Expression a_expr = a.Expr;
4650 Type parameter_type = pd.ParameterType (j);
4651 Parameter.Modifier pm = pd.ParameterModifier (j);
4652 Parameter.Modifier am = a.Modifier;
4654 if (pm == Parameter.Modifier.ARGLIST) {
4655 if (!(a.Expr is Arglist))
4660 if (pm == Parameter.Modifier.PARAMS) {
4661 pm = Parameter.Modifier.NONE;
4662 if (chose_params_expanded)
4663 parameter_type = TypeManager.GetElementType (parameter_type);
4669 if (!a.Type.Equals (parameter_type)) {
4670 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4673 Expression conv = Convert.ImplicitConversion (ec, a_expr, parameter_type, loc);
4677 // Update the argument with the implicit conversion
4682 if (parameter_type.IsPointer && !ec.InUnsafe) {
4692 Error_InvalidArguments (loc, j, method, delegate_type, (Argument) Arguments [j], pd);
4696 private bool resolved = false;
4697 public override Expression DoResolve (EmitContext ec)
4700 return this.method == null ? null : this;
4704 // First, resolve the expression that is used to
4705 // trigger the invocation
4707 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4711 if (!(expr is MethodGroupExpr)) {
4712 Type expr_type = expr.Type;
4714 if (expr_type != null){
4715 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4717 return (new DelegateInvocation (
4718 this.expr, Arguments, loc)).Resolve (ec);
4722 if (!(expr is MethodGroupExpr)){
4723 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4728 // Next, evaluate all the expressions in the argument list
4730 if (Arguments != null){
4731 foreach (Argument a in Arguments){
4732 if (!a.Resolve (ec, loc))
4737 MethodGroupExpr mg = (MethodGroupExpr) expr;
4738 MethodBase method = OverloadResolve (ec, mg, Arguments, false, loc);
4743 MethodInfo mi = method as MethodInfo;
4745 type = TypeManager.TypeToCoreType (mi.ReturnType);
4746 Expression iexpr = mg.InstanceExpression;
4748 if (iexpr == null ||
4749 iexpr is This || iexpr is EmptyExpression ||
4750 mg.IdenticalTypeName) {
4751 mg.InstanceExpression = null;
4753 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
4757 if (iexpr == null || iexpr is EmptyExpression) {
4758 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
4764 if (type.IsPointer){
4772 // Only base will allow this invocation to happen.
4774 if (mg.IsBase && method.IsAbstract){
4775 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
4779 if (Arguments == null && method.Name == "Finalize") {
4781 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
4783 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
4787 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
4791 if (mg.InstanceExpression != null)
4792 mg.InstanceExpression.CheckMarshalByRefAccess ();
4794 eclass = ExprClass.Value;
4795 this.method = method;
4799 bool IsSpecialMethodInvocation (MethodBase method)
4801 IMethodData md = TypeManager.GetMethod (method);
4803 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
4806 if (!TypeManager.IsSpecialMethod (method))
4809 int args = TypeManager.GetParameterData (method).Count;
4810 if (method.Name.StartsWith ("get_") && args > 0)
4812 else if (method.Name.StartsWith ("set_") && args > 2)
4815 // TODO: check operators and events as well ?
4818 Report.SymbolRelatedToPreviousError (method);
4819 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
4820 TypeManager.CSharpSignature (method, true));
4826 // Emits the list of arguments as an array
4828 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
4830 ILGenerator ig = ec.ig;
4831 int count = arguments.Count - idx;
4832 Argument a = (Argument) arguments [idx];
4833 Type t = a.Expr.Type;
4835 IntConstant.EmitInt (ig, count);
4836 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
4838 int top = arguments.Count;
4839 for (int j = idx; j < top; j++){
4840 a = (Argument) arguments [j];
4842 ig.Emit (OpCodes.Dup);
4843 IntConstant.EmitInt (ig, j - idx);
4846 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj);
4848 ig.Emit (OpCodes.Ldelema, t);
4853 ig.Emit (OpCodes.Stobj, t);
4860 /// Emits a list of resolved Arguments that are in the arguments
4863 /// The MethodBase argument might be null if the
4864 /// emission of the arguments is known not to contain
4865 /// a `params' field (for example in constructors or other routines
4866 /// that keep their arguments in this structure)
4868 /// if `dup_args' is true, a copy of the arguments will be left
4869 /// on the stack. If `dup_args' is true, you can specify `this_arg'
4870 /// which will be duplicated before any other args. Only EmitCall
4871 /// should be using this interface.
4873 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
4875 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
4876 int top = arguments == null ? 0 : arguments.Count;
4877 LocalTemporary [] temps = null;
4879 if (dup_args && top != 0)
4880 temps = new LocalTemporary [top];
4882 for (int i = 0; i < top; i++){
4883 Argument a = (Argument) arguments [i];
4886 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
4888 // Special case if we are passing the same data as the
4889 // params argument, do not put it in an array.
4891 if (pd.ParameterType (i) == a.Type)
4894 EmitParams (ec, i, arguments);
4901 ec.ig.Emit (OpCodes.Dup);
4902 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
4907 if (this_arg != null)
4910 for (int i = 0; i < top; i ++) {
4911 temps [i].Emit (ec);
4912 temps [i].Release (ec);
4916 if (pd != null && pd.Count > top &&
4917 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
4918 ILGenerator ig = ec.ig;
4920 IntConstant.EmitInt (ig, 0);
4921 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
4925 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
4927 ParameterData pd = TypeManager.GetParameterData (mb);
4929 if (arguments == null)
4930 return new Type [0];
4932 Argument a = (Argument) arguments [pd.Count - 1];
4933 Arglist list = (Arglist) a.Expr;
4935 return list.ArgumentTypes;
4939 /// This checks the ConditionalAttribute on the method
4941 static bool IsMethodExcluded (MethodBase method)
4943 if (method.IsConstructor)
4946 IMethodData md = TypeManager.GetMethod (method);
4948 return md.IsExcluded ();
4950 // For some methods (generated by delegate class) GetMethod returns null
4951 // because they are not included in builder_to_method table
4952 if (method.DeclaringType is TypeBuilder)
4955 return AttributeTester.IsConditionalMethodExcluded (method);
4959 /// is_base tells whether we want to force the use of the `call'
4960 /// opcode instead of using callvirt. Call is required to call
4961 /// a specific method, while callvirt will always use the most
4962 /// recent method in the vtable.
4964 /// is_static tells whether this is an invocation on a static method
4966 /// instance_expr is an expression that represents the instance
4967 /// it must be non-null if is_static is false.
4969 /// method is the method to invoke.
4971 /// Arguments is the list of arguments to pass to the method or constructor.
4973 public static void EmitCall (EmitContext ec, bool is_base,
4974 bool is_static, Expression instance_expr,
4975 MethodBase method, ArrayList Arguments, Location loc)
4977 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
4980 // `dup_args' leaves an extra copy of the arguments on the stack
4981 // `omit_args' does not leave any arguments at all.
4982 // So, basically, you could make one call with `dup_args' set to true,
4983 // and then another with `omit_args' set to true, and the two calls
4984 // would have the same set of arguments. However, each argument would
4985 // only have been evaluated once.
4986 public static void EmitCall (EmitContext ec, bool is_base,
4987 bool is_static, Expression instance_expr,
4988 MethodBase method, ArrayList Arguments, Location loc,
4989 bool dup_args, bool omit_args)
4991 ILGenerator ig = ec.ig;
4992 bool struct_call = false;
4993 bool this_call = false;
4994 LocalTemporary this_arg = null;
4996 Type decl_type = method.DeclaringType;
4998 if (!RootContext.StdLib) {
4999 // Replace any calls to the system's System.Array type with calls to
5000 // the newly created one.
5001 if (method == TypeManager.system_int_array_get_length)
5002 method = TypeManager.int_array_get_length;
5003 else if (method == TypeManager.system_int_array_get_rank)
5004 method = TypeManager.int_array_get_rank;
5005 else if (method == TypeManager.system_object_array_clone)
5006 method = TypeManager.object_array_clone;
5007 else if (method == TypeManager.system_int_array_get_length_int)
5008 method = TypeManager.int_array_get_length_int;
5009 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5010 method = TypeManager.int_array_get_lower_bound_int;
5011 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5012 method = TypeManager.int_array_get_upper_bound_int;
5013 else if (method == TypeManager.system_void_array_copyto_array_int)
5014 method = TypeManager.void_array_copyto_array_int;
5017 if (!ec.IsInObsoleteScope) {
5019 // This checks ObsoleteAttribute on the method and on the declaring type
5021 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5023 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5025 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5027 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5031 if (IsMethodExcluded (method))
5035 if (instance_expr == EmptyExpression.Null) {
5036 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5040 this_call = instance_expr is This;
5041 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5045 // If this is ourselves, push "this"
5050 // Push the instance expression
5052 if (TypeManager.IsValueType (instance_expr.Type)) {
5054 // Special case: calls to a function declared in a
5055 // reference-type with a value-type argument need
5056 // to have their value boxed.
5057 if (decl_type.IsValueType) {
5059 // If the expression implements IMemoryLocation, then
5060 // we can optimize and use AddressOf on the
5063 // If not we have to use some temporary storage for
5065 if (instance_expr is IMemoryLocation) {
5066 ((IMemoryLocation)instance_expr).
5067 AddressOf (ec, AddressOp.LoadStore);
5069 LocalTemporary temp = new LocalTemporary (instance_expr.Type);
5070 instance_expr.Emit (ec);
5072 temp.AddressOf (ec, AddressOp.Load);
5075 // avoid the overhead of doing this all the time.
5077 t = TypeManager.GetReferenceType (instance_expr.Type);
5079 instance_expr.Emit (ec);
5080 ig.Emit (OpCodes.Box, instance_expr.Type);
5081 t = TypeManager.object_type;
5084 instance_expr.Emit (ec);
5085 t = instance_expr.Type;
5089 ig.Emit (OpCodes.Dup);
5090 if (Arguments != null && Arguments.Count != 0) {
5091 this_arg = new LocalTemporary (t);
5092 this_arg.Store (ec);
5099 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5102 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5103 call_op = OpCodes.Call;
5105 call_op = OpCodes.Callvirt;
5107 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5108 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5109 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5116 // and DoFoo is not virtual, you can omit the callvirt,
5117 // because you don't need the null checking behavior.
5119 if (method is MethodInfo)
5120 ig.Emit (call_op, (MethodInfo) method);
5122 ig.Emit (call_op, (ConstructorInfo) method);
5125 public override void Emit (EmitContext ec)
5127 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5129 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5132 public override void EmitStatement (EmitContext ec)
5137 // Pop the return value if there is one
5139 if (method is MethodInfo){
5140 Type ret = ((MethodInfo)method).ReturnType;
5141 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5142 ec.ig.Emit (OpCodes.Pop);
5147 public class InvocationOrCast : ExpressionStatement
5150 Expression argument;
5152 public InvocationOrCast (Expression expr, Expression argument)
5155 this.argument = argument;
5156 this.loc = expr.Location;
5159 public override Expression DoResolve (EmitContext ec)
5162 // First try to resolve it as a cast.
5164 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5166 Cast cast = new Cast (te, argument, loc);
5167 return cast.Resolve (ec);
5171 // This can either be a type or a delegate invocation.
5172 // Let's just resolve it and see what we'll get.
5174 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5179 // Ok, so it's a Cast.
5181 if (expr.eclass == ExprClass.Type) {
5182 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5183 return cast.Resolve (ec);
5187 // It's a delegate invocation.
5189 if (!TypeManager.IsDelegateType (expr.Type)) {
5190 Error (149, "Method name expected");
5194 ArrayList args = new ArrayList ();
5195 args.Add (new Argument (argument, Argument.AType.Expression));
5196 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5197 return invocation.Resolve (ec);
5202 Error (201, "Only assignment, call, increment, decrement and new object " +
5203 "expressions can be used as a statement");
5206 public override ExpressionStatement ResolveStatement (EmitContext ec)
5209 // First try to resolve it as a cast.
5211 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5218 // This can either be a type or a delegate invocation.
5219 // Let's just resolve it and see what we'll get.
5221 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5222 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5228 // It's a delegate invocation.
5230 if (!TypeManager.IsDelegateType (expr.Type)) {
5231 Error (149, "Method name expected");
5235 ArrayList args = new ArrayList ();
5236 args.Add (new Argument (argument, Argument.AType.Expression));
5237 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5238 return invocation.ResolveStatement (ec);
5241 public override void Emit (EmitContext ec)
5243 throw new Exception ("Cannot happen");
5246 public override void EmitStatement (EmitContext ec)
5248 throw new Exception ("Cannot happen");
5253 // This class is used to "disable" the code generation for the
5254 // temporary variable when initializing value types.
5256 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5257 public void AddressOf (EmitContext ec, AddressOp Mode)
5264 /// Implements the new expression
5266 public class New : ExpressionStatement, IMemoryLocation {
5267 public readonly ArrayList Arguments;
5270 // During bootstrap, it contains the RequestedType,
5271 // but if `type' is not null, it *might* contain a NewDelegate
5272 // (because of field multi-initialization)
5274 public Expression RequestedType;
5276 MethodBase method = null;
5279 // If set, the new expression is for a value_target, and
5280 // we will not leave anything on the stack.
5282 Expression value_target;
5283 bool value_target_set = false;
5285 public New (Expression requested_type, ArrayList arguments, Location l)
5287 RequestedType = requested_type;
5288 Arguments = arguments;
5292 public bool SetValueTypeVariable (Expression value)
5294 value_target = value;
5295 value_target_set = true;
5296 if (!(value_target is IMemoryLocation)){
5297 Error_UnexpectedKind (null, "variable", loc);
5304 // This function is used to disable the following code sequence for
5305 // value type initialization:
5307 // AddressOf (temporary)
5311 // Instead the provide will have provided us with the address on the
5312 // stack to store the results.
5314 static Expression MyEmptyExpression;
5316 public void DisableTemporaryValueType ()
5318 if (MyEmptyExpression == null)
5319 MyEmptyExpression = new EmptyAddressOf ();
5322 // To enable this, look into:
5323 // test-34 and test-89 and self bootstrapping.
5325 // For instance, we can avoid a copy by using `newobj'
5326 // instead of Call + Push-temp on value types.
5327 // value_target = MyEmptyExpression;
5332 /// Converts complex core type syntax like 'new int ()' to simple constant
5334 public static Constant Constantify (Type t)
5336 if (t == TypeManager.int32_type)
5337 return new IntConstant (0, Location.Null);
5338 if (t == TypeManager.uint32_type)
5339 return new UIntConstant (0, Location.Null);
5340 if (t == TypeManager.int64_type)
5341 return new LongConstant (0, Location.Null);
5342 if (t == TypeManager.uint64_type)
5343 return new ULongConstant (0, Location.Null);
5344 if (t == TypeManager.float_type)
5345 return new FloatConstant (0, Location.Null);
5346 if (t == TypeManager.double_type)
5347 return new DoubleConstant (0, Location.Null);
5348 if (t == TypeManager.short_type)
5349 return new ShortConstant (0, Location.Null);
5350 if (t == TypeManager.ushort_type)
5351 return new UShortConstant (0, Location.Null);
5352 if (t == TypeManager.sbyte_type)
5353 return new SByteConstant (0, Location.Null);
5354 if (t == TypeManager.byte_type)
5355 return new ByteConstant (0, Location.Null);
5356 if (t == TypeManager.char_type)
5357 return new CharConstant ('\0', Location.Null);
5358 if (t == TypeManager.bool_type)
5359 return new BoolConstant (false, Location.Null);
5360 if (t == TypeManager.decimal_type)
5361 return new DecimalConstant (0, Location.Null);
5367 // Checks whether the type is an interface that has the
5368 // [ComImport, CoClass] attributes and must be treated
5371 public Expression CheckComImport (EmitContext ec)
5373 if (!type.IsInterface)
5377 // Turn the call into:
5378 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5380 Type real_class = AttributeTester.GetCoClassAttribute (type);
5381 if (real_class == null)
5384 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5385 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5386 return cast.Resolve (ec);
5389 public override Expression DoResolve (EmitContext ec)
5392 // The New DoResolve might be called twice when initializing field
5393 // expressions (see EmitFieldInitializers, the call to
5394 // GetInitializerExpression will perform a resolve on the expression,
5395 // and later the assign will trigger another resolution
5397 // This leads to bugs (#37014)
5400 if (RequestedType is NewDelegate)
5401 return RequestedType;
5405 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5411 if (Arguments == null) {
5412 Expression c = Constantify (type);
5417 if (TypeManager.IsDelegateType (type)) {
5418 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5419 if (RequestedType != null)
5420 if (!(RequestedType is DelegateCreation))
5421 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5422 return RequestedType;
5425 if (type.IsAbstract && type.IsSealed) {
5426 Report.SymbolRelatedToPreviousError (type);
5427 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5431 if (type.IsInterface || type.IsAbstract){
5432 RequestedType = CheckComImport (ec);
5433 if (RequestedType != null)
5434 return RequestedType;
5436 Report.SymbolRelatedToPreviousError (type);
5437 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5441 bool is_struct = type.IsValueType;
5442 eclass = ExprClass.Value;
5445 // SRE returns a match for .ctor () on structs (the object constructor),
5446 // so we have to manually ignore it.
5448 if (is_struct && Arguments == null)
5451 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5452 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5453 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5458 MethodGroupExpr mg = ml as MethodGroupExpr;
5461 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5465 if (Arguments != null){
5466 foreach (Argument a in Arguments){
5467 if (!a.Resolve (ec, loc))
5472 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5473 if (method == null) {
5474 if (almostMatchedMembers.Count != 0)
5475 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5483 // This DoEmit can be invoked in two contexts:
5484 // * As a mechanism that will leave a value on the stack (new object)
5485 // * As one that wont (init struct)
5487 // You can control whether a value is required on the stack by passing
5488 // need_value_on_stack. The code *might* leave a value on the stack
5489 // so it must be popped manually
5491 // If we are dealing with a ValueType, we have a few
5492 // situations to deal with:
5494 // * The target is a ValueType, and we have been provided
5495 // the instance (this is easy, we are being assigned).
5497 // * The target of New is being passed as an argument,
5498 // to a boxing operation or a function that takes a
5501 // In this case, we need to create a temporary variable
5502 // that is the argument of New.
5504 // Returns whether a value is left on the stack
5506 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5508 bool is_value_type = TypeManager.IsValueType (type);
5509 ILGenerator ig = ec.ig;
5514 // Allow DoEmit() to be called multiple times.
5515 // We need to create a new LocalTemporary each time since
5516 // you can't share LocalBuilders among ILGeneators.
5517 if (!value_target_set)
5518 value_target = new LocalTemporary (type);
5520 ml = (IMemoryLocation) value_target;
5521 ml.AddressOf (ec, AddressOp.Store);
5525 Invocation.EmitArguments (ec, method, Arguments, false, null);
5529 ig.Emit (OpCodes.Initobj, type);
5531 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5532 if (need_value_on_stack){
5533 value_target.Emit (ec);
5538 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5543 public override void Emit (EmitContext ec)
5548 public override void EmitStatement (EmitContext ec)
5550 if (DoEmit (ec, false))
5551 ec.ig.Emit (OpCodes.Pop);
5554 public void AddressOf (EmitContext ec, AddressOp Mode)
5556 if (!type.IsValueType){
5558 // We throw an exception. So far, I believe we only need to support
5560 // foreach (int j in new StructType ())
5563 throw new Exception ("AddressOf should not be used for classes");
5566 if (!value_target_set)
5567 value_target = new LocalTemporary (type);
5569 IMemoryLocation ml = (IMemoryLocation) value_target;
5570 ml.AddressOf (ec, AddressOp.Store);
5572 Invocation.EmitArguments (ec, method, Arguments, false, null);
5575 ec.ig.Emit (OpCodes.Initobj, type);
5577 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5579 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5584 /// 14.5.10.2: Represents an array creation expression.
5588 /// There are two possible scenarios here: one is an array creation
5589 /// expression that specifies the dimensions and optionally the
5590 /// initialization data and the other which does not need dimensions
5591 /// specified but where initialization data is mandatory.
5593 public class ArrayCreation : Expression {
5594 Expression requested_base_type;
5595 ArrayList initializers;
5598 // The list of Argument types.
5599 // This is used to construct the `newarray' or constructor signature
5601 ArrayList arguments;
5604 // Method used to create the array object.
5606 MethodBase new_method = null;
5608 Type array_element_type;
5609 Type underlying_type;
5610 bool is_one_dimensional = false;
5611 bool is_builtin_type = false;
5612 bool expect_initializers = false;
5613 int num_arguments = 0;
5617 ArrayList array_data;
5621 // The number of constants in array initializers
5622 int const_initializers_count;
5624 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5626 this.requested_base_type = requested_base_type;
5627 this.initializers = initializers;
5631 arguments = new ArrayList ();
5633 foreach (Expression e in exprs) {
5634 arguments.Add (new Argument (e, Argument.AType.Expression));
5639 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5641 this.requested_base_type = requested_base_type;
5642 this.initializers = initializers;
5646 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5648 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5650 //dimensions = tmp.Length - 1;
5651 expect_initializers = true;
5654 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5656 StringBuilder sb = new StringBuilder (rank);
5659 for (int i = 1; i < idx_count; i++)
5664 return new ComposedCast (base_type, sb.ToString (), loc);
5667 void Error_IncorrectArrayInitializer ()
5669 Error (178, "Invalid rank specifier: expected `,' or `]'");
5672 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5674 if (specified_dims) {
5675 Argument a = (Argument) arguments [idx];
5677 if (!a.Resolve (ec, loc))
5680 Constant c = a.Expr as Constant;
5682 c = c.ToType (TypeManager.int32_type, a.Expr.Location);
5686 Report.Error (150, a.Expr.Location, "A constant value is expected");
5690 int value = (int) c.GetValue ();
5692 if (value != probe.Count) {
5693 Error_IncorrectArrayInitializer ();
5697 bounds [idx] = value;
5700 int child_bounds = -1;
5701 for (int i = 0; i < probe.Count; ++i) {
5702 object o = probe [i];
5703 if (o is ArrayList) {
5704 ArrayList sub_probe = o as ArrayList;
5705 int current_bounds = sub_probe.Count;
5707 if (child_bounds == -1)
5708 child_bounds = current_bounds;
5710 else if (child_bounds != current_bounds){
5711 Error_IncorrectArrayInitializer ();
5714 if (idx + 1 >= dimensions){
5715 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
5719 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
5723 if (child_bounds != -1){
5724 Error_IncorrectArrayInitializer ();
5728 Expression tmp = (Expression) o;
5729 tmp = tmp.Resolve (ec);
5733 Expression conv = Convert.ImplicitConversionRequired (
5734 ec, tmp, underlying_type, loc);
5739 // Initializers with the default values can be ignored
5740 Constant c = tmp as Constant;
5742 if (c.IsDefaultInitializer (array_element_type)) {
5746 ++const_initializers_count;
5749 // Used to invalidate static initializer
5750 const_initializers_count = int.MinValue;
5753 array_data.Add (conv);
5760 public void UpdateIndices ()
5763 for (ArrayList probe = initializers; probe != null;) {
5764 if (probe.Count > 0 && probe [0] is ArrayList) {
5765 Expression e = new IntConstant (probe.Count, Location.Null);
5766 arguments.Add (new Argument (e, Argument.AType.Expression));
5768 bounds [i++] = probe.Count;
5770 probe = (ArrayList) probe [0];
5773 Expression e = new IntConstant (probe.Count, Location.Null);
5774 arguments.Add (new Argument (e, Argument.AType.Expression));
5776 bounds [i++] = probe.Count;
5783 bool ResolveInitializers (EmitContext ec)
5785 if (initializers == null) {
5786 return !expect_initializers;
5789 if (underlying_type == null)
5793 // We use this to store all the date values in the order in which we
5794 // will need to store them in the byte blob later
5796 array_data = new ArrayList ();
5797 bounds = new System.Collections.Specialized.HybridDictionary ();
5799 if (arguments != null)
5800 return CheckIndices (ec, initializers, 0, true);
5802 arguments = new ArrayList ();
5804 if (!CheckIndices (ec, initializers, 0, false))
5809 if (arguments.Count != dimensions) {
5810 Error_IncorrectArrayInitializer ();
5818 // Creates the type of the array
5820 bool LookupType (EmitContext ec)
5822 StringBuilder array_qualifier = new StringBuilder (rank);
5825 // `In the first form allocates an array instace of the type that results
5826 // from deleting each of the individual expression from the expression list'
5828 if (num_arguments > 0) {
5829 array_qualifier.Append ("[");
5830 for (int i = num_arguments-1; i > 0; i--)
5831 array_qualifier.Append (",");
5832 array_qualifier.Append ("]");
5838 TypeExpr array_type_expr;
5839 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
5840 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
5841 if (array_type_expr == null)
5844 type = array_type_expr.Type;
5845 underlying_type = TypeManager.GetElementType (type);
5846 dimensions = type.GetArrayRank ();
5851 public override Expression DoResolve (EmitContext ec)
5856 if (!LookupType (ec))
5859 array_element_type = TypeManager.GetElementType (type);
5860 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
5861 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
5866 // First step is to validate the initializers and fill
5867 // in any missing bits
5869 if (!ResolveInitializers (ec))
5873 if (arguments == null)
5876 arg_count = arguments.Count;
5877 foreach (Argument a in arguments){
5878 if (!a.Resolve (ec, loc))
5881 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
5882 if (real_arg == null)
5889 if (arg_count == 1) {
5890 is_one_dimensional = true;
5891 eclass = ExprClass.Value;
5895 is_builtin_type = TypeManager.IsBuiltinType (type);
5897 if (is_builtin_type) {
5900 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
5901 AllBindingFlags, loc);
5903 if (!(ml is MethodGroupExpr)) {
5904 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5909 Error (-6, "New invocation: Can not find a constructor for " +
5910 "this argument list");
5914 new_method = Invocation.OverloadResolve (
5915 ec, (MethodGroupExpr) ml, arguments, false, loc);
5917 if (new_method == null) {
5918 Error (-6, "New invocation: Can not find a constructor for " +
5919 "this argument list");
5923 eclass = ExprClass.Value;
5926 ModuleBuilder mb = CodeGen.Module.Builder;
5927 ArrayList args = new ArrayList ();
5929 if (arguments != null) {
5930 for (int i = 0; i < arg_count; i++)
5931 args.Add (TypeManager.int32_type);
5934 Type [] arg_types = null;
5937 arg_types = new Type [args.Count];
5939 args.CopyTo (arg_types, 0);
5941 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
5944 if (new_method == null) {
5945 Error (-6, "New invocation: Can not find a constructor for " +
5946 "this argument list");
5950 eclass = ExprClass.Value;
5955 byte [] MakeByteBlob ()
5960 int count = array_data.Count;
5962 if (underlying_type.IsEnum)
5963 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
5965 factor = GetTypeSize (underlying_type);
5967 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
5969 data = new byte [(count * factor + 4) & ~3];
5972 for (int i = 0; i < count; ++i) {
5973 object v = array_data [i];
5975 if (v is EnumConstant)
5976 v = ((EnumConstant) v).Child;
5978 if (v is Constant && !(v is StringConstant))
5979 v = ((Constant) v).GetValue ();
5985 if (underlying_type == TypeManager.int64_type){
5986 if (!(v is Expression)){
5987 long val = (long) v;
5989 for (int j = 0; j < factor; ++j) {
5990 data [idx + j] = (byte) (val & 0xFF);
5994 } else if (underlying_type == TypeManager.uint64_type){
5995 if (!(v is Expression)){
5996 ulong val = (ulong) v;
5998 for (int j = 0; j < factor; ++j) {
5999 data [idx + j] = (byte) (val & 0xFF);
6003 } else if (underlying_type == TypeManager.float_type) {
6004 if (!(v is Expression)){
6005 element = BitConverter.GetBytes ((float) v);
6007 for (int j = 0; j < factor; ++j)
6008 data [idx + j] = element [j];
6010 } else if (underlying_type == TypeManager.double_type) {
6011 if (!(v is Expression)){
6012 element = BitConverter.GetBytes ((double) v);
6014 for (int j = 0; j < factor; ++j)
6015 data [idx + j] = element [j];
6017 } else if (underlying_type == TypeManager.char_type){
6018 if (!(v is Expression)){
6019 int val = (int) ((char) v);
6021 data [idx] = (byte) (val & 0xff);
6022 data [idx+1] = (byte) (val >> 8);
6024 } else if (underlying_type == TypeManager.short_type){
6025 if (!(v is Expression)){
6026 int val = (int) ((short) v);
6028 data [idx] = (byte) (val & 0xff);
6029 data [idx+1] = (byte) (val >> 8);
6031 } else if (underlying_type == TypeManager.ushort_type){
6032 if (!(v is Expression)){
6033 int val = (int) ((ushort) v);
6035 data [idx] = (byte) (val & 0xff);
6036 data [idx+1] = (byte) (val >> 8);
6038 } else if (underlying_type == TypeManager.int32_type) {
6039 if (!(v is Expression)){
6042 data [idx] = (byte) (val & 0xff);
6043 data [idx+1] = (byte) ((val >> 8) & 0xff);
6044 data [idx+2] = (byte) ((val >> 16) & 0xff);
6045 data [idx+3] = (byte) (val >> 24);
6047 } else if (underlying_type == TypeManager.uint32_type) {
6048 if (!(v is Expression)){
6049 uint val = (uint) v;
6051 data [idx] = (byte) (val & 0xff);
6052 data [idx+1] = (byte) ((val >> 8) & 0xff);
6053 data [idx+2] = (byte) ((val >> 16) & 0xff);
6054 data [idx+3] = (byte) (val >> 24);
6056 } else if (underlying_type == TypeManager.sbyte_type) {
6057 if (!(v is Expression)){
6058 sbyte val = (sbyte) v;
6059 data [idx] = (byte) val;
6061 } else if (underlying_type == TypeManager.byte_type) {
6062 if (!(v is Expression)){
6063 byte val = (byte) v;
6064 data [idx] = (byte) val;
6066 } else if (underlying_type == TypeManager.bool_type) {
6067 if (!(v is Expression)){
6068 bool val = (bool) v;
6069 data [idx] = (byte) (val ? 1 : 0);
6071 } else if (underlying_type == TypeManager.decimal_type){
6072 if (!(v is Expression)){
6073 int [] bits = Decimal.GetBits ((decimal) v);
6076 // FIXME: For some reason, this doesn't work on the MS runtime.
6077 int [] nbits = new int [4];
6078 nbits [0] = bits [3];
6079 nbits [1] = bits [2];
6080 nbits [2] = bits [0];
6081 nbits [3] = bits [1];
6083 for (int j = 0; j < 4; j++){
6084 data [p++] = (byte) (nbits [j] & 0xff);
6085 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6086 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6087 data [p++] = (byte) (nbits [j] >> 24);
6091 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6100 // Emits the initializers for the array
6102 void EmitStaticInitializers (EmitContext ec)
6105 // First, the static data
6108 ILGenerator ig = ec.ig;
6110 byte [] data = MakeByteBlob ();
6112 fb = RootContext.MakeStaticData (data);
6114 ig.Emit (OpCodes.Dup);
6115 ig.Emit (OpCodes.Ldtoken, fb);
6116 ig.Emit (OpCodes.Call,
6117 TypeManager.void_initializearray_array_fieldhandle);
6121 // Emits pieces of the array that can not be computed at compile
6122 // time (variables and string locations).
6124 // This always expect the top value on the stack to be the array
6126 void EmitDynamicInitializers (EmitContext ec)
6128 ILGenerator ig = ec.ig;
6129 int dims = bounds.Count;
6130 int [] current_pos = new int [dims];
6132 MethodInfo set = null;
6135 Type [] args = new Type [dims + 1];
6137 for (int j = 0; j < dims; j++)
6138 args [j] = TypeManager.int32_type;
6139 args [dims] = array_element_type;
6141 set = CodeGen.Module.Builder.GetArrayMethod (
6143 CallingConventions.HasThis | CallingConventions.Standard,
6144 TypeManager.void_type, args);
6147 for (int i = 0; i < array_data.Count; i++){
6149 Expression e = (Expression)array_data [i];
6152 Type etype = e.Type;
6154 ig.Emit (OpCodes.Dup);
6156 for (int idx = 0; idx < dims; idx++)
6157 IntConstant.EmitInt (ig, current_pos [idx]);
6160 // If we are dealing with a struct, get the
6161 // address of it, so we can store it.
6164 TypeManager.IsValueType (etype) &&
6165 (!TypeManager.IsBuiltinOrEnum (etype) ||
6166 etype == TypeManager.decimal_type)) {
6171 // Let new know that we are providing
6172 // the address where to store the results
6174 n.DisableTemporaryValueType ();
6177 ig.Emit (OpCodes.Ldelema, etype);
6184 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj);
6186 ig.Emit (OpCodes.Stobj, etype);
6190 ig.Emit (OpCodes.Call, set);
6197 for (int j = dims - 1; j >= 0; j--){
6199 if (current_pos [j] < (int) bounds [j])
6201 current_pos [j] = 0;
6206 void EmitArrayArguments (EmitContext ec)
6208 ILGenerator ig = ec.ig;
6210 foreach (Argument a in arguments) {
6211 Type atype = a.Type;
6214 if (atype == TypeManager.uint64_type)
6215 ig.Emit (OpCodes.Conv_Ovf_U4);
6216 else if (atype == TypeManager.int64_type)
6217 ig.Emit (OpCodes.Conv_Ovf_I4);
6221 public override void Emit (EmitContext ec)
6223 ILGenerator ig = ec.ig;
6225 EmitArrayArguments (ec);
6226 if (is_one_dimensional)
6227 ig.Emit (OpCodes.Newarr, array_element_type);
6229 if (is_builtin_type)
6230 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6232 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6235 if (initializers == null)
6238 // This is a treshold for static initializers
6239 // I tried to make more accurate but it seems to me that Array.Initialize is
6240 // always slower (managed -> unmanaged switch?)
6241 const int max_automatic_initializers = 200;
6243 if (const_initializers_count > max_automatic_initializers && TypeManager.IsPrimitiveType (array_element_type)) {
6244 EmitStaticInitializers (ec);
6248 EmitDynamicInitializers (ec);
6251 public override bool GetAttributableValue (Type valueType, out object value)
6253 if (!is_one_dimensional){
6254 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6255 return base.GetAttributableValue (null, out value);
6258 if (array_data == null) {
6259 Constant c = (Constant)((Argument)arguments [0]).Expr;
6260 if (c.IsDefaultValue) {
6261 value = Array.CreateInstance (array_element_type, 0);
6264 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6265 return base.GetAttributableValue (null, out value);
6268 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6269 object element_value;
6270 for (int i = 0; i < ret.Length; ++i)
6272 Expression e = (Expression)array_data [i];
6273 if (e == null) // Is null when initializer is optimized away
6274 e = (Expression)initializers [i];
6276 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6280 ret.SetValue (element_value, i);
6287 public sealed class CompilerGeneratedThis : This
6289 public static This Instance = new CompilerGeneratedThis ();
6291 private CompilerGeneratedThis ()
6292 : base (Location.Null)
6296 public override Expression DoResolve (EmitContext ec)
6298 eclass = ExprClass.Variable;
6299 type = ec.ContainerType;
6305 /// Represents the `this' construct
6308 public class This : Expression, IAssignMethod, IMemoryLocation, IVariable {
6311 VariableInfo variable_info;
6313 public This (Block block, Location loc)
6319 public This (Location loc)
6324 public VariableInfo VariableInfo {
6325 get { return variable_info; }
6328 public bool VerifyFixed ()
6330 return !TypeManager.IsValueType (Type);
6333 public bool ResolveBase (EmitContext ec)
6335 eclass = ExprClass.Variable;
6336 type = ec.ContainerType;
6339 Error (26, "Keyword `this' is not valid in a static property, static method, or static field initializer");
6343 if (block != null && block.Toplevel.ThisVariable != null)
6344 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6346 if (ec.CurrentAnonymousMethod != null)
6352 public override Expression DoResolve (EmitContext ec)
6354 if (!ResolveBase (ec))
6357 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) && !variable_info.IsAssigned (ec)) {
6358 Error (188, "The `this' object cannot be used before all of its fields are assigned to");
6359 variable_info.SetAssigned (ec);
6363 if (ec.IsFieldInitializer) {
6364 Error (27, "Keyword `this' is not available in the current context");
6371 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6373 if (!ResolveBase (ec))
6376 if (variable_info != null)
6377 variable_info.SetAssigned (ec);
6379 if (ec.TypeContainer is Class){
6380 Error (1604, "Cannot assign to 'this' because it is read-only");
6387 public void Emit (EmitContext ec, bool leave_copy)
6391 ec.ig.Emit (OpCodes.Dup);
6394 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
6396 ILGenerator ig = ec.ig;
6398 if (ec.TypeContainer is Struct){
6399 ec.EmitThis (false);
6402 LocalTemporary t = null;
6404 t = new LocalTemporary (type);
6405 ec.ig.Emit (OpCodes.Dup);
6409 ig.Emit (OpCodes.Stobj, type);
6416 throw new Exception ("how did you get here");
6420 public override void Emit (EmitContext ec)
6422 ILGenerator ig = ec.ig;
6424 ec.EmitThis (false);
6425 if (ec.TypeContainer is Struct)
6426 ig.Emit (OpCodes.Ldobj, type);
6429 public override int GetHashCode()
6431 return block.GetHashCode ();
6434 public override bool Equals (object obj)
6436 This t = obj as This;
6440 return block == t.block;
6443 public void AddressOf (EmitContext ec, AddressOp mode)
6448 // FIGURE OUT WHY LDARG_S does not work
6450 // consider: struct X { int val; int P { set { val = value; }}}
6452 // Yes, this looks very bad. Look at `NOTAS' for
6454 // ec.ig.Emit (OpCodes.Ldarga_S, (byte) 0);
6459 /// Represents the `__arglist' construct
6461 public class ArglistAccess : Expression
6463 public ArglistAccess (Location loc)
6468 public override Expression DoResolve (EmitContext ec)
6470 eclass = ExprClass.Variable;
6471 type = TypeManager.runtime_argument_handle_type;
6473 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6475 Error (190, "The __arglist construct is valid only within " +
6476 "a variable argument method");
6483 public override void Emit (EmitContext ec)
6485 ec.ig.Emit (OpCodes.Arglist);
6490 /// Represents the `__arglist (....)' construct
6492 public class Arglist : Expression
6494 public readonly Argument[] Arguments;
6496 public Arglist (Argument[] args, Location l)
6502 public Type[] ArgumentTypes {
6504 Type[] retval = new Type [Arguments.Length];
6505 for (int i = 0; i < Arguments.Length; i++)
6506 retval [i] = Arguments [i].Type;
6511 public override Expression DoResolve (EmitContext ec)
6513 eclass = ExprClass.Variable;
6514 type = TypeManager.runtime_argument_handle_type;
6516 foreach (Argument arg in Arguments) {
6517 if (!arg.Resolve (ec, loc))
6524 public override void Emit (EmitContext ec)
6526 foreach (Argument arg in Arguments)
6532 // This produces the value that renders an instance, used by the iterators code
6534 public class ProxyInstance : Expression, IMemoryLocation {
6535 public override Expression DoResolve (EmitContext ec)
6537 eclass = ExprClass.Variable;
6538 type = ec.ContainerType;
6542 public override void Emit (EmitContext ec)
6544 ec.ig.Emit (OpCodes.Ldarg_0);
6548 public void AddressOf (EmitContext ec, AddressOp mode)
6550 ec.ig.Emit (OpCodes.Ldarg_0);
6555 /// Implements the typeof operator
6557 public class TypeOf : Expression {
6558 readonly Expression QueriedType;
6559 protected Type typearg;
6561 public TypeOf (Expression queried_type, Location l)
6563 QueriedType = queried_type;
6567 public override Expression DoResolve (EmitContext ec)
6569 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6573 typearg = texpr.Type;
6575 if (typearg == TypeManager.void_type) {
6576 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6580 if (typearg.IsPointer && !ec.InUnsafe){
6585 type = TypeManager.type_type;
6586 // Even though what is returned is a type object, it's treated as a value by the compiler.
6587 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6588 eclass = ExprClass.Value;
6592 public override void Emit (EmitContext ec)
6594 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6595 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6598 public override bool GetAttributableValue (Type valueType, out object value)
6600 if (valueType == TypeManager.object_type) {
6601 value = (object)typearg;
6608 public Type TypeArgument
6618 /// Implements the `typeof (void)' operator
6620 public class TypeOfVoid : TypeOf {
6621 public TypeOfVoid (Location l) : base (null, l)
6626 public override Expression DoResolve (EmitContext ec)
6628 type = TypeManager.type_type;
6629 typearg = TypeManager.void_type;
6630 // See description in TypeOf.
6631 eclass = ExprClass.Value;
6637 /// Implements the sizeof expression
6639 public class SizeOf : Expression {
6640 public Expression QueriedType;
6643 public SizeOf (Expression queried_type, Location l)
6645 this.QueriedType = queried_type;
6649 public override Expression DoResolve (EmitContext ec)
6651 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6655 type_queried = texpr.Type;
6656 if (type_queried.IsEnum)
6657 type_queried = TypeManager.EnumToUnderlying (type_queried);
6659 if (type_queried == TypeManager.void_type) {
6660 Expression.Error_VoidInvalidInTheContext (loc);
6664 int size_of = GetTypeSize (type_queried);
6666 return new IntConstant (size_of, loc);
6670 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)",
6671 TypeManager.CSharpName (type_queried));
6675 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
6679 type = TypeManager.int32_type;
6680 eclass = ExprClass.Value;
6684 public override void Emit (EmitContext ec)
6686 int size = GetTypeSize (type_queried);
6689 ec.ig.Emit (OpCodes.Sizeof, type_queried);
6691 IntConstant.EmitInt (ec.ig, size);
6696 /// Implements the qualified-alias-member (::) expression.
6698 public class QualifiedAliasMember : Expression
6700 string alias, identifier;
6702 public QualifiedAliasMember (string alias, string identifier, Location l)
6705 this.identifier = identifier;
6709 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
6711 if (alias == "global")
6712 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
6714 int errors = Report.Errors;
6715 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6717 if (errors == Report.Errors)
6718 Report.Error (432, loc, "Alias `{0}' not found", alias);
6721 if (fne.eclass != ExprClass.Namespace) {
6723 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6726 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
6729 public override Expression DoResolve (EmitContext ec)
6731 FullNamedExpression fne;
6732 if (alias == "global") {
6733 fne = RootNamespace.Global;
6735 int errors = Report.Errors;
6736 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6738 if (errors == Report.Errors)
6739 Report.Error (432, loc, "Alias `{0}' not found", alias);
6744 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
6748 if (!(retval is FullNamedExpression)) {
6749 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
6753 // We defer this check till the end to match the behaviour of CSC
6754 if (fne.eclass != ExprClass.Namespace) {
6755 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6761 public override void Emit (EmitContext ec)
6763 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
6767 public override string ToString ()
6769 return alias + "::" + identifier;
6772 public override string GetSignatureForError ()
6779 /// Implements the member access expression
6781 public class MemberAccess : Expression {
6782 public readonly string Identifier;
6785 public MemberAccess (Expression expr, string id)
6786 : this (expr, id, expr.Location)
6790 public MemberAccess (Expression expr, string identifier, Location loc)
6793 Identifier = identifier;
6797 public Expression Expr {
6798 get { return expr; }
6801 // TODO: this method has very poor performace for Enum fields and
6802 // probably for other constants as well
6803 Expression DoResolve (EmitContext ec, Expression right_side)
6806 throw new Exception ();
6809 // Resolve the expression with flow analysis turned off, we'll do the definite
6810 // assignment checks later. This is because we don't know yet what the expression
6811 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
6812 // definite assignment check on the actual field and not on the whole struct.
6815 SimpleName original = expr as SimpleName;
6816 Expression new_expr = expr.Resolve (ec,
6817 ResolveFlags.VariableOrValue | ResolveFlags.Type |
6818 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
6820 if (new_expr == null)
6823 if (new_expr is Namespace) {
6824 Namespace ns = (Namespace) new_expr;
6825 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, Identifier, loc);
6827 ns.Error_NamespaceDoesNotExist (loc, Identifier);
6831 Type expr_type = new_expr.Type;
6832 if (expr_type.IsPointer){
6833 Error (23, "The `.' operator can not be applied to pointer operands (" +
6834 TypeManager.CSharpName (expr_type) + ")");
6836 } else if (expr_type == TypeManager.void_type) {
6837 Error (23, "The `.' operator can not be applied to operands of type 'void'");
6839 } else if (expr_type == TypeManager.anonymous_method_type){
6840 Error (23, "The `.' operator can not be applied to anonymous methods");
6844 Expression member_lookup;
6845 member_lookup = MemberLookupFinal (ec, expr_type, expr_type, Identifier, loc);
6846 if (member_lookup == null)
6849 if (member_lookup is TypeExpr) {
6850 if (!(new_expr is TypeExpr) &&
6851 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
6852 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
6853 Identifier, member_lookup.GetSignatureForError ());
6857 return member_lookup;
6860 MemberExpr me = (MemberExpr) member_lookup;
6861 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
6862 if (member_lookup == null)
6865 if (original != null && !TypeManager.IsValueType (expr_type)) {
6866 me = member_lookup as MemberExpr;
6867 if (me != null && me.IsInstance) {
6868 LocalVariableReference var = new_expr as LocalVariableReference;
6869 if (var != null && !var.VerifyAssigned (ec))
6874 // The following DoResolve/DoResolveLValue will do the definite assignment
6877 if (right_side != null)
6878 return member_lookup.DoResolveLValue (ec, right_side);
6880 return member_lookup.DoResolve (ec);
6883 public override Expression DoResolve (EmitContext ec)
6885 return DoResolve (ec, null);
6888 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
6890 return DoResolve (ec, right_side);
6893 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
6895 return ResolveNamespaceOrType (ec, silent);
6898 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
6900 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
6902 if (new_expr == null)
6905 if (new_expr is Namespace) {
6906 Namespace ns = (Namespace) new_expr;
6907 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, Identifier, loc);
6908 if (!silent && retval == null)
6909 ns.Error_NamespaceDoesNotExist (loc, Identifier);
6913 Type expr_type = new_expr.Type;
6915 if (expr_type.IsPointer){
6916 Error (23, "The `.' operator can not be applied to pointer operands (" +
6917 TypeManager.CSharpName (expr_type) + ")");
6921 Expression member_lookup = MemberLookup (rc.DeclContainer.TypeBuilder, expr_type, expr_type, Identifier, loc);
6922 if (member_lookup == null) {
6923 int errors = Report.Errors;
6924 MemberLookupFailed (rc.DeclContainer.TypeBuilder, expr_type, expr_type, Identifier, null, false, loc);
6926 if (!silent && errors == Report.Errors) {
6927 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
6928 Identifier, new_expr.GetSignatureForError ());
6933 if (!(member_lookup is TypeExpr)) {
6934 new_expr.Error_UnexpectedKind (rc.DeclContainer, "type", loc);
6938 return member_lookup.ResolveAsTypeTerminal (rc, silent);
6941 public override void Emit (EmitContext ec)
6943 throw new Exception ("Should not happen");
6946 public override string ToString ()
6948 return expr + "." + Identifier;
6951 public override string GetSignatureForError ()
6953 return expr.GetSignatureForError () + "." + Identifier;
6958 /// Implements checked expressions
6960 public class CheckedExpr : Expression {
6962 public Expression Expr;
6964 public CheckedExpr (Expression e, Location l)
6970 public override Expression DoResolve (EmitContext ec)
6972 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
6973 Expr = Expr.Resolve (ec);
6978 if (Expr is Constant)
6981 eclass = Expr.eclass;
6986 public override void Emit (EmitContext ec)
6988 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
6992 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
6994 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
6995 Expr.EmitBranchable (ec, target, onTrue);
7000 /// Implements the unchecked expression
7002 public class UnCheckedExpr : Expression {
7004 public Expression Expr;
7006 public UnCheckedExpr (Expression e, Location l)
7012 public override Expression DoResolve (EmitContext ec)
7014 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7015 Expr = Expr.Resolve (ec);
7020 if (Expr is Constant)
7023 eclass = Expr.eclass;
7028 public override void Emit (EmitContext ec)
7030 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7034 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7036 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7037 Expr.EmitBranchable (ec, target, onTrue);
7042 /// An Element Access expression.
7044 /// During semantic analysis these are transformed into
7045 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7047 public class ElementAccess : Expression {
7048 public ArrayList Arguments;
7049 public Expression Expr;
7051 public ElementAccess (Expression e, ArrayList e_list)
7060 Arguments = new ArrayList ();
7061 foreach (Expression tmp in e_list)
7062 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7066 bool CommonResolve (EmitContext ec)
7068 Expr = Expr.Resolve (ec);
7073 if (Arguments == null)
7076 foreach (Argument a in Arguments){
7077 if (!a.Resolve (ec, loc))
7084 Expression MakePointerAccess (EmitContext ec, Type t)
7086 if (t == TypeManager.void_ptr_type){
7087 Error (242, "The array index operation is not valid on void pointers");
7090 if (Arguments.Count != 1){
7091 Error (196, "A pointer must be indexed by only one value");
7096 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7099 return new Indirection (p, loc).Resolve (ec);
7102 public override Expression DoResolve (EmitContext ec)
7104 if (!CommonResolve (ec))
7108 // We perform some simple tests, and then to "split" the emit and store
7109 // code we create an instance of a different class, and return that.
7111 // I am experimenting with this pattern.
7115 if (t == TypeManager.array_type){
7116 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7121 return (new ArrayAccess (this, loc)).Resolve (ec);
7123 return MakePointerAccess (ec, Expr.Type);
7125 FieldExpr fe = Expr as FieldExpr;
7127 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7129 return MakePointerAccess (ec, ff.ElementType);
7132 return (new IndexerAccess (this, loc)).Resolve (ec);
7135 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7137 if (!CommonResolve (ec))
7142 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7145 return MakePointerAccess (ec, Expr.Type);
7147 FieldExpr fe = Expr as FieldExpr;
7149 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7151 if (!(fe.InstanceExpression is LocalVariableReference) &&
7152 !(fe.InstanceExpression is This)) {
7153 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7156 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7157 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7160 return MakePointerAccess (ec, ff.ElementType);
7163 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7166 public override void Emit (EmitContext ec)
7168 throw new Exception ("Should never be reached");
7173 /// Implements array access
7175 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7177 // Points to our "data" repository
7181 LocalTemporary temp;
7184 public ArrayAccess (ElementAccess ea_data, Location l)
7187 eclass = ExprClass.Variable;
7191 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7193 return DoResolve (ec);
7196 public override Expression DoResolve (EmitContext ec)
7199 ExprClass eclass = ea.Expr.eclass;
7201 // As long as the type is valid
7202 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7203 eclass == ExprClass.Value)) {
7204 ea.Expr.Error_UnexpectedKind ("variable or value");
7209 Type t = ea.Expr.Type;
7210 if (t.GetArrayRank () != ea.Arguments.Count){
7211 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7212 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7216 type = TypeManager.GetElementType (t);
7217 if (type.IsPointer && !ec.InUnsafe){
7218 UnsafeError (ea.Location);
7222 foreach (Argument a in ea.Arguments){
7223 Type argtype = a.Type;
7225 if (argtype == TypeManager.int32_type ||
7226 argtype == TypeManager.uint32_type ||
7227 argtype == TypeManager.int64_type ||
7228 argtype == TypeManager.uint64_type) {
7229 Constant c = a.Expr as Constant;
7230 if (c != null && c.IsNegative) {
7231 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7237 // Mhm. This is strage, because the Argument.Type is not the same as
7238 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7240 // Wonder if I will run into trouble for this.
7242 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7247 eclass = ExprClass.Variable;
7253 /// Emits the right opcode to load an object of Type `t'
7254 /// from an array of T
7256 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7258 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7259 ig.Emit (OpCodes.Ldelem_U1);
7260 else if (type == TypeManager.sbyte_type)
7261 ig.Emit (OpCodes.Ldelem_I1);
7262 else if (type == TypeManager.short_type)
7263 ig.Emit (OpCodes.Ldelem_I2);
7264 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7265 ig.Emit (OpCodes.Ldelem_U2);
7266 else if (type == TypeManager.int32_type)
7267 ig.Emit (OpCodes.Ldelem_I4);
7268 else if (type == TypeManager.uint32_type)
7269 ig.Emit (OpCodes.Ldelem_U4);
7270 else if (type == TypeManager.uint64_type)
7271 ig.Emit (OpCodes.Ldelem_I8);
7272 else if (type == TypeManager.int64_type)
7273 ig.Emit (OpCodes.Ldelem_I8);
7274 else if (type == TypeManager.float_type)
7275 ig.Emit (OpCodes.Ldelem_R4);
7276 else if (type == TypeManager.double_type)
7277 ig.Emit (OpCodes.Ldelem_R8);
7278 else if (type == TypeManager.intptr_type)
7279 ig.Emit (OpCodes.Ldelem_I);
7280 else if (TypeManager.IsEnumType (type)){
7281 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7282 } else if (type.IsValueType){
7283 ig.Emit (OpCodes.Ldelema, type);
7284 ig.Emit (OpCodes.Ldobj, type);
7285 } else if (type.IsPointer)
7286 ig.Emit (OpCodes.Ldelem_I);
7288 ig.Emit (OpCodes.Ldelem_Ref);
7292 /// Returns the right opcode to store an object of Type `t'
7293 /// from an array of T.
7295 static public OpCode GetStoreOpcode (Type t, out bool is_stobj)
7297 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7299 t = TypeManager.TypeToCoreType (t);
7300 if (TypeManager.IsEnumType (t))
7301 t = TypeManager.EnumToUnderlying (t);
7302 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7303 t == TypeManager.bool_type)
7304 return OpCodes.Stelem_I1;
7305 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7306 t == TypeManager.char_type)
7307 return OpCodes.Stelem_I2;
7308 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7309 return OpCodes.Stelem_I4;
7310 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7311 return OpCodes.Stelem_I8;
7312 else if (t == TypeManager.float_type)
7313 return OpCodes.Stelem_R4;
7314 else if (t == TypeManager.double_type)
7315 return OpCodes.Stelem_R8;
7316 else if (t == TypeManager.intptr_type) {
7318 return OpCodes.Stobj;
7319 } else if (t.IsValueType) {
7321 return OpCodes.Stobj;
7322 } else if (t.IsPointer)
7323 return OpCodes.Stelem_I;
7325 return OpCodes.Stelem_Ref;
7328 MethodInfo FetchGetMethod ()
7330 ModuleBuilder mb = CodeGen.Module.Builder;
7331 int arg_count = ea.Arguments.Count;
7332 Type [] args = new Type [arg_count];
7335 for (int i = 0; i < arg_count; i++){
7336 //args [i++] = a.Type;
7337 args [i] = TypeManager.int32_type;
7340 get = mb.GetArrayMethod (
7341 ea.Expr.Type, "Get",
7342 CallingConventions.HasThis |
7343 CallingConventions.Standard,
7349 MethodInfo FetchAddressMethod ()
7351 ModuleBuilder mb = CodeGen.Module.Builder;
7352 int arg_count = ea.Arguments.Count;
7353 Type [] args = new Type [arg_count];
7357 ret_type = TypeManager.GetReferenceType (type);
7359 for (int i = 0; i < arg_count; i++){
7360 //args [i++] = a.Type;
7361 args [i] = TypeManager.int32_type;
7364 address = mb.GetArrayMethod (
7365 ea.Expr.Type, "Address",
7366 CallingConventions.HasThis |
7367 CallingConventions.Standard,
7374 // Load the array arguments into the stack.
7376 // If we have been requested to cache the values (cached_locations array
7377 // initialized), then load the arguments the first time and store them
7378 // in locals. otherwise load from local variables.
7380 void LoadArrayAndArguments (EmitContext ec)
7382 ILGenerator ig = ec.ig;
7385 foreach (Argument a in ea.Arguments){
7386 Type argtype = a.Expr.Type;
7390 if (argtype == TypeManager.int64_type)
7391 ig.Emit (OpCodes.Conv_Ovf_I);
7392 else if (argtype == TypeManager.uint64_type)
7393 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7397 public void Emit (EmitContext ec, bool leave_copy)
7399 int rank = ea.Expr.Type.GetArrayRank ();
7400 ILGenerator ig = ec.ig;
7403 LoadArrayAndArguments (ec);
7406 EmitLoadOpcode (ig, type);
7410 method = FetchGetMethod ();
7411 ig.Emit (OpCodes.Call, method);
7414 LoadFromPtr (ec.ig, this.type);
7417 ec.ig.Emit (OpCodes.Dup);
7418 temp = new LocalTemporary (this.type);
7423 public override void Emit (EmitContext ec)
7428 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7430 int rank = ea.Expr.Type.GetArrayRank ();
7431 ILGenerator ig = ec.ig;
7432 Type t = source.Type;
7433 prepared = prepare_for_load;
7435 if (prepare_for_load) {
7436 AddressOf (ec, AddressOp.LoadStore);
7437 ec.ig.Emit (OpCodes.Dup);
7440 ec.ig.Emit (OpCodes.Dup);
7441 temp = new LocalTemporary (this.type);
7444 StoreFromPtr (ec.ig, t);
7454 LoadArrayAndArguments (ec);
7458 OpCode op = GetStoreOpcode (t, out is_stobj);
7460 // The stobj opcode used by value types will need
7461 // an address on the stack, not really an array/array
7465 ig.Emit (OpCodes.Ldelema, t);
7469 ec.ig.Emit (OpCodes.Dup);
7470 temp = new LocalTemporary (this.type);
7475 ig.Emit (OpCodes.Stobj, t);
7479 ModuleBuilder mb = CodeGen.Module.Builder;
7480 int arg_count = ea.Arguments.Count;
7481 Type [] args = new Type [arg_count + 1];
7486 ec.ig.Emit (OpCodes.Dup);
7487 temp = new LocalTemporary (this.type);
7491 for (int i = 0; i < arg_count; i++){
7492 //args [i++] = a.Type;
7493 args [i] = TypeManager.int32_type;
7496 args [arg_count] = type;
7498 set = mb.GetArrayMethod (
7499 ea.Expr.Type, "Set",
7500 CallingConventions.HasThis |
7501 CallingConventions.Standard,
7502 TypeManager.void_type, args);
7504 ig.Emit (OpCodes.Call, set);
7513 public void AddressOf (EmitContext ec, AddressOp mode)
7515 int rank = ea.Expr.Type.GetArrayRank ();
7516 ILGenerator ig = ec.ig;
7518 LoadArrayAndArguments (ec);
7521 ig.Emit (OpCodes.Ldelema, type);
7523 MethodInfo address = FetchAddressMethod ();
7524 ig.Emit (OpCodes.Call, address);
7528 public void EmitGetLength (EmitContext ec, int dim)
7530 int rank = ea.Expr.Type.GetArrayRank ();
7531 ILGenerator ig = ec.ig;
7535 ig.Emit (OpCodes.Ldlen);
7536 ig.Emit (OpCodes.Conv_I4);
7538 IntLiteral.EmitInt (ig, dim);
7539 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7545 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7546 public readonly ArrayList Properties;
7547 static Indexers empty;
7549 public struct Indexer {
7550 public readonly PropertyInfo PropertyInfo;
7551 public readonly MethodInfo Getter, Setter;
7553 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7555 this.PropertyInfo = property_info;
7563 empty = new Indexers (null);
7566 Indexers (ArrayList array)
7571 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
7576 foreach (PropertyInfo property in mi){
7577 MethodInfo get, set;
7579 get = property.GetGetMethod (true);
7580 set = property.GetSetMethod (true);
7581 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
7583 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
7585 if (get != null || set != null) {
7587 ix = new Indexers (new ArrayList ());
7588 ix.Properties.Add (new Indexer (property, get, set));
7593 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
7595 string p_name = TypeManager.IndexerPropertyName (lookup_type);
7597 return TypeManager.MemberLookup (
7598 caller_type, caller_type, lookup_type, MemberTypes.Property,
7599 BindingFlags.Public | BindingFlags.Instance |
7600 BindingFlags.DeclaredOnly, p_name, null);
7603 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
7605 Indexers ix = empty;
7607 Type copy = lookup_type;
7608 while (copy != TypeManager.object_type && copy != null){
7609 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
7610 copy = copy.BaseType;
7613 if (lookup_type.IsInterface) {
7614 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
7615 if (ifaces != null) {
7616 foreach (Type itype in ifaces)
7617 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
7626 /// Expressions that represent an indexer call.
7628 public class IndexerAccess : Expression, IAssignMethod {
7630 // Points to our "data" repository
7632 MethodInfo get, set;
7633 ArrayList set_arguments;
7634 bool is_base_indexer;
7636 protected Type indexer_type;
7637 protected Type current_type;
7638 protected Expression instance_expr;
7639 protected ArrayList arguments;
7641 public IndexerAccess (ElementAccess ea, Location loc)
7642 : this (ea.Expr, false, loc)
7644 this.arguments = ea.Arguments;
7647 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
7650 this.instance_expr = instance_expr;
7651 this.is_base_indexer = is_base_indexer;
7652 this.eclass = ExprClass.Value;
7656 protected virtual bool CommonResolve (EmitContext ec)
7658 indexer_type = instance_expr.Type;
7659 current_type = ec.ContainerType;
7664 public override Expression DoResolve (EmitContext ec)
7666 if (!CommonResolve (ec))
7670 // Step 1: Query for all `Item' *properties*. Notice
7671 // that the actual methods are pointed from here.
7673 // This is a group of properties, piles of them.
7675 ArrayList AllGetters = null;
7677 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
7678 if (ilist.Properties != null) {
7679 AllGetters = new ArrayList(ilist.Properties.Count);
7680 foreach (Indexers.Indexer ix in ilist.Properties) {
7681 if (ix.Getter != null)
7682 AllGetters.Add (ix.Getter);
7686 if (AllGetters == null) {
7687 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7688 TypeManager.CSharpName (indexer_type));
7692 if (AllGetters.Count == 0) {
7693 // FIXME: we cannot simply select first one as the error message is missleading when
7694 // multiple indexers exist
7695 Indexers.Indexer first_indexer = (Indexers.Indexer)ilist.Properties[ilist.Properties.Count - 1];
7696 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
7697 TypeManager.GetFullNameSignature (first_indexer.PropertyInfo));
7701 get = (MethodInfo)Invocation.OverloadResolve (ec, new MethodGroupExpr (AllGetters, loc),
7702 arguments, false, loc);
7705 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7710 // Only base will allow this invocation to happen.
7712 if (get.IsAbstract && this is BaseIndexerAccess){
7713 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
7717 type = get.ReturnType;
7718 if (type.IsPointer && !ec.InUnsafe){
7723 instance_expr.CheckMarshalByRefAccess ();
7725 eclass = ExprClass.IndexerAccess;
7729 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7731 if (right_side == EmptyExpression.OutAccess) {
7732 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
7733 GetSignatureForError ());
7737 // if the indexer returns a value type, and we try to set a field in it
7738 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
7739 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
7740 GetSignatureForError ());
7744 ArrayList AllSetters = new ArrayList();
7745 if (!CommonResolve (ec))
7748 bool found_any = false, found_any_setters = false;
7750 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
7751 if (ilist.Properties != null) {
7753 foreach (Indexers.Indexer ix in ilist.Properties) {
7754 if (ix.Setter != null)
7755 AllSetters.Add (ix.Setter);
7758 if (AllSetters.Count > 0) {
7759 found_any_setters = true;
7760 set_arguments = (ArrayList) arguments.Clone ();
7761 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
7762 set = (MethodInfo) Invocation.OverloadResolve (
7763 ec, new MethodGroupExpr (AllSetters, loc),
7764 set_arguments, false, loc);
7768 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7769 TypeManager.CSharpName (indexer_type));
7773 if (!found_any_setters) {
7774 Error (154, "indexer can not be used in this context, because " +
7775 "it lacks a `set' accessor");
7780 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7785 // Only base will allow this invocation to happen.
7787 if (set.IsAbstract && this is BaseIndexerAccess){
7788 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
7793 // Now look for the actual match in the list of indexers to set our "return" type
7795 type = TypeManager.void_type; // default value
7796 foreach (Indexers.Indexer ix in ilist.Properties){
7797 if (ix.Setter == set){
7798 type = ix.PropertyInfo.PropertyType;
7803 instance_expr.CheckMarshalByRefAccess ();
7805 eclass = ExprClass.IndexerAccess;
7809 bool prepared = false;
7810 LocalTemporary temp;
7812 public void Emit (EmitContext ec, bool leave_copy)
7814 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
7816 ec.ig.Emit (OpCodes.Dup);
7817 temp = new LocalTemporary (Type);
7823 // source is ignored, because we already have a copy of it from the
7824 // LValue resolution and we have already constructed a pre-cached
7825 // version of the arguments (ea.set_arguments);
7827 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7829 prepared = prepare_for_load;
7830 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
7835 ec.ig.Emit (OpCodes.Dup);
7836 temp = new LocalTemporary (Type);
7839 } else if (leave_copy) {
7840 temp = new LocalTemporary (Type);
7846 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
7855 public override void Emit (EmitContext ec)
7860 public override string GetSignatureForError ()
7862 // FIXME: print the argument list of the indexer
7863 return instance_expr.GetSignatureForError () + ".this[...]";
7868 /// The base operator for method names
7870 public class BaseAccess : Expression {
7873 public BaseAccess (string member, Location l)
7875 this.member = member;
7879 public override Expression DoResolve (EmitContext ec)
7881 Expression c = CommonResolve (ec);
7887 // MethodGroups use this opportunity to flag an error on lacking ()
7889 if (!(c is MethodGroupExpr))
7890 return c.Resolve (ec);
7894 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7896 Expression c = CommonResolve (ec);
7902 // MethodGroups use this opportunity to flag an error on lacking ()
7904 if (! (c is MethodGroupExpr))
7905 return c.DoResolveLValue (ec, right_side);
7910 Expression CommonResolve (EmitContext ec)
7912 Expression member_lookup;
7913 Type current_type = ec.ContainerType;
7914 Type base_type = current_type.BaseType;
7917 Error (1511, "Keyword `base' is not available in a static method");
7921 if (ec.IsFieldInitializer){
7922 Error (1512, "Keyword `base' is not available in the current context");
7926 member_lookup = MemberLookup (ec.ContainerType, null, base_type, member,
7927 AllMemberTypes, AllBindingFlags, loc);
7928 if (member_lookup == null) {
7929 MemberLookupFailed (ec.ContainerType, base_type, base_type, member, null, true, loc);
7936 left = new TypeExpression (base_type, loc);
7938 left = ec.GetThis (loc);
7940 MemberExpr me = (MemberExpr) member_lookup;
7942 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
7944 if (e is PropertyExpr) {
7945 PropertyExpr pe = (PropertyExpr) e;
7950 if (e is MethodGroupExpr)
7951 ((MethodGroupExpr) e).IsBase = true;
7956 public override void Emit (EmitContext ec)
7958 throw new Exception ("Should never be called");
7963 /// The base indexer operator
7965 public class BaseIndexerAccess : IndexerAccess {
7966 public BaseIndexerAccess (ArrayList args, Location loc)
7967 : base (null, true, loc)
7969 arguments = new ArrayList ();
7970 foreach (Expression tmp in args)
7971 arguments.Add (new Argument (tmp, Argument.AType.Expression));
7974 protected override bool CommonResolve (EmitContext ec)
7976 instance_expr = ec.GetThis (loc);
7978 current_type = ec.ContainerType.BaseType;
7979 indexer_type = current_type;
7981 foreach (Argument a in arguments){
7982 if (!a.Resolve (ec, loc))
7991 /// This class exists solely to pass the Type around and to be a dummy
7992 /// that can be passed to the conversion functions (this is used by
7993 /// foreach implementation to typecast the object return value from
7994 /// get_Current into the proper type. All code has been generated and
7995 /// we only care about the side effect conversions to be performed
7997 /// This is also now used as a placeholder where a no-action expression
7998 /// is needed (the `New' class).
8000 public class EmptyExpression : Expression {
8001 public static readonly EmptyExpression Null = new EmptyExpression ();
8003 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8004 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8005 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8007 static EmptyExpression temp = new EmptyExpression ();
8008 public static EmptyExpression Grab ()
8011 throw new InternalErrorException ("Nested Grab");
8012 EmptyExpression retval = temp;
8017 public static void Release (EmptyExpression e)
8020 throw new InternalErrorException ("Already released");
8024 // TODO: should be protected
8025 public EmptyExpression ()
8027 type = TypeManager.object_type;
8028 eclass = ExprClass.Value;
8029 loc = Location.Null;
8032 public EmptyExpression (Type t)
8035 eclass = ExprClass.Value;
8036 loc = Location.Null;
8039 public override Expression DoResolve (EmitContext ec)
8044 public override void Emit (EmitContext ec)
8046 // nothing, as we only exist to not do anything.
8050 // This is just because we might want to reuse this bad boy
8051 // instead of creating gazillions of EmptyExpressions.
8052 // (CanImplicitConversion uses it)
8054 public void SetType (Type t)
8060 public class UserCast : Expression {
8064 public UserCast (MethodInfo method, Expression source, Location l)
8066 this.method = method;
8067 this.source = source;
8068 type = method.ReturnType;
8069 eclass = ExprClass.Value;
8073 public Expression Source {
8079 public override Expression DoResolve (EmitContext ec)
8082 // We are born fully resolved
8087 public override void Emit (EmitContext ec)
8089 ILGenerator ig = ec.ig;
8093 if (method is MethodInfo)
8094 ig.Emit (OpCodes.Call, (MethodInfo) method);
8096 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8102 // This class is used to "construct" the type during a typecast
8103 // operation. Since the Type.GetType class in .NET can parse
8104 // the type specification, we just use this to construct the type
8105 // one bit at a time.
8107 public class ComposedCast : TypeExpr {
8111 public ComposedCast (Expression left, string dim)
8112 : this (left, dim, left.Location)
8116 public ComposedCast (Expression left, string dim, Location l)
8123 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8125 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8129 Type ltype = lexpr.Type;
8130 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8131 Error_VoidInvalidInTheContext (loc);
8135 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc)) {
8139 type = TypeManager.GetConstructedType (ltype, dim);
8141 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8144 if (type.IsPointer && !ec.IsInUnsafeScope){
8149 if (type.IsArray && (type.GetElementType () == TypeManager.arg_iterator_type ||
8150 type.GetElementType () == TypeManager.typed_reference_type)) {
8151 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (type.GetElementType ()));
8155 eclass = ExprClass.Type;
8159 public override string Name {
8160 get { return left + dim; }
8163 public override string FullName {
8164 get { return type.FullName; }
8167 public override string GetSignatureForError ()
8169 return left.GetSignatureForError () + dim;
8173 public class FixedBufferPtr : Expression {
8176 public FixedBufferPtr (Expression array, Type array_type, Location l)
8181 type = TypeManager.GetPointerType (array_type);
8182 eclass = ExprClass.Value;
8185 public override void Emit(EmitContext ec)
8190 public override Expression DoResolve (EmitContext ec)
8193 // We are born fully resolved
8201 // This class is used to represent the address of an array, used
8202 // only by the Fixed statement, this generates "&a [0]" construct
8203 // for fixed (char *pa = a)
8205 public class ArrayPtr : FixedBufferPtr {
8208 public ArrayPtr (Expression array, Type array_type, Location l):
8209 base (array, array_type, l)
8211 this.array_type = array_type;
8214 public override void Emit (EmitContext ec)
8218 ILGenerator ig = ec.ig;
8219 IntLiteral.EmitInt (ig, 0);
8220 ig.Emit (OpCodes.Ldelema, array_type);
8225 // Used by the fixed statement
8227 public class StringPtr : Expression {
8230 public StringPtr (LocalBuilder b, Location l)
8233 eclass = ExprClass.Value;
8234 type = TypeManager.char_ptr_type;
8238 public override Expression DoResolve (EmitContext ec)
8240 // This should never be invoked, we are born in fully
8241 // initialized state.
8246 public override void Emit (EmitContext ec)
8248 ILGenerator ig = ec.ig;
8250 ig.Emit (OpCodes.Ldloc, b);
8251 ig.Emit (OpCodes.Conv_I);
8252 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8253 ig.Emit (OpCodes.Add);
8258 // Implements the `stackalloc' keyword
8260 public class StackAlloc : Expression {
8265 public StackAlloc (Expression type, Expression count, Location l)
8272 public override Expression DoResolve (EmitContext ec)
8274 count = count.Resolve (ec);
8278 if (count.Type != TypeManager.int32_type){
8279 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8284 Constant c = count as Constant;
8285 if (c != null && c.IsNegative) {
8286 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8290 if (ec.InCatch || ec.InFinally) {
8291 Error (255, "Cannot use stackalloc in finally or catch");
8295 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8301 if (!TypeManager.VerifyUnManaged (otype, loc))
8304 type = TypeManager.GetPointerType (otype);
8305 eclass = ExprClass.Value;
8310 public override void Emit (EmitContext ec)
8312 int size = GetTypeSize (otype);
8313 ILGenerator ig = ec.ig;
8316 ig.Emit (OpCodes.Sizeof, otype);
8318 IntConstant.EmitInt (ig, size);
8320 ig.Emit (OpCodes.Mul);
8321 ig.Emit (OpCodes.Localloc);