2 // expression.cs: Expression representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@seznam.cz)
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2003, 2004 Novell, Inc.
13 namespace Mono.CSharp {
15 using System.Collections;
16 using System.Reflection;
17 using System.Reflection.Emit;
21 /// This is just a helper class, it is generated by Unary, UnaryMutator
22 /// when an overloaded method has been found. It just emits the code for a
25 public class StaticCallExpr : ExpressionStatement {
29 public StaticCallExpr (MethodInfo m, ArrayList a, Location l)
35 eclass = ExprClass.Value;
39 public override Expression DoResolve (EmitContext ec)
42 // We are born fully resolved
47 public override void Emit (EmitContext ec)
50 Invocation.EmitArguments (ec, mi, args, false, null);
52 ec.ig.Emit (OpCodes.Call, mi);
56 static public StaticCallExpr MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
57 Expression e, Location loc)
62 args = new ArrayList (1);
63 Argument a = new Argument (e, Argument.AType.Expression);
65 // We need to resolve the arguments before sending them in !
66 if (!a.Resolve (ec, loc))
70 method = Invocation.OverloadResolve (
71 ec, (MethodGroupExpr) mg, args, false, loc);
76 return new StaticCallExpr ((MethodInfo) method, args, loc);
79 public override void EmitStatement (EmitContext ec)
82 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
83 ec.ig.Emit (OpCodes.Pop);
86 public MethodInfo Method {
91 public class ParenthesizedExpression : Expression
93 public Expression Expr;
95 public ParenthesizedExpression (Expression expr)
100 public override Expression DoResolve (EmitContext ec)
102 Expr = Expr.Resolve (ec);
106 public override void Emit (EmitContext ec)
108 throw new Exception ("Should not happen");
111 public override Location Location
114 return Expr.Location;
120 /// Unary expressions.
124 /// Unary implements unary expressions. It derives from
125 /// ExpressionStatement becuase the pre/post increment/decrement
126 /// operators can be used in a statement context.
128 public class Unary : Expression {
129 public enum Operator : byte {
130 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
131 Indirection, AddressOf, TOP
134 public Operator Oper;
135 public Expression Expr;
137 public Unary (Operator op, Expression expr, Location loc)
145 /// Returns a stringified representation of the Operator
147 static public string OperName (Operator oper)
150 case Operator.UnaryPlus:
152 case Operator.UnaryNegation:
154 case Operator.LogicalNot:
156 case Operator.OnesComplement:
158 case Operator.AddressOf:
160 case Operator.Indirection:
164 return oper.ToString ();
167 public static readonly string [] oper_names;
171 oper_names = new string [(int)Operator.TOP];
173 oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus";
174 oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation";
175 oper_names [(int) Operator.LogicalNot] = "op_LogicalNot";
176 oper_names [(int) Operator.OnesComplement] = "op_OnesComplement";
177 oper_names [(int) Operator.Indirection] = "op_Indirection";
178 oper_names [(int) Operator.AddressOf] = "op_AddressOf";
181 public static void Error_OperatorCannotBeApplied (Location loc, string oper, Type t)
183 Error_OperatorCannotBeApplied (loc, oper, TypeManager.CSharpName (t));
186 public static void Error_OperatorCannotBeApplied (Location loc, string oper, string type)
188 Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'",
192 void Error23 (Type t)
194 Error_OperatorCannotBeApplied (loc, OperName (Oper), t);
198 /// The result has been already resolved:
200 /// FIXME: a minus constant -128 sbyte cant be turned into a
203 static Expression TryReduceNegative (Constant expr)
207 if (expr is IntConstant)
208 e = new IntConstant (-((IntConstant) expr).Value, expr.Location);
209 else if (expr is UIntConstant){
210 uint value = ((UIntConstant) expr).Value;
212 if (value < 2147483649)
213 return new IntConstant (-(int)value, expr.Location);
215 e = new LongConstant (-value, expr.Location);
217 else if (expr is LongConstant)
218 e = new LongConstant (-((LongConstant) expr).Value, expr.Location);
219 else if (expr is ULongConstant){
220 ulong value = ((ULongConstant) expr).Value;
222 if (value < 9223372036854775809)
223 return new LongConstant(-(long)value, expr.Location);
225 else if (expr is FloatConstant)
226 e = new FloatConstant (-((FloatConstant) expr).Value, expr.Location);
227 else if (expr is DoubleConstant)
228 e = new DoubleConstant (-((DoubleConstant) expr).Value, expr.Location);
229 else if (expr is DecimalConstant)
230 e = new DecimalConstant (-((DecimalConstant) expr).Value, expr.Location);
231 else if (expr is ShortConstant)
232 e = new IntConstant (-((ShortConstant) expr).Value, expr.Location);
233 else if (expr is UShortConstant)
234 e = new IntConstant (-((UShortConstant) expr).Value, expr.Location);
235 else if (expr is SByteConstant)
236 e = new IntConstant (-((SByteConstant) expr).Value, expr.Location);
237 else if (expr is ByteConstant)
238 e = new IntConstant (-((ByteConstant) expr).Value, expr.Location);
243 // This routine will attempt to simplify the unary expression when the
244 // argument is a constant. The result is returned in `result' and the
245 // function returns true or false depending on whether a reduction
246 // was performed or not
248 bool Reduce (EmitContext ec, Constant e, out Expression result)
250 Type expr_type = e.Type;
253 case Operator.UnaryPlus:
254 if (expr_type == TypeManager.bool_type){
263 case Operator.UnaryNegation:
264 result = TryReduceNegative (e);
265 return result != null;
267 case Operator.LogicalNot:
268 if (expr_type != TypeManager.bool_type) {
274 BoolConstant b = (BoolConstant) e;
275 result = new BoolConstant (!(b.Value), b.Location);
278 case Operator.OnesComplement:
279 if (!((expr_type == TypeManager.int32_type) ||
280 (expr_type == TypeManager.uint32_type) ||
281 (expr_type == TypeManager.int64_type) ||
282 (expr_type == TypeManager.uint64_type) ||
283 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
286 if (Convert.ImplicitConversionExists (ec, e, TypeManager.int32_type)){
287 result = new Cast (new TypeExpression (TypeManager.int32_type, loc), e, loc);
288 result = result.Resolve (ec);
289 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.uint32_type)){
290 result = new Cast (new TypeExpression (TypeManager.uint32_type, loc), e, loc);
291 result = result.Resolve (ec);
292 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.int64_type)){
293 result = new Cast (new TypeExpression (TypeManager.int64_type, loc), e, loc);
294 result = result.Resolve (ec);
295 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.uint64_type)){
296 result = new Cast (new TypeExpression (TypeManager.uint64_type, loc), e, loc);
297 result = result.Resolve (ec);
300 if (result == null || !(result is Constant)){
306 expr_type = result.Type;
307 e = (Constant) result;
310 if (e is EnumConstant){
311 EnumConstant enum_constant = (EnumConstant) e;
314 if (Reduce (ec, enum_constant.Child, out reduced)){
315 result = new EnumConstant ((Constant) reduced, enum_constant.Type);
323 if (expr_type == TypeManager.int32_type){
324 result = new IntConstant (~ ((IntConstant) e).Value, e.Location);
325 } else if (expr_type == TypeManager.uint32_type){
326 result = new UIntConstant (~ ((UIntConstant) e).Value, e.Location);
327 } else if (expr_type == TypeManager.int64_type){
328 result = new LongConstant (~ ((LongConstant) e).Value, e.Location);
329 } else if (expr_type == TypeManager.uint64_type){
330 result = new ULongConstant (~ ((ULongConstant) e).Value, e.Location);
338 case Operator.AddressOf:
342 case Operator.Indirection:
346 throw new Exception ("Can not constant fold: " + Oper.ToString());
349 Expression ResolveOperator (EmitContext ec)
352 // Step 1: Default operations on CLI native types.
355 // Attempt to use a constant folding operation.
356 if (Expr is Constant){
359 if (Reduce (ec, (Constant) Expr, out result))
364 // Step 2: Perform Operator Overload location
366 Type expr_type = Expr.Type;
370 op_name = oper_names [(int) Oper];
372 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
375 Expression e = StaticCallExpr.MakeSimpleCall (
376 ec, (MethodGroupExpr) mg, Expr, loc);
386 // Only perform numeric promotions on:
389 if (expr_type == null)
393 case Operator.LogicalNot:
394 if (expr_type != TypeManager.bool_type) {
395 Expr = ResolveBoolean (ec, Expr, loc);
402 type = TypeManager.bool_type;
405 case Operator.OnesComplement:
406 if (!((expr_type == TypeManager.int32_type) ||
407 (expr_type == TypeManager.uint32_type) ||
408 (expr_type == TypeManager.int64_type) ||
409 (expr_type == TypeManager.uint64_type) ||
410 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
413 e = Convert.ImplicitConversion (ec, Expr, TypeManager.int32_type, loc);
416 e = Convert.ImplicitConversion (ec, Expr, TypeManager.uint32_type, loc);
419 e = Convert.ImplicitConversion (ec, Expr, TypeManager.int64_type, loc);
422 e = Convert.ImplicitConversion (ec, Expr, TypeManager.uint64_type, loc);
435 case Operator.AddressOf:
441 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){
445 IVariable variable = Expr as IVariable;
446 bool is_fixed = variable != null && variable.VerifyFixed ();
448 if (!ec.InFixedInitializer && !is_fixed) {
449 Error (212, "You can only take the address of unfixed expression inside " +
450 "of a fixed statement initializer");
454 if (ec.InFixedInitializer && is_fixed) {
455 Error (213, "You cannot use the fixed statement to take the address of an already fixed expression");
459 LocalVariableReference lr = Expr as LocalVariableReference;
461 if (lr.local_info.IsCaptured){
462 AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc);
465 lr.local_info.AddressTaken = true;
466 lr.local_info.Used = true;
469 ParameterReference pr = Expr as ParameterReference;
470 if ((pr != null) && pr.Parameter.IsCaptured) {
471 AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc);
475 // According to the specs, a variable is considered definitely assigned if you take
477 if ((variable != null) && (variable.VariableInfo != null)){
478 variable.VariableInfo.SetAssigned (ec);
481 type = TypeManager.GetPointerType (Expr.Type);
484 case Operator.Indirection:
490 if (!expr_type.IsPointer){
491 Error (193, "The * or -> operator must be applied to a pointer");
496 // We create an Indirection expression, because
497 // it can implement the IMemoryLocation.
499 return new Indirection (Expr, loc);
501 case Operator.UnaryPlus:
503 // A plus in front of something is just a no-op, so return the child.
507 case Operator.UnaryNegation:
509 // Deals with -literals
510 // int operator- (int x)
511 // long operator- (long x)
512 // float operator- (float f)
513 // double operator- (double d)
514 // decimal operator- (decimal d)
516 Expression expr = null;
519 // transform - - expr into expr
522 Unary unary = (Unary) Expr;
524 if (unary.Oper == Operator.UnaryNegation)
529 // perform numeric promotions to int,
533 // The following is inneficient, because we call
534 // ImplicitConversion too many times.
536 // It is also not clear if we should convert to Float
537 // or Double initially.
539 if (expr_type == TypeManager.uint32_type){
541 // FIXME: handle exception to this rule that
542 // permits the int value -2147483648 (-2^31) to
543 // bt wrote as a decimal interger literal
545 type = TypeManager.int64_type;
546 Expr = Convert.ImplicitConversion (ec, Expr, type, loc);
550 if (expr_type == TypeManager.uint64_type){
552 // FIXME: Handle exception of `long value'
553 // -92233720368547758087 (-2^63) to be wrote as
554 // decimal integer literal.
560 if (expr_type == TypeManager.float_type){
565 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int32_type, loc);
572 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int64_type, loc);
579 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.double_type, loc);
590 Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" +
591 TypeManager.CSharpName (expr_type) + "'");
595 public override Expression DoResolve (EmitContext ec)
597 if (Oper == Operator.AddressOf) {
598 Expr = Expr.DoResolveLValue (ec, new EmptyExpression ());
600 if (Expr == null || Expr.eclass != ExprClass.Variable){
601 Error (211, "Cannot take the address of the given expression");
606 Expr = Expr.Resolve (ec);
612 if (TypeManager.IsNullableValueType (Expr.Type))
613 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
616 eclass = ExprClass.Value;
617 return ResolveOperator (ec);
620 public override Expression DoResolveLValue (EmitContext ec, Expression right)
622 if (Oper == Operator.Indirection)
623 return DoResolve (ec);
628 public override void Emit (EmitContext ec)
630 ILGenerator ig = ec.ig;
633 case Operator.UnaryPlus:
634 throw new Exception ("This should be caught by Resolve");
636 case Operator.UnaryNegation:
637 if (ec.CheckState && type != TypeManager.float_type && type != TypeManager.double_type) {
638 ig.Emit (OpCodes.Ldc_I4_0);
639 if (type == TypeManager.int64_type)
640 ig.Emit (OpCodes.Conv_U8);
642 ig.Emit (OpCodes.Sub_Ovf);
645 ig.Emit (OpCodes.Neg);
650 case Operator.LogicalNot:
652 ig.Emit (OpCodes.Ldc_I4_0);
653 ig.Emit (OpCodes.Ceq);
656 case Operator.OnesComplement:
658 ig.Emit (OpCodes.Not);
661 case Operator.AddressOf:
662 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
666 throw new Exception ("This should not happen: Operator = "
671 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
673 if (Oper == Operator.LogicalNot)
674 Expr.EmitBranchable (ec, target, !onTrue);
676 base.EmitBranchable (ec, target, onTrue);
679 public override string ToString ()
681 return "Unary (" + Oper + ", " + Expr + ")";
687 // Unary operators are turned into Indirection expressions
688 // after semantic analysis (this is so we can take the address
689 // of an indirection).
691 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
693 LocalTemporary temporary;
696 public Indirection (Expression expr, Location l)
699 type = TypeManager.HasElementType (expr.Type) ? TypeManager.GetElementType (expr.Type) : expr.Type;
700 eclass = ExprClass.Variable;
704 public override void Emit (EmitContext ec)
709 LoadFromPtr (ec.ig, Type);
712 public void Emit (EmitContext ec, bool leave_copy)
716 ec.ig.Emit (OpCodes.Dup);
717 temporary = new LocalTemporary (expr.Type);
718 temporary.Store (ec);
722 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
724 prepared = prepare_for_load;
728 if (prepare_for_load)
729 ec.ig.Emit (OpCodes.Dup);
733 ec.ig.Emit (OpCodes.Dup);
734 temporary = new LocalTemporary (expr.Type);
735 temporary.Store (ec);
738 StoreFromPtr (ec.ig, type);
740 if (temporary != null) {
742 temporary.Release (ec);
746 public void AddressOf (EmitContext ec, AddressOp Mode)
751 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
753 return DoResolve (ec);
756 public override Expression DoResolve (EmitContext ec)
759 // Born fully resolved
764 public override string ToString ()
766 return "*(" + expr + ")";
769 #region IVariable Members
771 public VariableInfo VariableInfo {
775 public bool VerifyFixed ()
777 // A pointer-indirection is always fixed.
785 /// Unary Mutator expressions (pre and post ++ and --)
789 /// UnaryMutator implements ++ and -- expressions. It derives from
790 /// ExpressionStatement becuase the pre/post increment/decrement
791 /// operators can be used in a statement context.
793 /// FIXME: Idea, we could split this up in two classes, one simpler
794 /// for the common case, and one with the extra fields for more complex
795 /// classes (indexers require temporary access; overloaded require method)
798 public class UnaryMutator : ExpressionStatement {
800 public enum Mode : byte {
807 PreDecrement = IsDecrement,
808 PostIncrement = IsPost,
809 PostDecrement = IsPost | IsDecrement
813 bool is_expr = false;
814 bool recurse = false;
819 // This is expensive for the simplest case.
821 StaticCallExpr method;
823 public UnaryMutator (Mode m, Expression e, Location l)
830 static string OperName (Mode mode)
832 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
837 /// Returns whether an object of type `t' can be incremented
838 /// or decremented with add/sub (ie, basically whether we can
839 /// use pre-post incr-decr operations on it, but it is not a
840 /// System.Decimal, which we require operator overloading to catch)
842 static bool IsIncrementableNumber (Type t)
844 return (t == TypeManager.sbyte_type) ||
845 (t == TypeManager.byte_type) ||
846 (t == TypeManager.short_type) ||
847 (t == TypeManager.ushort_type) ||
848 (t == TypeManager.int32_type) ||
849 (t == TypeManager.uint32_type) ||
850 (t == TypeManager.int64_type) ||
851 (t == TypeManager.uint64_type) ||
852 (t == TypeManager.char_type) ||
853 (t.IsSubclassOf (TypeManager.enum_type)) ||
854 (t == TypeManager.float_type) ||
855 (t == TypeManager.double_type) ||
856 (t.IsPointer && t != TypeManager.void_ptr_type);
859 Expression ResolveOperator (EmitContext ec)
861 Type expr_type = expr.Type;
864 // Step 1: Perform Operator Overload location
869 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
870 op_name = "op_Increment";
872 op_name = "op_Decrement";
874 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
877 method = StaticCallExpr.MakeSimpleCall (
878 ec, (MethodGroupExpr) mg, expr, loc);
881 } else if (!IsIncrementableNumber (expr_type)) {
882 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
883 TypeManager.CSharpName (expr_type) + "'");
888 // The operand of the prefix/postfix increment decrement operators
889 // should be an expression that is classified as a variable,
890 // a property access or an indexer access
893 if (expr.eclass == ExprClass.Variable){
894 LocalVariableReference var = expr as LocalVariableReference;
895 if ((var != null) && var.IsReadOnly) {
896 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
899 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
900 expr = expr.ResolveLValue (ec, this, Location);
904 if (expr.eclass == ExprClass.Value) {
905 Error_ValueAssignment (loc);
907 expr.Error_UnexpectedKind (ec.DeclContainer, "variable, indexer or property access", loc);
915 public override Expression DoResolve (EmitContext ec)
917 expr = expr.Resolve (ec);
922 eclass = ExprClass.Value;
925 if (TypeManager.IsNullableValueType (expr.Type))
926 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
929 return ResolveOperator (ec);
932 static int PtrTypeSize (Type t)
934 return GetTypeSize (TypeManager.GetElementType (t));
938 // Loads the proper "1" into the stack based on the type, then it emits the
939 // opcode for the operation requested
941 void LoadOneAndEmitOp (EmitContext ec, Type t)
944 // Measure if getting the typecode and using that is more/less efficient
945 // that comparing types. t.GetTypeCode() is an internal call.
947 ILGenerator ig = ec.ig;
949 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
950 LongConstant.EmitLong (ig, 1);
951 else if (t == TypeManager.double_type)
952 ig.Emit (OpCodes.Ldc_R8, 1.0);
953 else if (t == TypeManager.float_type)
954 ig.Emit (OpCodes.Ldc_R4, 1.0F);
955 else if (t.IsPointer){
956 int n = PtrTypeSize (t);
959 ig.Emit (OpCodes.Sizeof, t);
961 IntConstant.EmitInt (ig, n);
963 ig.Emit (OpCodes.Ldc_I4_1);
966 // Now emit the operation
969 if (t == TypeManager.int32_type ||
970 t == TypeManager.int64_type){
971 if ((mode & Mode.IsDecrement) != 0)
972 ig.Emit (OpCodes.Sub_Ovf);
974 ig.Emit (OpCodes.Add_Ovf);
975 } else if (t == TypeManager.uint32_type ||
976 t == TypeManager.uint64_type){
977 if ((mode & Mode.IsDecrement) != 0)
978 ig.Emit (OpCodes.Sub_Ovf_Un);
980 ig.Emit (OpCodes.Add_Ovf_Un);
982 if ((mode & Mode.IsDecrement) != 0)
983 ig.Emit (OpCodes.Sub_Ovf);
985 ig.Emit (OpCodes.Add_Ovf);
988 if ((mode & Mode.IsDecrement) != 0)
989 ig.Emit (OpCodes.Sub);
991 ig.Emit (OpCodes.Add);
994 if (t == TypeManager.sbyte_type){
996 ig.Emit (OpCodes.Conv_Ovf_I1);
998 ig.Emit (OpCodes.Conv_I1);
999 } else if (t == TypeManager.byte_type){
1001 ig.Emit (OpCodes.Conv_Ovf_U1);
1003 ig.Emit (OpCodes.Conv_U1);
1004 } else if (t == TypeManager.short_type){
1006 ig.Emit (OpCodes.Conv_Ovf_I2);
1008 ig.Emit (OpCodes.Conv_I2);
1009 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
1011 ig.Emit (OpCodes.Conv_Ovf_U2);
1013 ig.Emit (OpCodes.Conv_U2);
1018 void EmitCode (EmitContext ec, bool is_expr)
1021 this.is_expr = is_expr;
1022 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1025 public override void Emit (EmitContext ec)
1028 // We use recurse to allow ourselfs to be the source
1029 // of an assignment. This little hack prevents us from
1030 // having to allocate another expression
1033 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1035 LoadOneAndEmitOp (ec, expr.Type);
1037 ec.ig.Emit (OpCodes.Call, method.Method);
1042 EmitCode (ec, true);
1045 public override void EmitStatement (EmitContext ec)
1047 EmitCode (ec, false);
1052 /// Base class for the `Is' and `As' classes.
1056 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1059 public abstract class Probe : Expression {
1060 public Expression ProbeType;
1061 protected Expression expr;
1062 protected TypeExpr probe_type_expr;
1064 public Probe (Expression expr, Expression probe_type, Location l)
1066 ProbeType = probe_type;
1071 public Expression Expr {
1077 public override Expression DoResolve (EmitContext ec)
1079 probe_type_expr = ProbeType.ResolveAsTypeTerminal (ec, false);
1080 if (probe_type_expr == null)
1083 expr = expr.Resolve (ec);
1087 if (expr.Type.IsPointer) {
1088 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1096 /// Implementation of the `is' operator.
1098 public class Is : Probe {
1099 public Is (Expression expr, Expression probe_type, Location l)
1100 : base (expr, probe_type, l)
1105 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1110 public override void Emit (EmitContext ec)
1112 ILGenerator ig = ec.ig;
1117 case Action.AlwaysFalse:
1118 ig.Emit (OpCodes.Pop);
1119 IntConstant.EmitInt (ig, 0);
1121 case Action.AlwaysTrue:
1122 ig.Emit (OpCodes.Pop);
1123 IntConstant.EmitInt (ig, 1);
1125 case Action.LeaveOnStack:
1126 // the `e != null' rule.
1127 ig.Emit (OpCodes.Ldnull);
1128 ig.Emit (OpCodes.Ceq);
1129 ig.Emit (OpCodes.Ldc_I4_0);
1130 ig.Emit (OpCodes.Ceq);
1133 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1134 ig.Emit (OpCodes.Ldnull);
1135 ig.Emit (OpCodes.Cgt_Un);
1138 throw new Exception ("never reached");
1141 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1143 ILGenerator ig = ec.ig;
1146 case Action.AlwaysFalse:
1148 ig.Emit (OpCodes.Br, target);
1151 case Action.AlwaysTrue:
1153 ig.Emit (OpCodes.Br, target);
1156 case Action.LeaveOnStack:
1157 // the `e != null' rule.
1159 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1163 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1164 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1167 throw new Exception ("never reached");
1170 public override Expression DoResolve (EmitContext ec)
1172 Expression e = base.DoResolve (ec);
1174 if ((e == null) || (expr == null))
1177 Type etype = expr.Type;
1178 type = TypeManager.bool_type;
1179 eclass = ExprClass.Value;
1182 // First case, if at compile time, there is an implicit conversion
1183 // then e != null (objects) or true (value types)
1185 Type probe_type = probe_type_expr.Type;
1186 e = Convert.ImplicitConversionStandard (ec, expr, probe_type, loc);
1189 if (etype.IsValueType)
1190 action = Action.AlwaysTrue;
1192 action = Action.LeaveOnStack;
1194 Constant c = e as Constant;
1195 if (c != null && c.GetValue () == null) {
1196 action = Action.AlwaysFalse;
1197 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
1198 TypeManager.CSharpName (probe_type));
1200 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
1201 TypeManager.CSharpName (probe_type));
1206 if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1207 if (TypeManager.IsGenericParameter (etype))
1208 expr = new BoxedCast (expr, etype);
1211 // Second case: explicit reference convresion
1213 if (expr is NullLiteral)
1214 action = Action.AlwaysFalse;
1216 action = Action.Probe;
1217 } else if (TypeManager.ContainsGenericParameters (etype) ||
1218 TypeManager.ContainsGenericParameters (probe_type)) {
1219 expr = new BoxedCast (expr, etype);
1220 action = Action.Probe;
1222 action = Action.AlwaysFalse;
1223 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1224 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1232 /// Implementation of the `as' operator.
1234 public class As : Probe {
1235 public As (Expression expr, Expression probe_type, Location l)
1236 : base (expr, probe_type, l)
1240 bool do_isinst = false;
1241 Expression resolved_type;
1243 public override void Emit (EmitContext ec)
1245 ILGenerator ig = ec.ig;
1250 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1253 static void Error_CannotConvertType (Type source, Type target, Location loc)
1255 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1256 TypeManager.CSharpName (source),
1257 TypeManager.CSharpName (target));
1260 public override Expression DoResolve (EmitContext ec)
1262 if (resolved_type == null) {
1263 resolved_type = base.DoResolve (ec);
1265 if (resolved_type == null)
1269 type = probe_type_expr.Type;
1270 eclass = ExprClass.Value;
1271 Type etype = expr.Type;
1273 if (type.IsValueType) {
1274 Report.Error (77, loc, "The as operator must be used with a reference type (`" +
1275 TypeManager.CSharpName (type) + "' is a value type)");
1282 // If the type is a type parameter, ensure
1283 // that it is constrained by a class
1285 TypeParameterExpr tpe = probe_type_expr as TypeParameterExpr;
1287 Constraints constraints = tpe.TypeParameter.Constraints;
1290 if (constraints == null)
1293 if (!constraints.HasClassConstraint)
1294 if ((constraints.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == 0)
1298 Report.Error (413, loc,
1299 "The as operator requires that the `{0}' type parameter be constrained by a class",
1300 probe_type_expr.GetSignatureForError ());
1306 Expression e = Convert.ImplicitConversion (ec, expr, type, loc);
1313 if (Convert.ExplicitReferenceConversionExists (etype, type)){
1314 if (TypeManager.IsGenericParameter (etype))
1315 expr = new BoxedCast (expr, etype);
1321 if (TypeManager.ContainsGenericParameters (etype) ||
1322 TypeManager.ContainsGenericParameters (type)) {
1323 expr = new BoxedCast (expr, etype);
1328 Error_CannotConvertType (etype, type, loc);
1332 public override bool GetAttributableValue (Type valueType, out object value)
1334 return expr.GetAttributableValue (valueType, out value);
1339 /// This represents a typecast in the source language.
1341 /// FIXME: Cast expressions have an unusual set of parsing
1342 /// rules, we need to figure those out.
1344 public class Cast : Expression {
1345 Expression target_type;
1348 public Cast (Expression cast_type, Expression expr)
1349 : this (cast_type, expr, cast_type.Location)
1353 public Cast (Expression cast_type, Expression expr, Location loc)
1355 this.target_type = cast_type;
1359 if (target_type == TypeManager.system_void_expr)
1360 Error_VoidInvalidInTheContext (loc);
1363 public Expression TargetType {
1364 get { return target_type; }
1367 public Expression Expr {
1368 get { return expr; }
1369 set { expr = value; }
1372 public override Expression DoResolve (EmitContext ec)
1374 expr = expr.Resolve (ec);
1378 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1384 if (type.IsAbstract && type.IsSealed) {
1385 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1389 eclass = ExprClass.Value;
1391 Constant c = expr as Constant;
1394 c = c.TryReduce (ec, type, loc);
1398 catch (OverflowException) {
1403 if (type.IsPointer && !ec.InUnsafe) {
1407 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1411 public override void Emit (EmitContext ec)
1413 throw new Exception ("Should not happen");
1418 /// Binary operators
1420 public class Binary : Expression {
1421 public enum Operator : byte {
1422 Multiply, Division, Modulus,
1423 Addition, Subtraction,
1424 LeftShift, RightShift,
1425 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1426 Equality, Inequality,
1436 Expression left, right;
1438 // This must be kept in sync with Operator!!!
1439 public static readonly string [] oper_names;
1443 oper_names = new string [(int) Operator.TOP];
1445 oper_names [(int) Operator.Multiply] = "op_Multiply";
1446 oper_names [(int) Operator.Division] = "op_Division";
1447 oper_names [(int) Operator.Modulus] = "op_Modulus";
1448 oper_names [(int) Operator.Addition] = "op_Addition";
1449 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1450 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1451 oper_names [(int) Operator.RightShift] = "op_RightShift";
1452 oper_names [(int) Operator.LessThan] = "op_LessThan";
1453 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1454 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1455 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1456 oper_names [(int) Operator.Equality] = "op_Equality";
1457 oper_names [(int) Operator.Inequality] = "op_Inequality";
1458 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1459 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1460 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1461 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1462 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1465 public Binary (Operator oper, Expression left, Expression right)
1470 this.loc = left.Location;
1473 public Operator Oper {
1482 public Expression Left {
1491 public Expression Right {
1502 /// Returns a stringified representation of the Operator
1504 public static string OperName (Operator oper)
1507 case Operator.Multiply:
1509 case Operator.Division:
1511 case Operator.Modulus:
1513 case Operator.Addition:
1515 case Operator.Subtraction:
1517 case Operator.LeftShift:
1519 case Operator.RightShift:
1521 case Operator.LessThan:
1523 case Operator.GreaterThan:
1525 case Operator.LessThanOrEqual:
1527 case Operator.GreaterThanOrEqual:
1529 case Operator.Equality:
1531 case Operator.Inequality:
1533 case Operator.BitwiseAnd:
1535 case Operator.BitwiseOr:
1537 case Operator.ExclusiveOr:
1539 case Operator.LogicalOr:
1541 case Operator.LogicalAnd:
1545 return oper.ToString ();
1548 public override string ToString ()
1550 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1551 right.ToString () + ")";
1554 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1556 if (expr.Type == target_type)
1559 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1562 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1565 34, loc, "Operator `" + OperName (oper)
1566 + "' is ambiguous on operands of type `"
1567 + TypeManager.CSharpName (l) + "' "
1568 + "and `" + TypeManager.CSharpName (r)
1572 bool IsConvertible (EmitContext ec, Expression le, Expression re, Type t)
1574 return Convert.ImplicitConversionExists (ec, le, t) && Convert.ImplicitConversionExists (ec, re, t);
1577 bool VerifyApplicable_Predefined (EmitContext ec, Type t)
1579 if (!IsConvertible (ec, left, right, t))
1581 left = ForceConversion (ec, left, t);
1582 right = ForceConversion (ec, right, t);
1587 bool IsApplicable_String (EmitContext ec, Expression le, Expression re, Operator oper)
1589 bool l = Convert.ImplicitConversionExists (ec, le, TypeManager.string_type);
1590 bool r = Convert.ImplicitConversionExists (ec, re, TypeManager.string_type);
1592 if (oper == Operator.Equality || oper == Operator.Inequality)
1594 if (oper == Operator.Addition)
1599 bool OverloadResolve_PredefinedString (EmitContext ec, Operator oper)
1601 if (!IsApplicable_String (ec, left, right, oper))
1603 Type t = TypeManager.string_type;
1604 if (Convert.ImplicitConversionExists (ec, left, t))
1605 left = ForceConversion (ec, left, t);
1606 if (Convert.ImplicitConversionExists (ec, right, t))
1607 right = ForceConversion (ec, right, t);
1612 bool OverloadResolve_PredefinedIntegral (EmitContext ec)
1614 return VerifyApplicable_Predefined (ec, TypeManager.int32_type) ||
1615 VerifyApplicable_Predefined (ec, TypeManager.uint32_type) ||
1616 VerifyApplicable_Predefined (ec, TypeManager.int64_type) ||
1617 VerifyApplicable_Predefined (ec, TypeManager.uint64_type) ||
1621 bool OverloadResolve_PredefinedFloating (EmitContext ec)
1623 return VerifyApplicable_Predefined (ec, TypeManager.float_type) ||
1624 VerifyApplicable_Predefined (ec, TypeManager.double_type) ||
1628 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1630 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1633 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1635 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1639 void Error_OperatorCannotBeApplied ()
1641 Error_OperatorCannotBeApplied (Location, OperName (oper), TypeManager.CSharpName (left.Type),
1642 TypeManager.CSharpName(right.Type));
1645 static bool is_unsigned (Type t)
1647 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1648 t == TypeManager.short_type || t == TypeManager.byte_type);
1651 Expression Make32or64 (EmitContext ec, Expression e)
1655 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1656 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1658 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1661 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1664 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1667 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1673 Expression CheckShiftArguments (EmitContext ec)
1675 Expression new_left = Make32or64 (ec, left);
1676 Expression new_right = ForceConversion (ec, right, TypeManager.int32_type);
1677 if (new_left == null || new_right == null) {
1678 Error_OperatorCannotBeApplied ();
1681 type = new_left.Type;
1682 int shiftmask = (type == TypeManager.int32_type || type == TypeManager.uint32_type) ? 31 : 63;
1684 right = new Binary (Binary.Operator.BitwiseAnd, new_right, new IntConstant (shiftmask, loc)).DoResolve (ec);
1689 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1690 // i.e., not invoke op_Equality.
1692 static bool EqualsNullIsReferenceEquals (Type t)
1694 return t == TypeManager.object_type || t == TypeManager.string_type ||
1695 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1698 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1700 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1701 "Possible unintended reference comparison; to get a value comparison, " +
1702 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1705 Expression ResolveOperator (EmitContext ec)
1708 Type r = right.Type;
1710 if (oper == Operator.Equality || oper == Operator.Inequality){
1711 if (TypeManager.IsGenericParameter (l) && (right is NullLiteral)) {
1712 if (l.BaseType == TypeManager.value_type) {
1713 Error_OperatorCannotBeApplied ();
1717 left = new BoxedCast (left, TypeManager.object_type);
1718 Type = TypeManager.bool_type;
1722 if (TypeManager.IsGenericParameter (r) && (left is NullLiteral)) {
1723 if (r.BaseType == TypeManager.value_type) {
1724 Error_OperatorCannotBeApplied ();
1728 right = new BoxedCast (right, TypeManager.object_type);
1729 Type = TypeManager.bool_type;
1734 // Optimize out call to op_Equality in a few cases.
1736 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1737 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1738 Type = TypeManager.bool_type;
1743 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1744 Type = TypeManager.bool_type;
1750 // Do not perform operator overload resolution when both sides are
1753 Expression left_operators = null, right_operators = null;
1754 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))) {
1756 // Step 1: Perform Operator Overload location
1758 string op = oper_names [(int) oper];
1760 MethodGroupExpr union;
1761 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc);
1763 right_operators = MemberLookup (
1764 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc);
1765 union = Invocation.MakeUnionSet (left_operators, right_operators, loc);
1767 union = (MethodGroupExpr) left_operators;
1769 if (union != null) {
1770 ArrayList args = new ArrayList (2);
1771 args.Add (new Argument (left, Argument.AType.Expression));
1772 args.Add (new Argument (right, Argument.AType.Expression));
1774 MethodBase method = Invocation.OverloadResolve (ec, union, args, true, Location.Null);
1776 if (method != null) {
1777 MethodInfo mi = (MethodInfo) method;
1778 return new BinaryMethod (mi.ReturnType, method, args);
1784 // Step 0: String concatenation (because overloading will get this wrong)
1786 if (oper == Operator.Addition){
1788 // If any of the arguments is a string, cast to string
1791 // Simple constant folding
1792 if (left is StringConstant && right is StringConstant)
1793 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1795 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1797 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1798 Error_OperatorCannotBeApplied ();
1802 // try to fold it in on the left
1803 if (left is StringConcat) {
1806 // We have to test here for not-null, since we can be doubly-resolved
1807 // take care of not appending twice
1810 type = TypeManager.string_type;
1811 ((StringConcat) left).Append (ec, right);
1812 return left.Resolve (ec);
1818 // Otherwise, start a new concat expression
1819 return new StringConcat (ec, loc, left, right).Resolve (ec);
1823 // Transform a + ( - b) into a - b
1825 if (right is Unary){
1826 Unary right_unary = (Unary) right;
1828 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1829 oper = Operator.Subtraction;
1830 right = right_unary.Expr;
1836 if (oper == Operator.Equality || oper == Operator.Inequality){
1837 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1838 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1839 Error_OperatorCannotBeApplied ();
1843 type = TypeManager.bool_type;
1847 if (l.IsPointer || r.IsPointer) {
1848 if (l.IsPointer && r.IsPointer) {
1849 type = TypeManager.bool_type;
1853 if (l.IsPointer && r == TypeManager.null_type) {
1854 right = new EmptyCast (NullPointer.Null, l);
1855 type = TypeManager.bool_type;
1859 if (r.IsPointer && l == TypeManager.null_type) {
1860 left = new EmptyCast (NullPointer.Null, r);
1861 type = TypeManager.bool_type;
1867 if (l.IsGenericParameter && r.IsGenericParameter) {
1868 GenericConstraints l_gc, r_gc;
1870 l_gc = TypeManager.GetTypeParameterConstraints (l);
1871 r_gc = TypeManager.GetTypeParameterConstraints (r);
1873 if ((l_gc == null) || (r_gc == null) ||
1874 !(l_gc.HasReferenceTypeConstraint || l_gc.HasClassConstraint) ||
1875 !(r_gc.HasReferenceTypeConstraint || r_gc.HasClassConstraint)) {
1876 Error_OperatorCannotBeApplied ();
1884 // operator != (object a, object b)
1885 // operator == (object a, object b)
1887 // For this to be used, both arguments have to be reference-types.
1888 // Read the rationale on the spec (14.9.6)
1890 if (!(l.IsValueType || r.IsValueType)){
1891 type = TypeManager.bool_type;
1897 // Also, a standard conversion must exist from either one
1899 bool left_to_right =
1900 Convert.ImplicitStandardConversionExists (left, r);
1901 bool right_to_left = !left_to_right &&
1902 Convert.ImplicitStandardConversionExists (right, l);
1904 if (!left_to_right && !right_to_left) {
1905 Error_OperatorCannotBeApplied ();
1909 if (left_to_right && left_operators != null &&
1910 RootContext.WarningLevel >= 2) {
1911 ArrayList args = new ArrayList (2);
1912 args.Add (new Argument (left, Argument.AType.Expression));
1913 args.Add (new Argument (left, Argument.AType.Expression));
1914 MethodBase method = Invocation.OverloadResolve (
1915 ec, (MethodGroupExpr) left_operators, args, true, Location.Null);
1917 Warning_UnintendedReferenceComparison (loc, "right", l);
1920 if (right_to_left && right_operators != null &&
1921 RootContext.WarningLevel >= 2) {
1922 ArrayList args = new ArrayList (2);
1923 args.Add (new Argument (right, Argument.AType.Expression));
1924 args.Add (new Argument (right, Argument.AType.Expression));
1925 MethodBase method = Invocation.OverloadResolve (
1926 ec, (MethodGroupExpr) right_operators, args, true, Location.Null);
1928 Warning_UnintendedReferenceComparison (loc, "left", r);
1932 // We are going to have to convert to an object to compare
1934 if (l != TypeManager.object_type)
1935 left = new EmptyCast (left, TypeManager.object_type);
1936 if (r != TypeManager.object_type)
1937 right = new EmptyCast (right, TypeManager.object_type);
1943 // Only perform numeric promotions on:
1944 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
1946 if (oper == Operator.Addition || oper == Operator.Subtraction) {
1947 if (TypeManager.IsDelegateType (l)){
1948 if (((right.eclass == ExprClass.MethodGroup) ||
1949 (r == TypeManager.anonymous_method_type))){
1950 if ((RootContext.Version != LanguageVersion.ISO_1)){
1951 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
1959 if (TypeManager.IsDelegateType (r) || right is NullLiteral){
1961 ArrayList args = new ArrayList (2);
1963 args = new ArrayList (2);
1964 args.Add (new Argument (left, Argument.AType.Expression));
1965 args.Add (new Argument (right, Argument.AType.Expression));
1967 if (oper == Operator.Addition)
1968 method = TypeManager.delegate_combine_delegate_delegate;
1970 method = TypeManager.delegate_remove_delegate_delegate;
1972 if (!TypeManager.IsEqual (l, r) && !(right is NullLiteral)) {
1973 Error_OperatorCannotBeApplied ();
1977 return new BinaryDelegate (l, method, args);
1982 // Pointer arithmetic:
1984 // T* operator + (T* x, int y);
1985 // T* operator + (T* x, uint y);
1986 // T* operator + (T* x, long y);
1987 // T* operator + (T* x, ulong y);
1989 // T* operator + (int y, T* x);
1990 // T* operator + (uint y, T *x);
1991 // T* operator + (long y, T *x);
1992 // T* operator + (ulong y, T *x);
1994 // T* operator - (T* x, int y);
1995 // T* operator - (T* x, uint y);
1996 // T* operator - (T* x, long y);
1997 // T* operator - (T* x, ulong y);
1999 // long operator - (T* x, T *y)
2002 if (r.IsPointer && oper == Operator.Subtraction){
2004 return new PointerArithmetic (
2005 false, left, right, TypeManager.int64_type,
2008 Expression t = Make32or64 (ec, right);
2010 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
2012 } else if (r.IsPointer && oper == Operator.Addition){
2013 Expression t = Make32or64 (ec, left);
2015 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
2020 // Enumeration operators
2022 bool lie = TypeManager.IsEnumType (l);
2023 bool rie = TypeManager.IsEnumType (r);
2027 // U operator - (E e, E f)
2029 if (oper == Operator.Subtraction){
2031 type = TypeManager.EnumToUnderlying (l);
2034 Error_OperatorCannotBeApplied ();
2040 // operator + (E e, U x)
2041 // operator - (E e, U x)
2043 if (oper == Operator.Addition || oper == Operator.Subtraction){
2044 Type enum_type = lie ? l : r;
2045 Type other_type = lie ? r : l;
2046 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2048 if (underlying_type != other_type){
2049 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2059 Error_OperatorCannotBeApplied ();
2068 temp = Convert.ImplicitConversion (ec, right, l, loc);
2072 Error_OperatorCannotBeApplied ();
2076 temp = Convert.ImplicitConversion (ec, left, r, loc);
2081 Error_OperatorCannotBeApplied ();
2086 if (oper == Operator.Equality || oper == Operator.Inequality ||
2087 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2088 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2089 if (left.Type != right.Type){
2090 Error_OperatorCannotBeApplied ();
2093 type = TypeManager.bool_type;
2097 if (oper == Operator.BitwiseAnd ||
2098 oper == Operator.BitwiseOr ||
2099 oper == Operator.ExclusiveOr){
2100 if (left.Type != right.Type){
2101 Error_OperatorCannotBeApplied ();
2107 Error_OperatorCannotBeApplied ();
2111 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2112 return CheckShiftArguments (ec);
2114 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2115 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2116 type = TypeManager.bool_type;
2121 Error_OperatorCannotBeApplied ();
2125 Expression e = new ConditionalLogicalOperator (
2126 oper == Operator.LogicalAnd, left, right, l, loc);
2127 return e.Resolve (ec);
2130 Expression orig_left = left;
2131 Expression orig_right = right;
2134 // operator & (bool x, bool y)
2135 // operator | (bool x, bool y)
2136 // operator ^ (bool x, bool y)
2138 if (oper == Operator.BitwiseAnd ||
2139 oper == Operator.BitwiseOr ||
2140 oper == Operator.ExclusiveOr) {
2141 if (OverloadResolve_PredefinedIntegral (ec)) {
2142 if (IsConvertible (ec, orig_left, orig_right, TypeManager.bool_type)) {
2143 Error_OperatorAmbiguous (loc, oper, l, r);
2147 if (oper == Operator.BitwiseOr && l != r && !(orig_right is Constant) && right is OpcodeCast &&
2148 (r == TypeManager.sbyte_type || r == TypeManager.short_type ||
2149 r == TypeManager.int32_type || r == TypeManager.int64_type)) {
2150 Report.Warning (675, 3, loc, "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first",
2151 TypeManager.CSharpName (r));
2154 } else if (!VerifyApplicable_Predefined (ec, TypeManager.bool_type)) {
2155 Error_OperatorCannotBeApplied ();
2162 // Pointer comparison
2164 if (l.IsPointer && r.IsPointer){
2165 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2166 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2167 type = TypeManager.bool_type;
2172 if (OverloadResolve_PredefinedIntegral (ec)) {
2173 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2174 Error_OperatorAmbiguous (loc, oper, l, r);
2177 } else if (OverloadResolve_PredefinedFloating (ec)) {
2178 if (IsConvertible (ec, orig_left, orig_right, TypeManager.decimal_type) ||
2179 IsApplicable_String (ec, orig_left, orig_right, oper)) {
2180 Error_OperatorAmbiguous (loc, oper, l, r);
2183 } else if (VerifyApplicable_Predefined (ec, TypeManager.decimal_type)) {
2184 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2185 Error_OperatorAmbiguous (loc, oper, l, r);
2188 } else if (!OverloadResolve_PredefinedString (ec, oper)) {
2189 Error_OperatorCannotBeApplied ();
2193 if (oper == Operator.Equality ||
2194 oper == Operator.Inequality ||
2195 oper == Operator.LessThanOrEqual ||
2196 oper == Operator.LessThan ||
2197 oper == Operator.GreaterThanOrEqual ||
2198 oper == Operator.GreaterThan)
2199 type = TypeManager.bool_type;
2204 if (l == TypeManager.decimal_type || l == TypeManager.string_type || r == TypeManager.string_type) {
2206 if (r == TypeManager.string_type)
2208 MethodGroupExpr ops = (MethodGroupExpr) MemberLookup (
2209 ec.ContainerType, lookup, oper_names [(int) oper],
2210 MemberTypes.Method, AllBindingFlags, loc);
2211 ArrayList args = new ArrayList (2);
2212 args.Add (new Argument (left, Argument.AType.Expression));
2213 args.Add (new Argument (right, Argument.AType.Expression));
2214 MethodBase method = Invocation.OverloadResolve (ec, ops, args, true, Location.Null);
2215 return new BinaryMethod (type, method, args);
2221 Constant EnumLiftUp (Constant left, Constant right)
2224 case Operator.BitwiseOr:
2225 case Operator.BitwiseAnd:
2226 case Operator.ExclusiveOr:
2227 case Operator.Equality:
2228 case Operator.Inequality:
2229 case Operator.LessThan:
2230 case Operator.LessThanOrEqual:
2231 case Operator.GreaterThan:
2232 case Operator.GreaterThanOrEqual:
2233 if (left is EnumConstant)
2236 if (left.IsZeroInteger)
2237 return new EnumConstant (left, right.Type);
2241 case Operator.Addition:
2242 case Operator.Subtraction:
2245 case Operator.Multiply:
2246 case Operator.Division:
2247 case Operator.Modulus:
2248 case Operator.LeftShift:
2249 case Operator.RightShift:
2250 if (right is EnumConstant || left is EnumConstant)
2254 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2258 public override Expression DoResolve (EmitContext ec)
2263 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2264 left = ((ParenthesizedExpression) left).Expr;
2265 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2269 if (left.eclass == ExprClass.Type) {
2270 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2274 left = left.Resolve (ec);
2279 Constant lc = left as Constant;
2280 if (lc != null && lc.Type == TypeManager.bool_type &&
2281 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2282 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2284 // TODO: make a sense to resolve unreachable expression as we do for statement
2285 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2289 right = right.Resolve (ec);
2293 eclass = ExprClass.Value;
2294 Constant rc = right as Constant;
2296 // The conversion rules are ignored in enum context but why
2297 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2298 left = lc = EnumLiftUp (lc, rc);
2302 right = rc = EnumLiftUp (rc, lc);
2307 if (oper == Operator.BitwiseAnd) {
2308 if (rc != null && rc.IsZeroInteger) {
2309 return lc is EnumConstant ?
2310 new EnumConstant (rc, lc.Type):
2314 if (lc != null && lc.IsZeroInteger) {
2315 return rc is EnumConstant ?
2316 new EnumConstant (lc, rc.Type):
2320 else if (oper == Operator.BitwiseOr) {
2321 if (lc is EnumConstant &&
2322 rc != null && rc.IsZeroInteger)
2324 if (rc is EnumConstant &&
2325 lc != null && lc.IsZeroInteger)
2327 } else if (oper == Operator.LogicalAnd) {
2328 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2330 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2334 if (rc != null && lc != null){
2335 int prev_e = Report.Errors;
2336 Expression e = ConstantFold.BinaryFold (
2337 ec, oper, lc, rc, loc);
2338 if (e != null || Report.Errors != prev_e)
2343 if ((left is NullLiteral || left.Type.IsValueType) &&
2344 (right is NullLiteral || right.Type.IsValueType) &&
2345 !(left is NullLiteral && right is NullLiteral) &&
2346 (TypeManager.IsNullableType (left.Type) || TypeManager.IsNullableType (right.Type)))
2347 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2350 // Comparison warnings
2351 if (oper == Operator.Equality || oper == Operator.Inequality ||
2352 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2353 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2354 if (left.Equals (right)) {
2355 Report.Warning (1718, 3, loc, "Comparison made to same variable; did you mean to compare something else?");
2357 CheckUselessComparison (lc, right.Type);
2358 CheckUselessComparison (rc, left.Type);
2361 return ResolveOperator (ec);
2364 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2369 private void CheckUselessComparison (Constant c, Type type)
2371 if (c == null || !IsTypeIntegral (type)
2372 || c is StringConstant
2373 || c is BoolConstant
2374 || c is CharConstant
2375 || c is FloatConstant
2376 || c is DoubleConstant
2377 || c is DecimalConstant
2383 if (c is ULongConstant) {
2384 ulong uvalue = ((ULongConstant) c).Value;
2385 if (uvalue > long.MaxValue) {
2386 if (type == TypeManager.byte_type ||
2387 type == TypeManager.sbyte_type ||
2388 type == TypeManager.short_type ||
2389 type == TypeManager.ushort_type ||
2390 type == TypeManager.int32_type ||
2391 type == TypeManager.uint32_type ||
2392 type == TypeManager.int64_type)
2393 WarnUselessComparison (type);
2396 value = (long) uvalue;
2398 else if (c is ByteConstant)
2399 value = ((ByteConstant) c).Value;
2400 else if (c is SByteConstant)
2401 value = ((SByteConstant) c).Value;
2402 else if (c is ShortConstant)
2403 value = ((ShortConstant) c).Value;
2404 else if (c is UShortConstant)
2405 value = ((UShortConstant) c).Value;
2406 else if (c is IntConstant)
2407 value = ((IntConstant) c).Value;
2408 else if (c is UIntConstant)
2409 value = ((UIntConstant) c).Value;
2410 else if (c is LongConstant)
2411 value = ((LongConstant) c).Value;
2414 if (IsValueOutOfRange (value, type))
2415 WarnUselessComparison (type);
2420 private bool IsValueOutOfRange (long value, Type type)
2422 if (IsTypeUnsigned (type) && value < 0)
2424 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2425 type == TypeManager.byte_type && value >= 0x100 ||
2426 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2427 type == TypeManager.ushort_type && value >= 0x10000 ||
2428 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2429 type == TypeManager.uint32_type && value >= 0x100000000;
2432 private static bool IsTypeIntegral (Type type)
2434 return type == TypeManager.uint64_type ||
2435 type == TypeManager.int64_type ||
2436 type == TypeManager.uint32_type ||
2437 type == TypeManager.int32_type ||
2438 type == TypeManager.ushort_type ||
2439 type == TypeManager.short_type ||
2440 type == TypeManager.sbyte_type ||
2441 type == TypeManager.byte_type;
2444 private static bool IsTypeUnsigned (Type type)
2446 return type == TypeManager.uint64_type ||
2447 type == TypeManager.uint32_type ||
2448 type == TypeManager.ushort_type ||
2449 type == TypeManager.byte_type;
2452 private void WarnUselessComparison (Type type)
2454 Report.Warning (652, 2, loc, "Comparison to integral constant is useless; the constant is outside the range of type `{0}'",
2455 TypeManager.CSharpName (type));
2459 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2460 /// context of a conditional bool expression. This function will return
2461 /// false if it is was possible to use EmitBranchable, or true if it was.
2463 /// The expression's code is generated, and we will generate a branch to `target'
2464 /// if the resulting expression value is equal to isTrue
2466 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2468 ILGenerator ig = ec.ig;
2471 // This is more complicated than it looks, but its just to avoid
2472 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2473 // but on top of that we want for == and != to use a special path
2474 // if we are comparing against null
2476 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2477 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2480 // put the constant on the rhs, for simplicity
2482 if (left is Constant) {
2483 Expression swap = right;
2488 if (((Constant) right).IsZeroInteger) {
2491 ig.Emit (OpCodes.Brtrue, target);
2493 ig.Emit (OpCodes.Brfalse, target);
2496 } else if (right is BoolConstant) {
2498 if (my_on_true != ((BoolConstant) right).Value)
2499 ig.Emit (OpCodes.Brtrue, target);
2501 ig.Emit (OpCodes.Brfalse, target);
2506 } else if (oper == Operator.LogicalAnd) {
2509 Label tests_end = ig.DefineLabel ();
2511 left.EmitBranchable (ec, tests_end, false);
2512 right.EmitBranchable (ec, target, true);
2513 ig.MarkLabel (tests_end);
2515 left.EmitBranchable (ec, target, false);
2516 right.EmitBranchable (ec, target, false);
2521 } else if (oper == Operator.LogicalOr){
2523 left.EmitBranchable (ec, target, true);
2524 right.EmitBranchable (ec, target, true);
2527 Label tests_end = ig.DefineLabel ();
2528 left.EmitBranchable (ec, tests_end, true);
2529 right.EmitBranchable (ec, target, false);
2530 ig.MarkLabel (tests_end);
2535 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2536 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2537 oper == Operator.Equality || oper == Operator.Inequality)) {
2538 base.EmitBranchable (ec, target, onTrue);
2546 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2549 case Operator.Equality:
2551 ig.Emit (OpCodes.Beq, target);
2553 ig.Emit (OpCodes.Bne_Un, target);
2556 case Operator.Inequality:
2558 ig.Emit (OpCodes.Bne_Un, target);
2560 ig.Emit (OpCodes.Beq, target);
2563 case Operator.LessThan:
2566 ig.Emit (OpCodes.Blt_Un, target);
2568 ig.Emit (OpCodes.Blt, target);
2571 ig.Emit (OpCodes.Bge_Un, target);
2573 ig.Emit (OpCodes.Bge, target);
2576 case Operator.GreaterThan:
2579 ig.Emit (OpCodes.Bgt_Un, target);
2581 ig.Emit (OpCodes.Bgt, target);
2584 ig.Emit (OpCodes.Ble_Un, target);
2586 ig.Emit (OpCodes.Ble, target);
2589 case Operator.LessThanOrEqual:
2592 ig.Emit (OpCodes.Ble_Un, target);
2594 ig.Emit (OpCodes.Ble, target);
2597 ig.Emit (OpCodes.Bgt_Un, target);
2599 ig.Emit (OpCodes.Bgt, target);
2603 case Operator.GreaterThanOrEqual:
2606 ig.Emit (OpCodes.Bge_Un, target);
2608 ig.Emit (OpCodes.Bge, target);
2611 ig.Emit (OpCodes.Blt_Un, target);
2613 ig.Emit (OpCodes.Blt, target);
2616 Console.WriteLine (oper);
2617 throw new Exception ("what is THAT");
2621 public override void Emit (EmitContext ec)
2623 ILGenerator ig = ec.ig;
2628 // Handle short-circuit operators differently
2631 if (oper == Operator.LogicalAnd) {
2632 Label load_zero = ig.DefineLabel ();
2633 Label end = ig.DefineLabel ();
2635 left.EmitBranchable (ec, load_zero, false);
2637 ig.Emit (OpCodes.Br, end);
2639 ig.MarkLabel (load_zero);
2640 ig.Emit (OpCodes.Ldc_I4_0);
2643 } else if (oper == Operator.LogicalOr) {
2644 Label load_one = ig.DefineLabel ();
2645 Label end = ig.DefineLabel ();
2647 left.EmitBranchable (ec, load_one, true);
2649 ig.Emit (OpCodes.Br, end);
2651 ig.MarkLabel (load_one);
2652 ig.Emit (OpCodes.Ldc_I4_1);
2660 bool isUnsigned = is_unsigned (left.Type);
2663 case Operator.Multiply:
2665 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2666 opcode = OpCodes.Mul_Ovf;
2667 else if (isUnsigned)
2668 opcode = OpCodes.Mul_Ovf_Un;
2670 opcode = OpCodes.Mul;
2672 opcode = OpCodes.Mul;
2676 case Operator.Division:
2678 opcode = OpCodes.Div_Un;
2680 opcode = OpCodes.Div;
2683 case Operator.Modulus:
2685 opcode = OpCodes.Rem_Un;
2687 opcode = OpCodes.Rem;
2690 case Operator.Addition:
2692 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2693 opcode = OpCodes.Add_Ovf;
2694 else if (isUnsigned)
2695 opcode = OpCodes.Add_Ovf_Un;
2697 opcode = OpCodes.Add;
2699 opcode = OpCodes.Add;
2702 case Operator.Subtraction:
2704 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2705 opcode = OpCodes.Sub_Ovf;
2706 else if (isUnsigned)
2707 opcode = OpCodes.Sub_Ovf_Un;
2709 opcode = OpCodes.Sub;
2711 opcode = OpCodes.Sub;
2714 case Operator.RightShift:
2716 opcode = OpCodes.Shr_Un;
2718 opcode = OpCodes.Shr;
2721 case Operator.LeftShift:
2722 opcode = OpCodes.Shl;
2725 case Operator.Equality:
2726 opcode = OpCodes.Ceq;
2729 case Operator.Inequality:
2730 ig.Emit (OpCodes.Ceq);
2731 ig.Emit (OpCodes.Ldc_I4_0);
2733 opcode = OpCodes.Ceq;
2736 case Operator.LessThan:
2738 opcode = OpCodes.Clt_Un;
2740 opcode = OpCodes.Clt;
2743 case Operator.GreaterThan:
2745 opcode = OpCodes.Cgt_Un;
2747 opcode = OpCodes.Cgt;
2750 case Operator.LessThanOrEqual:
2751 Type lt = left.Type;
2753 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2754 ig.Emit (OpCodes.Cgt_Un);
2756 ig.Emit (OpCodes.Cgt);
2757 ig.Emit (OpCodes.Ldc_I4_0);
2759 opcode = OpCodes.Ceq;
2762 case Operator.GreaterThanOrEqual:
2763 Type le = left.Type;
2765 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2766 ig.Emit (OpCodes.Clt_Un);
2768 ig.Emit (OpCodes.Clt);
2770 ig.Emit (OpCodes.Ldc_I4_0);
2772 opcode = OpCodes.Ceq;
2775 case Operator.BitwiseOr:
2776 opcode = OpCodes.Or;
2779 case Operator.BitwiseAnd:
2780 opcode = OpCodes.And;
2783 case Operator.ExclusiveOr:
2784 opcode = OpCodes.Xor;
2788 throw new Exception ("This should not happen: Operator = "
2789 + oper.ToString ());
2797 // Object created by Binary when the binary operator uses an method instead of being
2798 // a binary operation that maps to a CIL binary operation.
2800 public class BinaryMethod : Expression {
2801 public MethodBase method;
2802 public ArrayList Arguments;
2804 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2809 eclass = ExprClass.Value;
2812 public override Expression DoResolve (EmitContext ec)
2817 public override void Emit (EmitContext ec)
2819 ILGenerator ig = ec.ig;
2821 if (Arguments != null)
2822 Invocation.EmitArguments (ec, method, Arguments, false, null);
2824 if (method is MethodInfo)
2825 ig.Emit (OpCodes.Call, (MethodInfo) method);
2827 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2832 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2833 // b, c, d... may be strings or objects.
2835 public class StringConcat : Expression {
2837 bool invalid = false;
2838 bool emit_conv_done = false;
2840 // Are we also concating objects?
2842 bool is_strings_only = true;
2844 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2847 type = TypeManager.string_type;
2848 eclass = ExprClass.Value;
2850 operands = new ArrayList (2);
2855 public override Expression DoResolve (EmitContext ec)
2863 public void Append (EmitContext ec, Expression operand)
2868 StringConstant sc = operand as StringConstant;
2870 // TODO: it will be better to do this silently as an optimalization
2872 // string s = "" + i;
2873 // because this code has poor performace
2874 // if (sc.Value.Length == 0)
2875 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2877 if (operands.Count != 0) {
2878 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2879 if (last_operand != null) {
2880 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2887 // Conversion to object
2889 if (operand.Type != TypeManager.string_type) {
2890 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2893 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2899 operands.Add (operand);
2902 public override void Emit (EmitContext ec)
2904 MethodInfo concat_method = null;
2907 // Do conversion to arguments; check for strings only
2910 // This can get called multiple times, so we have to deal with that.
2911 if (!emit_conv_done) {
2912 emit_conv_done = true;
2913 for (int i = 0; i < operands.Count; i ++) {
2914 Expression e = (Expression) operands [i];
2915 is_strings_only &= e.Type == TypeManager.string_type;
2918 for (int i = 0; i < operands.Count; i ++) {
2919 Expression e = (Expression) operands [i];
2921 if (! is_strings_only && e.Type == TypeManager.string_type) {
2922 // need to make sure this is an object, because the EmitParams
2923 // method might look at the type of this expression, see it is a
2924 // string and emit a string [] when we want an object [];
2926 e = new EmptyCast (e, TypeManager.object_type);
2928 operands [i] = new Argument (e, Argument.AType.Expression);
2933 // Find the right method
2935 switch (operands.Count) {
2938 // This should not be possible, because simple constant folding
2939 // is taken care of in the Binary code.
2941 throw new Exception ("how did you get here?");
2944 concat_method = is_strings_only ?
2945 TypeManager.string_concat_string_string :
2946 TypeManager.string_concat_object_object ;
2949 concat_method = is_strings_only ?
2950 TypeManager.string_concat_string_string_string :
2951 TypeManager.string_concat_object_object_object ;
2955 // There is not a 4 param overlaod for object (the one that there is
2956 // is actually a varargs methods, and is only in corlib because it was
2957 // introduced there before.).
2959 if (!is_strings_only)
2962 concat_method = TypeManager.string_concat_string_string_string_string;
2965 concat_method = is_strings_only ?
2966 TypeManager.string_concat_string_dot_dot_dot :
2967 TypeManager.string_concat_object_dot_dot_dot ;
2971 Invocation.EmitArguments (ec, concat_method, operands, false, null);
2972 ec.ig.Emit (OpCodes.Call, concat_method);
2977 // Object created with +/= on delegates
2979 public class BinaryDelegate : Expression {
2983 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
2988 eclass = ExprClass.Value;
2991 public override Expression DoResolve (EmitContext ec)
2996 public override void Emit (EmitContext ec)
2998 ILGenerator ig = ec.ig;
3000 Invocation.EmitArguments (ec, method, args, false, null);
3002 ig.Emit (OpCodes.Call, (MethodInfo) method);
3003 ig.Emit (OpCodes.Castclass, type);
3006 public Expression Right {
3008 Argument arg = (Argument) args [1];
3013 public bool IsAddition {
3015 return method == TypeManager.delegate_combine_delegate_delegate;
3021 // User-defined conditional logical operator
3022 public class ConditionalLogicalOperator : Expression {
3023 Expression left, right;
3026 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
3029 eclass = ExprClass.Value;
3033 this.is_and = is_and;
3036 protected void Error19 ()
3038 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3041 protected void Error218 ()
3043 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3044 "declarations of operator true and operator false");
3047 Expression op_true, op_false, op;
3048 LocalTemporary left_temp;
3050 public override Expression DoResolve (EmitContext ec)
3053 Expression operator_group;
3055 operator_group = MethodLookup (ec, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
3056 if (operator_group == null) {
3061 left_temp = new LocalTemporary (type);
3063 ArrayList arguments = new ArrayList ();
3064 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3065 arguments.Add (new Argument (right, Argument.AType.Expression));
3066 method = Invocation.OverloadResolve (
3067 ec, (MethodGroupExpr) operator_group, arguments, false, loc)
3069 if (method == null) {
3074 if (method.ReturnType != type) {
3075 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3076 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3080 op = new StaticCallExpr (method, arguments, loc);
3082 op_true = GetOperatorTrue (ec, left_temp, loc);
3083 op_false = GetOperatorFalse (ec, left_temp, loc);
3084 if ((op_true == null) || (op_false == null)) {
3092 public override void Emit (EmitContext ec)
3094 ILGenerator ig = ec.ig;
3095 Label false_target = ig.DefineLabel ();
3096 Label end_target = ig.DefineLabel ();
3099 left_temp.Store (ec);
3101 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3102 left_temp.Emit (ec);
3103 ig.Emit (OpCodes.Br, end_target);
3104 ig.MarkLabel (false_target);
3106 ig.MarkLabel (end_target);
3108 // We release 'left_temp' here since 'op' may refer to it too
3109 left_temp.Release (ec);
3113 public class PointerArithmetic : Expression {
3114 Expression left, right;
3118 // We assume that `l' is always a pointer
3120 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3126 is_add = is_addition;
3129 public override Expression DoResolve (EmitContext ec)
3131 eclass = ExprClass.Variable;
3133 if (left.Type == TypeManager.void_ptr_type) {
3134 Error (242, "The operation in question is undefined on void pointers");
3141 public override void Emit (EmitContext ec)
3143 Type op_type = left.Type;
3144 ILGenerator ig = ec.ig;
3146 // It must be either array or fixed buffer
3147 Type element = TypeManager.HasElementType (op_type) ?
3148 element = TypeManager.GetElementType (op_type) :
3149 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3151 int size = GetTypeSize (element);
3152 Type rtype = right.Type;
3154 if (rtype.IsPointer){
3156 // handle (pointer - pointer)
3160 ig.Emit (OpCodes.Sub);
3164 ig.Emit (OpCodes.Sizeof, element);
3166 IntLiteral.EmitInt (ig, size);
3167 ig.Emit (OpCodes.Div);
3169 ig.Emit (OpCodes.Conv_I8);
3172 // handle + and - on (pointer op int)
3175 ig.Emit (OpCodes.Conv_I);
3177 Constant right_const = right as Constant;
3178 if (right_const != null && size != 0) {
3179 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3187 ig.Emit (OpCodes.Sizeof, element);
3189 IntLiteral.EmitInt (ig, size);
3190 if (rtype == TypeManager.int64_type)
3191 ig.Emit (OpCodes.Conv_I8);
3192 else if (rtype == TypeManager.uint64_type)
3193 ig.Emit (OpCodes.Conv_U8);
3194 ig.Emit (OpCodes.Mul);
3198 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3199 ig.Emit (OpCodes.Conv_I);
3202 ig.Emit (OpCodes.Add);
3204 ig.Emit (OpCodes.Sub);
3210 /// Implements the ternary conditional operator (?:)
3212 public class Conditional : Expression {
3213 Expression expr, trueExpr, falseExpr;
3215 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3218 this.trueExpr = trueExpr;
3219 this.falseExpr = falseExpr;
3220 this.loc = expr.Location;
3223 public Expression Expr {
3229 public Expression TrueExpr {
3235 public Expression FalseExpr {
3241 public override Expression DoResolve (EmitContext ec)
3243 expr = expr.Resolve (ec);
3249 if (TypeManager.IsNullableValueType (expr.Type))
3250 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
3253 if (expr.Type != TypeManager.bool_type){
3254 expr = Expression.ResolveBoolean (
3261 Assign ass = expr as Assign;
3262 if (ass != null && ass.Source is Constant) {
3263 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3266 trueExpr = trueExpr.Resolve (ec);
3267 falseExpr = falseExpr.Resolve (ec);
3269 if (trueExpr == null || falseExpr == null)
3272 eclass = ExprClass.Value;
3273 if (trueExpr.Type == falseExpr.Type) {
3274 type = trueExpr.Type;
3275 if (type == TypeManager.null_type) {
3276 // TODO: probably will have to implement ConditionalConstant
3277 // to call method without return constant as well
3278 Report.Warning (-101, 1, loc, "Conditional expression will always return same value");
3283 Type true_type = trueExpr.Type;
3284 Type false_type = falseExpr.Type;
3287 // First, if an implicit conversion exists from trueExpr
3288 // to falseExpr, then the result type is of type falseExpr.Type
3290 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3293 // Check if both can convert implicitl to each other's type
3295 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3297 "Can not compute type of conditional expression " +
3298 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3299 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3300 "' convert implicitly to each other");
3305 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3309 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3310 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3315 // Dead code optimalization
3316 if (expr is BoolConstant){
3317 BoolConstant bc = (BoolConstant) expr;
3319 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3320 return bc.Value ? trueExpr : falseExpr;
3326 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3331 public override void Emit (EmitContext ec)
3333 ILGenerator ig = ec.ig;
3334 Label false_target = ig.DefineLabel ();
3335 Label end_target = ig.DefineLabel ();
3337 expr.EmitBranchable (ec, false_target, false);
3339 ig.Emit (OpCodes.Br, end_target);
3340 ig.MarkLabel (false_target);
3341 falseExpr.Emit (ec);
3342 ig.MarkLabel (end_target);
3347 public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation {
3349 LocalTemporary temp;
3351 public abstract Variable Variable {
3355 public abstract bool IsRef {
3359 public override void Emit (EmitContext ec)
3365 // This method is used by parameters that are references, that are
3366 // being passed as references: we only want to pass the pointer (that
3367 // is already stored in the parameter, not the address of the pointer,
3368 // and not the value of the variable).
3370 public void EmitLoad (EmitContext ec)
3372 Report.Debug (64, "VARIABLE EMIT LOAD", this, Variable, type, loc);
3374 Variable.EmitInstance (ec);
3378 public void Emit (EmitContext ec, bool leave_copy)
3380 Report.Debug (64, "VARIABLE EMIT", this, Variable, type, IsRef, loc);
3386 ec.ig.Emit (OpCodes.Dup);
3389 // If we are a reference, we loaded on the stack a pointer
3390 // Now lets load the real value
3392 LoadFromPtr (ec.ig, type);
3396 ec.ig.Emit (OpCodes.Dup);
3398 if (IsRef || Variable.NeedsTemporary) {
3399 temp = new LocalTemporary (Type);
3405 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
3406 bool prepare_for_load)
3408 Report.Debug (64, "VARIABLE EMIT ASSIGN", this, Variable, type, IsRef,
3411 ILGenerator ig = ec.ig;
3412 prepared = prepare_for_load;
3414 Variable.EmitInstance (ec);
3415 if (prepare_for_load && Variable.HasInstance)
3416 ig.Emit (OpCodes.Dup);
3417 else if (IsRef && !prepared)
3423 ig.Emit (OpCodes.Dup);
3424 if (IsRef || Variable.NeedsTemporary) {
3425 temp = new LocalTemporary (Type);
3431 StoreFromPtr (ig, type);
3433 Variable.EmitAssign (ec);
3441 public void AddressOf (EmitContext ec, AddressOp mode)
3443 Variable.EmitInstance (ec);
3444 Variable.EmitAddressOf (ec);
3451 public class LocalVariableReference : VariableReference, IVariable {
3452 public readonly string Name;
3453 public readonly Block Block;
3454 public LocalInfo local_info;
3458 public LocalVariableReference (Block block, string name, Location l)
3463 eclass = ExprClass.Variable;
3467 // Setting `is_readonly' to false will allow you to create a writable
3468 // reference to a read-only variable. This is used by foreach and using.
3470 public LocalVariableReference (Block block, string name, Location l,
3471 LocalInfo local_info, bool is_readonly)
3472 : this (block, name, l)
3474 this.local_info = local_info;
3475 this.is_readonly = is_readonly;
3478 public VariableInfo VariableInfo {
3479 get { return local_info.VariableInfo; }
3482 public override bool IsRef {
3483 get { return false; }
3486 public bool IsReadOnly {
3487 get { return is_readonly; }
3490 public bool VerifyAssigned (EmitContext ec)
3492 VariableInfo variable_info = local_info.VariableInfo;
3493 return variable_info == null || variable_info.IsAssigned (ec, loc);
3496 void ResolveLocalInfo ()
3498 if (local_info == null) {
3499 local_info = Block.GetLocalInfo (Name);
3500 is_readonly = local_info.ReadOnly;
3504 protected Expression DoResolveBase (EmitContext ec)
3506 type = local_info.VariableType;
3508 Expression e = Block.GetConstantExpression (Name);
3510 return e.Resolve (ec);
3512 if (!VerifyAssigned (ec))
3516 // If we are referencing a variable from the external block
3517 // flag it for capturing
3519 if (ec.MustCaptureVariable (local_info)) {
3520 if (local_info.AddressTaken){
3521 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3525 ScopeInfo scope = local_info.Block.CreateScopeInfo ();
3526 variable = scope.AddLocal (local_info);
3527 type = variable.Type;
3533 public override Expression DoResolve (EmitContext ec)
3535 ResolveLocalInfo ();
3536 local_info.Used = true;
3537 return DoResolveBase (ec);
3540 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3542 ResolveLocalInfo ();
3547 if (right_side == EmptyExpression.OutAccess) {
3548 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3549 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3550 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3551 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3552 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3554 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
3556 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3561 if (right_side == EmptyExpression.OutAccess)
3562 local_info.Used = true;
3564 if (VariableInfo != null)
3565 VariableInfo.SetAssigned (ec);
3567 return DoResolveBase (ec);
3570 public bool VerifyFixed ()
3572 // A local Variable is always fixed.
3576 public override int GetHashCode ()
3578 return Name.GetHashCode ();
3581 public override bool Equals (object obj)
3583 LocalVariableReference lvr = obj as LocalVariableReference;
3587 return Name == lvr.Name && Block == lvr.Block;
3590 public override Variable Variable {
3591 get { return variable != null ? variable : local_info.Variable; }
3594 public override string ToString ()
3596 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3601 /// This represents a reference to a parameter in the intermediate
3604 public class ParameterReference : VariableReference, IVariable {
3610 public bool is_ref, is_out;
3618 public override bool IsRef {
3624 public string Name {
3630 public Parameter Parameter {
3638 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3641 this.name = par.Name;
3645 eclass = ExprClass.Variable;
3648 public VariableInfo VariableInfo {
3652 public override Variable Variable {
3653 get { return variable != null ? variable : par.Variable; }
3656 public bool VerifyFixed ()
3658 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3659 return par.ModFlags == Parameter.Modifier.NONE;
3662 public bool IsAssigned (EmitContext ec, Location loc)
3664 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3667 Report.Error (269, loc,
3668 "Use of unassigned out parameter `{0}'", par.Name);
3672 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3674 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3677 Report.Error (170, loc,
3678 "Use of possibly unassigned field `" + field_name + "'");
3682 public void SetAssigned (EmitContext ec)
3684 if (is_out && ec.DoFlowAnalysis)
3685 ec.CurrentBranching.SetAssigned (vi);
3688 public void SetFieldAssigned (EmitContext ec, string field_name)
3690 if (is_out && ec.DoFlowAnalysis)
3691 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3694 protected bool DoResolveBase (EmitContext ec)
3696 if (!par.Resolve (ec)) {
3700 type = par.ParameterType;
3701 Parameter.Modifier mod = par.ModFlags;
3702 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3703 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3704 eclass = ExprClass.Variable;
3707 vi = block.ParameterMap [idx];
3709 AnonymousContainer am = ec.CurrentAnonymousMethod;
3713 if (is_ref && !block.Toplevel.IsLocalParameter (name)){
3714 Report.Error (1628, Location,
3715 "Cannot use ref or out parameter `{0}' inside an " +
3716 "anonymous method block", par.Name);
3720 if (!am.IsIterator && block.Toplevel.IsLocalParameter (name))
3723 AnonymousMethodHost host = null;
3724 ToplevelBlock toplevel = block.Toplevel;
3725 while (toplevel != null) {
3726 if (toplevel.IsLocalParameter (name)) {
3727 host = toplevel.AnonymousMethodHost;
3731 toplevel = toplevel.Container;
3734 variable = host.AddParameter (par, idx);
3735 type = variable.Type;
3739 public override int GetHashCode()
3741 return name.GetHashCode ();
3744 public override bool Equals (object obj)
3746 ParameterReference pr = obj as ParameterReference;
3750 return name == pr.name && block == pr.block;
3754 // Notice that for ref/out parameters, the type exposed is not the
3755 // same type exposed externally.
3758 // externally we expose "int&"
3759 // here we expose "int".
3761 // We record this in "is_ref". This means that the type system can treat
3762 // the type as it is expected, but when we generate the code, we generate
3763 // the alternate kind of code.
3765 public override Expression DoResolve (EmitContext ec)
3767 if (!DoResolveBase (ec))
3770 if (is_out && ec.DoFlowAnalysis &&
3771 (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3777 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3779 if (!DoResolveBase (ec))
3787 static public void EmitLdArg (ILGenerator ig, int x)
3791 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3792 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3793 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3794 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3795 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3798 ig.Emit (OpCodes.Ldarg, x);
3801 public override string ToString ()
3803 return "ParameterReference[" + name + "]";
3808 /// Used for arguments to New(), Invocation()
3810 public class Argument {
3811 public enum AType : byte {
3818 public readonly AType ArgType;
3819 public Expression Expr;
3821 public Argument (Expression expr, AType type)
3824 this.ArgType = type;
3827 public Argument (Expression expr)
3830 this.ArgType = AType.Expression;
3835 if (ArgType == AType.Ref || ArgType == AType.Out)
3836 return TypeManager.GetReferenceType (Expr.Type);
3842 public Parameter.Modifier Modifier
3847 return Parameter.Modifier.OUT;
3850 return Parameter.Modifier.REF;
3853 return Parameter.Modifier.NONE;
3858 public static string FullDesc (Argument a)
3860 if (a.ArgType == AType.ArgList)
3863 return (a.ArgType == AType.Ref ? "ref " :
3864 (a.ArgType == AType.Out ? "out " : "")) +
3865 TypeManager.CSharpName (a.Expr.Type);
3868 public bool ResolveMethodGroup (EmitContext ec)
3870 SimpleName sn = Expr as SimpleName;
3872 Expr = sn.GetMethodGroup ();
3874 // FIXME: csc doesn't report any error if you try to use `ref' or
3875 // `out' in a delegate creation expression.
3876 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3883 public bool Resolve (EmitContext ec, Location loc)
3885 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3886 // Verify that the argument is readable
3887 if (ArgType != AType.Out)
3888 Expr = Expr.Resolve (ec);
3890 // Verify that the argument is writeable
3891 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3892 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3894 return Expr != null;
3898 public void Emit (EmitContext ec)
3900 if (ArgType != AType.Ref && ArgType != AType.Out) {
3905 AddressOp mode = AddressOp.Store;
3906 if (ArgType == AType.Ref)
3907 mode |= AddressOp.Load;
3909 IMemoryLocation ml = (IMemoryLocation) Expr;
3910 ParameterReference pr = ml as ParameterReference;
3913 // ParameterReferences might already be references, so we want
3914 // to pass just the value
3916 if (pr != null && pr.IsRef)
3919 ml.AddressOf (ec, mode);
3924 /// Invocation of methods or delegates.
3926 public class Invocation : ExpressionStatement {
3927 public readonly ArrayList Arguments;
3930 MethodBase method = null;
3933 // arguments is an ArrayList, but we do not want to typecast,
3934 // as it might be null.
3936 // FIXME: only allow expr to be a method invocation or a
3937 // delegate invocation (7.5.5)
3939 public Invocation (Expression expr, ArrayList arguments)
3942 Arguments = arguments;
3943 loc = expr.Location;
3946 public Expression Expr {
3953 /// Determines "better conversion" as specified in 14.4.2.3
3955 /// Returns : p if a->p is better,
3956 /// q if a->q is better,
3957 /// null if neither is better
3959 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q)
3961 Type argument_type = TypeManager.TypeToCoreType (a.Type);
3962 Expression argument_expr = a.Expr;
3964 if (argument_type == null)
3965 throw new Exception ("Expression of type " + a.Expr +
3966 " does not resolve its type");
3968 if (p == null || q == null)
3969 throw new InternalErrorException ("BetterConversion Got a null conversion");
3974 if (argument_expr is NullLiteral) {
3976 // If the argument is null and one of the types to compare is 'object' and
3977 // the other is a reference type, we prefer the other.
3979 // This follows from the usual rules:
3980 // * There is an implicit conversion from 'null' to type 'object'
3981 // * There is an implicit conversion from 'null' to any reference type
3982 // * There is an implicit conversion from any reference type to type 'object'
3983 // * There is no implicit conversion from type 'object' to other reference types
3984 // => Conversion of 'null' to a reference type is better than conversion to 'object'
3986 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
3987 // null type. I think it used to be 'object' and thus needed a special
3988 // case to avoid the immediately following two checks.
3990 if (!p.IsValueType && q == TypeManager.object_type)
3992 if (!q.IsValueType && p == TypeManager.object_type)
3996 if (argument_type == p)
3999 if (argument_type == q)
4002 Expression p_tmp = new EmptyExpression (p);
4003 Expression q_tmp = new EmptyExpression (q);
4005 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
4006 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
4008 if (p_to_q && !q_to_p)
4011 if (q_to_p && !p_to_q)
4014 if (p == TypeManager.sbyte_type)
4015 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
4016 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4018 if (q == TypeManager.sbyte_type)
4019 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
4020 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4023 if (p == TypeManager.short_type)
4024 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
4025 q == TypeManager.uint64_type)
4027 if (q == TypeManager.short_type)
4028 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
4029 p == TypeManager.uint64_type)
4032 if (p == TypeManager.int32_type)
4033 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4035 if (q == TypeManager.int32_type)
4036 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4039 if (p == TypeManager.int64_type)
4040 if (q == TypeManager.uint64_type)
4042 if (q == TypeManager.int64_type)
4043 if (p == TypeManager.uint64_type)
4049 static Type MoreSpecific (Type p, Type q)
4051 if (TypeManager.IsGenericParameter (p) && !TypeManager.IsGenericParameter (q))
4053 if (!TypeManager.IsGenericParameter (p) && TypeManager.IsGenericParameter (q))
4056 if (TypeManager.HasElementType (p)) {
4057 Type pe = TypeManager.GetElementType (p);
4058 Type qe = TypeManager.GetElementType (q);
4059 Type specific = MoreSpecific (pe, qe);
4064 } else if (TypeManager.IsGenericType (p)) {
4065 Type[] pargs = TypeManager.GetTypeArguments (p);
4066 Type[] qargs = TypeManager.GetTypeArguments (q);
4068 bool p_specific_at_least_once = false;
4069 bool q_specific_at_least_once = false;
4071 for (int i = 0; i < pargs.Length; i++) {
4072 Type specific = MoreSpecific (pargs [i], qargs [i]);
4073 if (specific == pargs [i])
4074 p_specific_at_least_once = true;
4075 if (specific == qargs [i])
4076 q_specific_at_least_once = true;
4079 if (p_specific_at_least_once && !q_specific_at_least_once)
4081 if (!p_specific_at_least_once && q_specific_at_least_once)
4089 /// Determines "Better function" between candidate
4090 /// and the current best match
4093 /// Returns a boolean indicating :
4094 /// false if candidate ain't better
4095 /// true if candidate is better than the current best match
4097 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4098 MethodBase candidate, bool candidate_params,
4099 MethodBase best, bool best_params)
4101 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4102 ParameterData best_pd = TypeManager.GetParameterData (best);
4104 bool better_at_least_one = false;
4106 for (int j = 0; j < argument_count; ++j) {
4107 Argument a = (Argument) args [j];
4109 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
4110 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
4112 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4113 if (candidate_params)
4114 ct = TypeManager.GetElementType (ct);
4116 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4118 bt = TypeManager.GetElementType (bt);
4124 Type better = BetterConversion (ec, a, ct, bt);
4126 // for each argument, the conversion to 'ct' should be no worse than
4127 // the conversion to 'bt'.
4131 // for at least one argument, the conversion to 'ct' should be better than
4132 // the conversion to 'bt'.
4134 better_at_least_one = true;
4137 if (better_at_least_one)
4141 // This handles the case
4143 // Add (float f1, float f2, float f3);
4144 // Add (params decimal [] foo);
4146 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4147 // first candidate would've chosen as better.
4153 // The two methods have equal parameter types. Now apply tie-breaking rules
4155 if (TypeManager.IsGenericMethod (best) && !TypeManager.IsGenericMethod (candidate))
4157 if (!TypeManager.IsGenericMethod (best) && TypeManager.IsGenericMethod (candidate))
4161 // This handles the following cases:
4163 // Trim () is better than Trim (params char[] chars)
4164 // Concat (string s1, string s2, string s3) is better than
4165 // Concat (string s1, params string [] srest)
4166 // Foo (int, params int [] rest) is better than Foo (params int [] rest)
4168 if (!candidate_params && best_params)
4170 if (candidate_params && !best_params)
4173 int candidate_param_count = candidate_pd.Count;
4174 int best_param_count = best_pd.Count;
4176 if (candidate_param_count != best_param_count)
4177 // can only happen if (candidate_params && best_params)
4178 return candidate_param_count > best_param_count;
4181 // now, both methods have the same number of parameters, and the parameters have the same types
4182 // Pick the "more specific" signature
4185 MethodBase orig_candidate = TypeManager.DropGenericMethodArguments (candidate);
4186 MethodBase orig_best = TypeManager.DropGenericMethodArguments (best);
4188 ParameterData orig_candidate_pd = TypeManager.GetParameterData (orig_candidate);
4189 ParameterData orig_best_pd = TypeManager.GetParameterData (orig_best);
4191 bool specific_at_least_once = false;
4192 for (int j = 0; j < candidate_param_count; ++j) {
4193 Type ct = TypeManager.TypeToCoreType (orig_candidate_pd.ParameterType (j));
4194 Type bt = TypeManager.TypeToCoreType (orig_best_pd.ParameterType (j));
4197 Type specific = MoreSpecific (ct, bt);
4201 specific_at_least_once = true;
4204 if (specific_at_least_once)
4207 // FIXME: handle lifted operators
4213 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4215 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4218 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4219 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4221 if (cand_pd.Count != base_pd.Count)
4224 for (int j = 0; j < cand_pd.Count; ++j) {
4225 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4226 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4227 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4228 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4230 if (cm != bm || ct != bt)
4237 public static string FullMethodDesc (MethodBase mb)
4243 if (mb is MethodInfo) {
4244 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4248 sb = new StringBuilder ();
4250 sb.Append (TypeManager.CSharpSignature (mb));
4251 return sb.ToString ();
4254 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4256 MemberInfo [] miset;
4257 MethodGroupExpr union;
4262 return (MethodGroupExpr) mg2;
4265 return (MethodGroupExpr) mg1;
4268 MethodGroupExpr left_set = null, right_set = null;
4269 int length1 = 0, length2 = 0;
4271 left_set = (MethodGroupExpr) mg1;
4272 length1 = left_set.Methods.Length;
4274 right_set = (MethodGroupExpr) mg2;
4275 length2 = right_set.Methods.Length;
4277 ArrayList common = new ArrayList ();
4279 foreach (MethodBase r in right_set.Methods){
4280 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4284 miset = new MemberInfo [length1 + length2 - common.Count];
4285 left_set.Methods.CopyTo (miset, 0);
4289 foreach (MethodBase r in right_set.Methods) {
4290 if (!common.Contains (r))
4294 union = new MethodGroupExpr (miset, loc);
4299 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4300 ArrayList arguments, int arg_count,
4301 ref MethodBase candidate)
4303 return IsParamsMethodApplicable (
4304 ec, me, arguments, arg_count, false, ref candidate) ||
4305 IsParamsMethodApplicable (
4306 ec, me, arguments, arg_count, true, ref candidate);
4311 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4312 ArrayList arguments, int arg_count,
4313 bool do_varargs, ref MethodBase candidate)
4316 if (!me.HasTypeArguments &&
4317 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4320 if (TypeManager.IsGenericMethodDefinition (candidate))
4321 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4324 return IsParamsMethodApplicable (
4325 ec, arguments, arg_count, candidate, do_varargs);
4329 /// Determines if the candidate method, if a params method, is applicable
4330 /// in its expanded form to the given set of arguments
4332 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4333 int arg_count, MethodBase candidate,
4336 ParameterData pd = TypeManager.GetParameterData (candidate);
4338 int pd_count = pd.Count;
4342 int count = pd_count - 1;
4344 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4346 if (pd_count != arg_count)
4353 if (count > arg_count)
4356 if (pd_count == 1 && arg_count == 0)
4360 // If we have come this far, the case which
4361 // remains is when the number of parameters is
4362 // less than or equal to the argument count.
4364 for (int i = 0; i < count; ++i) {
4366 Argument a = (Argument) arguments [i];
4368 Parameter.Modifier a_mod = a.Modifier &
4369 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4370 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4371 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4373 if (a_mod == p_mod) {
4375 if (a_mod == Parameter.Modifier.NONE)
4376 if (!Convert.ImplicitConversionExists (ec,
4378 pd.ParameterType (i)))
4381 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4382 Type pt = pd.ParameterType (i);
4385 pt = TypeManager.GetReferenceType (pt);
4396 Argument a = (Argument) arguments [count];
4397 if (!(a.Expr is Arglist))
4403 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
4405 for (int i = pd_count - 1; i < arg_count; i++) {
4406 Argument a = (Argument) arguments [i];
4408 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4415 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4416 ArrayList arguments, int arg_count,
4417 ref MethodBase candidate)
4420 if (!me.HasTypeArguments &&
4421 !TypeManager.InferTypeArguments (arguments, ref candidate))
4424 if (TypeManager.IsGenericMethodDefinition (candidate))
4425 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4428 return IsApplicable (ec, arguments, arg_count, candidate);
4432 /// Determines if the candidate method is applicable (section 14.4.2.1)
4433 /// to the given set of arguments
4435 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4436 MethodBase candidate)
4438 ParameterData pd = TypeManager.GetParameterData (candidate);
4440 if (arg_count != pd.Count)
4443 for (int i = arg_count; i > 0; ) {
4446 Argument a = (Argument) arguments [i];
4448 Parameter.Modifier a_mod = a.Modifier &
4449 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4451 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4452 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4454 if (a_mod == p_mod) {
4455 Type pt = pd.ParameterType (i);
4457 if (a_mod == Parameter.Modifier.NONE) {
4458 if (!TypeManager.IsEqual (a.Type, pt) &&
4459 !Convert.ImplicitConversionExists (ec, a.Expr, pt))
4473 static internal bool IsAncestralType (Type first_type, Type second_type)
4475 return first_type != second_type &&
4476 (TypeManager.IsSubclassOf (second_type, first_type) ||
4477 TypeManager.ImplementsInterface (second_type, first_type));
4481 /// Find the Applicable Function Members (7.4.2.1)
4483 /// me: Method Group expression with the members to select.
4484 /// it might contain constructors or methods (or anything
4485 /// that maps to a method).
4487 /// Arguments: ArrayList containing resolved Argument objects.
4489 /// loc: The location if we want an error to be reported, or a Null
4490 /// location for "probing" purposes.
4492 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4493 /// that is the best match of me on Arguments.
4496 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4497 ArrayList Arguments, bool may_fail,
4500 MethodBase method = null;
4501 bool method_params = false;
4502 Type applicable_type = null;
4504 ArrayList candidates = new ArrayList (2);
4505 ArrayList candidate_overrides = null;
4508 // Used to keep a map between the candidate
4509 // and whether it is being considered in its
4510 // normal or expanded form
4512 // false is normal form, true is expanded form
4514 Hashtable candidate_to_form = null;
4516 if (Arguments != null)
4517 arg_count = Arguments.Count;
4519 if ((me.Name == "Invoke") &&
4520 TypeManager.IsDelegateType (me.DeclaringType)) {
4521 Error_InvokeOnDelegate (loc);
4525 MethodBase[] methods = me.Methods;
4527 int nmethods = methods.Length;
4531 // Methods marked 'override' don't take part in 'applicable_type'
4532 // computation, nor in the actual overload resolution.
4533 // However, they still need to be emitted instead of a base virtual method.
4534 // So, we salt them away into the 'candidate_overrides' array.
4536 // In case of reflected methods, we replace each overriding method with
4537 // its corresponding base virtual method. This is to improve compatibility
4538 // with non-C# libraries which change the visibility of overrides (#75636)
4541 for (int i = 0; i < methods.Length; ++i) {
4542 MethodBase m = methods [i];
4544 Type [] gen_args = null;
4545 if (m.IsGenericMethod && !m.IsGenericMethodDefinition)
4546 gen_args = m.GetGenericArguments ();
4548 if (TypeManager.IsOverride (m)) {
4549 if (candidate_overrides == null)
4550 candidate_overrides = new ArrayList ();
4551 candidate_overrides.Add (m);
4552 m = TypeManager.TryGetBaseDefinition (m);
4554 if (m != null && gen_args != null) {
4555 if (!m.IsGenericMethodDefinition)
4556 throw new InternalErrorException ("GetBaseDefinition didn't return a GenericMethodDefinition");
4557 m = ((MethodInfo) m).MakeGenericMethod (gen_args);
4567 int applicable_errors = Report.Errors;
4570 // First we construct the set of applicable methods
4572 bool is_sorted = true;
4573 for (int i = 0; i < nmethods; i++){
4574 Type decl_type = methods [i].DeclaringType;
4577 // If we have already found an applicable method
4578 // we eliminate all base types (Section 14.5.5.1)
4580 if (applicable_type != null && IsAncestralType (decl_type, applicable_type))
4584 // Check if candidate is applicable (section 14.4.2.1)
4585 // Is candidate applicable in normal form?
4587 bool is_applicable = IsApplicable (ec, me, Arguments, arg_count, ref methods [i]);
4589 if (!is_applicable && IsParamsMethodApplicable (ec, me, Arguments, arg_count, ref methods [i])) {
4590 MethodBase candidate = methods [i];
4591 if (candidate_to_form == null)
4592 candidate_to_form = new PtrHashtable ();
4593 candidate_to_form [candidate] = candidate;
4594 // Candidate is applicable in expanded form
4595 is_applicable = true;
4601 candidates.Add (methods [i]);
4603 if (applicable_type == null)
4604 applicable_type = decl_type;
4605 else if (applicable_type != decl_type) {
4607 if (IsAncestralType (applicable_type, decl_type))
4608 applicable_type = decl_type;
4612 if (applicable_errors != Report.Errors)
4615 int candidate_top = candidates.Count;
4617 if (applicable_type == null) {
4619 // Okay so we have failed to find anything so we
4620 // return by providing info about the closest match
4622 int errors = Report.Errors;
4623 for (int i = 0; i < nmethods; ++i) {
4624 MethodBase c = (MethodBase) methods [i];
4625 ParameterData pd = TypeManager.GetParameterData (c);
4627 if (pd.Count != arg_count)
4631 if (!TypeManager.InferTypeArguments (Arguments, ref c))
4633 if (TypeManager.IsGenericMethodDefinition (c))
4637 VerifyArgumentsCompat (ec, Arguments, arg_count,
4638 c, false, null, may_fail, loc);
4640 if (!may_fail && errors == Report.Errors)
4641 throw new InternalErrorException (
4642 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4643 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4648 if (!may_fail && errors == Report.Errors) {
4649 string report_name = me.Name;
4650 if (report_name == ".ctor")
4651 report_name = TypeManager.CSharpName (me.DeclaringType);
4657 for (int i = 0; i < methods.Length; ++i) {
4658 MethodBase c = methods [i];
4659 ParameterData pd = TypeManager.GetParameterData (c);
4661 if (pd.Count != arg_count)
4664 if (TypeManager.InferTypeArguments (Arguments, ref c))
4668 411, loc, "The type arguments for " +
4669 "method `{0}' cannot be infered from " +
4670 "the usage. Try specifying the type " +
4671 "arguments explicitly.", report_name);
4676 Error_WrongNumArguments (loc, report_name, arg_count);
4684 // At this point, applicable_type is _one_ of the most derived types
4685 // in the set of types containing the methods in this MethodGroup.
4686 // Filter the candidates so that they only contain methods from the
4687 // most derived types.
4690 int finalized = 0; // Number of finalized candidates
4693 // Invariant: applicable_type is a most derived type
4695 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4696 // eliminating all it's base types. At the same time, we'll also move
4697 // every unrelated type to the end of the array, and pick the next
4698 // 'applicable_type'.
4700 Type next_applicable_type = null;
4701 int j = finalized; // where to put the next finalized candidate
4702 int k = finalized; // where to put the next undiscarded candidate
4703 for (int i = finalized; i < candidate_top; ++i) {
4704 MethodBase candidate = (MethodBase) candidates [i];
4705 Type decl_type = candidate.DeclaringType;
4707 if (decl_type == applicable_type) {
4708 candidates [k++] = candidates [j];
4709 candidates [j++] = candidates [i];
4713 if (IsAncestralType (decl_type, applicable_type))
4716 if (next_applicable_type != null &&
4717 IsAncestralType (decl_type, next_applicable_type))
4720 candidates [k++] = candidates [i];
4722 if (next_applicable_type == null ||
4723 IsAncestralType (next_applicable_type, decl_type))
4724 next_applicable_type = decl_type;
4727 applicable_type = next_applicable_type;
4730 } while (applicable_type != null);
4734 // Now we actually find the best method
4737 method = (MethodBase) candidates [0];
4738 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4739 for (int ix = 1; ix < candidate_top; ix++){
4740 MethodBase candidate = (MethodBase) candidates [ix];
4742 if (candidate == method)
4745 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4747 if (BetterFunction (ec, Arguments, arg_count,
4748 candidate, cand_params,
4749 method, method_params)) {
4751 method_params = cand_params;
4755 // Now check that there are no ambiguities i.e the selected method
4756 // should be better than all the others
4758 MethodBase ambiguous = null;
4759 for (int ix = 0; ix < candidate_top; ix++){
4760 MethodBase candidate = (MethodBase) candidates [ix];
4762 if (candidate == method)
4765 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4766 if (!BetterFunction (ec, Arguments, arg_count,
4767 method, method_params,
4768 candidate, cand_params)) {
4769 Report.SymbolRelatedToPreviousError (candidate);
4770 ambiguous = candidate;
4774 if (ambiguous != null) {
4775 Report.SymbolRelatedToPreviousError (method);
4776 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4777 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4782 // If the method is a virtual function, pick an override closer to the LHS type.
4784 if (!me.IsBase && method.IsVirtual) {
4785 if (TypeManager.IsOverride (method))
4786 throw new InternalErrorException (
4787 "Should not happen. An 'override' method took part in overload resolution: " + method);
4789 if (candidate_overrides != null)
4790 foreach (MethodBase candidate in candidate_overrides) {
4791 if (IsOverride (candidate, method))
4797 // And now check if the arguments are all
4798 // compatible, perform conversions if
4799 // necessary etc. and return if everything is
4802 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4803 method_params, null, may_fail, loc))
4809 MethodBase the_method = TypeManager.DropGenericMethodArguments (method);
4811 if (the_method.IsGenericMethodDefinition &&
4812 !ConstraintChecker.CheckConstraints (ec, the_method, method, loc))
4816 IMethodData data = TypeManager.GetMethod (the_method);
4818 data.SetMemberIsUsed ();
4823 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4825 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4826 name, arg_count.ToString ());
4829 static void Error_InvokeOnDelegate (Location loc)
4831 Report.Error (1533, loc,
4832 "Invoke cannot be called directly on a delegate");
4835 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4836 Type delegate_type, Argument a, ParameterData expected_par)
4838 if (delegate_type == null)
4839 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4840 TypeManager.CSharpSignature (method));
4842 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4843 TypeManager.CSharpName (delegate_type));
4845 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4847 string index = (idx + 1).ToString ();
4848 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4849 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4850 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4851 index, Parameter.GetModifierSignature (a.Modifier));
4853 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4854 index, Parameter.GetModifierSignature (mod));
4856 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4857 index, Argument.FullDesc (a), expected_par.ParameterDesc (idx));
4861 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4862 int arg_count, MethodBase method,
4863 bool chose_params_expanded,
4864 Type delegate_type, bool may_fail,
4867 ParameterData pd = TypeManager.GetParameterData (method);
4869 for (j = 0; j < arg_count; j++) {
4870 Argument a = (Argument) Arguments [j];
4871 Expression a_expr = a.Expr;
4872 Type parameter_type = pd.ParameterType (j);
4873 Parameter.Modifier pm = pd.ParameterModifier (j);
4874 Parameter.Modifier am = a.Modifier;
4876 if (pm == Parameter.Modifier.ARGLIST) {
4877 if (!(a.Expr is Arglist))
4882 if (pm == Parameter.Modifier.PARAMS) {
4883 pm = Parameter.Modifier.NONE;
4884 if (chose_params_expanded)
4885 parameter_type = TypeManager.GetElementType (parameter_type);
4891 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4892 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4895 Expression conv = Convert.ImplicitConversion (ec, a_expr, parameter_type, loc);
4899 // Update the argument with the implicit conversion
4904 if (parameter_type.IsPointer && !ec.InUnsafe) {
4914 Error_InvalidArguments (loc, j, method, delegate_type, (Argument) Arguments [j], pd);
4918 private bool resolved = false;
4919 public override Expression DoResolve (EmitContext ec)
4922 return this.method == null ? null : this;
4926 // First, resolve the expression that is used to
4927 // trigger the invocation
4929 SimpleName sn = expr as SimpleName;
4931 expr = sn.GetMethodGroup ();
4933 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4937 if (!(expr is MethodGroupExpr)) {
4938 Type expr_type = expr.Type;
4940 if (expr_type != null){
4941 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4943 return (new DelegateInvocation (
4944 this.expr, Arguments, loc)).Resolve (ec);
4948 if (!(expr is MethodGroupExpr)){
4949 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4954 // Next, evaluate all the expressions in the argument list
4956 if (Arguments != null){
4957 foreach (Argument a in Arguments){
4958 if (!a.Resolve (ec, loc))
4963 MethodGroupExpr mg = (MethodGroupExpr) expr;
4964 MethodBase method = OverloadResolve (ec, mg, Arguments, false, loc);
4969 MethodInfo mi = method as MethodInfo;
4971 type = TypeManager.TypeToCoreType (mi.ReturnType);
4972 Expression iexpr = mg.InstanceExpression;
4974 if (iexpr == null ||
4975 iexpr is This || iexpr is EmptyExpression ||
4976 mg.IdenticalTypeName) {
4977 mg.InstanceExpression = null;
4979 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
4983 if (iexpr == null || iexpr is EmptyExpression) {
4984 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
4990 if (type.IsPointer){
4998 // Only base will allow this invocation to happen.
5000 if (mg.IsBase && method.IsAbstract){
5001 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
5005 if (Arguments == null && method.Name == "Finalize") {
5007 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
5009 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
5013 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
5017 if (mg.InstanceExpression != null)
5018 mg.InstanceExpression.CheckMarshalByRefAccess ();
5020 eclass = ExprClass.Value;
5021 this.method = method;
5025 bool IsSpecialMethodInvocation (MethodBase method)
5027 IMethodData md = TypeManager.GetMethod (method);
5029 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
5032 if (!TypeManager.IsSpecialMethod (method))
5035 int args = TypeManager.GetParameterData (method).Count;
5036 if (method.Name.StartsWith ("get_") && args > 0)
5038 else if (method.Name.StartsWith ("set_") && args > 2)
5041 // TODO: check operators and events as well ?
5044 Report.SymbolRelatedToPreviousError (method);
5045 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
5046 TypeManager.CSharpSignature (method, true));
5052 // Emits the list of arguments as an array
5054 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
5056 ILGenerator ig = ec.ig;
5057 int count = arguments.Count - idx;
5058 Argument a = (Argument) arguments [idx];
5059 Type t = a.Expr.Type;
5061 IntConstant.EmitInt (ig, count);
5062 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
5064 int top = arguments.Count;
5065 for (int j = idx; j < top; j++){
5066 a = (Argument) arguments [j];
5068 ig.Emit (OpCodes.Dup);
5069 IntConstant.EmitInt (ig, j - idx);
5071 bool is_stobj, has_type_arg;
5072 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
5074 ig.Emit (OpCodes.Ldelema, t);
5086 /// Emits a list of resolved Arguments that are in the arguments
5089 /// The MethodBase argument might be null if the
5090 /// emission of the arguments is known not to contain
5091 /// a `params' field (for example in constructors or other routines
5092 /// that keep their arguments in this structure)
5094 /// if `dup_args' is true, a copy of the arguments will be left
5095 /// on the stack. If `dup_args' is true, you can specify `this_arg'
5096 /// which will be duplicated before any other args. Only EmitCall
5097 /// should be using this interface.
5099 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
5101 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
5102 int top = arguments == null ? 0 : arguments.Count;
5103 LocalTemporary [] temps = null;
5105 if (dup_args && top != 0)
5106 temps = new LocalTemporary [top];
5108 for (int i = 0; i < top; i++){
5109 Argument a = (Argument) arguments [i];
5112 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
5114 // Special case if we are passing the same data as the
5115 // params argument, do not put it in an array.
5117 if (pd.ParameterType (i) == a.Type)
5120 EmitParams (ec, i, arguments);
5127 ec.ig.Emit (OpCodes.Dup);
5128 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
5133 if (this_arg != null)
5136 for (int i = 0; i < top; i ++) {
5137 temps [i].Emit (ec);
5138 temps [i].Release (ec);
5142 if (pd != null && pd.Count > top &&
5143 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
5144 ILGenerator ig = ec.ig;
5146 IntConstant.EmitInt (ig, 0);
5147 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
5151 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
5153 ParameterData pd = TypeManager.GetParameterData (mb);
5155 if (arguments == null)
5156 return new Type [0];
5158 Argument a = (Argument) arguments [pd.Count - 1];
5159 Arglist list = (Arglist) a.Expr;
5161 return list.ArgumentTypes;
5165 /// This checks the ConditionalAttribute on the method
5167 static bool IsMethodExcluded (MethodBase method)
5169 if (method.IsConstructor)
5172 IMethodData md = TypeManager.GetMethod (method);
5174 return md.IsExcluded ();
5176 // For some methods (generated by delegate class) GetMethod returns null
5177 // because they are not included in builder_to_method table
5178 if (method.DeclaringType is TypeBuilder)
5181 return AttributeTester.IsConditionalMethodExcluded (method);
5185 /// is_base tells whether we want to force the use of the `call'
5186 /// opcode instead of using callvirt. Call is required to call
5187 /// a specific method, while callvirt will always use the most
5188 /// recent method in the vtable.
5190 /// is_static tells whether this is an invocation on a static method
5192 /// instance_expr is an expression that represents the instance
5193 /// it must be non-null if is_static is false.
5195 /// method is the method to invoke.
5197 /// Arguments is the list of arguments to pass to the method or constructor.
5199 public static void EmitCall (EmitContext ec, bool is_base,
5200 bool is_static, Expression instance_expr,
5201 MethodBase method, ArrayList Arguments, Location loc)
5203 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
5206 // `dup_args' leaves an extra copy of the arguments on the stack
5207 // `omit_args' does not leave any arguments at all.
5208 // So, basically, you could make one call with `dup_args' set to true,
5209 // and then another with `omit_args' set to true, and the two calls
5210 // would have the same set of arguments. However, each argument would
5211 // only have been evaluated once.
5212 public static void EmitCall (EmitContext ec, bool is_base,
5213 bool is_static, Expression instance_expr,
5214 MethodBase method, ArrayList Arguments, Location loc,
5215 bool dup_args, bool omit_args)
5217 ILGenerator ig = ec.ig;
5218 bool struct_call = false;
5219 bool this_call = false;
5220 LocalTemporary this_arg = null;
5222 Type decl_type = method.DeclaringType;
5224 if (!RootContext.StdLib) {
5225 // Replace any calls to the system's System.Array type with calls to
5226 // the newly created one.
5227 if (method == TypeManager.system_int_array_get_length)
5228 method = TypeManager.int_array_get_length;
5229 else if (method == TypeManager.system_int_array_get_rank)
5230 method = TypeManager.int_array_get_rank;
5231 else if (method == TypeManager.system_object_array_clone)
5232 method = TypeManager.object_array_clone;
5233 else if (method == TypeManager.system_int_array_get_length_int)
5234 method = TypeManager.int_array_get_length_int;
5235 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5236 method = TypeManager.int_array_get_lower_bound_int;
5237 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5238 method = TypeManager.int_array_get_upper_bound_int;
5239 else if (method == TypeManager.system_void_array_copyto_array_int)
5240 method = TypeManager.void_array_copyto_array_int;
5243 if (!ec.IsInObsoleteScope) {
5245 // This checks ObsoleteAttribute on the method and on the declaring type
5247 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5249 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5251 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5253 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5257 if (IsMethodExcluded (method))
5261 if (instance_expr == EmptyExpression.Null) {
5262 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5266 this_call = instance_expr is This;
5267 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5271 // If this is ourselves, push "this"
5275 Type iexpr_type = instance_expr.Type;
5278 // Push the instance expression
5280 if (TypeManager.IsValueType (iexpr_type)) {
5282 // Special case: calls to a function declared in a
5283 // reference-type with a value-type argument need
5284 // to have their value boxed.
5285 if (decl_type.IsValueType ||
5286 TypeManager.IsGenericParameter (iexpr_type)) {
5288 // If the expression implements IMemoryLocation, then
5289 // we can optimize and use AddressOf on the
5292 // If not we have to use some temporary storage for
5294 if (instance_expr is IMemoryLocation) {
5295 ((IMemoryLocation)instance_expr).
5296 AddressOf (ec, AddressOp.LoadStore);
5298 LocalTemporary temp = new LocalTemporary (iexpr_type);
5299 instance_expr.Emit (ec);
5301 temp.AddressOf (ec, AddressOp.Load);
5304 // avoid the overhead of doing this all the time.
5306 t = TypeManager.GetReferenceType (iexpr_type);
5308 instance_expr.Emit (ec);
5309 ig.Emit (OpCodes.Box, instance_expr.Type);
5310 t = TypeManager.object_type;
5313 instance_expr.Emit (ec);
5314 t = instance_expr.Type;
5318 ig.Emit (OpCodes.Dup);
5319 if (Arguments != null && Arguments.Count != 0) {
5320 this_arg = new LocalTemporary (t);
5321 this_arg.Store (ec);
5328 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5331 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
5332 ig.Emit (OpCodes.Constrained, instance_expr.Type);
5336 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5337 call_op = OpCodes.Call;
5339 call_op = OpCodes.Callvirt;
5341 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5342 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5343 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5350 // and DoFoo is not virtual, you can omit the callvirt,
5351 // because you don't need the null checking behavior.
5353 if (method is MethodInfo)
5354 ig.Emit (call_op, (MethodInfo) method);
5356 ig.Emit (call_op, (ConstructorInfo) method);
5359 public override void Emit (EmitContext ec)
5361 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5363 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5366 public override void EmitStatement (EmitContext ec)
5371 // Pop the return value if there is one
5373 if (method is MethodInfo){
5374 Type ret = ((MethodInfo)method).ReturnType;
5375 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5376 ec.ig.Emit (OpCodes.Pop);
5381 public class InvocationOrCast : ExpressionStatement
5384 Expression argument;
5386 public InvocationOrCast (Expression expr, Expression argument)
5389 this.argument = argument;
5390 this.loc = expr.Location;
5393 public override Expression DoResolve (EmitContext ec)
5396 // First try to resolve it as a cast.
5398 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5399 if ((te != null) && (te.eclass == ExprClass.Type)) {
5400 Cast cast = new Cast (te, argument, loc);
5401 return cast.Resolve (ec);
5405 // This can either be a type or a delegate invocation.
5406 // Let's just resolve it and see what we'll get.
5408 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5413 // Ok, so it's a Cast.
5415 if (expr.eclass == ExprClass.Type) {
5416 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5417 return cast.Resolve (ec);
5421 // It's a delegate invocation.
5423 if (!TypeManager.IsDelegateType (expr.Type)) {
5424 Error (149, "Method name expected");
5428 ArrayList args = new ArrayList ();
5429 args.Add (new Argument (argument, Argument.AType.Expression));
5430 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5431 return invocation.Resolve (ec);
5436 Error (201, "Only assignment, call, increment, decrement and new object " +
5437 "expressions can be used as a statement");
5440 public override ExpressionStatement ResolveStatement (EmitContext ec)
5443 // First try to resolve it as a cast.
5445 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5446 if ((te != null) && (te.eclass == ExprClass.Type)) {
5452 // This can either be a type or a delegate invocation.
5453 // Let's just resolve it and see what we'll get.
5455 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5456 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5462 // It's a delegate invocation.
5464 if (!TypeManager.IsDelegateType (expr.Type)) {
5465 Error (149, "Method name expected");
5469 ArrayList args = new ArrayList ();
5470 args.Add (new Argument (argument, Argument.AType.Expression));
5471 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5472 return invocation.ResolveStatement (ec);
5475 public override void Emit (EmitContext ec)
5477 throw new Exception ("Cannot happen");
5480 public override void EmitStatement (EmitContext ec)
5482 throw new Exception ("Cannot happen");
5487 // This class is used to "disable" the code generation for the
5488 // temporary variable when initializing value types.
5490 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5491 public void AddressOf (EmitContext ec, AddressOp Mode)
5498 /// Implements the new expression
5500 public class New : ExpressionStatement, IMemoryLocation {
5501 public readonly ArrayList Arguments;
5504 // During bootstrap, it contains the RequestedType,
5505 // but if `type' is not null, it *might* contain a NewDelegate
5506 // (because of field multi-initialization)
5508 public Expression RequestedType;
5510 MethodBase method = null;
5513 // If set, the new expression is for a value_target, and
5514 // we will not leave anything on the stack.
5516 Expression value_target;
5517 bool value_target_set = false;
5518 bool is_type_parameter = false;
5520 public New (Expression requested_type, ArrayList arguments, Location l)
5522 RequestedType = requested_type;
5523 Arguments = arguments;
5527 public bool SetValueTypeVariable (Expression value)
5529 value_target = value;
5530 value_target_set = true;
5531 if (!(value_target is IMemoryLocation)){
5532 Error_UnexpectedKind (null, "variable", loc);
5539 // This function is used to disable the following code sequence for
5540 // value type initialization:
5542 // AddressOf (temporary)
5546 // Instead the provide will have provided us with the address on the
5547 // stack to store the results.
5549 static Expression MyEmptyExpression;
5551 public void DisableTemporaryValueType ()
5553 if (MyEmptyExpression == null)
5554 MyEmptyExpression = new EmptyAddressOf ();
5557 // To enable this, look into:
5558 // test-34 and test-89 and self bootstrapping.
5560 // For instance, we can avoid a copy by using `newobj'
5561 // instead of Call + Push-temp on value types.
5562 // value_target = MyEmptyExpression;
5567 /// Converts complex core type syntax like 'new int ()' to simple constant
5569 public static Constant Constantify (Type t)
5571 if (t == TypeManager.int32_type)
5572 return new IntConstant (0, Location.Null);
5573 if (t == TypeManager.uint32_type)
5574 return new UIntConstant (0, Location.Null);
5575 if (t == TypeManager.int64_type)
5576 return new LongConstant (0, Location.Null);
5577 if (t == TypeManager.uint64_type)
5578 return new ULongConstant (0, Location.Null);
5579 if (t == TypeManager.float_type)
5580 return new FloatConstant (0, Location.Null);
5581 if (t == TypeManager.double_type)
5582 return new DoubleConstant (0, Location.Null);
5583 if (t == TypeManager.short_type)
5584 return new ShortConstant (0, Location.Null);
5585 if (t == TypeManager.ushort_type)
5586 return new UShortConstant (0, Location.Null);
5587 if (t == TypeManager.sbyte_type)
5588 return new SByteConstant (0, Location.Null);
5589 if (t == TypeManager.byte_type)
5590 return new ByteConstant (0, Location.Null);
5591 if (t == TypeManager.char_type)
5592 return new CharConstant ('\0', Location.Null);
5593 if (t == TypeManager.bool_type)
5594 return new BoolConstant (false, Location.Null);
5595 if (t == TypeManager.decimal_type)
5596 return new DecimalConstant (0, Location.Null);
5597 if (TypeManager.IsEnumType (t))
5598 return new EnumConstant (Constantify (TypeManager.EnumToUnderlying (t)), t);
5604 // Checks whether the type is an interface that has the
5605 // [ComImport, CoClass] attributes and must be treated
5608 public Expression CheckComImport (EmitContext ec)
5610 if (!type.IsInterface)
5614 // Turn the call into:
5615 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5617 Type real_class = AttributeTester.GetCoClassAttribute (type);
5618 if (real_class == null)
5621 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5622 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5623 return cast.Resolve (ec);
5626 public override Expression DoResolve (EmitContext ec)
5629 // The New DoResolve might be called twice when initializing field
5630 // expressions (see EmitFieldInitializers, the call to
5631 // GetInitializerExpression will perform a resolve on the expression,
5632 // and later the assign will trigger another resolution
5634 // This leads to bugs (#37014)
5637 if (RequestedType is NewDelegate)
5638 return RequestedType;
5642 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5648 if (type == TypeManager.void_type) {
5649 Error_VoidInvalidInTheContext (loc);
5653 if (Arguments == null) {
5654 Expression c = Constantify (type);
5659 if (TypeManager.IsDelegateType (type)) {
5660 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5661 if (RequestedType != null)
5662 if (!(RequestedType is DelegateCreation))
5663 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5664 return RequestedType;
5668 if (type.IsGenericParameter) {
5669 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5671 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5672 Error (304, String.Format (
5673 "Cannot create an instance of the " +
5674 "variable type '{0}' because it " +
5675 "doesn't have the new() constraint",
5680 if ((Arguments != null) && (Arguments.Count != 0)) {
5681 Error (417, String.Format (
5682 "`{0}': cannot provide arguments " +
5683 "when creating an instance of a " +
5684 "variable type.", type));
5688 is_type_parameter = true;
5689 eclass = ExprClass.Value;
5694 if (type.IsAbstract && type.IsSealed) {
5695 Report.SymbolRelatedToPreviousError (type);
5696 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5700 if (type.IsInterface || type.IsAbstract){
5701 RequestedType = CheckComImport (ec);
5702 if (RequestedType != null)
5703 return RequestedType;
5705 Report.SymbolRelatedToPreviousError (type);
5706 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5710 bool is_struct = type.IsValueType;
5711 eclass = ExprClass.Value;
5714 // SRE returns a match for .ctor () on structs (the object constructor),
5715 // so we have to manually ignore it.
5717 if (is_struct && Arguments == null)
5720 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5721 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5722 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5727 MethodGroupExpr mg = ml as MethodGroupExpr;
5730 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5734 if (Arguments != null){
5735 foreach (Argument a in Arguments){
5736 if (!a.Resolve (ec, loc))
5741 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5742 if (method == null) {
5743 if (almostMatchedMembers.Count != 0)
5744 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5751 bool DoEmitTypeParameter (EmitContext ec)
5754 ILGenerator ig = ec.ig;
5756 ig.Emit (OpCodes.Ldtoken, type);
5757 ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
5758 ig.Emit (OpCodes.Call, TypeManager.activator_create_instance);
5759 ig.Emit (OpCodes.Unbox_Any, type);
5762 throw new InternalErrorException ();
5767 // This DoEmit can be invoked in two contexts:
5768 // * As a mechanism that will leave a value on the stack (new object)
5769 // * As one that wont (init struct)
5771 // You can control whether a value is required on the stack by passing
5772 // need_value_on_stack. The code *might* leave a value on the stack
5773 // so it must be popped manually
5775 // If we are dealing with a ValueType, we have a few
5776 // situations to deal with:
5778 // * The target is a ValueType, and we have been provided
5779 // the instance (this is easy, we are being assigned).
5781 // * The target of New is being passed as an argument,
5782 // to a boxing operation or a function that takes a
5785 // In this case, we need to create a temporary variable
5786 // that is the argument of New.
5788 // Returns whether a value is left on the stack
5790 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5792 bool is_value_type = TypeManager.IsValueType (type);
5793 ILGenerator ig = ec.ig;
5798 // Allow DoEmit() to be called multiple times.
5799 // We need to create a new LocalTemporary each time since
5800 // you can't share LocalBuilders among ILGeneators.
5801 if (!value_target_set)
5802 value_target = new LocalTemporary (type);
5804 ml = (IMemoryLocation) value_target;
5805 ml.AddressOf (ec, AddressOp.Store);
5809 Invocation.EmitArguments (ec, method, Arguments, false, null);
5813 ig.Emit (OpCodes.Initobj, type);
5815 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5816 if (need_value_on_stack){
5817 value_target.Emit (ec);
5822 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5827 public override void Emit (EmitContext ec)
5829 if (is_type_parameter)
5830 DoEmitTypeParameter (ec);
5835 public override void EmitStatement (EmitContext ec)
5837 if (is_type_parameter)
5838 throw new InvalidOperationException ();
5840 if (DoEmit (ec, false))
5841 ec.ig.Emit (OpCodes.Pop);
5844 public void AddressOf (EmitContext ec, AddressOp Mode)
5846 if (is_type_parameter)
5847 throw new InvalidOperationException ();
5849 if (!type.IsValueType){
5851 // We throw an exception. So far, I believe we only need to support
5853 // foreach (int j in new StructType ())
5856 throw new Exception ("AddressOf should not be used for classes");
5859 if (!value_target_set)
5860 value_target = new LocalTemporary (type);
5862 IMemoryLocation ml = (IMemoryLocation) value_target;
5863 ml.AddressOf (ec, AddressOp.Store);
5865 Invocation.EmitArguments (ec, method, Arguments, false, null);
5868 ec.ig.Emit (OpCodes.Initobj, type);
5870 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5872 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5877 /// 14.5.10.2: Represents an array creation expression.
5881 /// There are two possible scenarios here: one is an array creation
5882 /// expression that specifies the dimensions and optionally the
5883 /// initialization data and the other which does not need dimensions
5884 /// specified but where initialization data is mandatory.
5886 public class ArrayCreation : Expression {
5887 Expression requested_base_type;
5888 ArrayList initializers;
5891 // The list of Argument types.
5892 // This is used to construct the `newarray' or constructor signature
5894 ArrayList arguments;
5897 // Method used to create the array object.
5899 MethodBase new_method = null;
5901 Type array_element_type;
5902 Type underlying_type;
5903 bool is_one_dimensional = false;
5904 bool is_builtin_type = false;
5905 bool expect_initializers = false;
5906 int num_arguments = 0;
5910 ArrayList array_data;
5914 // The number of constants in array initializers
5915 int const_initializers_count;
5917 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5919 this.requested_base_type = requested_base_type;
5920 this.initializers = initializers;
5924 arguments = new ArrayList ();
5926 foreach (Expression e in exprs) {
5927 arguments.Add (new Argument (e, Argument.AType.Expression));
5932 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5934 this.requested_base_type = requested_base_type;
5935 this.initializers = initializers;
5939 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5941 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5943 //dimensions = tmp.Length - 1;
5944 expect_initializers = true;
5947 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5949 StringBuilder sb = new StringBuilder (rank);
5952 for (int i = 1; i < idx_count; i++)
5957 return new ComposedCast (base_type, sb.ToString (), loc);
5960 void Error_IncorrectArrayInitializer ()
5962 Error (178, "Invalid rank specifier: expected `,' or `]'");
5965 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5967 if (specified_dims) {
5968 Argument a = (Argument) arguments [idx];
5970 if (!a.Resolve (ec, loc))
5973 Constant c = a.Expr as Constant;
5975 c = c.ImplicitConversionRequired (TypeManager.int32_type, a.Expr.Location);
5979 Report.Error (150, a.Expr.Location, "A constant value is expected");
5983 int value = (int) c.GetValue ();
5985 if (value != probe.Count) {
5986 Error_IncorrectArrayInitializer ();
5990 bounds [idx] = value;
5993 int child_bounds = -1;
5994 for (int i = 0; i < probe.Count; ++i) {
5995 object o = probe [i];
5996 if (o is ArrayList) {
5997 ArrayList sub_probe = o as ArrayList;
5998 int current_bounds = sub_probe.Count;
6000 if (child_bounds == -1)
6001 child_bounds = current_bounds;
6003 else if (child_bounds != current_bounds){
6004 Error_IncorrectArrayInitializer ();
6007 if (idx + 1 >= dimensions){
6008 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
6012 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
6016 if (child_bounds != -1){
6017 Error_IncorrectArrayInitializer ();
6021 Expression tmp = (Expression) o;
6022 tmp = tmp.Resolve (ec);
6026 Expression conv = Convert.ImplicitConversionRequired (
6027 ec, tmp, underlying_type, loc);
6032 // Initializers with the default values can be ignored
6033 Constant c = tmp as Constant;
6035 if (c.IsDefaultInitializer (array_element_type)) {
6039 ++const_initializers_count;
6042 // Used to invalidate static initializer
6043 const_initializers_count = int.MinValue;
6046 array_data.Add (conv);
6053 public void UpdateIndices ()
6056 for (ArrayList probe = initializers; probe != null;) {
6057 if (probe.Count > 0 && probe [0] is ArrayList) {
6058 Expression e = new IntConstant (probe.Count, Location.Null);
6059 arguments.Add (new Argument (e, Argument.AType.Expression));
6061 bounds [i++] = probe.Count;
6063 probe = (ArrayList) probe [0];
6066 Expression e = new IntConstant (probe.Count, Location.Null);
6067 arguments.Add (new Argument (e, Argument.AType.Expression));
6069 bounds [i++] = probe.Count;
6076 bool ResolveInitializers (EmitContext ec)
6078 if (initializers == null) {
6079 return !expect_initializers;
6082 if (underlying_type == null)
6086 // We use this to store all the date values in the order in which we
6087 // will need to store them in the byte blob later
6089 array_data = new ArrayList ();
6090 bounds = new System.Collections.Specialized.HybridDictionary ();
6092 if (arguments != null)
6093 return CheckIndices (ec, initializers, 0, true);
6095 arguments = new ArrayList ();
6097 if (!CheckIndices (ec, initializers, 0, false))
6102 if (arguments.Count != dimensions) {
6103 Error_IncorrectArrayInitializer ();
6111 // Creates the type of the array
6113 bool LookupType (EmitContext ec)
6115 StringBuilder array_qualifier = new StringBuilder (rank);
6118 // `In the first form allocates an array instace of the type that results
6119 // from deleting each of the individual expression from the expression list'
6121 if (num_arguments > 0) {
6122 array_qualifier.Append ("[");
6123 for (int i = num_arguments-1; i > 0; i--)
6124 array_qualifier.Append (",");
6125 array_qualifier.Append ("]");
6131 TypeExpr array_type_expr;
6132 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
6133 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
6134 if (array_type_expr == null)
6137 type = array_type_expr.Type;
6138 underlying_type = TypeManager.GetElementType (type);
6139 dimensions = type.GetArrayRank ();
6144 public override Expression DoResolve (EmitContext ec)
6149 if (!LookupType (ec))
6152 array_element_type = TypeManager.GetElementType (type);
6153 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
6154 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
6159 // First step is to validate the initializers and fill
6160 // in any missing bits
6162 if (!ResolveInitializers (ec))
6166 if (arguments == null)
6169 arg_count = arguments.Count;
6170 foreach (Argument a in arguments){
6171 if (!a.Resolve (ec, loc))
6174 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
6175 if (real_arg == null)
6182 if (arg_count == 1) {
6183 is_one_dimensional = true;
6184 eclass = ExprClass.Value;
6188 is_builtin_type = TypeManager.IsBuiltinType (type);
6190 if (is_builtin_type) {
6193 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
6194 AllBindingFlags, loc);
6196 if (!(ml is MethodGroupExpr)) {
6197 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
6202 Error (-6, "New invocation: Can not find a constructor for " +
6203 "this argument list");
6207 new_method = Invocation.OverloadResolve (
6208 ec, (MethodGroupExpr) ml, arguments, false, loc);
6210 if (new_method == null) {
6211 Error (-6, "New invocation: Can not find a constructor for " +
6212 "this argument list");
6216 eclass = ExprClass.Value;
6219 ModuleBuilder mb = CodeGen.Module.Builder;
6220 ArrayList args = new ArrayList ();
6222 if (arguments != null) {
6223 for (int i = 0; i < arg_count; i++)
6224 args.Add (TypeManager.int32_type);
6227 Type [] arg_types = null;
6230 arg_types = new Type [args.Count];
6232 args.CopyTo (arg_types, 0);
6234 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6237 if (new_method == null) {
6238 Error (-6, "New invocation: Can not find a constructor for " +
6239 "this argument list");
6243 eclass = ExprClass.Value;
6248 byte [] MakeByteBlob ()
6253 int count = array_data.Count;
6255 if (underlying_type.IsEnum)
6256 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
6258 factor = GetTypeSize (underlying_type);
6260 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
6262 data = new byte [(count * factor + 4) & ~3];
6265 for (int i = 0; i < count; ++i) {
6266 object v = array_data [i];
6268 if (v is EnumConstant)
6269 v = ((EnumConstant) v).Child;
6271 if (v is Constant && !(v is StringConstant))
6272 v = ((Constant) v).GetValue ();
6278 if (underlying_type == TypeManager.int64_type){
6279 if (!(v is Expression)){
6280 long val = (long) v;
6282 for (int j = 0; j < factor; ++j) {
6283 data [idx + j] = (byte) (val & 0xFF);
6287 } else if (underlying_type == TypeManager.uint64_type){
6288 if (!(v is Expression)){
6289 ulong val = (ulong) v;
6291 for (int j = 0; j < factor; ++j) {
6292 data [idx + j] = (byte) (val & 0xFF);
6296 } else if (underlying_type == TypeManager.float_type) {
6297 if (!(v is Expression)){
6298 element = BitConverter.GetBytes ((float) v);
6300 for (int j = 0; j < factor; ++j)
6301 data [idx + j] = element [j];
6303 } else if (underlying_type == TypeManager.double_type) {
6304 if (!(v is Expression)){
6305 element = BitConverter.GetBytes ((double) v);
6307 for (int j = 0; j < factor; ++j)
6308 data [idx + j] = element [j];
6310 } else if (underlying_type == TypeManager.char_type){
6311 if (!(v is Expression)){
6312 int val = (int) ((char) v);
6314 data [idx] = (byte) (val & 0xff);
6315 data [idx+1] = (byte) (val >> 8);
6317 } else if (underlying_type == TypeManager.short_type){
6318 if (!(v is Expression)){
6319 int val = (int) ((short) v);
6321 data [idx] = (byte) (val & 0xff);
6322 data [idx+1] = (byte) (val >> 8);
6324 } else if (underlying_type == TypeManager.ushort_type){
6325 if (!(v is Expression)){
6326 int val = (int) ((ushort) v);
6328 data [idx] = (byte) (val & 0xff);
6329 data [idx+1] = (byte) (val >> 8);
6331 } else if (underlying_type == TypeManager.int32_type) {
6332 if (!(v is Expression)){
6335 data [idx] = (byte) (val & 0xff);
6336 data [idx+1] = (byte) ((val >> 8) & 0xff);
6337 data [idx+2] = (byte) ((val >> 16) & 0xff);
6338 data [idx+3] = (byte) (val >> 24);
6340 } else if (underlying_type == TypeManager.uint32_type) {
6341 if (!(v is Expression)){
6342 uint val = (uint) v;
6344 data [idx] = (byte) (val & 0xff);
6345 data [idx+1] = (byte) ((val >> 8) & 0xff);
6346 data [idx+2] = (byte) ((val >> 16) & 0xff);
6347 data [idx+3] = (byte) (val >> 24);
6349 } else if (underlying_type == TypeManager.sbyte_type) {
6350 if (!(v is Expression)){
6351 sbyte val = (sbyte) v;
6352 data [idx] = (byte) val;
6354 } else if (underlying_type == TypeManager.byte_type) {
6355 if (!(v is Expression)){
6356 byte val = (byte) v;
6357 data [idx] = (byte) val;
6359 } else if (underlying_type == TypeManager.bool_type) {
6360 if (!(v is Expression)){
6361 bool val = (bool) v;
6362 data [idx] = (byte) (val ? 1 : 0);
6364 } else if (underlying_type == TypeManager.decimal_type){
6365 if (!(v is Expression)){
6366 int [] bits = Decimal.GetBits ((decimal) v);
6369 // FIXME: For some reason, this doesn't work on the MS runtime.
6370 int [] nbits = new int [4];
6371 nbits [0] = bits [3];
6372 nbits [1] = bits [2];
6373 nbits [2] = bits [0];
6374 nbits [3] = bits [1];
6376 for (int j = 0; j < 4; j++){
6377 data [p++] = (byte) (nbits [j] & 0xff);
6378 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6379 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6380 data [p++] = (byte) (nbits [j] >> 24);
6384 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6393 // Emits the initializers for the array
6395 void EmitStaticInitializers (EmitContext ec)
6398 // First, the static data
6401 ILGenerator ig = ec.ig;
6403 byte [] data = MakeByteBlob ();
6405 fb = RootContext.MakeStaticData (data);
6407 ig.Emit (OpCodes.Dup);
6408 ig.Emit (OpCodes.Ldtoken, fb);
6409 ig.Emit (OpCodes.Call,
6410 TypeManager.void_initializearray_array_fieldhandle);
6414 // Emits pieces of the array that can not be computed at compile
6415 // time (variables and string locations).
6417 // This always expect the top value on the stack to be the array
6419 void EmitDynamicInitializers (EmitContext ec)
6421 ILGenerator ig = ec.ig;
6422 int dims = bounds.Count;
6423 int [] current_pos = new int [dims];
6425 MethodInfo set = null;
6428 Type [] args = new Type [dims + 1];
6430 for (int j = 0; j < dims; j++)
6431 args [j] = TypeManager.int32_type;
6432 args [dims] = array_element_type;
6434 set = CodeGen.Module.Builder.GetArrayMethod (
6436 CallingConventions.HasThis | CallingConventions.Standard,
6437 TypeManager.void_type, args);
6440 for (int i = 0; i < array_data.Count; i++){
6442 Expression e = (Expression)array_data [i];
6445 Type etype = e.Type;
6447 ig.Emit (OpCodes.Dup);
6449 for (int idx = 0; idx < dims; idx++)
6450 IntConstant.EmitInt (ig, current_pos [idx]);
6453 // If we are dealing with a struct, get the
6454 // address of it, so we can store it.
6456 if ((dims == 1) && etype.IsValueType &&
6457 (!TypeManager.IsBuiltinOrEnum (etype) ||
6458 etype == TypeManager.decimal_type)) {
6463 // Let new know that we are providing
6464 // the address where to store the results
6466 n.DisableTemporaryValueType ();
6469 ig.Emit (OpCodes.Ldelema, etype);
6475 bool is_stobj, has_type_arg;
6476 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
6478 ig.Emit (OpCodes.Stobj, etype);
6479 else if (has_type_arg)
6480 ig.Emit (op, etype);
6484 ig.Emit (OpCodes.Call, set);
6491 for (int j = dims - 1; j >= 0; j--){
6493 if (current_pos [j] < (int) bounds [j])
6495 current_pos [j] = 0;
6500 void EmitArrayArguments (EmitContext ec)
6502 ILGenerator ig = ec.ig;
6504 foreach (Argument a in arguments) {
6505 Type atype = a.Type;
6508 if (atype == TypeManager.uint64_type)
6509 ig.Emit (OpCodes.Conv_Ovf_U4);
6510 else if (atype == TypeManager.int64_type)
6511 ig.Emit (OpCodes.Conv_Ovf_I4);
6515 public override void Emit (EmitContext ec)
6517 ILGenerator ig = ec.ig;
6519 EmitArrayArguments (ec);
6520 if (is_one_dimensional)
6521 ig.Emit (OpCodes.Newarr, array_element_type);
6523 if (is_builtin_type)
6524 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6526 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6529 if (initializers == null)
6532 // This is a treshold for static initializers
6533 // I tried to make more accurate but it seems to me that Array.Initialize is
6534 // always slower (managed -> unmanaged switch?)
6535 const int max_automatic_initializers = 200;
6537 if (const_initializers_count > max_automatic_initializers && TypeManager.IsPrimitiveType (array_element_type)) {
6538 EmitStaticInitializers (ec);
6542 EmitDynamicInitializers (ec);
6545 public override bool GetAttributableValue (Type valueType, out object value)
6547 if (!is_one_dimensional){
6548 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6549 return base.GetAttributableValue (null, out value);
6552 if (array_data == null) {
6553 Constant c = (Constant)((Argument)arguments [0]).Expr;
6554 if (c.IsDefaultValue) {
6555 value = Array.CreateInstance (array_element_type, 0);
6558 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6559 return base.GetAttributableValue (null, out value);
6562 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6563 object element_value;
6564 for (int i = 0; i < ret.Length; ++i)
6566 Expression e = (Expression)array_data [i];
6568 // Is null when an initializer is optimized (value == predefined value)
6572 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6576 ret.SetValue (element_value, i);
6583 public sealed class CompilerGeneratedThis : This
6585 public static This Instance = new CompilerGeneratedThis ();
6587 private CompilerGeneratedThis ()
6588 : base (Location.Null)
6592 public override Expression DoResolve (EmitContext ec)
6594 eclass = ExprClass.Variable;
6595 type = ec.ContainerType;
6596 variable = new SimpleThis (type);
6602 /// Represents the `this' construct
6605 public class This : VariableReference, IVariable
6608 VariableInfo variable_info;
6609 protected Variable variable;
6612 public This (Block block, Location loc)
6618 public This (Location loc)
6623 public VariableInfo VariableInfo {
6624 get { return variable_info; }
6627 public bool VerifyFixed ()
6629 return !TypeManager.IsValueType (Type);
6632 public override bool IsRef {
6633 get { return is_struct; }
6636 public override Variable Variable {
6637 get { return variable; }
6640 public bool ResolveBase (EmitContext ec)
6642 eclass = ExprClass.Variable;
6644 if (ec.TypeContainer.CurrentType != null)
6645 type = ec.TypeContainer.CurrentType;
6647 type = ec.ContainerType;
6649 is_struct = ec.TypeContainer is Struct;
6652 Error (26, "Keyword `this' is not valid in a static property, " +
6653 "static method, or static field initializer");
6657 if (block != null) {
6658 if (block.Toplevel.ThisVariable != null)
6659 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6661 AnonymousContainer am = ec.CurrentAnonymousMethod;
6662 if (is_struct && (am != null) && !am.IsIterator) {
6663 Report.Error (1673, loc, "Anonymous methods inside structs " +
6664 "cannot access instance members of `this'. " +
6665 "Consider copying `this' to a local variable " +
6666 "outside the anonymous method and using the " +
6671 AnonymousMethodHost host = block.Toplevel.AnonymousMethodHost;
6672 if ((host != null) && (!is_struct || host.IsIterator)) {
6673 variable = host.CaptureThis ();
6674 type = variable.Type;
6679 if (variable == null)
6680 variable = new SimpleThis (type);
6685 public override Expression DoResolve (EmitContext ec)
6687 if (!ResolveBase (ec))
6690 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) &&
6691 !variable_info.IsAssigned (ec)) {
6692 Error (188, "The `this' object cannot be used before all of its " +
6693 "fields are assigned to");
6694 variable_info.SetAssigned (ec);
6698 if (ec.IsFieldInitializer) {
6699 Error (27, "Keyword `this' is not available in the current context");
6706 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6708 if (!ResolveBase (ec))
6711 if (variable_info != null)
6712 variable_info.SetAssigned (ec);
6714 if (ec.TypeContainer is Class){
6715 Error (1604, "Cannot assign to 'this' because it is read-only");
6721 public override int GetHashCode()
6723 return block.GetHashCode ();
6726 public override bool Equals (object obj)
6728 This t = obj as This;
6732 return block == t.block;
6735 protected class SimpleThis : Variable
6739 public SimpleThis (Type type)
6744 public override Type Type {
6745 get { return type; }
6748 public override bool HasInstance {
6749 get { return false; }
6752 public override bool NeedsTemporary {
6753 get { return false; }
6756 public override void EmitInstance (EmitContext ec)
6761 public override void Emit (EmitContext ec)
6763 ec.ig.Emit (OpCodes.Ldarg_0);
6766 public override void EmitAssign (EmitContext ec)
6768 throw new InvalidOperationException ();
6771 public override void EmitAddressOf (EmitContext ec)
6773 ec.ig.Emit (OpCodes.Ldarg_0);
6779 /// Represents the `__arglist' construct
6781 public class ArglistAccess : Expression
6783 public ArglistAccess (Location loc)
6788 public override Expression DoResolve (EmitContext ec)
6790 eclass = ExprClass.Variable;
6791 type = TypeManager.runtime_argument_handle_type;
6793 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6795 Error (190, "The __arglist construct is valid only within " +
6796 "a variable argument method");
6803 public override void Emit (EmitContext ec)
6805 ec.ig.Emit (OpCodes.Arglist);
6810 /// Represents the `__arglist (....)' construct
6812 public class Arglist : Expression
6814 public readonly Argument[] Arguments;
6816 public Arglist (Argument[] args, Location l)
6822 public Type[] ArgumentTypes {
6824 Type[] retval = new Type [Arguments.Length];
6825 for (int i = 0; i < Arguments.Length; i++)
6826 retval [i] = Arguments [i].Type;
6831 public override Expression DoResolve (EmitContext ec)
6833 eclass = ExprClass.Variable;
6834 type = TypeManager.runtime_argument_handle_type;
6836 foreach (Argument arg in Arguments) {
6837 if (!arg.Resolve (ec, loc))
6844 public override void Emit (EmitContext ec)
6846 foreach (Argument arg in Arguments)
6852 // This produces the value that renders an instance, used by the iterators code
6854 public class ProxyInstance : Expression, IMemoryLocation {
6855 public override Expression DoResolve (EmitContext ec)
6857 eclass = ExprClass.Variable;
6858 type = ec.ContainerType;
6862 public override void Emit (EmitContext ec)
6864 ec.ig.Emit (OpCodes.Ldarg_0);
6868 public void AddressOf (EmitContext ec, AddressOp mode)
6870 ec.ig.Emit (OpCodes.Ldarg_0);
6875 /// Implements the typeof operator
6877 public class TypeOf : Expression {
6878 readonly Expression QueriedType;
6879 protected Type typearg;
6881 public TypeOf (Expression queried_type, Location l)
6883 QueriedType = queried_type;
6887 public override Expression DoResolve (EmitContext ec)
6889 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6893 typearg = texpr.Type;
6895 if (typearg == TypeManager.void_type) {
6896 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6900 if (typearg.IsPointer && !ec.InUnsafe){
6905 type = TypeManager.type_type;
6906 // Even though what is returned is a type object, it's treated as a value by the compiler.
6907 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6908 eclass = ExprClass.Value;
6912 public override void Emit (EmitContext ec)
6914 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6915 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6918 public override bool GetAttributableValue (Type valueType, out object value)
6920 if (TypeManager.ContainsGenericParameters (typearg)) {
6921 Report.SymbolRelatedToPreviousError(typearg);
6922 Report.Error(416, loc, "`{0}': an attribute argument cannot use type parameters",
6923 TypeManager.CSharpName(typearg));
6928 if (valueType == TypeManager.object_type) {
6929 value = (object)typearg;
6936 public Type TypeArgument
6946 /// Implements the `typeof (void)' operator
6948 public class TypeOfVoid : TypeOf {
6949 public TypeOfVoid (Location l) : base (null, l)
6954 public override Expression DoResolve (EmitContext ec)
6956 type = TypeManager.type_type;
6957 typearg = TypeManager.void_type;
6958 // See description in TypeOf.
6959 eclass = ExprClass.Value;
6965 /// Implements the sizeof expression
6967 public class SizeOf : Expression {
6968 public Expression QueriedType;
6971 public SizeOf (Expression queried_type, Location l)
6973 this.QueriedType = queried_type;
6977 public override Expression DoResolve (EmitContext ec)
6979 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6984 if (texpr is TypeParameterExpr){
6985 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
6990 type_queried = texpr.Type;
6991 if (type_queried.IsEnum)
6992 type_queried = TypeManager.EnumToUnderlying (type_queried);
6994 if (type_queried == TypeManager.void_type) {
6995 Expression.Error_VoidInvalidInTheContext (loc);
6999 int size_of = GetTypeSize (type_queried);
7001 return new IntConstant (size_of, loc);
7005 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)",
7006 TypeManager.CSharpName (type_queried));
7010 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
7014 type = TypeManager.int32_type;
7015 eclass = ExprClass.Value;
7019 public override void Emit (EmitContext ec)
7021 int size = GetTypeSize (type_queried);
7024 ec.ig.Emit (OpCodes.Sizeof, type_queried);
7026 IntConstant.EmitInt (ec.ig, size);
7031 /// Implements the qualified-alias-member (::) expression.
7033 public class QualifiedAliasMember : Expression
7035 string alias, identifier;
7037 public QualifiedAliasMember (string alias, string identifier, Location l)
7040 this.identifier = identifier;
7044 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7046 if (alias == "global")
7047 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
7049 int errors = Report.Errors;
7050 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7052 if (errors == Report.Errors)
7053 Report.Error (432, loc, "Alias `{0}' not found", alias);
7056 if (fne.eclass != ExprClass.Namespace) {
7058 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7061 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
7064 public override Expression DoResolve (EmitContext ec)
7066 FullNamedExpression fne;
7067 if (alias == "global") {
7068 fne = RootNamespace.Global;
7070 int errors = Report.Errors;
7071 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7073 if (errors == Report.Errors)
7074 Report.Error (432, loc, "Alias `{0}' not found", alias);
7079 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
7083 if (!(retval is FullNamedExpression)) {
7084 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
7088 // We defer this check till the end to match the behaviour of CSC
7089 if (fne.eclass != ExprClass.Namespace) {
7090 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7096 public override void Emit (EmitContext ec)
7098 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
7102 public override string ToString ()
7104 return alias + "::" + identifier;
7107 public override string GetSignatureForError ()
7114 /// Implements the member access expression
7116 public class MemberAccess : Expression {
7117 public readonly string Identifier;
7120 public MemberAccess (Expression expr, string id)
7121 : this (expr, id, expr.Location)
7125 public MemberAccess (Expression expr, string identifier, Location loc)
7128 Identifier = identifier;
7132 public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
7133 : this (expr, identifier, loc)
7140 public Expression Expr {
7141 get { return expr; }
7144 protected string LookupIdentifier {
7145 get { return MemberName.MakeName (Identifier, args); }
7148 // TODO: this method has very poor performace for Enum fields and
7149 // probably for other constants as well
7150 Expression DoResolve (EmitContext ec, Expression right_side)
7153 throw new Exception ();
7156 // Resolve the expression with flow analysis turned off, we'll do the definite
7157 // assignment checks later. This is because we don't know yet what the expression
7158 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7159 // definite assignment check on the actual field and not on the whole struct.
7162 SimpleName original = expr as SimpleName;
7163 Expression new_expr = expr.Resolve (ec,
7164 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7165 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7167 if (new_expr == null)
7170 if (new_expr is Namespace) {
7171 Namespace ns = (Namespace) new_expr;
7172 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, LookupIdentifier, loc);
7174 if ((retval != null) && (args != null))
7175 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
7179 ns.Error_NamespaceDoesNotExist (ec.DeclContainer, loc, Identifier);
7183 Type expr_type = new_expr.Type;
7184 if (expr_type.IsPointer || expr_type == TypeManager.void_type || new_expr is NullLiteral){
7185 Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type);
7188 if (expr_type == TypeManager.anonymous_method_type){
7189 Unary.Error_OperatorCannotBeApplied (loc, ".", "anonymous method");
7193 Constant c = new_expr as Constant;
7194 if (c != null && c.GetValue () == null) {
7195 Report.Warning (1720, 1, loc, "Expression will always cause a `{0}'",
7196 "System.NullReferenceException");
7199 Expression member_lookup;
7200 member_lookup = MemberLookup (
7201 ec.ContainerType, expr_type, expr_type, Identifier, loc);
7203 if ((member_lookup == null) && (args != null)) {
7204 member_lookup = MemberLookup (
7205 ec.ContainerType, expr_type, expr_type, LookupIdentifier, loc);
7208 if (member_lookup == null) {
7209 MemberLookupFailed (
7210 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
7214 TypeExpr texpr = member_lookup as TypeExpr;
7215 if (texpr != null) {
7216 if (!(new_expr is TypeExpr) &&
7217 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
7218 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7219 Identifier, member_lookup.GetSignatureForError ());
7223 if (!texpr.CheckAccessLevel (ec.DeclContainer)) {
7224 Report.SymbolRelatedToPreviousError (member_lookup.Type);
7225 ErrorIsInaccesible (loc, TypeManager.CSharpName (member_lookup.Type));
7230 ConstructedType ct = new_expr as ConstructedType;
7233 // When looking up a nested type in a generic instance
7234 // via reflection, we always get a generic type definition
7235 // and not a generic instance - so we have to do this here.
7237 // See gtest-172-lib.cs and gtest-172.cs for an example.
7239 ct = new ConstructedType (
7240 member_lookup.Type, ct.TypeArguments, loc);
7242 return ct.ResolveAsTypeStep (ec, false);
7245 return member_lookup;
7248 MemberExpr me = (MemberExpr) member_lookup;
7249 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
7250 if (member_lookup == null)
7254 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
7256 throw new InternalErrorException ();
7258 return mg.ResolveGeneric (ec, args);
7261 if (original != null && !TypeManager.IsValueType (expr_type)) {
7262 me = member_lookup as MemberExpr;
7263 if (me != null && me.IsInstance) {
7264 LocalVariableReference var = new_expr as LocalVariableReference;
7265 if (var != null && !var.VerifyAssigned (ec))
7270 // The following DoResolve/DoResolveLValue will do the definite assignment
7273 if (right_side != null)
7274 return member_lookup.DoResolveLValue (ec, right_side);
7276 return member_lookup.DoResolve (ec);
7279 public override Expression DoResolve (EmitContext ec)
7281 return DoResolve (ec, null);
7284 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7286 return DoResolve (ec, right_side);
7289 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7291 return ResolveNamespaceOrType (ec, silent);
7294 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7296 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
7298 if (new_expr == null)
7301 if (new_expr is Namespace) {
7302 Namespace ns = (Namespace) new_expr;
7303 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, LookupIdentifier, loc);
7305 if ((retval != null) && (args != null))
7306 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
7308 if (!silent && retval == null)
7309 ns.Error_NamespaceDoesNotExist (rc.DeclContainer, loc, LookupIdentifier);
7313 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
7314 if (tnew_expr == null)
7317 Type expr_type = tnew_expr.Type;
7319 if (expr_type.IsPointer){
7320 Error (23, "The `.' operator can not be applied to pointer operands (" +
7321 TypeManager.CSharpName (expr_type) + ")");
7325 Expression member_lookup = MemberLookup (
7326 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
7327 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7328 if (member_lookup == null) {
7332 member_lookup = MemberLookup(
7333 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
7334 MemberTypes.All, BindingFlags.Public | BindingFlags.NonPublic, loc);
7336 if (member_lookup == null) {
7337 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7338 Identifier, new_expr.GetSignatureForError ());
7340 // TODO: Report.SymbolRelatedToPreviousError
7341 member_lookup.Error_UnexpectedKind (null, "type", loc);
7346 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
7351 TypeArguments the_args = args;
7352 if (TypeManager.HasGenericArguments (expr_type)) {
7353 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
7355 TypeArguments new_args = new TypeArguments (loc);
7356 foreach (Type decl in decl_args)
7357 new_args.Add (new TypeExpression (decl, loc));
7360 new_args.Add (args);
7362 the_args = new_args;
7365 if (the_args != null) {
7366 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
7367 return ctype.ResolveAsTypeStep (rc, false);
7374 public override void Emit (EmitContext ec)
7376 throw new Exception ("Should not happen");
7379 public override string ToString ()
7381 return expr + "." + MemberName.MakeName (Identifier, args);
7384 public override string GetSignatureForError ()
7386 return expr.GetSignatureForError () + "." + Identifier;
7391 /// Implements checked expressions
7393 public class CheckedExpr : Expression {
7395 public Expression Expr;
7397 public CheckedExpr (Expression e, Location l)
7403 public override Expression DoResolve (EmitContext ec)
7405 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7406 Expr = Expr.Resolve (ec);
7411 if (Expr is Constant)
7414 eclass = Expr.eclass;
7419 public override void Emit (EmitContext ec)
7421 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7425 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7427 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7428 Expr.EmitBranchable (ec, target, onTrue);
7433 /// Implements the unchecked expression
7435 public class UnCheckedExpr : Expression {
7437 public Expression Expr;
7439 public UnCheckedExpr (Expression e, Location l)
7445 public override Expression DoResolve (EmitContext ec)
7447 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7448 Expr = Expr.Resolve (ec);
7453 if (Expr is Constant)
7456 eclass = Expr.eclass;
7461 public override void Emit (EmitContext ec)
7463 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7467 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7469 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7470 Expr.EmitBranchable (ec, target, onTrue);
7475 /// An Element Access expression.
7477 /// During semantic analysis these are transformed into
7478 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7480 public class ElementAccess : Expression {
7481 public ArrayList Arguments;
7482 public Expression Expr;
7484 public ElementAccess (Expression e, ArrayList e_list)
7493 Arguments = new ArrayList ();
7494 foreach (Expression tmp in e_list)
7495 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7499 bool CommonResolve (EmitContext ec)
7501 Expr = Expr.Resolve (ec);
7506 if (Arguments == null)
7509 foreach (Argument a in Arguments){
7510 if (!a.Resolve (ec, loc))
7517 Expression MakePointerAccess (EmitContext ec, Type t)
7519 if (t == TypeManager.void_ptr_type){
7520 Error (242, "The array index operation is not valid on void pointers");
7523 if (Arguments.Count != 1){
7524 Error (196, "A pointer must be indexed by only one value");
7529 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7532 return new Indirection (p, loc).Resolve (ec);
7535 public override Expression DoResolve (EmitContext ec)
7537 if (!CommonResolve (ec))
7541 // We perform some simple tests, and then to "split" the emit and store
7542 // code we create an instance of a different class, and return that.
7544 // I am experimenting with this pattern.
7548 if (t == TypeManager.array_type){
7549 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7554 return (new ArrayAccess (this, loc)).Resolve (ec);
7556 return MakePointerAccess (ec, Expr.Type);
7558 FieldExpr fe = Expr as FieldExpr;
7560 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7562 return MakePointerAccess (ec, ff.ElementType);
7565 return (new IndexerAccess (this, loc)).Resolve (ec);
7568 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7570 if (!CommonResolve (ec))
7575 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7578 return MakePointerAccess (ec, Expr.Type);
7580 FieldExpr fe = Expr as FieldExpr;
7582 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7584 if (!(fe.InstanceExpression is LocalVariableReference) &&
7585 !(fe.InstanceExpression is This)) {
7586 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7589 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7590 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7593 return MakePointerAccess (ec, ff.ElementType);
7596 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7599 public override void Emit (EmitContext ec)
7601 throw new Exception ("Should never be reached");
7606 /// Implements array access
7608 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7610 // Points to our "data" repository
7614 LocalTemporary temp;
7617 public ArrayAccess (ElementAccess ea_data, Location l)
7620 eclass = ExprClass.Variable;
7624 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7626 return DoResolve (ec);
7629 public override Expression DoResolve (EmitContext ec)
7632 ExprClass eclass = ea.Expr.eclass;
7634 // As long as the type is valid
7635 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7636 eclass == ExprClass.Value)) {
7637 ea.Expr.Error_UnexpectedKind ("variable or value");
7642 Type t = ea.Expr.Type;
7643 if (t.GetArrayRank () != ea.Arguments.Count){
7644 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7645 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7649 type = TypeManager.GetElementType (t);
7650 if (type.IsPointer && !ec.InUnsafe){
7651 UnsafeError (ea.Location);
7655 foreach (Argument a in ea.Arguments){
7656 Type argtype = a.Type;
7658 if (argtype == TypeManager.int32_type ||
7659 argtype == TypeManager.uint32_type ||
7660 argtype == TypeManager.int64_type ||
7661 argtype == TypeManager.uint64_type) {
7662 Constant c = a.Expr as Constant;
7663 if (c != null && c.IsNegative) {
7664 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7670 // Mhm. This is strage, because the Argument.Type is not the same as
7671 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7673 // Wonder if I will run into trouble for this.
7675 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7680 eclass = ExprClass.Variable;
7686 /// Emits the right opcode to load an object of Type `t'
7687 /// from an array of T
7689 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7691 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7692 ig.Emit (OpCodes.Ldelem_U1);
7693 else if (type == TypeManager.sbyte_type)
7694 ig.Emit (OpCodes.Ldelem_I1);
7695 else if (type == TypeManager.short_type)
7696 ig.Emit (OpCodes.Ldelem_I2);
7697 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7698 ig.Emit (OpCodes.Ldelem_U2);
7699 else if (type == TypeManager.int32_type)
7700 ig.Emit (OpCodes.Ldelem_I4);
7701 else if (type == TypeManager.uint32_type)
7702 ig.Emit (OpCodes.Ldelem_U4);
7703 else if (type == TypeManager.uint64_type)
7704 ig.Emit (OpCodes.Ldelem_I8);
7705 else if (type == TypeManager.int64_type)
7706 ig.Emit (OpCodes.Ldelem_I8);
7707 else if (type == TypeManager.float_type)
7708 ig.Emit (OpCodes.Ldelem_R4);
7709 else if (type == TypeManager.double_type)
7710 ig.Emit (OpCodes.Ldelem_R8);
7711 else if (type == TypeManager.intptr_type)
7712 ig.Emit (OpCodes.Ldelem_I);
7713 else if (TypeManager.IsEnumType (type)){
7714 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7715 } else if (type.IsValueType){
7716 ig.Emit (OpCodes.Ldelema, type);
7717 ig.Emit (OpCodes.Ldobj, type);
7719 } else if (type.IsGenericParameter) {
7721 ig.Emit (OpCodes.Ldelem, type);
7723 ig.Emit (OpCodes.Ldelem_Any, type);
7726 } else if (type.IsPointer)
7727 ig.Emit (OpCodes.Ldelem_I);
7729 ig.Emit (OpCodes.Ldelem_Ref);
7733 /// Returns the right opcode to store an object of Type `t'
7734 /// from an array of T.
7736 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7738 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7739 has_type_arg = false; is_stobj = false;
7740 t = TypeManager.TypeToCoreType (t);
7741 if (TypeManager.IsEnumType (t))
7742 t = TypeManager.EnumToUnderlying (t);
7743 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7744 t == TypeManager.bool_type)
7745 return OpCodes.Stelem_I1;
7746 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7747 t == TypeManager.char_type)
7748 return OpCodes.Stelem_I2;
7749 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7750 return OpCodes.Stelem_I4;
7751 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7752 return OpCodes.Stelem_I8;
7753 else if (t == TypeManager.float_type)
7754 return OpCodes.Stelem_R4;
7755 else if (t == TypeManager.double_type)
7756 return OpCodes.Stelem_R8;
7757 else if (t == TypeManager.intptr_type) {
7758 has_type_arg = true;
7760 return OpCodes.Stobj;
7761 } else if (t.IsValueType) {
7762 has_type_arg = true;
7764 return OpCodes.Stobj;
7766 } else if (t.IsGenericParameter) {
7767 has_type_arg = true;
7769 return OpCodes.Stelem;
7771 return OpCodes.Stelem_Any;
7775 } else if (t.IsPointer)
7776 return OpCodes.Stelem_I;
7778 return OpCodes.Stelem_Ref;
7781 MethodInfo FetchGetMethod ()
7783 ModuleBuilder mb = CodeGen.Module.Builder;
7784 int arg_count = ea.Arguments.Count;
7785 Type [] args = new Type [arg_count];
7788 for (int i = 0; i < arg_count; i++){
7789 //args [i++] = a.Type;
7790 args [i] = TypeManager.int32_type;
7793 get = mb.GetArrayMethod (
7794 ea.Expr.Type, "Get",
7795 CallingConventions.HasThis |
7796 CallingConventions.Standard,
7802 MethodInfo FetchAddressMethod ()
7804 ModuleBuilder mb = CodeGen.Module.Builder;
7805 int arg_count = ea.Arguments.Count;
7806 Type [] args = new Type [arg_count];
7810 ret_type = TypeManager.GetReferenceType (type);
7812 for (int i = 0; i < arg_count; i++){
7813 //args [i++] = a.Type;
7814 args [i] = TypeManager.int32_type;
7817 address = mb.GetArrayMethod (
7818 ea.Expr.Type, "Address",
7819 CallingConventions.HasThis |
7820 CallingConventions.Standard,
7827 // Load the array arguments into the stack.
7829 // If we have been requested to cache the values (cached_locations array
7830 // initialized), then load the arguments the first time and store them
7831 // in locals. otherwise load from local variables.
7833 void LoadArrayAndArguments (EmitContext ec)
7835 ILGenerator ig = ec.ig;
7838 foreach (Argument a in ea.Arguments){
7839 Type argtype = a.Expr.Type;
7843 if (argtype == TypeManager.int64_type)
7844 ig.Emit (OpCodes.Conv_Ovf_I);
7845 else if (argtype == TypeManager.uint64_type)
7846 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7850 public void Emit (EmitContext ec, bool leave_copy)
7852 int rank = ea.Expr.Type.GetArrayRank ();
7853 ILGenerator ig = ec.ig;
7856 LoadArrayAndArguments (ec);
7859 EmitLoadOpcode (ig, type);
7863 method = FetchGetMethod ();
7864 ig.Emit (OpCodes.Call, method);
7867 LoadFromPtr (ec.ig, this.type);
7870 ec.ig.Emit (OpCodes.Dup);
7871 temp = new LocalTemporary (this.type);
7876 public override void Emit (EmitContext ec)
7881 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7883 int rank = ea.Expr.Type.GetArrayRank ();
7884 ILGenerator ig = ec.ig;
7885 Type t = source.Type;
7886 prepared = prepare_for_load;
7888 if (prepare_for_load) {
7889 AddressOf (ec, AddressOp.LoadStore);
7890 ec.ig.Emit (OpCodes.Dup);
7893 ec.ig.Emit (OpCodes.Dup);
7894 temp = new LocalTemporary (this.type);
7897 StoreFromPtr (ec.ig, t);
7907 LoadArrayAndArguments (ec);
7910 bool is_stobj, has_type_arg;
7911 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7913 // The stobj opcode used by value types will need
7914 // an address on the stack, not really an array/array
7918 ig.Emit (OpCodes.Ldelema, t);
7922 ec.ig.Emit (OpCodes.Dup);
7923 temp = new LocalTemporary (this.type);
7928 ig.Emit (OpCodes.Stobj, t);
7929 else if (has_type_arg)
7934 ModuleBuilder mb = CodeGen.Module.Builder;
7935 int arg_count = ea.Arguments.Count;
7936 Type [] args = new Type [arg_count + 1];
7941 ec.ig.Emit (OpCodes.Dup);
7942 temp = new LocalTemporary (this.type);
7946 for (int i = 0; i < arg_count; i++){
7947 //args [i++] = a.Type;
7948 args [i] = TypeManager.int32_type;
7951 args [arg_count] = type;
7953 set = mb.GetArrayMethod (
7954 ea.Expr.Type, "Set",
7955 CallingConventions.HasThis |
7956 CallingConventions.Standard,
7957 TypeManager.void_type, args);
7959 ig.Emit (OpCodes.Call, set);
7968 public void AddressOf (EmitContext ec, AddressOp mode)
7970 int rank = ea.Expr.Type.GetArrayRank ();
7971 ILGenerator ig = ec.ig;
7973 LoadArrayAndArguments (ec);
7976 ig.Emit (OpCodes.Ldelema, type);
7978 MethodInfo address = FetchAddressMethod ();
7979 ig.Emit (OpCodes.Call, address);
7983 public void EmitGetLength (EmitContext ec, int dim)
7985 int rank = ea.Expr.Type.GetArrayRank ();
7986 ILGenerator ig = ec.ig;
7990 ig.Emit (OpCodes.Ldlen);
7991 ig.Emit (OpCodes.Conv_I4);
7993 IntLiteral.EmitInt (ig, dim);
7994 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
8000 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
8001 public readonly ArrayList Properties;
8002 static Indexers empty;
8004 public struct Indexer {
8005 public readonly PropertyInfo PropertyInfo;
8006 public readonly MethodInfo Getter, Setter;
8008 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
8010 this.PropertyInfo = property_info;
8018 empty = new Indexers (null);
8021 Indexers (ArrayList array)
8026 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
8031 foreach (PropertyInfo property in mi){
8032 MethodInfo get, set;
8034 get = property.GetGetMethod (true);
8035 set = property.GetSetMethod (true);
8036 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
8038 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
8040 if (get != null || set != null) {
8042 ix = new Indexers (new ArrayList ());
8043 ix.Properties.Add (new Indexer (property, get, set));
8048 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
8050 string p_name = TypeManager.IndexerPropertyName (lookup_type);
8052 return TypeManager.MemberLookup (
8053 caller_type, caller_type, lookup_type, MemberTypes.Property,
8054 BindingFlags.Public | BindingFlags.Instance |
8055 BindingFlags.DeclaredOnly, p_name, null);
8058 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
8060 Indexers ix = empty;
8063 if (lookup_type.IsGenericParameter) {
8064 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
8068 if (gc.HasClassConstraint)
8069 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
8071 Type[] ifaces = gc.InterfaceConstraints;
8072 foreach (Type itype in ifaces)
8073 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8079 Type copy = lookup_type;
8080 while (copy != TypeManager.object_type && copy != null){
8081 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
8082 copy = copy.BaseType;
8085 if (lookup_type.IsInterface) {
8086 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
8087 if (ifaces != null) {
8088 foreach (Type itype in ifaces)
8089 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8098 /// Expressions that represent an indexer call.
8100 public class IndexerAccess : Expression, IAssignMethod {
8102 // Points to our "data" repository
8104 MethodInfo get, set;
8105 ArrayList set_arguments;
8106 bool is_base_indexer;
8108 protected Type indexer_type;
8109 protected Type current_type;
8110 protected Expression instance_expr;
8111 protected ArrayList arguments;
8113 public IndexerAccess (ElementAccess ea, Location loc)
8114 : this (ea.Expr, false, loc)
8116 this.arguments = ea.Arguments;
8119 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
8122 this.instance_expr = instance_expr;
8123 this.is_base_indexer = is_base_indexer;
8124 this.eclass = ExprClass.Value;
8128 protected virtual bool CommonResolve (EmitContext ec)
8130 indexer_type = instance_expr.Type;
8131 current_type = ec.ContainerType;
8136 public override Expression DoResolve (EmitContext ec)
8138 if (!CommonResolve (ec))
8142 // Step 1: Query for all `Item' *properties*. Notice
8143 // that the actual methods are pointed from here.
8145 // This is a group of properties, piles of them.
8147 ArrayList AllGetters = null;
8149 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8150 if (ilist.Properties != null) {
8151 AllGetters = new ArrayList(ilist.Properties.Count);
8152 foreach (Indexers.Indexer ix in ilist.Properties) {
8153 if (ix.Getter != null)
8154 AllGetters.Add (ix.Getter);
8158 if (AllGetters == null) {
8159 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8160 TypeManager.CSharpName (indexer_type));
8164 if (AllGetters.Count == 0) {
8165 // FIXME: we cannot simply select first one as the error message is missleading when
8166 // multiple indexers exist
8167 Indexers.Indexer first_indexer = (Indexers.Indexer)ilist.Properties[ilist.Properties.Count - 1];
8168 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
8169 TypeManager.GetFullNameSignature (first_indexer.PropertyInfo));
8173 get = (MethodInfo)Invocation.OverloadResolve (ec, new MethodGroupExpr (AllGetters, loc),
8174 arguments, false, loc);
8177 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8182 // Only base will allow this invocation to happen.
8184 if (get.IsAbstract && this is BaseIndexerAccess){
8185 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
8189 type = get.ReturnType;
8190 if (type.IsPointer && !ec.InUnsafe){
8195 instance_expr.CheckMarshalByRefAccess ();
8197 eclass = ExprClass.IndexerAccess;
8201 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8203 if (right_side == EmptyExpression.OutAccess) {
8204 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
8205 GetSignatureForError ());
8209 // if the indexer returns a value type, and we try to set a field in it
8210 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
8211 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
8212 GetSignatureForError ());
8216 ArrayList AllSetters = new ArrayList();
8217 if (!CommonResolve (ec))
8220 bool found_any = false, found_any_setters = false;
8222 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8223 if (ilist.Properties != null) {
8225 foreach (Indexers.Indexer ix in ilist.Properties) {
8226 if (ix.Setter != null)
8227 AllSetters.Add (ix.Setter);
8230 if (AllSetters.Count > 0) {
8231 found_any_setters = true;
8232 set_arguments = (ArrayList) arguments.Clone ();
8233 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
8234 set = (MethodInfo) Invocation.OverloadResolve (
8235 ec, new MethodGroupExpr (AllSetters, loc),
8236 set_arguments, false, loc);
8240 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8241 TypeManager.CSharpName (indexer_type));
8245 if (!found_any_setters) {
8246 Error (154, "indexer can not be used in this context, because " +
8247 "it lacks a `set' accessor");
8252 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8257 // Only base will allow this invocation to happen.
8259 if (set.IsAbstract && this is BaseIndexerAccess){
8260 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
8265 // Now look for the actual match in the list of indexers to set our "return" type
8267 type = TypeManager.void_type; // default value
8268 foreach (Indexers.Indexer ix in ilist.Properties){
8269 if (ix.Setter == set){
8270 type = ix.PropertyInfo.PropertyType;
8275 instance_expr.CheckMarshalByRefAccess ();
8277 eclass = ExprClass.IndexerAccess;
8281 bool prepared = false;
8282 LocalTemporary temp;
8284 public void Emit (EmitContext ec, bool leave_copy)
8286 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
8288 ec.ig.Emit (OpCodes.Dup);
8289 temp = new LocalTemporary (Type);
8295 // source is ignored, because we already have a copy of it from the
8296 // LValue resolution and we have already constructed a pre-cached
8297 // version of the arguments (ea.set_arguments);
8299 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8301 prepared = prepare_for_load;
8302 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
8307 ec.ig.Emit (OpCodes.Dup);
8308 temp = new LocalTemporary (Type);
8311 } else if (leave_copy) {
8312 temp = new LocalTemporary (Type);
8318 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
8327 public override void Emit (EmitContext ec)
8332 public override string GetSignatureForError ()
8334 // FIXME: print the argument list of the indexer
8335 return instance_expr.GetSignatureForError () + ".this[...]";
8340 /// The base operator for method names
8342 public class BaseAccess : Expression {
8343 public readonly string Identifier;
8345 public BaseAccess (string member, Location l)
8347 this.Identifier = member;
8351 public BaseAccess (string member, TypeArguments args, Location l)
8359 public override Expression DoResolve (EmitContext ec)
8361 Expression c = CommonResolve (ec);
8367 // MethodGroups use this opportunity to flag an error on lacking ()
8369 if (!(c is MethodGroupExpr))
8370 return c.Resolve (ec);
8374 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8376 Expression c = CommonResolve (ec);
8382 // MethodGroups use this opportunity to flag an error on lacking ()
8384 if (! (c is MethodGroupExpr))
8385 return c.DoResolveLValue (ec, right_side);
8390 Expression CommonResolve (EmitContext ec)
8392 Expression member_lookup;
8393 Type current_type = ec.ContainerType;
8394 Type base_type = current_type.BaseType;
8397 Error (1511, "Keyword `base' is not available in a static method");
8401 if (ec.IsFieldInitializer){
8402 Error (1512, "Keyword `base' is not available in the current context");
8406 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8407 AllMemberTypes, AllBindingFlags, loc);
8408 if (member_lookup == null) {
8409 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8416 left = new TypeExpression (base_type, loc);
8418 left = ec.GetThis (loc);
8420 MemberExpr me = (MemberExpr) member_lookup;
8422 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8424 if (e is PropertyExpr) {
8425 PropertyExpr pe = (PropertyExpr) e;
8430 MethodGroupExpr mg = e as MethodGroupExpr;
8436 return mg.ResolveGeneric (ec, args);
8438 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8446 public override void Emit (EmitContext ec)
8448 throw new Exception ("Should never be called");
8453 /// The base indexer operator
8455 public class BaseIndexerAccess : IndexerAccess {
8456 public BaseIndexerAccess (ArrayList args, Location loc)
8457 : base (null, true, loc)
8459 arguments = new ArrayList ();
8460 foreach (Expression tmp in args)
8461 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8464 protected override bool CommonResolve (EmitContext ec)
8466 instance_expr = ec.GetThis (loc);
8468 current_type = ec.ContainerType.BaseType;
8469 indexer_type = current_type;
8471 foreach (Argument a in arguments){
8472 if (!a.Resolve (ec, loc))
8481 /// This class exists solely to pass the Type around and to be a dummy
8482 /// that can be passed to the conversion functions (this is used by
8483 /// foreach implementation to typecast the object return value from
8484 /// get_Current into the proper type. All code has been generated and
8485 /// we only care about the side effect conversions to be performed
8487 /// This is also now used as a placeholder where a no-action expression
8488 /// is needed (the `New' class).
8490 public class EmptyExpression : Expression {
8491 public static readonly EmptyExpression Null = new EmptyExpression ();
8493 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8494 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8495 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8497 static EmptyExpression temp = new EmptyExpression ();
8498 public static EmptyExpression Grab ()
8500 EmptyExpression retval = temp == null ? new EmptyExpression () : temp;
8505 public static void Release (EmptyExpression e)
8510 // TODO: should be protected
8511 public EmptyExpression ()
8513 type = TypeManager.object_type;
8514 eclass = ExprClass.Value;
8515 loc = Location.Null;
8518 public EmptyExpression (Type t)
8521 eclass = ExprClass.Value;
8522 loc = Location.Null;
8525 public override Expression DoResolve (EmitContext ec)
8530 public override void Emit (EmitContext ec)
8532 // nothing, as we only exist to not do anything.
8536 // This is just because we might want to reuse this bad boy
8537 // instead of creating gazillions of EmptyExpressions.
8538 // (CanImplicitConversion uses it)
8540 public void SetType (Type t)
8546 public class UserCast : Expression {
8550 public UserCast (MethodInfo method, Expression source, Location l)
8552 this.method = method;
8553 this.source = source;
8554 type = method.ReturnType;
8555 eclass = ExprClass.Value;
8559 public Expression Source {
8565 public override Expression DoResolve (EmitContext ec)
8568 // We are born fully resolved
8573 public override void Emit (EmitContext ec)
8575 ILGenerator ig = ec.ig;
8579 if (method is MethodInfo)
8580 ig.Emit (OpCodes.Call, (MethodInfo) method);
8582 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8588 // This class is used to "construct" the type during a typecast
8589 // operation. Since the Type.GetType class in .NET can parse
8590 // the type specification, we just use this to construct the type
8591 // one bit at a time.
8593 public class ComposedCast : TypeExpr {
8597 public ComposedCast (Expression left, string dim)
8598 : this (left, dim, left.Location)
8602 public ComposedCast (Expression left, string dim, Location l)
8610 public Expression RemoveNullable ()
8612 if (dim.EndsWith ("?")) {
8613 dim = dim.Substring (0, dim.Length - 1);
8622 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8624 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8628 Type ltype = lexpr.Type;
8629 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8630 Error_VoidInvalidInTheContext (loc);
8635 if ((dim.Length > 0) && (dim [0] == '?')) {
8636 TypeExpr nullable = new NullableType (left, loc);
8638 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8639 return nullable.ResolveAsTypeTerminal (ec, false);
8643 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc))
8646 if (dim != "" && dim [0] == '[' &&
8647 (ltype == TypeManager.arg_iterator_type || ltype == TypeManager.typed_reference_type)) {
8648 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (ltype));
8653 type = TypeManager.GetConstructedType (ltype, dim);
8658 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8660 if (type.IsPointer && !ec.IsInUnsafeScope){
8665 eclass = ExprClass.Type;
8669 public override string Name {
8670 get { return left + dim; }
8673 public override string FullName {
8674 get { return type.FullName; }
8677 public override string GetSignatureForError ()
8679 return left.GetSignatureForError () + dim;
8683 public class FixedBufferPtr : Expression {
8686 public FixedBufferPtr (Expression array, Type array_type, Location l)
8691 type = TypeManager.GetPointerType (array_type);
8692 eclass = ExprClass.Value;
8695 public override void Emit(EmitContext ec)
8700 public override Expression DoResolve (EmitContext ec)
8703 // We are born fully resolved
8711 // This class is used to represent the address of an array, used
8712 // only by the Fixed statement, this generates "&a [0]" construct
8713 // for fixed (char *pa = a)
8715 public class ArrayPtr : FixedBufferPtr {
8718 public ArrayPtr (Expression array, Type array_type, Location l):
8719 base (array, array_type, l)
8721 this.array_type = array_type;
8724 public override void Emit (EmitContext ec)
8728 ILGenerator ig = ec.ig;
8729 IntLiteral.EmitInt (ig, 0);
8730 ig.Emit (OpCodes.Ldelema, array_type);
8735 // Used by the fixed statement
8737 public class StringPtr : Expression {
8740 public StringPtr (LocalBuilder b, Location l)
8743 eclass = ExprClass.Value;
8744 type = TypeManager.char_ptr_type;
8748 public override Expression DoResolve (EmitContext ec)
8750 // This should never be invoked, we are born in fully
8751 // initialized state.
8756 public override void Emit (EmitContext ec)
8758 ILGenerator ig = ec.ig;
8760 ig.Emit (OpCodes.Ldloc, b);
8761 ig.Emit (OpCodes.Conv_I);
8762 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8763 ig.Emit (OpCodes.Add);
8768 // Implements the `stackalloc' keyword
8770 public class StackAlloc : Expression {
8775 public StackAlloc (Expression type, Expression count, Location l)
8782 public override Expression DoResolve (EmitContext ec)
8784 count = count.Resolve (ec);
8788 if (count.Type != TypeManager.int32_type){
8789 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8794 Constant c = count as Constant;
8795 if (c != null && c.IsNegative) {
8796 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8800 if (ec.InCatch || ec.InFinally) {
8801 Error (255, "Cannot use stackalloc in finally or catch");
8805 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8811 if (!TypeManager.VerifyUnManaged (otype, loc))
8814 type = TypeManager.GetPointerType (otype);
8815 eclass = ExprClass.Value;
8820 public override void Emit (EmitContext ec)
8822 int size = GetTypeSize (otype);
8823 ILGenerator ig = ec.ig;
8826 ig.Emit (OpCodes.Sizeof, otype);
8828 IntConstant.EmitInt (ig, size);
8830 ig.Emit (OpCodes.Mul);
8831 ig.Emit (OpCodes.Localloc);