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.ContainerType, 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 RootScopeInfo host = null;
3724 ToplevelBlock toplevel = block.Toplevel;
3725 while (toplevel != null) {
3726 if (toplevel.IsLocalParameter (name))
3729 toplevel = toplevel.Container;
3732 ScopeInfo scope = toplevel.CreateScopeInfo ();
3733 variable = scope.AddParameter (par, idx);
3734 type = variable.Type;
3738 public override int GetHashCode()
3740 return name.GetHashCode ();
3743 public override bool Equals (object obj)
3745 ParameterReference pr = obj as ParameterReference;
3749 return name == pr.name && block == pr.block;
3753 // Notice that for ref/out parameters, the type exposed is not the
3754 // same type exposed externally.
3757 // externally we expose "int&"
3758 // here we expose "int".
3760 // We record this in "is_ref". This means that the type system can treat
3761 // the type as it is expected, but when we generate the code, we generate
3762 // the alternate kind of code.
3764 public override Expression DoResolve (EmitContext ec)
3766 if (!DoResolveBase (ec))
3769 if (is_out && ec.DoFlowAnalysis &&
3770 (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3776 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3778 if (!DoResolveBase (ec))
3786 static public void EmitLdArg (ILGenerator ig, int x)
3790 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3791 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3792 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3793 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3794 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3797 ig.Emit (OpCodes.Ldarg, x);
3800 public override string ToString ()
3802 return "ParameterReference[" + name + "]";
3807 /// Used for arguments to New(), Invocation()
3809 public class Argument {
3810 public enum AType : byte {
3817 public readonly AType ArgType;
3818 public Expression Expr;
3820 public Argument (Expression expr, AType type)
3823 this.ArgType = type;
3826 public Argument (Expression expr)
3829 this.ArgType = AType.Expression;
3834 if (ArgType == AType.Ref || ArgType == AType.Out)
3835 return TypeManager.GetReferenceType (Expr.Type);
3841 public Parameter.Modifier Modifier
3846 return Parameter.Modifier.OUT;
3849 return Parameter.Modifier.REF;
3852 return Parameter.Modifier.NONE;
3857 public static string FullDesc (Argument a)
3859 if (a.ArgType == AType.ArgList)
3862 return (a.ArgType == AType.Ref ? "ref " :
3863 (a.ArgType == AType.Out ? "out " : "")) +
3864 TypeManager.CSharpName (a.Expr.Type);
3867 public bool ResolveMethodGroup (EmitContext ec)
3869 SimpleName sn = Expr as SimpleName;
3871 Expr = sn.GetMethodGroup ();
3873 // FIXME: csc doesn't report any error if you try to use `ref' or
3874 // `out' in a delegate creation expression.
3875 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3882 public bool Resolve (EmitContext ec, Location loc)
3884 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3885 // Verify that the argument is readable
3886 if (ArgType != AType.Out)
3887 Expr = Expr.Resolve (ec);
3889 // Verify that the argument is writeable
3890 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3891 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3893 return Expr != null;
3897 public void Emit (EmitContext ec)
3899 if (ArgType != AType.Ref && ArgType != AType.Out) {
3904 AddressOp mode = AddressOp.Store;
3905 if (ArgType == AType.Ref)
3906 mode |= AddressOp.Load;
3908 IMemoryLocation ml = (IMemoryLocation) Expr;
3909 ParameterReference pr = ml as ParameterReference;
3912 // ParameterReferences might already be references, so we want
3913 // to pass just the value
3915 if (pr != null && pr.IsRef)
3918 ml.AddressOf (ec, mode);
3923 /// Invocation of methods or delegates.
3925 public class Invocation : ExpressionStatement {
3926 public readonly ArrayList Arguments;
3929 MethodBase method = null;
3932 // arguments is an ArrayList, but we do not want to typecast,
3933 // as it might be null.
3935 // FIXME: only allow expr to be a method invocation or a
3936 // delegate invocation (7.5.5)
3938 public Invocation (Expression expr, ArrayList arguments)
3941 Arguments = arguments;
3942 loc = expr.Location;
3945 public Expression Expr {
3952 /// Determines "better conversion" as specified in 14.4.2.3
3954 /// Returns : p if a->p is better,
3955 /// q if a->q is better,
3956 /// null if neither is better
3958 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q)
3960 Type argument_type = TypeManager.TypeToCoreType (a.Type);
3961 Expression argument_expr = a.Expr;
3963 if (argument_type == null)
3964 throw new Exception ("Expression of type " + a.Expr +
3965 " does not resolve its type");
3967 if (p == null || q == null)
3968 throw new InternalErrorException ("BetterConversion Got a null conversion");
3973 if (argument_expr is NullLiteral) {
3975 // If the argument is null and one of the types to compare is 'object' and
3976 // the other is a reference type, we prefer the other.
3978 // This follows from the usual rules:
3979 // * There is an implicit conversion from 'null' to type 'object'
3980 // * There is an implicit conversion from 'null' to any reference type
3981 // * There is an implicit conversion from any reference type to type 'object'
3982 // * There is no implicit conversion from type 'object' to other reference types
3983 // => Conversion of 'null' to a reference type is better than conversion to 'object'
3985 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
3986 // null type. I think it used to be 'object' and thus needed a special
3987 // case to avoid the immediately following two checks.
3989 if (!p.IsValueType && q == TypeManager.object_type)
3991 if (!q.IsValueType && p == TypeManager.object_type)
3995 if (argument_type == p)
3998 if (argument_type == q)
4001 Expression p_tmp = new EmptyExpression (p);
4002 Expression q_tmp = new EmptyExpression (q);
4004 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
4005 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
4007 if (p_to_q && !q_to_p)
4010 if (q_to_p && !p_to_q)
4013 if (p == TypeManager.sbyte_type)
4014 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
4015 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4017 if (q == TypeManager.sbyte_type)
4018 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
4019 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4022 if (p == TypeManager.short_type)
4023 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
4024 q == TypeManager.uint64_type)
4026 if (q == TypeManager.short_type)
4027 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
4028 p == TypeManager.uint64_type)
4031 if (p == TypeManager.int32_type)
4032 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4034 if (q == TypeManager.int32_type)
4035 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4038 if (p == TypeManager.int64_type)
4039 if (q == TypeManager.uint64_type)
4041 if (q == TypeManager.int64_type)
4042 if (p == TypeManager.uint64_type)
4048 static Type MoreSpecific (Type p, Type q)
4050 if (TypeManager.IsGenericParameter (p) && !TypeManager.IsGenericParameter (q))
4052 if (!TypeManager.IsGenericParameter (p) && TypeManager.IsGenericParameter (q))
4055 if (TypeManager.HasElementType (p)) {
4056 Type pe = TypeManager.GetElementType (p);
4057 Type qe = TypeManager.GetElementType (q);
4058 Type specific = MoreSpecific (pe, qe);
4063 } else if (TypeManager.IsGenericType (p)) {
4064 Type[] pargs = TypeManager.GetTypeArguments (p);
4065 Type[] qargs = TypeManager.GetTypeArguments (q);
4067 bool p_specific_at_least_once = false;
4068 bool q_specific_at_least_once = false;
4070 for (int i = 0; i < pargs.Length; i++) {
4071 Type specific = MoreSpecific (pargs [i], qargs [i]);
4072 if (specific == pargs [i])
4073 p_specific_at_least_once = true;
4074 if (specific == qargs [i])
4075 q_specific_at_least_once = true;
4078 if (p_specific_at_least_once && !q_specific_at_least_once)
4080 if (!p_specific_at_least_once && q_specific_at_least_once)
4088 /// Determines "Better function" between candidate
4089 /// and the current best match
4092 /// Returns a boolean indicating :
4093 /// false if candidate ain't better
4094 /// true if candidate is better than the current best match
4096 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4097 MethodBase candidate, bool candidate_params,
4098 MethodBase best, bool best_params)
4100 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4101 ParameterData best_pd = TypeManager.GetParameterData (best);
4103 bool better_at_least_one = false;
4105 for (int j = 0; j < argument_count; ++j) {
4106 Argument a = (Argument) args [j];
4108 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
4109 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
4111 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4112 if (candidate_params)
4113 ct = TypeManager.GetElementType (ct);
4115 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4117 bt = TypeManager.GetElementType (bt);
4123 Type better = BetterConversion (ec, a, ct, bt);
4125 // for each argument, the conversion to 'ct' should be no worse than
4126 // the conversion to 'bt'.
4130 // for at least one argument, the conversion to 'ct' should be better than
4131 // the conversion to 'bt'.
4133 better_at_least_one = true;
4136 if (better_at_least_one)
4140 // This handles the case
4142 // Add (float f1, float f2, float f3);
4143 // Add (params decimal [] foo);
4145 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4146 // first candidate would've chosen as better.
4152 // The two methods have equal parameter types. Now apply tie-breaking rules
4154 if (TypeManager.IsGenericMethod (best) && !TypeManager.IsGenericMethod (candidate))
4156 if (!TypeManager.IsGenericMethod (best) && TypeManager.IsGenericMethod (candidate))
4160 // This handles the following cases:
4162 // Trim () is better than Trim (params char[] chars)
4163 // Concat (string s1, string s2, string s3) is better than
4164 // Concat (string s1, params string [] srest)
4165 // Foo (int, params int [] rest) is better than Foo (params int [] rest)
4167 if (!candidate_params && best_params)
4169 if (candidate_params && !best_params)
4172 int candidate_param_count = candidate_pd.Count;
4173 int best_param_count = best_pd.Count;
4175 if (candidate_param_count != best_param_count)
4176 // can only happen if (candidate_params && best_params)
4177 return candidate_param_count > best_param_count;
4180 // now, both methods have the same number of parameters, and the parameters have the same types
4181 // Pick the "more specific" signature
4184 MethodBase orig_candidate = TypeManager.DropGenericMethodArguments (candidate);
4185 MethodBase orig_best = TypeManager.DropGenericMethodArguments (best);
4187 ParameterData orig_candidate_pd = TypeManager.GetParameterData (orig_candidate);
4188 ParameterData orig_best_pd = TypeManager.GetParameterData (orig_best);
4190 bool specific_at_least_once = false;
4191 for (int j = 0; j < candidate_param_count; ++j) {
4192 Type ct = TypeManager.TypeToCoreType (orig_candidate_pd.ParameterType (j));
4193 Type bt = TypeManager.TypeToCoreType (orig_best_pd.ParameterType (j));
4196 Type specific = MoreSpecific (ct, bt);
4200 specific_at_least_once = true;
4203 if (specific_at_least_once)
4206 // FIXME: handle lifted operators
4212 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4214 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4217 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4218 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4220 if (cand_pd.Count != base_pd.Count)
4223 for (int j = 0; j < cand_pd.Count; ++j) {
4224 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4225 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4226 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4227 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4229 if (cm != bm || ct != bt)
4236 public static string FullMethodDesc (MethodBase mb)
4242 if (mb is MethodInfo) {
4243 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4247 sb = new StringBuilder ();
4249 sb.Append (TypeManager.CSharpSignature (mb));
4250 return sb.ToString ();
4253 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4255 MemberInfo [] miset;
4256 MethodGroupExpr union;
4261 return (MethodGroupExpr) mg2;
4264 return (MethodGroupExpr) mg1;
4267 MethodGroupExpr left_set = null, right_set = null;
4268 int length1 = 0, length2 = 0;
4270 left_set = (MethodGroupExpr) mg1;
4271 length1 = left_set.Methods.Length;
4273 right_set = (MethodGroupExpr) mg2;
4274 length2 = right_set.Methods.Length;
4276 ArrayList common = new ArrayList ();
4278 foreach (MethodBase r in right_set.Methods){
4279 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4283 miset = new MemberInfo [length1 + length2 - common.Count];
4284 left_set.Methods.CopyTo (miset, 0);
4288 foreach (MethodBase r in right_set.Methods) {
4289 if (!common.Contains (r))
4293 union = new MethodGroupExpr (miset, loc);
4298 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4299 ArrayList arguments, int arg_count,
4300 ref MethodBase candidate)
4302 return IsParamsMethodApplicable (
4303 ec, me, arguments, arg_count, false, ref candidate) ||
4304 IsParamsMethodApplicable (
4305 ec, me, arguments, arg_count, true, ref candidate);
4310 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4311 ArrayList arguments, int arg_count,
4312 bool do_varargs, ref MethodBase candidate)
4315 if (!me.HasTypeArguments &&
4316 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4319 if (TypeManager.IsGenericMethodDefinition (candidate))
4320 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4323 return IsParamsMethodApplicable (
4324 ec, arguments, arg_count, candidate, do_varargs);
4328 /// Determines if the candidate method, if a params method, is applicable
4329 /// in its expanded form to the given set of arguments
4331 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4332 int arg_count, MethodBase candidate,
4335 ParameterData pd = TypeManager.GetParameterData (candidate);
4337 int pd_count = pd.Count;
4341 int count = pd_count - 1;
4343 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4345 if (pd_count != arg_count)
4352 if (count > arg_count)
4355 if (pd_count == 1 && arg_count == 0)
4359 // If we have come this far, the case which
4360 // remains is when the number of parameters is
4361 // less than or equal to the argument count.
4363 for (int i = 0; i < count; ++i) {
4365 Argument a = (Argument) arguments [i];
4367 Parameter.Modifier a_mod = a.Modifier &
4368 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4369 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4370 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4372 if (a_mod == p_mod) {
4374 if (a_mod == Parameter.Modifier.NONE)
4375 if (!Convert.ImplicitConversionExists (ec,
4377 pd.ParameterType (i)))
4380 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4381 Type pt = pd.ParameterType (i);
4384 pt = TypeManager.GetReferenceType (pt);
4395 Argument a = (Argument) arguments [count];
4396 if (!(a.Expr is Arglist))
4402 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
4404 for (int i = pd_count - 1; i < arg_count; i++) {
4405 Argument a = (Argument) arguments [i];
4407 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4414 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4415 ArrayList arguments, int arg_count,
4416 ref MethodBase candidate)
4419 if (!me.HasTypeArguments &&
4420 !TypeManager.InferTypeArguments (arguments, ref candidate))
4423 if (TypeManager.IsGenericMethodDefinition (candidate))
4424 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4427 return IsApplicable (ec, arguments, arg_count, candidate);
4431 /// Determines if the candidate method is applicable (section 14.4.2.1)
4432 /// to the given set of arguments
4434 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4435 MethodBase candidate)
4437 ParameterData pd = TypeManager.GetParameterData (candidate);
4439 if (arg_count != pd.Count)
4442 for (int i = arg_count; i > 0; ) {
4445 Argument a = (Argument) arguments [i];
4447 Parameter.Modifier a_mod = a.Modifier &
4448 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4450 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4451 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4453 if (a_mod == p_mod) {
4454 Type pt = pd.ParameterType (i);
4456 if (a_mod == Parameter.Modifier.NONE) {
4457 if (!TypeManager.IsEqual (a.Type, pt) &&
4458 !Convert.ImplicitConversionExists (ec, a.Expr, pt))
4472 static internal bool IsAncestralType (Type first_type, Type second_type)
4474 return first_type != second_type &&
4475 (TypeManager.IsSubclassOf (second_type, first_type) ||
4476 TypeManager.ImplementsInterface (second_type, first_type));
4480 /// Find the Applicable Function Members (7.4.2.1)
4482 /// me: Method Group expression with the members to select.
4483 /// it might contain constructors or methods (or anything
4484 /// that maps to a method).
4486 /// Arguments: ArrayList containing resolved Argument objects.
4488 /// loc: The location if we want an error to be reported, or a Null
4489 /// location for "probing" purposes.
4491 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4492 /// that is the best match of me on Arguments.
4495 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4496 ArrayList Arguments, bool may_fail,
4499 MethodBase method = null;
4500 bool method_params = false;
4501 Type applicable_type = null;
4503 ArrayList candidates = new ArrayList (2);
4504 ArrayList candidate_overrides = null;
4507 // Used to keep a map between the candidate
4508 // and whether it is being considered in its
4509 // normal or expanded form
4511 // false is normal form, true is expanded form
4513 Hashtable candidate_to_form = null;
4515 if (Arguments != null)
4516 arg_count = Arguments.Count;
4518 if ((me.Name == "Invoke") &&
4519 TypeManager.IsDelegateType (me.DeclaringType)) {
4520 Error_InvokeOnDelegate (loc);
4524 MethodBase[] methods = me.Methods;
4526 int nmethods = methods.Length;
4530 // Methods marked 'override' don't take part in 'applicable_type'
4531 // computation, nor in the actual overload resolution.
4532 // However, they still need to be emitted instead of a base virtual method.
4533 // So, we salt them away into the 'candidate_overrides' array.
4535 // In case of reflected methods, we replace each overriding method with
4536 // its corresponding base virtual method. This is to improve compatibility
4537 // with non-C# libraries which change the visibility of overrides (#75636)
4540 for (int i = 0; i < methods.Length; ++i) {
4541 MethodBase m = methods [i];
4543 Type [] gen_args = null;
4544 if (m.IsGenericMethod && !m.IsGenericMethodDefinition)
4545 gen_args = m.GetGenericArguments ();
4547 if (TypeManager.IsOverride (m)) {
4548 if (candidate_overrides == null)
4549 candidate_overrides = new ArrayList ();
4550 candidate_overrides.Add (m);
4551 m = TypeManager.TryGetBaseDefinition (m);
4553 if (m != null && gen_args != null) {
4554 if (!m.IsGenericMethodDefinition)
4555 throw new InternalErrorException ("GetBaseDefinition didn't return a GenericMethodDefinition");
4556 m = ((MethodInfo) m).MakeGenericMethod (gen_args);
4566 int applicable_errors = Report.Errors;
4569 // First we construct the set of applicable methods
4571 bool is_sorted = true;
4572 for (int i = 0; i < nmethods; i++){
4573 Type decl_type = methods [i].DeclaringType;
4576 // If we have already found an applicable method
4577 // we eliminate all base types (Section 14.5.5.1)
4579 if (applicable_type != null && IsAncestralType (decl_type, applicable_type))
4583 // Check if candidate is applicable (section 14.4.2.1)
4584 // Is candidate applicable in normal form?
4586 bool is_applicable = IsApplicable (ec, me, Arguments, arg_count, ref methods [i]);
4588 if (!is_applicable && IsParamsMethodApplicable (ec, me, Arguments, arg_count, ref methods [i])) {
4589 MethodBase candidate = methods [i];
4590 if (candidate_to_form == null)
4591 candidate_to_form = new PtrHashtable ();
4592 candidate_to_form [candidate] = candidate;
4593 // Candidate is applicable in expanded form
4594 is_applicable = true;
4600 candidates.Add (methods [i]);
4602 if (applicable_type == null)
4603 applicable_type = decl_type;
4604 else if (applicable_type != decl_type) {
4606 if (IsAncestralType (applicable_type, decl_type))
4607 applicable_type = decl_type;
4611 if (applicable_errors != Report.Errors)
4614 int candidate_top = candidates.Count;
4616 if (applicable_type == null) {
4618 // Okay so we have failed to find anything so we
4619 // return by providing info about the closest match
4621 int errors = Report.Errors;
4622 for (int i = 0; i < nmethods; ++i) {
4623 MethodBase c = (MethodBase) methods [i];
4624 ParameterData pd = TypeManager.GetParameterData (c);
4626 if (pd.Count != arg_count)
4630 if (!TypeManager.InferTypeArguments (Arguments, ref c))
4632 if (TypeManager.IsGenericMethodDefinition (c))
4636 VerifyArgumentsCompat (ec, Arguments, arg_count,
4637 c, false, null, may_fail, loc);
4639 if (!may_fail && errors == Report.Errors)
4640 throw new InternalErrorException (
4641 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4642 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4647 if (!may_fail && errors == Report.Errors) {
4648 string report_name = me.Name;
4649 if (report_name == ".ctor")
4650 report_name = TypeManager.CSharpName (me.DeclaringType);
4656 for (int i = 0; i < methods.Length; ++i) {
4657 MethodBase c = methods [i];
4658 ParameterData pd = TypeManager.GetParameterData (c);
4660 if (pd.Count != arg_count)
4663 if (TypeManager.InferTypeArguments (Arguments, ref c))
4667 411, loc, "The type arguments for " +
4668 "method `{0}' cannot be infered from " +
4669 "the usage. Try specifying the type " +
4670 "arguments explicitly.", report_name);
4675 Error_WrongNumArguments (loc, report_name, arg_count);
4683 // At this point, applicable_type is _one_ of the most derived types
4684 // in the set of types containing the methods in this MethodGroup.
4685 // Filter the candidates so that they only contain methods from the
4686 // most derived types.
4689 int finalized = 0; // Number of finalized candidates
4692 // Invariant: applicable_type is a most derived type
4694 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4695 // eliminating all it's base types. At the same time, we'll also move
4696 // every unrelated type to the end of the array, and pick the next
4697 // 'applicable_type'.
4699 Type next_applicable_type = null;
4700 int j = finalized; // where to put the next finalized candidate
4701 int k = finalized; // where to put the next undiscarded candidate
4702 for (int i = finalized; i < candidate_top; ++i) {
4703 MethodBase candidate = (MethodBase) candidates [i];
4704 Type decl_type = candidate.DeclaringType;
4706 if (decl_type == applicable_type) {
4707 candidates [k++] = candidates [j];
4708 candidates [j++] = candidates [i];
4712 if (IsAncestralType (decl_type, applicable_type))
4715 if (next_applicable_type != null &&
4716 IsAncestralType (decl_type, next_applicable_type))
4719 candidates [k++] = candidates [i];
4721 if (next_applicable_type == null ||
4722 IsAncestralType (next_applicable_type, decl_type))
4723 next_applicable_type = decl_type;
4726 applicable_type = next_applicable_type;
4729 } while (applicable_type != null);
4733 // Now we actually find the best method
4736 method = (MethodBase) candidates [0];
4737 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4738 for (int ix = 1; ix < candidate_top; ix++){
4739 MethodBase candidate = (MethodBase) candidates [ix];
4741 if (candidate == method)
4744 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4746 if (BetterFunction (ec, Arguments, arg_count,
4747 candidate, cand_params,
4748 method, method_params)) {
4750 method_params = cand_params;
4754 // Now check that there are no ambiguities i.e the selected method
4755 // should be better than all the others
4757 MethodBase ambiguous = null;
4758 for (int ix = 0; ix < candidate_top; ix++){
4759 MethodBase candidate = (MethodBase) candidates [ix];
4761 if (candidate == method)
4764 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4765 if (!BetterFunction (ec, Arguments, arg_count,
4766 method, method_params,
4767 candidate, cand_params)) {
4768 Report.SymbolRelatedToPreviousError (candidate);
4769 ambiguous = candidate;
4773 if (ambiguous != null) {
4774 Report.SymbolRelatedToPreviousError (method);
4775 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4776 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4781 // If the method is a virtual function, pick an override closer to the LHS type.
4783 if (!me.IsBase && method.IsVirtual) {
4784 if (TypeManager.IsOverride (method))
4785 throw new InternalErrorException (
4786 "Should not happen. An 'override' method took part in overload resolution: " + method);
4788 if (candidate_overrides != null)
4789 foreach (MethodBase candidate in candidate_overrides) {
4790 if (IsOverride (candidate, method))
4796 // And now check if the arguments are all
4797 // compatible, perform conversions if
4798 // necessary etc. and return if everything is
4801 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4802 method_params, null, may_fail, loc))
4808 MethodBase the_method = TypeManager.DropGenericMethodArguments (method);
4810 if (the_method.IsGenericMethodDefinition &&
4811 !ConstraintChecker.CheckConstraints (ec, the_method, method, loc))
4815 IMethodData data = TypeManager.GetMethod (the_method);
4817 data.SetMemberIsUsed ();
4822 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4824 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4825 name, arg_count.ToString ());
4828 static void Error_InvokeOnDelegate (Location loc)
4830 Report.Error (1533, loc,
4831 "Invoke cannot be called directly on a delegate");
4834 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4835 Type delegate_type, Argument a, ParameterData expected_par)
4837 if (delegate_type == null)
4838 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4839 TypeManager.CSharpSignature (method));
4841 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4842 TypeManager.CSharpName (delegate_type));
4844 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4846 string index = (idx + 1).ToString ();
4847 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4848 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4849 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4850 index, Parameter.GetModifierSignature (a.Modifier));
4852 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4853 index, Parameter.GetModifierSignature (mod));
4855 string p1 = Argument.FullDesc (a);
4856 string p2 = expected_par.ParameterDesc (idx);
4859 // The parameter names are the same, most likely they come from different
4863 Report.Error (1503, loc,
4864 "Argument {0}: Cannot conver from equally named types from different " +
4865 "assemblies {0} (from {1}) and {2} (from {3})",
4866 p1, a.Expr.Type.Assembly.FullName, p2,
4867 expected_par.ParameterType (idx).Assembly.FullName);
4869 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4874 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4875 int arg_count, MethodBase method,
4876 bool chose_params_expanded,
4877 Type delegate_type, bool may_fail,
4880 ParameterData pd = TypeManager.GetParameterData (method);
4882 for (j = 0; j < arg_count; j++) {
4883 Argument a = (Argument) Arguments [j];
4884 Expression a_expr = a.Expr;
4885 Type parameter_type = pd.ParameterType (j);
4886 Parameter.Modifier pm = pd.ParameterModifier (j);
4887 Parameter.Modifier am = a.Modifier;
4889 if (pm == Parameter.Modifier.ARGLIST) {
4890 if (!(a.Expr is Arglist))
4895 if (pm == Parameter.Modifier.PARAMS) {
4896 pm = Parameter.Modifier.NONE;
4897 if (chose_params_expanded)
4898 parameter_type = TypeManager.GetElementType (parameter_type);
4904 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4905 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4908 Expression conv = Convert.ImplicitConversion (ec, a_expr, parameter_type, loc);
4912 // Update the argument with the implicit conversion
4917 if (parameter_type.IsPointer && !ec.InUnsafe) {
4927 Error_InvalidArguments (loc, j, method, delegate_type, (Argument) Arguments [j], pd);
4931 private bool resolved = false;
4932 public override Expression DoResolve (EmitContext ec)
4935 return this.method == null ? null : this;
4939 // First, resolve the expression that is used to
4940 // trigger the invocation
4942 SimpleName sn = expr as SimpleName;
4944 expr = sn.GetMethodGroup ();
4946 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4950 if (!(expr is MethodGroupExpr)) {
4951 Type expr_type = expr.Type;
4953 if (expr_type != null){
4954 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4956 return (new DelegateInvocation (
4957 this.expr, Arguments, loc)).Resolve (ec);
4961 if (!(expr is MethodGroupExpr)){
4962 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4967 // Next, evaluate all the expressions in the argument list
4969 if (Arguments != null){
4970 foreach (Argument a in Arguments){
4971 if (!a.Resolve (ec, loc))
4976 MethodGroupExpr mg = (MethodGroupExpr) expr;
4977 MethodBase method = OverloadResolve (ec, mg, Arguments, false, loc);
4982 MethodInfo mi = method as MethodInfo;
4984 type = TypeManager.TypeToCoreType (mi.ReturnType);
4985 Expression iexpr = mg.InstanceExpression;
4987 if (iexpr == null ||
4988 iexpr is This || iexpr is EmptyExpression ||
4989 mg.IdenticalTypeName) {
4990 mg.InstanceExpression = null;
4992 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
4996 if (iexpr == null || iexpr is EmptyExpression) {
4997 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
5003 if (type.IsPointer){
5011 // Only base will allow this invocation to happen.
5013 if (mg.IsBase && method.IsAbstract){
5014 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
5018 if (Arguments == null && method.Name == "Finalize") {
5020 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
5022 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
5026 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
5030 if (mg.InstanceExpression != null)
5031 mg.InstanceExpression.CheckMarshalByRefAccess ();
5033 eclass = ExprClass.Value;
5034 this.method = method;
5038 bool IsSpecialMethodInvocation (MethodBase method)
5040 IMethodData md = TypeManager.GetMethod (method);
5042 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
5045 if (!TypeManager.IsSpecialMethod (method))
5048 int args = TypeManager.GetParameterData (method).Count;
5049 if (method.Name.StartsWith ("get_") && args > 0)
5051 else if (method.Name.StartsWith ("set_") && args > 2)
5054 // TODO: check operators and events as well ?
5057 Report.SymbolRelatedToPreviousError (method);
5058 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
5059 TypeManager.CSharpSignature (method, true));
5065 // Emits the list of arguments as an array
5067 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
5069 ILGenerator ig = ec.ig;
5070 int count = arguments.Count - idx;
5071 Argument a = (Argument) arguments [idx];
5072 Type t = a.Expr.Type;
5074 IntConstant.EmitInt (ig, count);
5075 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
5077 int top = arguments.Count;
5078 for (int j = idx; j < top; j++){
5079 a = (Argument) arguments [j];
5081 ig.Emit (OpCodes.Dup);
5082 IntConstant.EmitInt (ig, j - idx);
5084 bool is_stobj, has_type_arg;
5085 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
5087 ig.Emit (OpCodes.Ldelema, t);
5099 /// Emits a list of resolved Arguments that are in the arguments
5102 /// The MethodBase argument might be null if the
5103 /// emission of the arguments is known not to contain
5104 /// a `params' field (for example in constructors or other routines
5105 /// that keep their arguments in this structure)
5107 /// if `dup_args' is true, a copy of the arguments will be left
5108 /// on the stack. If `dup_args' is true, you can specify `this_arg'
5109 /// which will be duplicated before any other args. Only EmitCall
5110 /// should be using this interface.
5112 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
5114 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
5115 int top = arguments == null ? 0 : arguments.Count;
5116 LocalTemporary [] temps = null;
5118 if (dup_args && top != 0)
5119 temps = new LocalTemporary [top];
5121 for (int i = 0; i < top; i++){
5122 Argument a = (Argument) arguments [i];
5125 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
5127 // Special case if we are passing the same data as the
5128 // params argument, do not put it in an array.
5130 if (pd.ParameterType (i) == a.Type)
5133 EmitParams (ec, i, arguments);
5140 ec.ig.Emit (OpCodes.Dup);
5141 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
5146 if (this_arg != null)
5149 for (int i = 0; i < top; i ++) {
5150 temps [i].Emit (ec);
5151 temps [i].Release (ec);
5155 if (pd != null && pd.Count > top &&
5156 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
5157 ILGenerator ig = ec.ig;
5159 IntConstant.EmitInt (ig, 0);
5160 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
5164 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
5166 ParameterData pd = TypeManager.GetParameterData (mb);
5168 if (arguments == null)
5169 return new Type [0];
5171 Argument a = (Argument) arguments [pd.Count - 1];
5172 Arglist list = (Arglist) a.Expr;
5174 return list.ArgumentTypes;
5178 /// This checks the ConditionalAttribute on the method
5180 static bool IsMethodExcluded (MethodBase method)
5182 if (method.IsConstructor)
5185 IMethodData md = TypeManager.GetMethod (method);
5187 return md.IsExcluded ();
5189 // For some methods (generated by delegate class) GetMethod returns null
5190 // because they are not included in builder_to_method table
5191 if (method.DeclaringType is TypeBuilder)
5194 return AttributeTester.IsConditionalMethodExcluded (method);
5198 /// is_base tells whether we want to force the use of the `call'
5199 /// opcode instead of using callvirt. Call is required to call
5200 /// a specific method, while callvirt will always use the most
5201 /// recent method in the vtable.
5203 /// is_static tells whether this is an invocation on a static method
5205 /// instance_expr is an expression that represents the instance
5206 /// it must be non-null if is_static is false.
5208 /// method is the method to invoke.
5210 /// Arguments is the list of arguments to pass to the method or constructor.
5212 public static void EmitCall (EmitContext ec, bool is_base,
5213 bool is_static, Expression instance_expr,
5214 MethodBase method, ArrayList Arguments, Location loc)
5216 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
5219 // `dup_args' leaves an extra copy of the arguments on the stack
5220 // `omit_args' does not leave any arguments at all.
5221 // So, basically, you could make one call with `dup_args' set to true,
5222 // and then another with `omit_args' set to true, and the two calls
5223 // would have the same set of arguments. However, each argument would
5224 // only have been evaluated once.
5225 public static void EmitCall (EmitContext ec, bool is_base,
5226 bool is_static, Expression instance_expr,
5227 MethodBase method, ArrayList Arguments, Location loc,
5228 bool dup_args, bool omit_args)
5230 ILGenerator ig = ec.ig;
5231 bool struct_call = false;
5232 bool this_call = false;
5233 LocalTemporary this_arg = null;
5235 Type decl_type = method.DeclaringType;
5237 if (!RootContext.StdLib) {
5238 // Replace any calls to the system's System.Array type with calls to
5239 // the newly created one.
5240 if (method == TypeManager.system_int_array_get_length)
5241 method = TypeManager.int_array_get_length;
5242 else if (method == TypeManager.system_int_array_get_rank)
5243 method = TypeManager.int_array_get_rank;
5244 else if (method == TypeManager.system_object_array_clone)
5245 method = TypeManager.object_array_clone;
5246 else if (method == TypeManager.system_int_array_get_length_int)
5247 method = TypeManager.int_array_get_length_int;
5248 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5249 method = TypeManager.int_array_get_lower_bound_int;
5250 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5251 method = TypeManager.int_array_get_upper_bound_int;
5252 else if (method == TypeManager.system_void_array_copyto_array_int)
5253 method = TypeManager.void_array_copyto_array_int;
5256 if (!ec.IsInObsoleteScope) {
5258 // This checks ObsoleteAttribute on the method and on the declaring type
5260 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5262 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5264 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5266 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5270 if (IsMethodExcluded (method))
5274 if (instance_expr == EmptyExpression.Null) {
5275 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5279 this_call = instance_expr is This;
5280 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5284 // If this is ourselves, push "this"
5288 Type iexpr_type = instance_expr.Type;
5291 // Push the instance expression
5293 if (TypeManager.IsValueType (iexpr_type)) {
5295 // Special case: calls to a function declared in a
5296 // reference-type with a value-type argument need
5297 // to have their value boxed.
5298 if (decl_type.IsValueType ||
5299 TypeManager.IsGenericParameter (iexpr_type)) {
5301 // If the expression implements IMemoryLocation, then
5302 // we can optimize and use AddressOf on the
5305 // If not we have to use some temporary storage for
5307 if (instance_expr is IMemoryLocation) {
5308 ((IMemoryLocation)instance_expr).
5309 AddressOf (ec, AddressOp.LoadStore);
5311 LocalTemporary temp = new LocalTemporary (iexpr_type);
5312 instance_expr.Emit (ec);
5314 temp.AddressOf (ec, AddressOp.Load);
5317 // avoid the overhead of doing this all the time.
5319 t = TypeManager.GetReferenceType (iexpr_type);
5321 instance_expr.Emit (ec);
5322 ig.Emit (OpCodes.Box, instance_expr.Type);
5323 t = TypeManager.object_type;
5326 instance_expr.Emit (ec);
5327 t = instance_expr.Type;
5331 ig.Emit (OpCodes.Dup);
5332 if (Arguments != null && Arguments.Count != 0) {
5333 this_arg = new LocalTemporary (t);
5334 this_arg.Store (ec);
5341 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5344 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
5345 ig.Emit (OpCodes.Constrained, instance_expr.Type);
5349 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5350 call_op = OpCodes.Call;
5352 call_op = OpCodes.Callvirt;
5354 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5355 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5356 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5363 // and DoFoo is not virtual, you can omit the callvirt,
5364 // because you don't need the null checking behavior.
5366 if (method is MethodInfo)
5367 ig.Emit (call_op, (MethodInfo) method);
5369 ig.Emit (call_op, (ConstructorInfo) method);
5372 public override void Emit (EmitContext ec)
5374 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5376 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5379 public override void EmitStatement (EmitContext ec)
5384 // Pop the return value if there is one
5386 if (method is MethodInfo){
5387 Type ret = ((MethodInfo)method).ReturnType;
5388 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5389 ec.ig.Emit (OpCodes.Pop);
5394 public class InvocationOrCast : ExpressionStatement
5397 Expression argument;
5399 public InvocationOrCast (Expression expr, Expression argument)
5402 this.argument = argument;
5403 this.loc = expr.Location;
5406 public override Expression DoResolve (EmitContext ec)
5409 // First try to resolve it as a cast.
5411 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5412 if ((te != null) && (te.eclass == ExprClass.Type)) {
5413 Cast cast = new Cast (te, argument, loc);
5414 return cast.Resolve (ec);
5418 // This can either be a type or a delegate invocation.
5419 // Let's just resolve it and see what we'll get.
5421 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5426 // Ok, so it's a Cast.
5428 if (expr.eclass == ExprClass.Type) {
5429 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5430 return cast.Resolve (ec);
5434 // It's a delegate invocation.
5436 if (!TypeManager.IsDelegateType (expr.Type)) {
5437 Error (149, "Method name expected");
5441 ArrayList args = new ArrayList ();
5442 args.Add (new Argument (argument, Argument.AType.Expression));
5443 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5444 return invocation.Resolve (ec);
5449 Error (201, "Only assignment, call, increment, decrement and new object " +
5450 "expressions can be used as a statement");
5453 public override ExpressionStatement ResolveStatement (EmitContext ec)
5456 // First try to resolve it as a cast.
5458 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5459 if ((te != null) && (te.eclass == ExprClass.Type)) {
5465 // This can either be a type or a delegate invocation.
5466 // Let's just resolve it and see what we'll get.
5468 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5469 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5475 // It's a delegate invocation.
5477 if (!TypeManager.IsDelegateType (expr.Type)) {
5478 Error (149, "Method name expected");
5482 ArrayList args = new ArrayList ();
5483 args.Add (new Argument (argument, Argument.AType.Expression));
5484 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5485 return invocation.ResolveStatement (ec);
5488 public override void Emit (EmitContext ec)
5490 throw new Exception ("Cannot happen");
5493 public override void EmitStatement (EmitContext ec)
5495 throw new Exception ("Cannot happen");
5500 // This class is used to "disable" the code generation for the
5501 // temporary variable when initializing value types.
5503 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5504 public void AddressOf (EmitContext ec, AddressOp Mode)
5511 /// Implements the new expression
5513 public class New : ExpressionStatement, IMemoryLocation {
5514 public readonly ArrayList Arguments;
5517 // During bootstrap, it contains the RequestedType,
5518 // but if `type' is not null, it *might* contain a NewDelegate
5519 // (because of field multi-initialization)
5521 public Expression RequestedType;
5523 MethodBase method = null;
5526 // If set, the new expression is for a value_target, and
5527 // we will not leave anything on the stack.
5529 Expression value_target;
5530 bool value_target_set = false;
5531 bool is_type_parameter = false;
5533 public New (Expression requested_type, ArrayList arguments, Location l)
5535 RequestedType = requested_type;
5536 Arguments = arguments;
5540 public bool SetValueTypeVariable (Expression value)
5542 value_target = value;
5543 value_target_set = true;
5544 if (!(value_target is IMemoryLocation)){
5545 Error_UnexpectedKind (null, "variable", loc);
5552 // This function is used to disable the following code sequence for
5553 // value type initialization:
5555 // AddressOf (temporary)
5559 // Instead the provide will have provided us with the address on the
5560 // stack to store the results.
5562 static Expression MyEmptyExpression;
5564 public void DisableTemporaryValueType ()
5566 if (MyEmptyExpression == null)
5567 MyEmptyExpression = new EmptyAddressOf ();
5570 // To enable this, look into:
5571 // test-34 and test-89 and self bootstrapping.
5573 // For instance, we can avoid a copy by using `newobj'
5574 // instead of Call + Push-temp on value types.
5575 // value_target = MyEmptyExpression;
5580 /// Converts complex core type syntax like 'new int ()' to simple constant
5582 public static Constant Constantify (Type t)
5584 if (t == TypeManager.int32_type)
5585 return new IntConstant (0, Location.Null);
5586 if (t == TypeManager.uint32_type)
5587 return new UIntConstant (0, Location.Null);
5588 if (t == TypeManager.int64_type)
5589 return new LongConstant (0, Location.Null);
5590 if (t == TypeManager.uint64_type)
5591 return new ULongConstant (0, Location.Null);
5592 if (t == TypeManager.float_type)
5593 return new FloatConstant (0, Location.Null);
5594 if (t == TypeManager.double_type)
5595 return new DoubleConstant (0, Location.Null);
5596 if (t == TypeManager.short_type)
5597 return new ShortConstant (0, Location.Null);
5598 if (t == TypeManager.ushort_type)
5599 return new UShortConstant (0, Location.Null);
5600 if (t == TypeManager.sbyte_type)
5601 return new SByteConstant (0, Location.Null);
5602 if (t == TypeManager.byte_type)
5603 return new ByteConstant (0, Location.Null);
5604 if (t == TypeManager.char_type)
5605 return new CharConstant ('\0', Location.Null);
5606 if (t == TypeManager.bool_type)
5607 return new BoolConstant (false, Location.Null);
5608 if (t == TypeManager.decimal_type)
5609 return new DecimalConstant (0, Location.Null);
5610 if (TypeManager.IsEnumType (t))
5611 return new EnumConstant (Constantify (TypeManager.EnumToUnderlying (t)), t);
5617 // Checks whether the type is an interface that has the
5618 // [ComImport, CoClass] attributes and must be treated
5621 public Expression CheckComImport (EmitContext ec)
5623 if (!type.IsInterface)
5627 // Turn the call into:
5628 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5630 Type real_class = AttributeTester.GetCoClassAttribute (type);
5631 if (real_class == null)
5634 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5635 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5636 return cast.Resolve (ec);
5639 public override Expression DoResolve (EmitContext ec)
5642 // The New DoResolve might be called twice when initializing field
5643 // expressions (see EmitFieldInitializers, the call to
5644 // GetInitializerExpression will perform a resolve on the expression,
5645 // and later the assign will trigger another resolution
5647 // This leads to bugs (#37014)
5650 if (RequestedType is NewDelegate)
5651 return RequestedType;
5655 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5661 if (type == TypeManager.void_type) {
5662 Error_VoidInvalidInTheContext (loc);
5666 if (Arguments == null) {
5667 Expression c = Constantify (type);
5672 if (TypeManager.IsDelegateType (type)) {
5673 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5674 if (RequestedType != null)
5675 if (!(RequestedType is DelegateCreation))
5676 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5677 return RequestedType;
5681 if (type.IsGenericParameter) {
5682 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5684 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5685 Error (304, String.Format (
5686 "Cannot create an instance of the " +
5687 "variable type '{0}' because it " +
5688 "doesn't have the new() constraint",
5693 if ((Arguments != null) && (Arguments.Count != 0)) {
5694 Error (417, String.Format (
5695 "`{0}': cannot provide arguments " +
5696 "when creating an instance of a " +
5697 "variable type.", type));
5701 is_type_parameter = true;
5702 eclass = ExprClass.Value;
5707 if (type.IsAbstract && type.IsSealed) {
5708 Report.SymbolRelatedToPreviousError (type);
5709 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5713 if (type.IsInterface || type.IsAbstract){
5714 RequestedType = CheckComImport (ec);
5715 if (RequestedType != null)
5716 return RequestedType;
5718 Report.SymbolRelatedToPreviousError (type);
5719 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5723 bool is_struct = type.IsValueType;
5724 eclass = ExprClass.Value;
5727 // SRE returns a match for .ctor () on structs (the object constructor),
5728 // so we have to manually ignore it.
5730 if (is_struct && Arguments == null)
5733 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5734 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5735 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5740 MethodGroupExpr mg = ml as MethodGroupExpr;
5743 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5747 if (Arguments != null){
5748 foreach (Argument a in Arguments){
5749 if (!a.Resolve (ec, loc))
5754 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5755 if (method == null) {
5756 if (almostMatchedMembers.Count != 0)
5757 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5764 bool DoEmitTypeParameter (EmitContext ec)
5767 ILGenerator ig = ec.ig;
5769 ig.Emit (OpCodes.Ldtoken, type);
5770 ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
5771 ig.Emit (OpCodes.Call, TypeManager.activator_create_instance);
5772 ig.Emit (OpCodes.Unbox_Any, type);
5775 throw new InternalErrorException ();
5780 // This DoEmit can be invoked in two contexts:
5781 // * As a mechanism that will leave a value on the stack (new object)
5782 // * As one that wont (init struct)
5784 // You can control whether a value is required on the stack by passing
5785 // need_value_on_stack. The code *might* leave a value on the stack
5786 // so it must be popped manually
5788 // If we are dealing with a ValueType, we have a few
5789 // situations to deal with:
5791 // * The target is a ValueType, and we have been provided
5792 // the instance (this is easy, we are being assigned).
5794 // * The target of New is being passed as an argument,
5795 // to a boxing operation or a function that takes a
5798 // In this case, we need to create a temporary variable
5799 // that is the argument of New.
5801 // Returns whether a value is left on the stack
5803 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5805 bool is_value_type = TypeManager.IsValueType (type);
5806 ILGenerator ig = ec.ig;
5811 // Allow DoEmit() to be called multiple times.
5812 // We need to create a new LocalTemporary each time since
5813 // you can't share LocalBuilders among ILGeneators.
5814 if (!value_target_set)
5815 value_target = new LocalTemporary (type);
5817 ml = (IMemoryLocation) value_target;
5818 ml.AddressOf (ec, AddressOp.Store);
5822 Invocation.EmitArguments (ec, method, Arguments, false, null);
5826 ig.Emit (OpCodes.Initobj, type);
5828 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5829 if (need_value_on_stack){
5830 value_target.Emit (ec);
5835 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5840 public override void Emit (EmitContext ec)
5842 if (is_type_parameter)
5843 DoEmitTypeParameter (ec);
5848 public override void EmitStatement (EmitContext ec)
5850 if (is_type_parameter)
5851 throw new InvalidOperationException ();
5853 if (DoEmit (ec, false))
5854 ec.ig.Emit (OpCodes.Pop);
5857 public void AddressOf (EmitContext ec, AddressOp Mode)
5859 if (is_type_parameter)
5860 throw new InvalidOperationException ();
5862 if (!type.IsValueType){
5864 // We throw an exception. So far, I believe we only need to support
5866 // foreach (int j in new StructType ())
5869 throw new Exception ("AddressOf should not be used for classes");
5872 if (!value_target_set)
5873 value_target = new LocalTemporary (type);
5875 IMemoryLocation ml = (IMemoryLocation) value_target;
5876 ml.AddressOf (ec, AddressOp.Store);
5878 Invocation.EmitArguments (ec, method, Arguments, false, null);
5881 ec.ig.Emit (OpCodes.Initobj, type);
5883 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5885 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5890 /// 14.5.10.2: Represents an array creation expression.
5894 /// There are two possible scenarios here: one is an array creation
5895 /// expression that specifies the dimensions and optionally the
5896 /// initialization data and the other which does not need dimensions
5897 /// specified but where initialization data is mandatory.
5899 public class ArrayCreation : Expression {
5900 Expression requested_base_type;
5901 ArrayList initializers;
5904 // The list of Argument types.
5905 // This is used to construct the `newarray' or constructor signature
5907 ArrayList arguments;
5910 // Method used to create the array object.
5912 MethodBase new_method = null;
5914 Type array_element_type;
5915 Type underlying_type;
5916 bool is_one_dimensional = false;
5917 bool is_builtin_type = false;
5918 bool expect_initializers = false;
5919 int num_arguments = 0;
5923 ArrayList array_data;
5927 // The number of constants in array initializers
5928 int const_initializers_count;
5930 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5932 this.requested_base_type = requested_base_type;
5933 this.initializers = initializers;
5937 arguments = new ArrayList ();
5939 foreach (Expression e in exprs) {
5940 arguments.Add (new Argument (e, Argument.AType.Expression));
5945 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5947 this.requested_base_type = requested_base_type;
5948 this.initializers = initializers;
5952 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5954 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5956 //dimensions = tmp.Length - 1;
5957 expect_initializers = true;
5960 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5962 StringBuilder sb = new StringBuilder (rank);
5965 for (int i = 1; i < idx_count; i++)
5970 return new ComposedCast (base_type, sb.ToString (), loc);
5973 void Error_IncorrectArrayInitializer ()
5975 Error (178, "Invalid rank specifier: expected `,' or `]'");
5978 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5980 if (specified_dims) {
5981 Argument a = (Argument) arguments [idx];
5983 if (!a.Resolve (ec, loc))
5986 Constant c = a.Expr as Constant;
5988 c = c.ImplicitConversionRequired (TypeManager.int32_type, a.Expr.Location);
5992 Report.Error (150, a.Expr.Location, "A constant value is expected");
5996 int value = (int) c.GetValue ();
5998 if (value != probe.Count) {
5999 Error_IncorrectArrayInitializer ();
6003 bounds [idx] = value;
6006 int child_bounds = -1;
6007 for (int i = 0; i < probe.Count; ++i) {
6008 object o = probe [i];
6009 if (o is ArrayList) {
6010 ArrayList sub_probe = o as ArrayList;
6011 int current_bounds = sub_probe.Count;
6013 if (child_bounds == -1)
6014 child_bounds = current_bounds;
6016 else if (child_bounds != current_bounds){
6017 Error_IncorrectArrayInitializer ();
6020 if (idx + 1 >= dimensions){
6021 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
6025 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
6029 if (child_bounds != -1){
6030 Error_IncorrectArrayInitializer ();
6034 Expression tmp = (Expression) o;
6035 tmp = tmp.Resolve (ec);
6039 Expression conv = Convert.ImplicitConversionRequired (
6040 ec, tmp, underlying_type, loc);
6045 // Initializers with the default values can be ignored
6046 Constant c = tmp as Constant;
6048 if (c.IsDefaultInitializer (array_element_type)) {
6052 ++const_initializers_count;
6055 // Used to invalidate static initializer
6056 const_initializers_count = int.MinValue;
6059 array_data.Add (conv);
6066 public void UpdateIndices ()
6069 for (ArrayList probe = initializers; probe != null;) {
6070 if (probe.Count > 0 && probe [0] is ArrayList) {
6071 Expression e = new IntConstant (probe.Count, Location.Null);
6072 arguments.Add (new Argument (e, Argument.AType.Expression));
6074 bounds [i++] = probe.Count;
6076 probe = (ArrayList) probe [0];
6079 Expression e = new IntConstant (probe.Count, Location.Null);
6080 arguments.Add (new Argument (e, Argument.AType.Expression));
6082 bounds [i++] = probe.Count;
6089 bool ResolveInitializers (EmitContext ec)
6091 if (initializers == null) {
6092 return !expect_initializers;
6095 if (underlying_type == null)
6099 // We use this to store all the date values in the order in which we
6100 // will need to store them in the byte blob later
6102 array_data = new ArrayList ();
6103 bounds = new System.Collections.Specialized.HybridDictionary ();
6105 if (arguments != null)
6106 return CheckIndices (ec, initializers, 0, true);
6108 arguments = new ArrayList ();
6110 if (!CheckIndices (ec, initializers, 0, false))
6115 if (arguments.Count != dimensions) {
6116 Error_IncorrectArrayInitializer ();
6124 // Creates the type of the array
6126 bool LookupType (EmitContext ec)
6128 StringBuilder array_qualifier = new StringBuilder (rank);
6131 // `In the first form allocates an array instace of the type that results
6132 // from deleting each of the individual expression from the expression list'
6134 if (num_arguments > 0) {
6135 array_qualifier.Append ("[");
6136 for (int i = num_arguments-1; i > 0; i--)
6137 array_qualifier.Append (",");
6138 array_qualifier.Append ("]");
6144 TypeExpr array_type_expr;
6145 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
6146 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
6147 if (array_type_expr == null)
6150 type = array_type_expr.Type;
6151 underlying_type = TypeManager.GetElementType (type);
6152 dimensions = type.GetArrayRank ();
6157 public override Expression DoResolve (EmitContext ec)
6162 if (!LookupType (ec))
6165 array_element_type = TypeManager.GetElementType (type);
6166 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
6167 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
6172 // First step is to validate the initializers and fill
6173 // in any missing bits
6175 if (!ResolveInitializers (ec))
6179 if (arguments == null)
6182 arg_count = arguments.Count;
6183 foreach (Argument a in arguments){
6184 if (!a.Resolve (ec, loc))
6187 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
6188 if (real_arg == null)
6195 if (arg_count == 1) {
6196 is_one_dimensional = true;
6197 eclass = ExprClass.Value;
6201 is_builtin_type = TypeManager.IsBuiltinType (type);
6203 if (is_builtin_type) {
6206 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
6207 AllBindingFlags, loc);
6209 if (!(ml is MethodGroupExpr)) {
6210 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
6215 Error (-6, "New invocation: Can not find a constructor for " +
6216 "this argument list");
6220 new_method = Invocation.OverloadResolve (
6221 ec, (MethodGroupExpr) ml, arguments, false, loc);
6223 if (new_method == null) {
6224 Error (-6, "New invocation: Can not find a constructor for " +
6225 "this argument list");
6229 eclass = ExprClass.Value;
6232 ModuleBuilder mb = CodeGen.Module.Builder;
6233 ArrayList args = new ArrayList ();
6235 if (arguments != null) {
6236 for (int i = 0; i < arg_count; i++)
6237 args.Add (TypeManager.int32_type);
6240 Type [] arg_types = null;
6243 arg_types = new Type [args.Count];
6245 args.CopyTo (arg_types, 0);
6247 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6250 if (new_method == null) {
6251 Error (-6, "New invocation: Can not find a constructor for " +
6252 "this argument list");
6256 eclass = ExprClass.Value;
6261 byte [] MakeByteBlob ()
6266 int count = array_data.Count;
6268 if (underlying_type.IsEnum)
6269 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
6271 factor = GetTypeSize (underlying_type);
6273 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
6275 data = new byte [(count * factor + 4) & ~3];
6278 for (int i = 0; i < count; ++i) {
6279 object v = array_data [i];
6281 if (v is EnumConstant)
6282 v = ((EnumConstant) v).Child;
6284 if (v is Constant && !(v is StringConstant))
6285 v = ((Constant) v).GetValue ();
6291 if (underlying_type == TypeManager.int64_type){
6292 if (!(v is Expression)){
6293 long val = (long) v;
6295 for (int j = 0; j < factor; ++j) {
6296 data [idx + j] = (byte) (val & 0xFF);
6300 } else if (underlying_type == TypeManager.uint64_type){
6301 if (!(v is Expression)){
6302 ulong val = (ulong) v;
6304 for (int j = 0; j < factor; ++j) {
6305 data [idx + j] = (byte) (val & 0xFF);
6309 } else if (underlying_type == TypeManager.float_type) {
6310 if (!(v is Expression)){
6311 element = BitConverter.GetBytes ((float) v);
6313 for (int j = 0; j < factor; ++j)
6314 data [idx + j] = element [j];
6316 } else if (underlying_type == TypeManager.double_type) {
6317 if (!(v is Expression)){
6318 element = BitConverter.GetBytes ((double) v);
6320 for (int j = 0; j < factor; ++j)
6321 data [idx + j] = element [j];
6323 } else if (underlying_type == TypeManager.char_type){
6324 if (!(v is Expression)){
6325 int val = (int) ((char) v);
6327 data [idx] = (byte) (val & 0xff);
6328 data [idx+1] = (byte) (val >> 8);
6330 } else if (underlying_type == TypeManager.short_type){
6331 if (!(v is Expression)){
6332 int val = (int) ((short) v);
6334 data [idx] = (byte) (val & 0xff);
6335 data [idx+1] = (byte) (val >> 8);
6337 } else if (underlying_type == TypeManager.ushort_type){
6338 if (!(v is Expression)){
6339 int val = (int) ((ushort) v);
6341 data [idx] = (byte) (val & 0xff);
6342 data [idx+1] = (byte) (val >> 8);
6344 } else if (underlying_type == TypeManager.int32_type) {
6345 if (!(v is Expression)){
6348 data [idx] = (byte) (val & 0xff);
6349 data [idx+1] = (byte) ((val >> 8) & 0xff);
6350 data [idx+2] = (byte) ((val >> 16) & 0xff);
6351 data [idx+3] = (byte) (val >> 24);
6353 } else if (underlying_type == TypeManager.uint32_type) {
6354 if (!(v is Expression)){
6355 uint val = (uint) v;
6357 data [idx] = (byte) (val & 0xff);
6358 data [idx+1] = (byte) ((val >> 8) & 0xff);
6359 data [idx+2] = (byte) ((val >> 16) & 0xff);
6360 data [idx+3] = (byte) (val >> 24);
6362 } else if (underlying_type == TypeManager.sbyte_type) {
6363 if (!(v is Expression)){
6364 sbyte val = (sbyte) v;
6365 data [idx] = (byte) val;
6367 } else if (underlying_type == TypeManager.byte_type) {
6368 if (!(v is Expression)){
6369 byte val = (byte) v;
6370 data [idx] = (byte) val;
6372 } else if (underlying_type == TypeManager.bool_type) {
6373 if (!(v is Expression)){
6374 bool val = (bool) v;
6375 data [idx] = (byte) (val ? 1 : 0);
6377 } else if (underlying_type == TypeManager.decimal_type){
6378 if (!(v is Expression)){
6379 int [] bits = Decimal.GetBits ((decimal) v);
6382 // FIXME: For some reason, this doesn't work on the MS runtime.
6383 int [] nbits = new int [4];
6384 nbits [0] = bits [3];
6385 nbits [1] = bits [2];
6386 nbits [2] = bits [0];
6387 nbits [3] = bits [1];
6389 for (int j = 0; j < 4; j++){
6390 data [p++] = (byte) (nbits [j] & 0xff);
6391 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6392 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6393 data [p++] = (byte) (nbits [j] >> 24);
6397 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6406 // Emits the initializers for the array
6408 void EmitStaticInitializers (EmitContext ec)
6411 // First, the static data
6414 ILGenerator ig = ec.ig;
6416 byte [] data = MakeByteBlob ();
6418 fb = RootContext.MakeStaticData (data);
6420 ig.Emit (OpCodes.Dup);
6421 ig.Emit (OpCodes.Ldtoken, fb);
6422 ig.Emit (OpCodes.Call,
6423 TypeManager.void_initializearray_array_fieldhandle);
6427 // Emits pieces of the array that can not be computed at compile
6428 // time (variables and string locations).
6430 // This always expect the top value on the stack to be the array
6432 void EmitDynamicInitializers (EmitContext ec)
6434 ILGenerator ig = ec.ig;
6435 int dims = bounds.Count;
6436 int [] current_pos = new int [dims];
6438 MethodInfo set = null;
6441 Type [] args = new Type [dims + 1];
6443 for (int j = 0; j < dims; j++)
6444 args [j] = TypeManager.int32_type;
6445 args [dims] = array_element_type;
6447 set = CodeGen.Module.Builder.GetArrayMethod (
6449 CallingConventions.HasThis | CallingConventions.Standard,
6450 TypeManager.void_type, args);
6453 for (int i = 0; i < array_data.Count; i++){
6455 Expression e = (Expression)array_data [i];
6458 Type etype = e.Type;
6460 ig.Emit (OpCodes.Dup);
6462 for (int idx = 0; idx < dims; idx++)
6463 IntConstant.EmitInt (ig, current_pos [idx]);
6466 // If we are dealing with a struct, get the
6467 // address of it, so we can store it.
6469 if ((dims == 1) && etype.IsValueType &&
6470 (!TypeManager.IsBuiltinOrEnum (etype) ||
6471 etype == TypeManager.decimal_type)) {
6476 // Let new know that we are providing
6477 // the address where to store the results
6479 n.DisableTemporaryValueType ();
6482 ig.Emit (OpCodes.Ldelema, etype);
6488 bool is_stobj, has_type_arg;
6489 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
6491 ig.Emit (OpCodes.Stobj, etype);
6492 else if (has_type_arg)
6493 ig.Emit (op, etype);
6497 ig.Emit (OpCodes.Call, set);
6504 for (int j = dims - 1; j >= 0; j--){
6506 if (current_pos [j] < (int) bounds [j])
6508 current_pos [j] = 0;
6513 void EmitArrayArguments (EmitContext ec)
6515 ILGenerator ig = ec.ig;
6517 foreach (Argument a in arguments) {
6518 Type atype = a.Type;
6521 if (atype == TypeManager.uint64_type)
6522 ig.Emit (OpCodes.Conv_Ovf_U4);
6523 else if (atype == TypeManager.int64_type)
6524 ig.Emit (OpCodes.Conv_Ovf_I4);
6528 public override void Emit (EmitContext ec)
6530 ILGenerator ig = ec.ig;
6532 EmitArrayArguments (ec);
6533 if (is_one_dimensional)
6534 ig.Emit (OpCodes.Newarr, array_element_type);
6536 if (is_builtin_type)
6537 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6539 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6542 if (initializers == null)
6545 // This is a treshold for static initializers
6546 // I tried to make more accurate but it seems to me that Array.Initialize is
6547 // always slower (managed -> unmanaged switch?)
6548 const int max_automatic_initializers = 200;
6550 if (const_initializers_count > max_automatic_initializers && TypeManager.IsPrimitiveType (array_element_type)) {
6551 EmitStaticInitializers (ec);
6555 EmitDynamicInitializers (ec);
6558 public override bool GetAttributableValue (Type valueType, out object value)
6560 if (!is_one_dimensional){
6561 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6562 return base.GetAttributableValue (null, out value);
6565 if (array_data == null) {
6566 Constant c = (Constant)((Argument)arguments [0]).Expr;
6567 if (c.IsDefaultValue) {
6568 value = Array.CreateInstance (array_element_type, 0);
6571 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6572 return base.GetAttributableValue (null, out value);
6575 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6576 object element_value;
6577 for (int i = 0; i < ret.Length; ++i)
6579 Expression e = (Expression)array_data [i];
6581 // Is null when an initializer is optimized (value == predefined value)
6585 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6589 ret.SetValue (element_value, i);
6596 public sealed class CompilerGeneratedThis : This
6598 public static This Instance = new CompilerGeneratedThis ();
6600 private CompilerGeneratedThis ()
6601 : base (Location.Null)
6605 public override Expression DoResolve (EmitContext ec)
6607 eclass = ExprClass.Variable;
6608 type = ec.ContainerType;
6609 variable = new SimpleThis (type);
6615 /// Represents the `this' construct
6618 public class This : VariableReference, IVariable
6621 VariableInfo variable_info;
6622 protected Variable variable;
6625 public This (Block block, Location loc)
6631 public This (Location loc)
6636 public VariableInfo VariableInfo {
6637 get { return variable_info; }
6640 public bool VerifyFixed ()
6642 return !TypeManager.IsValueType (Type);
6645 public override bool IsRef {
6646 get { return is_struct; }
6649 public override Variable Variable {
6650 get { return variable; }
6653 public bool ResolveBase (EmitContext ec)
6655 eclass = ExprClass.Variable;
6657 if (ec.TypeContainer.CurrentType != null)
6658 type = ec.TypeContainer.CurrentType;
6660 type = ec.ContainerType;
6662 is_struct = ec.TypeContainer is Struct;
6665 Error (26, "Keyword `this' is not valid in a static property, " +
6666 "static method, or static field initializer");
6670 if (block != null) {
6671 if (block.Toplevel.ThisVariable != null)
6672 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6674 AnonymousContainer am = ec.CurrentAnonymousMethod;
6675 if (is_struct && (am != null) && !am.IsIterator) {
6676 Report.Error (1673, loc, "Anonymous methods inside structs " +
6677 "cannot access instance members of `this'. " +
6678 "Consider copying `this' to a local variable " +
6679 "outside the anonymous method and using the " +
6684 RootScopeInfo host = block.Toplevel.RootScope;
6685 if ((host != null) && !ec.IsConstructor &&
6686 (!is_struct || host.IsIterator)) {
6687 variable = host.CaptureThis ();
6688 type = variable.Type;
6693 if (variable == null)
6694 variable = new SimpleThis (type);
6699 public override Expression DoResolve (EmitContext ec)
6701 if (!ResolveBase (ec))
6704 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) &&
6705 !variable_info.IsAssigned (ec)) {
6706 Error (188, "The `this' object cannot be used before all of its " +
6707 "fields are assigned to");
6708 variable_info.SetAssigned (ec);
6712 if (ec.IsFieldInitializer) {
6713 Error (27, "Keyword `this' is not available in the current context");
6720 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6722 if (!ResolveBase (ec))
6725 if (variable_info != null)
6726 variable_info.SetAssigned (ec);
6728 if (ec.TypeContainer is Class){
6729 Error (1604, "Cannot assign to 'this' because it is read-only");
6735 public override int GetHashCode()
6737 return block.GetHashCode ();
6740 public override bool Equals (object obj)
6742 This t = obj as This;
6746 return block == t.block;
6749 protected class SimpleThis : Variable
6753 public SimpleThis (Type type)
6758 public override Type Type {
6759 get { return type; }
6762 public override bool HasInstance {
6763 get { return false; }
6766 public override bool NeedsTemporary {
6767 get { return false; }
6770 public override void EmitInstance (EmitContext ec)
6775 public override void Emit (EmitContext ec)
6777 ec.ig.Emit (OpCodes.Ldarg_0);
6780 public override void EmitAssign (EmitContext ec)
6782 throw new InvalidOperationException ();
6785 public override void EmitAddressOf (EmitContext ec)
6787 ec.ig.Emit (OpCodes.Ldarg_0);
6793 /// Represents the `__arglist' construct
6795 public class ArglistAccess : Expression
6797 public ArglistAccess (Location loc)
6802 public override Expression DoResolve (EmitContext ec)
6804 eclass = ExprClass.Variable;
6805 type = TypeManager.runtime_argument_handle_type;
6807 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6809 Error (190, "The __arglist construct is valid only within " +
6810 "a variable argument method");
6817 public override void Emit (EmitContext ec)
6819 ec.ig.Emit (OpCodes.Arglist);
6824 /// Represents the `__arglist (....)' construct
6826 public class Arglist : Expression
6828 public readonly Argument[] Arguments;
6830 public Arglist (Argument[] args, Location l)
6836 public Type[] ArgumentTypes {
6838 Type[] retval = new Type [Arguments.Length];
6839 for (int i = 0; i < Arguments.Length; i++)
6840 retval [i] = Arguments [i].Type;
6845 public override Expression DoResolve (EmitContext ec)
6847 eclass = ExprClass.Variable;
6848 type = TypeManager.runtime_argument_handle_type;
6850 foreach (Argument arg in Arguments) {
6851 if (!arg.Resolve (ec, loc))
6858 public override void Emit (EmitContext ec)
6860 foreach (Argument arg in Arguments)
6866 // This produces the value that renders an instance, used by the iterators code
6868 public class ProxyInstance : Expression, IMemoryLocation {
6869 public override Expression DoResolve (EmitContext ec)
6871 eclass = ExprClass.Variable;
6872 type = ec.ContainerType;
6876 public override void Emit (EmitContext ec)
6878 ec.ig.Emit (OpCodes.Ldarg_0);
6882 public void AddressOf (EmitContext ec, AddressOp mode)
6884 ec.ig.Emit (OpCodes.Ldarg_0);
6889 /// Implements the typeof operator
6891 public class TypeOf : Expression {
6892 readonly Expression QueriedType;
6893 protected Type typearg;
6895 public TypeOf (Expression queried_type, Location l)
6897 QueriedType = queried_type;
6901 public override Expression DoResolve (EmitContext ec)
6903 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6907 typearg = texpr.Type;
6909 if (typearg == TypeManager.void_type) {
6910 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6914 if (typearg.IsPointer && !ec.InUnsafe){
6919 type = TypeManager.type_type;
6920 // Even though what is returned is a type object, it's treated as a value by the compiler.
6921 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6922 eclass = ExprClass.Value;
6926 public override void Emit (EmitContext ec)
6928 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6929 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6932 public override bool GetAttributableValue (Type valueType, out object value)
6934 if (TypeManager.ContainsGenericParameters (typearg)) {
6935 Report.SymbolRelatedToPreviousError(typearg);
6936 Report.Error(416, loc, "`{0}': an attribute argument cannot use type parameters",
6937 TypeManager.CSharpName(typearg));
6942 if (valueType == TypeManager.object_type) {
6943 value = (object)typearg;
6950 public Type TypeArgument
6960 /// Implements the `typeof (void)' operator
6962 public class TypeOfVoid : TypeOf {
6963 public TypeOfVoid (Location l) : base (null, l)
6968 public override Expression DoResolve (EmitContext ec)
6970 type = TypeManager.type_type;
6971 typearg = TypeManager.void_type;
6972 // See description in TypeOf.
6973 eclass = ExprClass.Value;
6979 /// Implements the sizeof expression
6981 public class SizeOf : Expression {
6982 public Expression QueriedType;
6985 public SizeOf (Expression queried_type, Location l)
6987 this.QueriedType = queried_type;
6991 public override Expression DoResolve (EmitContext ec)
6993 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6998 if (texpr is TypeParameterExpr){
6999 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
7004 type_queried = texpr.Type;
7005 if (type_queried.IsEnum)
7006 type_queried = TypeManager.EnumToUnderlying (type_queried);
7008 if (type_queried == TypeManager.void_type) {
7009 Expression.Error_VoidInvalidInTheContext (loc);
7013 int size_of = GetTypeSize (type_queried);
7015 return new IntConstant (size_of, loc);
7019 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)",
7020 TypeManager.CSharpName (type_queried));
7024 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
7028 type = TypeManager.int32_type;
7029 eclass = ExprClass.Value;
7033 public override void Emit (EmitContext ec)
7035 int size = GetTypeSize (type_queried);
7038 ec.ig.Emit (OpCodes.Sizeof, type_queried);
7040 IntConstant.EmitInt (ec.ig, size);
7045 /// Implements the qualified-alias-member (::) expression.
7047 public class QualifiedAliasMember : Expression
7049 string alias, identifier;
7051 public QualifiedAliasMember (string alias, string identifier, Location l)
7054 this.identifier = identifier;
7058 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7060 if (alias == "global")
7061 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
7063 int errors = Report.Errors;
7064 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7066 if (errors == Report.Errors)
7067 Report.Error (432, loc, "Alias `{0}' not found", alias);
7070 if (fne.eclass != ExprClass.Namespace) {
7072 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7075 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
7078 public override Expression DoResolve (EmitContext ec)
7080 FullNamedExpression fne;
7081 if (alias == "global") {
7082 fne = RootNamespace.Global;
7084 int errors = Report.Errors;
7085 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7087 if (errors == Report.Errors)
7088 Report.Error (432, loc, "Alias `{0}' not found", alias);
7093 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
7097 if (!(retval is FullNamedExpression)) {
7098 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
7102 // We defer this check till the end to match the behaviour of CSC
7103 if (fne.eclass != ExprClass.Namespace) {
7104 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7110 public override void Emit (EmitContext ec)
7112 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
7116 public override string ToString ()
7118 return alias + "::" + identifier;
7121 public override string GetSignatureForError ()
7128 /// Implements the member access expression
7130 public class MemberAccess : Expression {
7131 public readonly string Identifier;
7134 public MemberAccess (Expression expr, string id)
7135 : this (expr, id, expr.Location)
7139 public MemberAccess (Expression expr, string identifier, Location loc)
7142 Identifier = identifier;
7146 public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
7147 : this (expr, identifier, loc)
7154 public Expression Expr {
7155 get { return expr; }
7158 protected string LookupIdentifier {
7159 get { return MemberName.MakeName (Identifier, args); }
7162 // TODO: this method has very poor performace for Enum fields and
7163 // probably for other constants as well
7164 Expression DoResolve (EmitContext ec, Expression right_side)
7167 throw new Exception ();
7170 // Resolve the expression with flow analysis turned off, we'll do the definite
7171 // assignment checks later. This is because we don't know yet what the expression
7172 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7173 // definite assignment check on the actual field and not on the whole struct.
7176 SimpleName original = expr as SimpleName;
7177 Expression new_expr = expr.Resolve (ec,
7178 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7179 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7181 if (new_expr == null)
7184 if (new_expr is Namespace) {
7185 Namespace ns = (Namespace) new_expr;
7186 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, LookupIdentifier, loc);
7188 if ((retval != null) && (args != null))
7189 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
7193 ns.Error_NamespaceDoesNotExist (ec.DeclContainer, loc, Identifier);
7197 Type expr_type = new_expr.Type;
7198 if (expr_type.IsPointer || expr_type == TypeManager.void_type || new_expr is NullLiteral){
7199 Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type);
7202 if (expr_type == TypeManager.anonymous_method_type){
7203 Unary.Error_OperatorCannotBeApplied (loc, ".", "anonymous method");
7207 Constant c = new_expr as Constant;
7208 if (c != null && c.GetValue () == null) {
7209 Report.Warning (1720, 1, loc, "Expression will always cause a `{0}'",
7210 "System.NullReferenceException");
7213 Expression member_lookup;
7214 member_lookup = MemberLookup (
7215 ec.ContainerType, expr_type, expr_type, Identifier, loc);
7217 if ((member_lookup == null) && (args != null)) {
7218 member_lookup = MemberLookup (
7219 ec.ContainerType, expr_type, expr_type, LookupIdentifier, loc);
7222 if (member_lookup == null) {
7223 MemberLookupFailed (
7224 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
7228 TypeExpr texpr = member_lookup as TypeExpr;
7229 if (texpr != null) {
7230 if (!(new_expr is TypeExpr) &&
7231 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
7232 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7233 Identifier, member_lookup.GetSignatureForError ());
7237 if (!texpr.CheckAccessLevel (ec.DeclContainer)) {
7238 Report.SymbolRelatedToPreviousError (member_lookup.Type);
7239 ErrorIsInaccesible (loc, TypeManager.CSharpName (member_lookup.Type));
7244 ConstructedType ct = new_expr as ConstructedType;
7247 // When looking up a nested type in a generic instance
7248 // via reflection, we always get a generic type definition
7249 // and not a generic instance - so we have to do this here.
7251 // See gtest-172-lib.cs and gtest-172.cs for an example.
7253 ct = new ConstructedType (
7254 member_lookup.Type, ct.TypeArguments, loc);
7256 return ct.ResolveAsTypeStep (ec, false);
7259 return member_lookup;
7262 MemberExpr me = (MemberExpr) member_lookup;
7263 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
7264 if (member_lookup == null)
7268 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
7270 throw new InternalErrorException ();
7272 return mg.ResolveGeneric (ec, args);
7275 if (original != null && !TypeManager.IsValueType (expr_type)) {
7276 me = member_lookup as MemberExpr;
7277 if (me != null && me.IsInstance) {
7278 LocalVariableReference var = new_expr as LocalVariableReference;
7279 if (var != null && !var.VerifyAssigned (ec))
7284 // The following DoResolve/DoResolveLValue will do the definite assignment
7287 if (right_side != null)
7288 return member_lookup.DoResolveLValue (ec, right_side);
7290 return member_lookup.DoResolve (ec);
7293 public override Expression DoResolve (EmitContext ec)
7295 return DoResolve (ec, null);
7298 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7300 return DoResolve (ec, right_side);
7303 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7305 return ResolveNamespaceOrType (ec, silent);
7308 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7310 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
7312 if (new_expr == null)
7315 if (new_expr is Namespace) {
7316 Namespace ns = (Namespace) new_expr;
7317 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, LookupIdentifier, loc);
7319 if ((retval != null) && (args != null))
7320 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
7322 if (!silent && retval == null)
7323 ns.Error_NamespaceDoesNotExist (rc.DeclContainer, loc, LookupIdentifier);
7327 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
7328 if (tnew_expr == null)
7331 Type expr_type = tnew_expr.Type;
7333 if (expr_type.IsPointer){
7334 Error (23, "The `.' operator can not be applied to pointer operands (" +
7335 TypeManager.CSharpName (expr_type) + ")");
7339 Expression member_lookup = MemberLookup (
7340 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
7341 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7342 if (member_lookup == null) {
7346 member_lookup = MemberLookup(
7347 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
7348 MemberTypes.All, BindingFlags.Public | BindingFlags.NonPublic, loc);
7350 if (member_lookup == null) {
7351 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7352 Identifier, new_expr.GetSignatureForError ());
7354 // TODO: Report.SymbolRelatedToPreviousError
7355 member_lookup.Error_UnexpectedKind (null, "type", loc);
7360 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
7365 TypeArguments the_args = args;
7366 if (TypeManager.HasGenericArguments (expr_type)) {
7367 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
7369 TypeArguments new_args = new TypeArguments (loc);
7370 foreach (Type decl in decl_args)
7371 new_args.Add (new TypeExpression (decl, loc));
7374 new_args.Add (args);
7376 the_args = new_args;
7379 if (the_args != null) {
7380 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
7381 return ctype.ResolveAsTypeStep (rc, false);
7388 public override void Emit (EmitContext ec)
7390 throw new Exception ("Should not happen");
7393 public override string ToString ()
7395 return expr + "." + MemberName.MakeName (Identifier, args);
7398 public override string GetSignatureForError ()
7400 return expr.GetSignatureForError () + "." + Identifier;
7405 /// Implements checked expressions
7407 public class CheckedExpr : Expression {
7409 public Expression Expr;
7411 public CheckedExpr (Expression e, Location l)
7417 public override Expression DoResolve (EmitContext ec)
7419 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7420 Expr = Expr.Resolve (ec);
7425 if (Expr is Constant)
7428 eclass = Expr.eclass;
7433 public override void Emit (EmitContext ec)
7435 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7439 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7441 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7442 Expr.EmitBranchable (ec, target, onTrue);
7447 /// Implements the unchecked expression
7449 public class UnCheckedExpr : Expression {
7451 public Expression Expr;
7453 public UnCheckedExpr (Expression e, Location l)
7459 public override Expression DoResolve (EmitContext ec)
7461 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7462 Expr = Expr.Resolve (ec);
7467 if (Expr is Constant)
7470 eclass = Expr.eclass;
7475 public override void Emit (EmitContext ec)
7477 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7481 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7483 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7484 Expr.EmitBranchable (ec, target, onTrue);
7489 /// An Element Access expression.
7491 /// During semantic analysis these are transformed into
7492 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7494 public class ElementAccess : Expression {
7495 public ArrayList Arguments;
7496 public Expression Expr;
7498 public ElementAccess (Expression e, ArrayList e_list)
7507 Arguments = new ArrayList ();
7508 foreach (Expression tmp in e_list)
7509 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7513 bool CommonResolve (EmitContext ec)
7515 Expr = Expr.Resolve (ec);
7520 if (Arguments == null)
7523 foreach (Argument a in Arguments){
7524 if (!a.Resolve (ec, loc))
7531 Expression MakePointerAccess (EmitContext ec, Type t)
7533 if (t == TypeManager.void_ptr_type){
7534 Error (242, "The array index operation is not valid on void pointers");
7537 if (Arguments.Count != 1){
7538 Error (196, "A pointer must be indexed by only one value");
7543 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7546 return new Indirection (p, loc).Resolve (ec);
7549 public override Expression DoResolve (EmitContext ec)
7551 if (!CommonResolve (ec))
7555 // We perform some simple tests, and then to "split" the emit and store
7556 // code we create an instance of a different class, and return that.
7558 // I am experimenting with this pattern.
7562 if (t == TypeManager.array_type){
7563 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7568 return (new ArrayAccess (this, loc)).Resolve (ec);
7570 return MakePointerAccess (ec, Expr.Type);
7572 FieldExpr fe = Expr as FieldExpr;
7574 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7576 return MakePointerAccess (ec, ff.ElementType);
7579 return (new IndexerAccess (this, loc)).Resolve (ec);
7582 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7584 if (!CommonResolve (ec))
7589 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7592 return MakePointerAccess (ec, Expr.Type);
7594 FieldExpr fe = Expr as FieldExpr;
7596 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7598 if (!(fe.InstanceExpression is LocalVariableReference) &&
7599 !(fe.InstanceExpression is This)) {
7600 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7603 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7604 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7607 return MakePointerAccess (ec, ff.ElementType);
7610 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7613 public override void Emit (EmitContext ec)
7615 throw new Exception ("Should never be reached");
7620 /// Implements array access
7622 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7624 // Points to our "data" repository
7628 LocalTemporary temp;
7631 public ArrayAccess (ElementAccess ea_data, Location l)
7634 eclass = ExprClass.Variable;
7638 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7640 return DoResolve (ec);
7643 public override Expression DoResolve (EmitContext ec)
7646 ExprClass eclass = ea.Expr.eclass;
7648 // As long as the type is valid
7649 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7650 eclass == ExprClass.Value)) {
7651 ea.Expr.Error_UnexpectedKind ("variable or value");
7656 Type t = ea.Expr.Type;
7657 if (t.GetArrayRank () != ea.Arguments.Count){
7658 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7659 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7663 type = TypeManager.GetElementType (t);
7664 if (type.IsPointer && !ec.InUnsafe){
7665 UnsafeError (ea.Location);
7669 foreach (Argument a in ea.Arguments){
7670 Type argtype = a.Type;
7672 if (argtype == TypeManager.int32_type ||
7673 argtype == TypeManager.uint32_type ||
7674 argtype == TypeManager.int64_type ||
7675 argtype == TypeManager.uint64_type) {
7676 Constant c = a.Expr as Constant;
7677 if (c != null && c.IsNegative) {
7678 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7684 // Mhm. This is strage, because the Argument.Type is not the same as
7685 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7687 // Wonder if I will run into trouble for this.
7689 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7694 eclass = ExprClass.Variable;
7700 /// Emits the right opcode to load an object of Type `t'
7701 /// from an array of T
7703 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7705 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7706 ig.Emit (OpCodes.Ldelem_U1);
7707 else if (type == TypeManager.sbyte_type)
7708 ig.Emit (OpCodes.Ldelem_I1);
7709 else if (type == TypeManager.short_type)
7710 ig.Emit (OpCodes.Ldelem_I2);
7711 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7712 ig.Emit (OpCodes.Ldelem_U2);
7713 else if (type == TypeManager.int32_type)
7714 ig.Emit (OpCodes.Ldelem_I4);
7715 else if (type == TypeManager.uint32_type)
7716 ig.Emit (OpCodes.Ldelem_U4);
7717 else if (type == TypeManager.uint64_type)
7718 ig.Emit (OpCodes.Ldelem_I8);
7719 else if (type == TypeManager.int64_type)
7720 ig.Emit (OpCodes.Ldelem_I8);
7721 else if (type == TypeManager.float_type)
7722 ig.Emit (OpCodes.Ldelem_R4);
7723 else if (type == TypeManager.double_type)
7724 ig.Emit (OpCodes.Ldelem_R8);
7725 else if (type == TypeManager.intptr_type)
7726 ig.Emit (OpCodes.Ldelem_I);
7727 else if (TypeManager.IsEnumType (type)){
7728 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7729 } else if (type.IsValueType){
7730 ig.Emit (OpCodes.Ldelema, type);
7731 ig.Emit (OpCodes.Ldobj, type);
7733 } else if (type.IsGenericParameter) {
7735 ig.Emit (OpCodes.Ldelem, type);
7737 ig.Emit (OpCodes.Ldelem_Any, type);
7740 } else if (type.IsPointer)
7741 ig.Emit (OpCodes.Ldelem_I);
7743 ig.Emit (OpCodes.Ldelem_Ref);
7747 /// Returns the right opcode to store an object of Type `t'
7748 /// from an array of T.
7750 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7752 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7753 has_type_arg = false; is_stobj = false;
7754 t = TypeManager.TypeToCoreType (t);
7755 if (TypeManager.IsEnumType (t))
7756 t = TypeManager.EnumToUnderlying (t);
7757 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7758 t == TypeManager.bool_type)
7759 return OpCodes.Stelem_I1;
7760 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7761 t == TypeManager.char_type)
7762 return OpCodes.Stelem_I2;
7763 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7764 return OpCodes.Stelem_I4;
7765 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7766 return OpCodes.Stelem_I8;
7767 else if (t == TypeManager.float_type)
7768 return OpCodes.Stelem_R4;
7769 else if (t == TypeManager.double_type)
7770 return OpCodes.Stelem_R8;
7771 else if (t == TypeManager.intptr_type) {
7772 has_type_arg = true;
7774 return OpCodes.Stobj;
7775 } else if (t.IsValueType) {
7776 has_type_arg = true;
7778 return OpCodes.Stobj;
7780 } else if (t.IsGenericParameter) {
7781 has_type_arg = true;
7783 return OpCodes.Stelem;
7785 return OpCodes.Stelem_Any;
7789 } else if (t.IsPointer)
7790 return OpCodes.Stelem_I;
7792 return OpCodes.Stelem_Ref;
7795 MethodInfo FetchGetMethod ()
7797 ModuleBuilder mb = CodeGen.Module.Builder;
7798 int arg_count = ea.Arguments.Count;
7799 Type [] args = new Type [arg_count];
7802 for (int i = 0; i < arg_count; i++){
7803 //args [i++] = a.Type;
7804 args [i] = TypeManager.int32_type;
7807 get = mb.GetArrayMethod (
7808 ea.Expr.Type, "Get",
7809 CallingConventions.HasThis |
7810 CallingConventions.Standard,
7816 MethodInfo FetchAddressMethod ()
7818 ModuleBuilder mb = CodeGen.Module.Builder;
7819 int arg_count = ea.Arguments.Count;
7820 Type [] args = new Type [arg_count];
7824 ret_type = TypeManager.GetReferenceType (type);
7826 for (int i = 0; i < arg_count; i++){
7827 //args [i++] = a.Type;
7828 args [i] = TypeManager.int32_type;
7831 address = mb.GetArrayMethod (
7832 ea.Expr.Type, "Address",
7833 CallingConventions.HasThis |
7834 CallingConventions.Standard,
7841 // Load the array arguments into the stack.
7843 // If we have been requested to cache the values (cached_locations array
7844 // initialized), then load the arguments the first time and store them
7845 // in locals. otherwise load from local variables.
7847 void LoadArrayAndArguments (EmitContext ec)
7849 ILGenerator ig = ec.ig;
7852 foreach (Argument a in ea.Arguments){
7853 Type argtype = a.Expr.Type;
7857 if (argtype == TypeManager.int64_type)
7858 ig.Emit (OpCodes.Conv_Ovf_I);
7859 else if (argtype == TypeManager.uint64_type)
7860 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7864 public void Emit (EmitContext ec, bool leave_copy)
7866 int rank = ea.Expr.Type.GetArrayRank ();
7867 ILGenerator ig = ec.ig;
7870 LoadArrayAndArguments (ec);
7873 EmitLoadOpcode (ig, type);
7877 method = FetchGetMethod ();
7878 ig.Emit (OpCodes.Call, method);
7881 LoadFromPtr (ec.ig, this.type);
7884 ec.ig.Emit (OpCodes.Dup);
7885 temp = new LocalTemporary (this.type);
7890 public override void Emit (EmitContext ec)
7895 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7897 int rank = ea.Expr.Type.GetArrayRank ();
7898 ILGenerator ig = ec.ig;
7899 Type t = source.Type;
7900 prepared = prepare_for_load;
7902 if (prepare_for_load) {
7903 AddressOf (ec, AddressOp.LoadStore);
7904 ec.ig.Emit (OpCodes.Dup);
7907 ec.ig.Emit (OpCodes.Dup);
7908 temp = new LocalTemporary (this.type);
7911 StoreFromPtr (ec.ig, t);
7921 LoadArrayAndArguments (ec);
7924 bool is_stobj, has_type_arg;
7925 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7927 // The stobj opcode used by value types will need
7928 // an address on the stack, not really an array/array
7932 ig.Emit (OpCodes.Ldelema, t);
7936 ec.ig.Emit (OpCodes.Dup);
7937 temp = new LocalTemporary (this.type);
7942 ig.Emit (OpCodes.Stobj, t);
7943 else if (has_type_arg)
7948 ModuleBuilder mb = CodeGen.Module.Builder;
7949 int arg_count = ea.Arguments.Count;
7950 Type [] args = new Type [arg_count + 1];
7955 ec.ig.Emit (OpCodes.Dup);
7956 temp = new LocalTemporary (this.type);
7960 for (int i = 0; i < arg_count; i++){
7961 //args [i++] = a.Type;
7962 args [i] = TypeManager.int32_type;
7965 args [arg_count] = type;
7967 set = mb.GetArrayMethod (
7968 ea.Expr.Type, "Set",
7969 CallingConventions.HasThis |
7970 CallingConventions.Standard,
7971 TypeManager.void_type, args);
7973 ig.Emit (OpCodes.Call, set);
7982 public void AddressOf (EmitContext ec, AddressOp mode)
7984 int rank = ea.Expr.Type.GetArrayRank ();
7985 ILGenerator ig = ec.ig;
7987 LoadArrayAndArguments (ec);
7990 ig.Emit (OpCodes.Ldelema, type);
7992 MethodInfo address = FetchAddressMethod ();
7993 ig.Emit (OpCodes.Call, address);
7997 public void EmitGetLength (EmitContext ec, int dim)
7999 int rank = ea.Expr.Type.GetArrayRank ();
8000 ILGenerator ig = ec.ig;
8004 ig.Emit (OpCodes.Ldlen);
8005 ig.Emit (OpCodes.Conv_I4);
8007 IntLiteral.EmitInt (ig, dim);
8008 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
8014 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
8015 public readonly ArrayList Properties;
8016 static Indexers empty;
8018 public struct Indexer {
8019 public readonly PropertyInfo PropertyInfo;
8020 public readonly MethodInfo Getter, Setter;
8022 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
8024 this.PropertyInfo = property_info;
8032 empty = new Indexers (null);
8035 Indexers (ArrayList array)
8040 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
8045 foreach (PropertyInfo property in mi){
8046 MethodInfo get, set;
8048 get = property.GetGetMethod (true);
8049 set = property.GetSetMethod (true);
8050 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
8052 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
8054 if (get != null || set != null) {
8056 ix = new Indexers (new ArrayList ());
8057 ix.Properties.Add (new Indexer (property, get, set));
8062 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
8064 string p_name = TypeManager.IndexerPropertyName (lookup_type);
8066 return TypeManager.MemberLookup (
8067 caller_type, caller_type, lookup_type, MemberTypes.Property,
8068 BindingFlags.Public | BindingFlags.Instance |
8069 BindingFlags.DeclaredOnly, p_name, null);
8072 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
8074 Indexers ix = empty;
8077 if (lookup_type.IsGenericParameter) {
8078 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
8082 if (gc.HasClassConstraint)
8083 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
8085 Type[] ifaces = gc.InterfaceConstraints;
8086 foreach (Type itype in ifaces)
8087 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8093 Type copy = lookup_type;
8094 while (copy != TypeManager.object_type && copy != null){
8095 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
8096 copy = copy.BaseType;
8099 if (lookup_type.IsInterface) {
8100 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
8101 if (ifaces != null) {
8102 foreach (Type itype in ifaces)
8103 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8112 /// Expressions that represent an indexer call.
8114 public class IndexerAccess : Expression, IAssignMethod {
8116 // Points to our "data" repository
8118 MethodInfo get, set;
8119 ArrayList set_arguments;
8120 bool is_base_indexer;
8122 protected Type indexer_type;
8123 protected Type current_type;
8124 protected Expression instance_expr;
8125 protected ArrayList arguments;
8127 public IndexerAccess (ElementAccess ea, Location loc)
8128 : this (ea.Expr, false, loc)
8130 this.arguments = ea.Arguments;
8133 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
8136 this.instance_expr = instance_expr;
8137 this.is_base_indexer = is_base_indexer;
8138 this.eclass = ExprClass.Value;
8142 protected virtual bool CommonResolve (EmitContext ec)
8144 indexer_type = instance_expr.Type;
8145 current_type = ec.ContainerType;
8150 public override Expression DoResolve (EmitContext ec)
8152 if (!CommonResolve (ec))
8156 // Step 1: Query for all `Item' *properties*. Notice
8157 // that the actual methods are pointed from here.
8159 // This is a group of properties, piles of them.
8161 ArrayList AllGetters = null;
8163 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8164 if (ilist.Properties != null) {
8165 AllGetters = new ArrayList(ilist.Properties.Count);
8166 foreach (Indexers.Indexer ix in ilist.Properties) {
8167 if (ix.Getter != null)
8168 AllGetters.Add (ix.Getter);
8172 if (AllGetters == null) {
8173 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8174 TypeManager.CSharpName (indexer_type));
8178 if (AllGetters.Count == 0) {
8179 // FIXME: we cannot simply select first one as the error message is missleading when
8180 // multiple indexers exist
8181 Indexers.Indexer first_indexer = (Indexers.Indexer)ilist.Properties[ilist.Properties.Count - 1];
8182 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
8183 TypeManager.GetFullNameSignature (first_indexer.PropertyInfo));
8187 get = (MethodInfo)Invocation.OverloadResolve (ec, new MethodGroupExpr (AllGetters, loc),
8188 arguments, false, loc);
8191 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8196 // Only base will allow this invocation to happen.
8198 if (get.IsAbstract && this is BaseIndexerAccess){
8199 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
8203 type = get.ReturnType;
8204 if (type.IsPointer && !ec.InUnsafe){
8209 instance_expr.CheckMarshalByRefAccess ();
8211 eclass = ExprClass.IndexerAccess;
8215 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8217 if (right_side == EmptyExpression.OutAccess) {
8218 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
8219 GetSignatureForError ());
8223 // if the indexer returns a value type, and we try to set a field in it
8224 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
8225 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
8226 GetSignatureForError ());
8230 ArrayList AllSetters = new ArrayList();
8231 if (!CommonResolve (ec))
8234 bool found_any = false, found_any_setters = false;
8236 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8237 if (ilist.Properties != null) {
8239 foreach (Indexers.Indexer ix in ilist.Properties) {
8240 if (ix.Setter != null)
8241 AllSetters.Add (ix.Setter);
8244 if (AllSetters.Count > 0) {
8245 found_any_setters = true;
8246 set_arguments = (ArrayList) arguments.Clone ();
8247 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
8248 set = (MethodInfo) Invocation.OverloadResolve (
8249 ec, new MethodGroupExpr (AllSetters, loc),
8250 set_arguments, false, loc);
8254 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8255 TypeManager.CSharpName (indexer_type));
8259 if (!found_any_setters) {
8260 Error (154, "indexer can not be used in this context, because " +
8261 "it lacks a `set' accessor");
8266 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8271 // Only base will allow this invocation to happen.
8273 if (set.IsAbstract && this is BaseIndexerAccess){
8274 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
8279 // Now look for the actual match in the list of indexers to set our "return" type
8281 type = TypeManager.void_type; // default value
8282 foreach (Indexers.Indexer ix in ilist.Properties){
8283 if (ix.Setter == set){
8284 type = ix.PropertyInfo.PropertyType;
8289 instance_expr.CheckMarshalByRefAccess ();
8291 eclass = ExprClass.IndexerAccess;
8295 bool prepared = false;
8296 LocalTemporary temp;
8298 public void Emit (EmitContext ec, bool leave_copy)
8300 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
8302 ec.ig.Emit (OpCodes.Dup);
8303 temp = new LocalTemporary (Type);
8309 // source is ignored, because we already have a copy of it from the
8310 // LValue resolution and we have already constructed a pre-cached
8311 // version of the arguments (ea.set_arguments);
8313 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8315 prepared = prepare_for_load;
8316 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
8321 ec.ig.Emit (OpCodes.Dup);
8322 temp = new LocalTemporary (Type);
8325 } else if (leave_copy) {
8326 temp = new LocalTemporary (Type);
8332 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
8341 public override void Emit (EmitContext ec)
8346 public override string GetSignatureForError ()
8348 // FIXME: print the argument list of the indexer
8349 return instance_expr.GetSignatureForError () + ".this[...]";
8354 /// The base operator for method names
8356 public class BaseAccess : Expression {
8357 public readonly string Identifier;
8359 public BaseAccess (string member, Location l)
8361 this.Identifier = member;
8365 public BaseAccess (string member, TypeArguments args, Location l)
8373 public override Expression DoResolve (EmitContext ec)
8375 Expression c = CommonResolve (ec);
8381 // MethodGroups use this opportunity to flag an error on lacking ()
8383 if (!(c is MethodGroupExpr))
8384 return c.Resolve (ec);
8388 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8390 Expression c = CommonResolve (ec);
8396 // MethodGroups use this opportunity to flag an error on lacking ()
8398 if (! (c is MethodGroupExpr))
8399 return c.DoResolveLValue (ec, right_side);
8404 Expression CommonResolve (EmitContext ec)
8406 Expression member_lookup;
8407 Type current_type = ec.ContainerType;
8408 Type base_type = current_type.BaseType;
8411 Error (1511, "Keyword `base' is not available in a static method");
8415 if (ec.IsFieldInitializer){
8416 Error (1512, "Keyword `base' is not available in the current context");
8420 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8421 AllMemberTypes, AllBindingFlags, loc);
8422 if (member_lookup == null) {
8423 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8430 left = new TypeExpression (base_type, loc);
8432 left = ec.GetThis (loc);
8434 MemberExpr me = (MemberExpr) member_lookup;
8436 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8438 if (e is PropertyExpr) {
8439 PropertyExpr pe = (PropertyExpr) e;
8444 MethodGroupExpr mg = e as MethodGroupExpr;
8450 return mg.ResolveGeneric (ec, args);
8452 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8460 public override void Emit (EmitContext ec)
8462 throw new Exception ("Should never be called");
8467 /// The base indexer operator
8469 public class BaseIndexerAccess : IndexerAccess {
8470 public BaseIndexerAccess (ArrayList args, Location loc)
8471 : base (null, true, loc)
8473 arguments = new ArrayList ();
8474 foreach (Expression tmp in args)
8475 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8478 protected override bool CommonResolve (EmitContext ec)
8480 instance_expr = ec.GetThis (loc);
8482 current_type = ec.ContainerType.BaseType;
8483 indexer_type = current_type;
8485 foreach (Argument a in arguments){
8486 if (!a.Resolve (ec, loc))
8495 /// This class exists solely to pass the Type around and to be a dummy
8496 /// that can be passed to the conversion functions (this is used by
8497 /// foreach implementation to typecast the object return value from
8498 /// get_Current into the proper type. All code has been generated and
8499 /// we only care about the side effect conversions to be performed
8501 /// This is also now used as a placeholder where a no-action expression
8502 /// is needed (the `New' class).
8504 public class EmptyExpression : Expression {
8505 public static readonly EmptyExpression Null = new EmptyExpression ();
8507 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8508 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8509 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8511 static EmptyExpression temp = new EmptyExpression ();
8512 public static EmptyExpression Grab ()
8514 EmptyExpression retval = temp == null ? new EmptyExpression () : temp;
8519 public static void Release (EmptyExpression e)
8524 // TODO: should be protected
8525 public EmptyExpression ()
8527 type = TypeManager.object_type;
8528 eclass = ExprClass.Value;
8529 loc = Location.Null;
8532 public EmptyExpression (Type t)
8535 eclass = ExprClass.Value;
8536 loc = Location.Null;
8539 public override Expression DoResolve (EmitContext ec)
8544 public override void Emit (EmitContext ec)
8546 // nothing, as we only exist to not do anything.
8550 // This is just because we might want to reuse this bad boy
8551 // instead of creating gazillions of EmptyExpressions.
8552 // (CanImplicitConversion uses it)
8554 public void SetType (Type t)
8560 public class UserCast : Expression {
8564 public UserCast (MethodInfo method, Expression source, Location l)
8566 this.method = method;
8567 this.source = source;
8568 type = method.ReturnType;
8569 eclass = ExprClass.Value;
8573 public Expression Source {
8579 public override Expression DoResolve (EmitContext ec)
8582 // We are born fully resolved
8587 public override void Emit (EmitContext ec)
8589 ILGenerator ig = ec.ig;
8593 if (method is MethodInfo)
8594 ig.Emit (OpCodes.Call, (MethodInfo) method);
8596 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8602 // This class is used to "construct" the type during a typecast
8603 // operation. Since the Type.GetType class in .NET can parse
8604 // the type specification, we just use this to construct the type
8605 // one bit at a time.
8607 public class ComposedCast : TypeExpr {
8611 public ComposedCast (Expression left, string dim)
8612 : this (left, dim, left.Location)
8616 public ComposedCast (Expression left, string dim, Location l)
8624 public Expression RemoveNullable ()
8626 if (dim.EndsWith ("?")) {
8627 dim = dim.Substring (0, dim.Length - 1);
8636 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8638 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8642 Type ltype = lexpr.Type;
8643 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8644 Error_VoidInvalidInTheContext (loc);
8649 if ((dim.Length > 0) && (dim [0] == '?')) {
8650 TypeExpr nullable = new NullableType (left, loc);
8652 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8653 return nullable.ResolveAsTypeTerminal (ec, false);
8657 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc))
8660 if (dim != "" && dim [0] == '[' &&
8661 (ltype == TypeManager.arg_iterator_type || ltype == TypeManager.typed_reference_type)) {
8662 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (ltype));
8667 type = TypeManager.GetConstructedType (ltype, dim);
8672 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8674 if (type.IsPointer && !ec.IsInUnsafeScope){
8679 eclass = ExprClass.Type;
8683 public override string Name {
8684 get { return left + dim; }
8687 public override string FullName {
8688 get { return type.FullName; }
8691 public override string GetSignatureForError ()
8693 return left.GetSignatureForError () + dim;
8697 public class FixedBufferPtr : Expression {
8700 public FixedBufferPtr (Expression array, Type array_type, Location l)
8705 type = TypeManager.GetPointerType (array_type);
8706 eclass = ExprClass.Value;
8709 public override void Emit(EmitContext ec)
8714 public override Expression DoResolve (EmitContext ec)
8717 // We are born fully resolved
8725 // This class is used to represent the address of an array, used
8726 // only by the Fixed statement, this generates "&a [0]" construct
8727 // for fixed (char *pa = a)
8729 public class ArrayPtr : FixedBufferPtr {
8732 public ArrayPtr (Expression array, Type array_type, Location l):
8733 base (array, array_type, l)
8735 this.array_type = array_type;
8738 public override void Emit (EmitContext ec)
8742 ILGenerator ig = ec.ig;
8743 IntLiteral.EmitInt (ig, 0);
8744 ig.Emit (OpCodes.Ldelema, array_type);
8749 // Used by the fixed statement
8751 public class StringPtr : Expression {
8754 public StringPtr (LocalBuilder b, Location l)
8757 eclass = ExprClass.Value;
8758 type = TypeManager.char_ptr_type;
8762 public override Expression DoResolve (EmitContext ec)
8764 // This should never be invoked, we are born in fully
8765 // initialized state.
8770 public override void Emit (EmitContext ec)
8772 ILGenerator ig = ec.ig;
8774 ig.Emit (OpCodes.Ldloc, b);
8775 ig.Emit (OpCodes.Conv_I);
8776 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8777 ig.Emit (OpCodes.Add);
8782 // Implements the `stackalloc' keyword
8784 public class StackAlloc : Expression {
8789 public StackAlloc (Expression type, Expression count, Location l)
8796 public override Expression DoResolve (EmitContext ec)
8798 count = count.Resolve (ec);
8802 if (count.Type != TypeManager.int32_type){
8803 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8808 Constant c = count as Constant;
8809 if (c != null && c.IsNegative) {
8810 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8814 if (ec.InCatch || ec.InFinally) {
8815 Error (255, "Cannot use stackalloc in finally or catch");
8819 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8825 if (!TypeManager.VerifyUnManaged (otype, loc))
8828 type = TypeManager.GetPointerType (otype);
8829 eclass = ExprClass.Value;
8834 public override void Emit (EmitContext ec)
8836 int size = GetTypeSize (otype);
8837 ILGenerator ig = ec.ig;
8840 ig.Emit (OpCodes.Sizeof, otype);
8842 IntConstant.EmitInt (ig, size);
8844 ig.Emit (OpCodes.Mul);
8845 ig.Emit (OpCodes.Localloc);