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) && (ec.capture_context != null) &&
471 ec.capture_context.IsParameterCaptured (pr.Name)) {
472 AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc);
476 // According to the specs, a variable is considered definitely assigned if you take
478 if ((variable != null) && (variable.VariableInfo != null)){
479 variable.VariableInfo.SetAssigned (ec);
482 type = TypeManager.GetPointerType (Expr.Type);
485 case Operator.Indirection:
491 if (!expr_type.IsPointer){
492 Error (193, "The * or -> operator must be applied to a pointer");
497 // We create an Indirection expression, because
498 // it can implement the IMemoryLocation.
500 return new Indirection (Expr, loc);
502 case Operator.UnaryPlus:
504 // A plus in front of something is just a no-op, so return the child.
508 case Operator.UnaryNegation:
510 // Deals with -literals
511 // int operator- (int x)
512 // long operator- (long x)
513 // float operator- (float f)
514 // double operator- (double d)
515 // decimal operator- (decimal d)
517 Expression expr = null;
520 // transform - - expr into expr
523 Unary unary = (Unary) Expr;
525 if (unary.Oper == Operator.UnaryNegation)
530 // perform numeric promotions to int,
534 // The following is inneficient, because we call
535 // ImplicitConversion too many times.
537 // It is also not clear if we should convert to Float
538 // or Double initially.
540 if (expr_type == TypeManager.uint32_type){
542 // FIXME: handle exception to this rule that
543 // permits the int value -2147483648 (-2^31) to
544 // bt wrote as a decimal interger literal
546 type = TypeManager.int64_type;
547 Expr = Convert.ImplicitConversion (ec, Expr, type, loc);
551 if (expr_type == TypeManager.uint64_type){
553 // FIXME: Handle exception of `long value'
554 // -92233720368547758087 (-2^63) to be wrote as
555 // decimal integer literal.
561 if (expr_type == TypeManager.float_type){
566 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int32_type, loc);
573 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int64_type, loc);
580 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.double_type, loc);
591 Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" +
592 TypeManager.CSharpName (expr_type) + "'");
596 public override Expression DoResolve (EmitContext ec)
598 if (Oper == Operator.AddressOf) {
599 Expr = Expr.DoResolveLValue (ec, new EmptyExpression ());
601 if (Expr == null || Expr.eclass != ExprClass.Variable){
602 Error (211, "Cannot take the address of the given expression");
607 Expr = Expr.Resolve (ec);
612 if (TypeManager.IsNullableValueType (Expr.Type))
613 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
615 eclass = ExprClass.Value;
616 return ResolveOperator (ec);
619 public override Expression DoResolveLValue (EmitContext ec, Expression right)
621 if (Oper == Operator.Indirection)
622 return DoResolve (ec);
627 public override void Emit (EmitContext ec)
629 ILGenerator ig = ec.ig;
632 case Operator.UnaryPlus:
633 throw new Exception ("This should be caught by Resolve");
635 case Operator.UnaryNegation:
636 if (ec.CheckState && type != TypeManager.float_type && type != TypeManager.double_type) {
637 ig.Emit (OpCodes.Ldc_I4_0);
638 if (type == TypeManager.int64_type)
639 ig.Emit (OpCodes.Conv_U8);
641 ig.Emit (OpCodes.Sub_Ovf);
644 ig.Emit (OpCodes.Neg);
649 case Operator.LogicalNot:
651 ig.Emit (OpCodes.Ldc_I4_0);
652 ig.Emit (OpCodes.Ceq);
655 case Operator.OnesComplement:
657 ig.Emit (OpCodes.Not);
660 case Operator.AddressOf:
661 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
665 throw new Exception ("This should not happen: Operator = "
670 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
672 if (Oper == Operator.LogicalNot)
673 Expr.EmitBranchable (ec, target, !onTrue);
675 base.EmitBranchable (ec, target, onTrue);
678 public override string ToString ()
680 return "Unary (" + Oper + ", " + Expr + ")";
686 // Unary operators are turned into Indirection expressions
687 // after semantic analysis (this is so we can take the address
688 // of an indirection).
690 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
692 LocalTemporary temporary;
695 public Indirection (Expression expr, Location l)
698 type = TypeManager.HasElementType (expr.Type) ? TypeManager.GetElementType (expr.Type) : expr.Type;
699 eclass = ExprClass.Variable;
703 public override void Emit (EmitContext ec)
708 LoadFromPtr (ec.ig, Type);
711 public void Emit (EmitContext ec, bool leave_copy)
715 ec.ig.Emit (OpCodes.Dup);
716 temporary = new LocalTemporary (expr.Type);
717 temporary.Store (ec);
721 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
723 prepared = prepare_for_load;
727 if (prepare_for_load)
728 ec.ig.Emit (OpCodes.Dup);
732 ec.ig.Emit (OpCodes.Dup);
733 temporary = new LocalTemporary (expr.Type);
734 temporary.Store (ec);
737 StoreFromPtr (ec.ig, type);
739 if (temporary != null) {
741 temporary.Release (ec);
745 public void AddressOf (EmitContext ec, AddressOp Mode)
750 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
752 return DoResolve (ec);
755 public override Expression DoResolve (EmitContext ec)
758 // Born fully resolved
763 public override string ToString ()
765 return "*(" + expr + ")";
768 #region IVariable Members
770 public VariableInfo VariableInfo {
774 public bool VerifyFixed ()
776 // A pointer-indirection is always fixed.
784 /// Unary Mutator expressions (pre and post ++ and --)
788 /// UnaryMutator implements ++ and -- expressions. It derives from
789 /// ExpressionStatement becuase the pre/post increment/decrement
790 /// operators can be used in a statement context.
792 /// FIXME: Idea, we could split this up in two classes, one simpler
793 /// for the common case, and one with the extra fields for more complex
794 /// classes (indexers require temporary access; overloaded require method)
797 public class UnaryMutator : ExpressionStatement {
799 public enum Mode : byte {
806 PreDecrement = IsDecrement,
807 PostIncrement = IsPost,
808 PostDecrement = IsPost | IsDecrement
812 bool is_expr = false;
813 bool recurse = false;
818 // This is expensive for the simplest case.
820 StaticCallExpr method;
822 public UnaryMutator (Mode m, Expression e, Location l)
829 static string OperName (Mode mode)
831 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
836 /// Returns whether an object of type `t' can be incremented
837 /// or decremented with add/sub (ie, basically whether we can
838 /// use pre-post incr-decr operations on it, but it is not a
839 /// System.Decimal, which we require operator overloading to catch)
841 static bool IsIncrementableNumber (Type t)
843 return (t == TypeManager.sbyte_type) ||
844 (t == TypeManager.byte_type) ||
845 (t == TypeManager.short_type) ||
846 (t == TypeManager.ushort_type) ||
847 (t == TypeManager.int32_type) ||
848 (t == TypeManager.uint32_type) ||
849 (t == TypeManager.int64_type) ||
850 (t == TypeManager.uint64_type) ||
851 (t == TypeManager.char_type) ||
852 (t.IsSubclassOf (TypeManager.enum_type)) ||
853 (t == TypeManager.float_type) ||
854 (t == TypeManager.double_type) ||
855 (t.IsPointer && t != TypeManager.void_ptr_type);
858 Expression ResolveOperator (EmitContext ec)
860 Type expr_type = expr.Type;
863 // Step 1: Perform Operator Overload location
868 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
869 op_name = "op_Increment";
871 op_name = "op_Decrement";
873 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
876 method = StaticCallExpr.MakeSimpleCall (
877 ec, (MethodGroupExpr) mg, expr, loc);
880 } else if (!IsIncrementableNumber (expr_type)) {
881 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
882 TypeManager.CSharpName (expr_type) + "'");
887 // The operand of the prefix/postfix increment decrement operators
888 // should be an expression that is classified as a variable,
889 // a property access or an indexer access
892 if (expr.eclass == ExprClass.Variable){
893 LocalVariableReference var = expr as LocalVariableReference;
894 if ((var != null) && var.IsReadOnly) {
895 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
898 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
899 expr = expr.ResolveLValue (ec, this, Location);
903 expr.Error_UnexpectedKind (ec.DeclContainer, "variable, indexer or property access", loc);
910 public override Expression DoResolve (EmitContext ec)
912 expr = expr.Resolve (ec);
917 eclass = ExprClass.Value;
919 if (TypeManager.IsNullableValueType (expr.Type))
920 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
922 return ResolveOperator (ec);
925 static int PtrTypeSize (Type t)
927 return GetTypeSize (TypeManager.GetElementType (t));
931 // Loads the proper "1" into the stack based on the type, then it emits the
932 // opcode for the operation requested
934 void LoadOneAndEmitOp (EmitContext ec, Type t)
937 // Measure if getting the typecode and using that is more/less efficient
938 // that comparing types. t.GetTypeCode() is an internal call.
940 ILGenerator ig = ec.ig;
942 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
943 LongConstant.EmitLong (ig, 1);
944 else if (t == TypeManager.double_type)
945 ig.Emit (OpCodes.Ldc_R8, 1.0);
946 else if (t == TypeManager.float_type)
947 ig.Emit (OpCodes.Ldc_R4, 1.0F);
948 else if (t.IsPointer){
949 int n = PtrTypeSize (t);
952 ig.Emit (OpCodes.Sizeof, t);
954 IntConstant.EmitInt (ig, n);
956 ig.Emit (OpCodes.Ldc_I4_1);
959 // Now emit the operation
962 if (t == TypeManager.int32_type ||
963 t == TypeManager.int64_type){
964 if ((mode & Mode.IsDecrement) != 0)
965 ig.Emit (OpCodes.Sub_Ovf);
967 ig.Emit (OpCodes.Add_Ovf);
968 } else if (t == TypeManager.uint32_type ||
969 t == TypeManager.uint64_type){
970 if ((mode & Mode.IsDecrement) != 0)
971 ig.Emit (OpCodes.Sub_Ovf_Un);
973 ig.Emit (OpCodes.Add_Ovf_Un);
975 if ((mode & Mode.IsDecrement) != 0)
976 ig.Emit (OpCodes.Sub_Ovf);
978 ig.Emit (OpCodes.Add_Ovf);
981 if ((mode & Mode.IsDecrement) != 0)
982 ig.Emit (OpCodes.Sub);
984 ig.Emit (OpCodes.Add);
987 if (t == TypeManager.sbyte_type){
989 ig.Emit (OpCodes.Conv_Ovf_I1);
991 ig.Emit (OpCodes.Conv_I1);
992 } else if (t == TypeManager.byte_type){
994 ig.Emit (OpCodes.Conv_Ovf_U1);
996 ig.Emit (OpCodes.Conv_U1);
997 } else if (t == TypeManager.short_type){
999 ig.Emit (OpCodes.Conv_Ovf_I2);
1001 ig.Emit (OpCodes.Conv_I2);
1002 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
1004 ig.Emit (OpCodes.Conv_Ovf_U2);
1006 ig.Emit (OpCodes.Conv_U2);
1011 void EmitCode (EmitContext ec, bool is_expr)
1014 this.is_expr = is_expr;
1015 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1018 public override void Emit (EmitContext ec)
1021 // We use recurse to allow ourselfs to be the source
1022 // of an assignment. This little hack prevents us from
1023 // having to allocate another expression
1026 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1028 LoadOneAndEmitOp (ec, expr.Type);
1030 ec.ig.Emit (OpCodes.Call, method.Method);
1035 EmitCode (ec, true);
1038 public override void EmitStatement (EmitContext ec)
1040 EmitCode (ec, false);
1045 /// Base class for the `Is' and `As' classes.
1049 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1052 public abstract class Probe : Expression {
1053 public Expression ProbeType;
1054 protected Expression expr;
1055 protected TypeExpr probe_type_expr;
1057 public Probe (Expression expr, Expression probe_type, Location l)
1059 ProbeType = probe_type;
1064 public Expression Expr {
1070 public override Expression DoResolve (EmitContext ec)
1072 probe_type_expr = ProbeType.ResolveAsTypeTerminal (ec, false);
1073 if (probe_type_expr == null)
1076 expr = expr.Resolve (ec);
1080 if (expr.Type.IsPointer) {
1081 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1089 /// Implementation of the `is' operator.
1091 public class Is : Probe {
1092 public Is (Expression expr, Expression probe_type, Location l)
1093 : base (expr, probe_type, l)
1098 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1103 public override void Emit (EmitContext ec)
1105 ILGenerator ig = ec.ig;
1110 case Action.AlwaysFalse:
1111 ig.Emit (OpCodes.Pop);
1112 IntConstant.EmitInt (ig, 0);
1114 case Action.AlwaysTrue:
1115 ig.Emit (OpCodes.Pop);
1116 IntConstant.EmitInt (ig, 1);
1118 case Action.LeaveOnStack:
1119 // the `e != null' rule.
1120 ig.Emit (OpCodes.Ldnull);
1121 ig.Emit (OpCodes.Ceq);
1122 ig.Emit (OpCodes.Ldc_I4_0);
1123 ig.Emit (OpCodes.Ceq);
1126 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1127 ig.Emit (OpCodes.Ldnull);
1128 ig.Emit (OpCodes.Cgt_Un);
1131 throw new Exception ("never reached");
1134 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1136 ILGenerator ig = ec.ig;
1139 case Action.AlwaysFalse:
1141 ig.Emit (OpCodes.Br, target);
1144 case Action.AlwaysTrue:
1146 ig.Emit (OpCodes.Br, target);
1149 case Action.LeaveOnStack:
1150 // the `e != null' rule.
1152 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1156 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1157 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1160 throw new Exception ("never reached");
1163 public override Expression DoResolve (EmitContext ec)
1165 Expression e = base.DoResolve (ec);
1167 if ((e == null) || (expr == null))
1170 Type etype = expr.Type;
1171 bool warning_always_matches = false;
1172 bool warning_never_matches = false;
1174 type = TypeManager.bool_type;
1175 eclass = ExprClass.Value;
1178 // First case, if at compile time, there is an implicit conversion
1179 // then e != null (objects) or true (value types)
1181 Type probe_type = probe_type_expr.Type;
1182 e = Convert.ImplicitConversionStandard (ec, expr, probe_type, loc);
1183 if (e != null && !(e is NullCast)){
1185 if (etype.IsValueType)
1186 action = Action.AlwaysTrue;
1188 action = Action.LeaveOnStack;
1190 warning_always_matches = true;
1191 } else if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1192 if (etype.IsGenericParameter)
1193 expr = new BoxedCast (expr, etype);
1196 // Second case: explicit reference convresion
1198 if (expr is NullLiteral)
1199 action = Action.AlwaysFalse;
1201 action = Action.Probe;
1202 } else if (etype.ContainsGenericParameters || probe_type.ContainsGenericParameters) {
1203 expr = new BoxedCast (expr, etype);
1204 action = Action.Probe;
1206 action = Action.AlwaysFalse;
1207 warning_never_matches = true;
1210 if (warning_always_matches)
1211 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1212 else if (warning_never_matches){
1213 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1214 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1222 /// Implementation of the `as' operator.
1224 public class As : Probe {
1225 public As (Expression expr, Expression probe_type, Location l)
1226 : base (expr, probe_type, l)
1230 bool do_isinst = false;
1231 Expression resolved_type;
1233 public override void Emit (EmitContext ec)
1235 ILGenerator ig = ec.ig;
1240 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1243 static void Error_CannotConvertType (Type source, Type target, Location loc)
1245 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1246 TypeManager.CSharpName (source),
1247 TypeManager.CSharpName (target));
1250 public override Expression DoResolve (EmitContext ec)
1252 if (resolved_type == null) {
1253 resolved_type = base.DoResolve (ec);
1255 if (resolved_type == null)
1259 type = probe_type_expr.Type;
1260 eclass = ExprClass.Value;
1261 Type etype = expr.Type;
1263 if (type.IsValueType) {
1264 Report.Error (77, loc, "The as operator must be used with a reference type (`" +
1265 TypeManager.CSharpName (type) + "' is a value type)");
1271 // If the type is a type parameter, ensure
1272 // that it is constrained by a class
1274 TypeParameterExpr tpe = probe_type_expr as TypeParameterExpr;
1276 Constraints constraints = tpe.TypeParameter.Constraints;
1279 if (constraints == null)
1282 if (!constraints.HasClassConstraint)
1283 if ((constraints.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == 0)
1287 Report.Error (413, loc,
1288 "The as operator requires that the `{0}' type parameter be constrained by a class",
1289 probe_type_expr.GetSignatureForError ());
1294 Expression e = Convert.ImplicitConversion (ec, expr, type, loc);
1301 if (Convert.ExplicitReferenceConversionExists (etype, type)){
1302 if (etype.IsGenericParameter)
1303 expr = new BoxedCast (expr, etype);
1309 if (etype.ContainsGenericParameters || type.ContainsGenericParameters) {
1310 expr = new BoxedCast (expr, etype);
1315 Error_CannotConvertType (etype, type, loc);
1319 public override bool GetAttributableValue (Type valueType, out object value)
1321 return expr.GetAttributableValue (valueType, out value);
1326 /// This represents a typecast in the source language.
1328 /// FIXME: Cast expressions have an unusual set of parsing
1329 /// rules, we need to figure those out.
1331 public class Cast : Expression {
1332 Expression target_type;
1335 public Cast (Expression cast_type, Expression expr)
1336 : this (cast_type, expr, cast_type.Location)
1340 public Cast (Expression cast_type, Expression expr, Location loc)
1342 this.target_type = cast_type;
1346 if (target_type == TypeManager.system_void_expr)
1347 Error_VoidInvalidInTheContext (loc);
1350 public Expression TargetType {
1351 get { return target_type; }
1354 public Expression Expr {
1355 get { return expr; }
1356 set { expr = value; }
1359 public override Expression DoResolve (EmitContext ec)
1361 expr = expr.Resolve (ec);
1365 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1371 if (type.IsAbstract && type.IsSealed) {
1372 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1376 eclass = ExprClass.Value;
1378 Constant c = expr as Constant;
1381 c = c.TryReduce (ec, type, loc);
1385 catch (OverflowException) {
1390 if (type.IsPointer && !ec.InUnsafe) {
1394 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1398 public override void Emit (EmitContext ec)
1400 throw new Exception ("Should not happen");
1405 /// Binary operators
1407 public class Binary : Expression {
1408 public enum Operator : byte {
1409 Multiply, Division, Modulus,
1410 Addition, Subtraction,
1411 LeftShift, RightShift,
1412 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1413 Equality, Inequality,
1423 Expression left, right;
1425 // This must be kept in sync with Operator!!!
1426 public static readonly string [] oper_names;
1430 oper_names = new string [(int) Operator.TOP];
1432 oper_names [(int) Operator.Multiply] = "op_Multiply";
1433 oper_names [(int) Operator.Division] = "op_Division";
1434 oper_names [(int) Operator.Modulus] = "op_Modulus";
1435 oper_names [(int) Operator.Addition] = "op_Addition";
1436 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1437 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1438 oper_names [(int) Operator.RightShift] = "op_RightShift";
1439 oper_names [(int) Operator.LessThan] = "op_LessThan";
1440 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1441 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1442 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1443 oper_names [(int) Operator.Equality] = "op_Equality";
1444 oper_names [(int) Operator.Inequality] = "op_Inequality";
1445 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1446 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1447 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1448 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1449 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1452 public Binary (Operator oper, Expression left, Expression right)
1457 this.loc = left.Location;
1460 public Operator Oper {
1469 public Expression Left {
1478 public Expression Right {
1489 /// Returns a stringified representation of the Operator
1491 public static string OperName (Operator oper)
1494 case Operator.Multiply:
1496 case Operator.Division:
1498 case Operator.Modulus:
1500 case Operator.Addition:
1502 case Operator.Subtraction:
1504 case Operator.LeftShift:
1506 case Operator.RightShift:
1508 case Operator.LessThan:
1510 case Operator.GreaterThan:
1512 case Operator.LessThanOrEqual:
1514 case Operator.GreaterThanOrEqual:
1516 case Operator.Equality:
1518 case Operator.Inequality:
1520 case Operator.BitwiseAnd:
1522 case Operator.BitwiseOr:
1524 case Operator.ExclusiveOr:
1526 case Operator.LogicalOr:
1528 case Operator.LogicalAnd:
1532 return oper.ToString ();
1535 public override string ToString ()
1537 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1538 right.ToString () + ")";
1541 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1543 if (expr.Type == target_type)
1546 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1549 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1552 34, loc, "Operator `" + OperName (oper)
1553 + "' is ambiguous on operands of type `"
1554 + TypeManager.CSharpName (l) + "' "
1555 + "and `" + TypeManager.CSharpName (r)
1559 bool IsConvertible (EmitContext ec, Expression le, Expression re, Type t)
1561 return Convert.ImplicitConversionExists (ec, le, t) && Convert.ImplicitConversionExists (ec, re, t);
1564 bool VerifyApplicable_Predefined (EmitContext ec, Type t)
1566 if (!IsConvertible (ec, left, right, t))
1568 left = ForceConversion (ec, left, t);
1569 right = ForceConversion (ec, right, t);
1574 bool IsApplicable_String (EmitContext ec, Expression le, Expression re, Operator oper)
1576 bool l = Convert.ImplicitConversionExists (ec, le, TypeManager.string_type);
1577 bool r = Convert.ImplicitConversionExists (ec, re, TypeManager.string_type);
1579 if (oper == Operator.Equality || oper == Operator.Inequality)
1581 if (oper == Operator.Addition)
1586 bool OverloadResolve_PredefinedString (EmitContext ec, Operator oper)
1588 if (!IsApplicable_String (ec, left, right, oper))
1590 Type t = TypeManager.string_type;
1591 if (Convert.ImplicitConversionExists (ec, left, t))
1592 left = ForceConversion (ec, left, t);
1593 if (Convert.ImplicitConversionExists (ec, right, t))
1594 right = ForceConversion (ec, right, t);
1599 bool OverloadResolve_PredefinedIntegral (EmitContext ec)
1601 return VerifyApplicable_Predefined (ec, TypeManager.int32_type) ||
1602 VerifyApplicable_Predefined (ec, TypeManager.uint32_type) ||
1603 VerifyApplicable_Predefined (ec, TypeManager.int64_type) ||
1604 VerifyApplicable_Predefined (ec, TypeManager.uint64_type) ||
1608 bool OverloadResolve_PredefinedFloating (EmitContext ec)
1610 return VerifyApplicable_Predefined (ec, TypeManager.float_type) ||
1611 VerifyApplicable_Predefined (ec, TypeManager.double_type) ||
1615 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1617 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1620 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1622 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1626 void Error_OperatorCannotBeApplied ()
1628 Error_OperatorCannotBeApplied (Location, OperName (oper), TypeManager.CSharpName (left.Type),
1629 TypeManager.CSharpName(right.Type));
1632 static bool is_unsigned (Type t)
1634 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1635 t == TypeManager.short_type || t == TypeManager.byte_type);
1638 Expression Make32or64 (EmitContext ec, Expression e)
1642 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1643 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1645 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1648 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1651 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1654 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1660 Expression CheckShiftArguments (EmitContext ec)
1662 Expression new_left = Make32or64 (ec, left);
1663 Expression new_right = ForceConversion (ec, right, TypeManager.int32_type);
1664 if (new_left == null || new_right == null) {
1665 Error_OperatorCannotBeApplied ();
1668 type = new_left.Type;
1669 int shiftmask = (type == TypeManager.int32_type || type == TypeManager.uint32_type) ? 31 : 63;
1671 right = new Binary (Binary.Operator.BitwiseAnd, new_right, new IntConstant (shiftmask, loc)).DoResolve (ec);
1676 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1677 // i.e., not invoke op_Equality.
1679 static bool EqualsNullIsReferenceEquals (Type t)
1681 return t == TypeManager.object_type || t == TypeManager.string_type ||
1682 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1685 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1687 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1688 "Possible unintended reference comparison; to get a value comparison, " +
1689 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1692 Expression ResolveOperator (EmitContext ec)
1695 Type r = right.Type;
1697 if (oper == Operator.Equality || oper == Operator.Inequality){
1698 if (l.IsGenericParameter && (right is NullLiteral)) {
1699 if (l.BaseType == TypeManager.value_type) {
1700 Error_OperatorCannotBeApplied ();
1704 left = new BoxedCast (left, TypeManager.object_type);
1705 Type = TypeManager.bool_type;
1709 if (r.IsGenericParameter && (left is NullLiteral)) {
1710 if (r.BaseType == TypeManager.value_type) {
1711 Error_OperatorCannotBeApplied ();
1715 right = new BoxedCast (right, TypeManager.object_type);
1716 Type = TypeManager.bool_type;
1721 // Optimize out call to op_Equality in a few cases.
1723 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1724 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1725 Type = TypeManager.bool_type;
1730 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1731 Type = TypeManager.bool_type;
1738 // Do not perform operator overload resolution when both sides are
1741 Expression left_operators = null, right_operators = null;
1742 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))) {
1744 // Step 1: Perform Operator Overload location
1746 string op = oper_names [(int) oper];
1748 MethodGroupExpr union;
1749 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc);
1751 right_operators = MemberLookup (
1752 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc);
1753 union = Invocation.MakeUnionSet (left_operators, right_operators, loc);
1755 union = (MethodGroupExpr) left_operators;
1757 if (union != null) {
1758 ArrayList args = new ArrayList (2);
1759 args.Add (new Argument (left, Argument.AType.Expression));
1760 args.Add (new Argument (right, Argument.AType.Expression));
1762 MethodBase method = Invocation.OverloadResolve (ec, union, args, true, Location.Null);
1764 if (method != null) {
1765 MethodInfo mi = (MethodInfo) method;
1766 return new BinaryMethod (mi.ReturnType, method, args);
1772 // Step 0: String concatenation (because overloading will get this wrong)
1774 if (oper == Operator.Addition){
1776 // If any of the arguments is a string, cast to string
1779 // Simple constant folding
1780 if (left is StringConstant && right is StringConstant)
1781 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1783 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1785 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1786 Error_OperatorCannotBeApplied ();
1790 // try to fold it in on the left
1791 if (left is StringConcat) {
1794 // We have to test here for not-null, since we can be doubly-resolved
1795 // take care of not appending twice
1798 type = TypeManager.string_type;
1799 ((StringConcat) left).Append (ec, right);
1800 return left.Resolve (ec);
1806 // Otherwise, start a new concat expression
1807 return new StringConcat (ec, loc, left, right).Resolve (ec);
1811 // Transform a + ( - b) into a - b
1813 if (right is Unary){
1814 Unary right_unary = (Unary) right;
1816 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1817 oper = Operator.Subtraction;
1818 right = right_unary.Expr;
1824 if (oper == Operator.Equality || oper == Operator.Inequality){
1825 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1826 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1827 Error_OperatorCannotBeApplied ();
1831 type = TypeManager.bool_type;
1835 if (l.IsPointer || r.IsPointer) {
1836 if (l.IsPointer && r.IsPointer) {
1837 type = TypeManager.bool_type;
1841 if (l.IsPointer && r == TypeManager.null_type) {
1842 right = new EmptyCast (NullPointer.Null, l);
1843 type = TypeManager.bool_type;
1847 if (r.IsPointer && l == TypeManager.null_type) {
1848 left = new EmptyCast (NullPointer.Null, r);
1849 type = TypeManager.bool_type;
1854 if (l.IsGenericParameter && r.IsGenericParameter) {
1855 GenericConstraints l_gc, r_gc;
1857 l_gc = TypeManager.GetTypeParameterConstraints (l);
1858 r_gc = TypeManager.GetTypeParameterConstraints (r);
1860 if ((l_gc == null) || (r_gc == null) ||
1861 !(l_gc.HasReferenceTypeConstraint || l_gc.HasClassConstraint) ||
1862 !(r_gc.HasReferenceTypeConstraint || r_gc.HasClassConstraint)) {
1863 Error_OperatorCannotBeApplied ();
1870 // operator != (object a, object b)
1871 // operator == (object a, object b)
1873 // For this to be used, both arguments have to be reference-types.
1874 // Read the rationale on the spec (14.9.6)
1876 if (!(l.IsValueType || r.IsValueType)){
1877 type = TypeManager.bool_type;
1883 // Also, a standard conversion must exist from either one
1885 bool left_to_right =
1886 Convert.ImplicitStandardConversionExists (left, r);
1887 bool right_to_left = !left_to_right &&
1888 Convert.ImplicitStandardConversionExists (right, l);
1890 if (!left_to_right && !right_to_left) {
1891 Error_OperatorCannotBeApplied ();
1895 if (left_to_right && left_operators != null &&
1896 RootContext.WarningLevel >= 2) {
1897 ArrayList args = new ArrayList (2);
1898 args.Add (new Argument (left, Argument.AType.Expression));
1899 args.Add (new Argument (left, Argument.AType.Expression));
1900 MethodBase method = Invocation.OverloadResolve (
1901 ec, (MethodGroupExpr) left_operators, args, true, Location.Null);
1903 Warning_UnintendedReferenceComparison (loc, "right", l);
1906 if (right_to_left && right_operators != null &&
1907 RootContext.WarningLevel >= 2) {
1908 ArrayList args = new ArrayList (2);
1909 args.Add (new Argument (right, Argument.AType.Expression));
1910 args.Add (new Argument (right, Argument.AType.Expression));
1911 MethodBase method = Invocation.OverloadResolve (
1912 ec, (MethodGroupExpr) right_operators, args, true, Location.Null);
1914 Warning_UnintendedReferenceComparison (loc, "left", r);
1918 // We are going to have to convert to an object to compare
1920 if (l != TypeManager.object_type)
1921 left = new EmptyCast (left, TypeManager.object_type);
1922 if (r != TypeManager.object_type)
1923 right = new EmptyCast (right, TypeManager.object_type);
1929 // Only perform numeric promotions on:
1930 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
1932 if (oper == Operator.Addition || oper == Operator.Subtraction) {
1933 if (TypeManager.IsDelegateType (l)){
1934 if (((right.eclass == ExprClass.MethodGroup) ||
1935 (r == TypeManager.anonymous_method_type))){
1936 if ((RootContext.Version != LanguageVersion.ISO_1)){
1937 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
1945 if (TypeManager.IsDelegateType (r) || right is NullLiteral){
1947 ArrayList args = new ArrayList (2);
1949 args = new ArrayList (2);
1950 args.Add (new Argument (left, Argument.AType.Expression));
1951 args.Add (new Argument (right, Argument.AType.Expression));
1953 if (oper == Operator.Addition)
1954 method = TypeManager.delegate_combine_delegate_delegate;
1956 method = TypeManager.delegate_remove_delegate_delegate;
1958 if (!TypeManager.IsEqual (l, r) && !(right is NullLiteral)) {
1959 Error_OperatorCannotBeApplied ();
1963 return new BinaryDelegate (l, method, args);
1968 // Pointer arithmetic:
1970 // T* operator + (T* x, int y);
1971 // T* operator + (T* x, uint y);
1972 // T* operator + (T* x, long y);
1973 // T* operator + (T* x, ulong y);
1975 // T* operator + (int y, T* x);
1976 // T* operator + (uint y, T *x);
1977 // T* operator + (long y, T *x);
1978 // T* operator + (ulong y, T *x);
1980 // T* operator - (T* x, int y);
1981 // T* operator - (T* x, uint y);
1982 // T* operator - (T* x, long y);
1983 // T* operator - (T* x, ulong y);
1985 // long operator - (T* x, T *y)
1988 if (r.IsPointer && oper == Operator.Subtraction){
1990 return new PointerArithmetic (
1991 false, left, right, TypeManager.int64_type,
1994 Expression t = Make32or64 (ec, right);
1996 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
1998 } else if (r.IsPointer && oper == Operator.Addition){
1999 Expression t = Make32or64 (ec, left);
2001 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
2006 // Enumeration operators
2008 bool lie = TypeManager.IsEnumType (l);
2009 bool rie = TypeManager.IsEnumType (r);
2013 // U operator - (E e, E f)
2015 if (oper == Operator.Subtraction){
2017 type = TypeManager.EnumToUnderlying (l);
2020 Error_OperatorCannotBeApplied ();
2026 // operator + (E e, U x)
2027 // operator - (E e, U x)
2029 if (oper == Operator.Addition || oper == Operator.Subtraction){
2030 Type enum_type = lie ? l : r;
2031 Type other_type = lie ? r : l;
2032 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2034 if (underlying_type != other_type){
2035 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2045 Error_OperatorCannotBeApplied ();
2054 temp = Convert.ImplicitConversion (ec, right, l, loc);
2058 Error_OperatorCannotBeApplied ();
2062 temp = Convert.ImplicitConversion (ec, left, r, loc);
2067 Error_OperatorCannotBeApplied ();
2072 if (oper == Operator.Equality || oper == Operator.Inequality ||
2073 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2074 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2075 if (left.Type != right.Type){
2076 Error_OperatorCannotBeApplied ();
2079 type = TypeManager.bool_type;
2083 if (oper == Operator.BitwiseAnd ||
2084 oper == Operator.BitwiseOr ||
2085 oper == Operator.ExclusiveOr){
2086 if (left.Type != right.Type){
2087 Error_OperatorCannotBeApplied ();
2093 Error_OperatorCannotBeApplied ();
2097 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2098 return CheckShiftArguments (ec);
2100 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2101 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2102 type = TypeManager.bool_type;
2107 Error_OperatorCannotBeApplied ();
2111 Expression e = new ConditionalLogicalOperator (
2112 oper == Operator.LogicalAnd, left, right, l, loc);
2113 return e.Resolve (ec);
2116 Expression orig_left = left;
2117 Expression orig_right = right;
2120 // operator & (bool x, bool y)
2121 // operator | (bool x, bool y)
2122 // operator ^ (bool x, bool y)
2124 if (oper == Operator.BitwiseAnd ||
2125 oper == Operator.BitwiseOr ||
2126 oper == Operator.ExclusiveOr) {
2127 if (OverloadResolve_PredefinedIntegral (ec)) {
2128 if (IsConvertible (ec, orig_left, orig_right, TypeManager.bool_type)) {
2129 Error_OperatorAmbiguous (loc, oper, l, r);
2132 } else if (!VerifyApplicable_Predefined (ec, TypeManager.bool_type)) {
2133 Error_OperatorCannotBeApplied ();
2140 // Pointer comparison
2142 if (l.IsPointer && r.IsPointer){
2143 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2144 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2145 type = TypeManager.bool_type;
2150 if (OverloadResolve_PredefinedIntegral (ec)) {
2151 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2152 Error_OperatorAmbiguous (loc, oper, l, r);
2155 } else if (OverloadResolve_PredefinedFloating (ec)) {
2156 if (IsConvertible (ec, orig_left, orig_right, TypeManager.decimal_type) ||
2157 IsApplicable_String (ec, orig_left, orig_right, oper)) {
2158 Error_OperatorAmbiguous (loc, oper, l, r);
2161 } else if (VerifyApplicable_Predefined (ec, TypeManager.decimal_type)) {
2162 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2163 Error_OperatorAmbiguous (loc, oper, l, r);
2166 } else if (!OverloadResolve_PredefinedString (ec, oper)) {
2167 Error_OperatorCannotBeApplied ();
2171 if (oper == Operator.Equality ||
2172 oper == Operator.Inequality ||
2173 oper == Operator.LessThanOrEqual ||
2174 oper == Operator.LessThan ||
2175 oper == Operator.GreaterThanOrEqual ||
2176 oper == Operator.GreaterThan)
2177 type = TypeManager.bool_type;
2182 if (l == TypeManager.decimal_type || l == TypeManager.string_type || r == TypeManager.string_type) {
2184 if (r == TypeManager.string_type)
2186 MethodGroupExpr ops = (MethodGroupExpr) MemberLookup (
2187 ec.ContainerType, lookup, oper_names [(int) oper],
2188 MemberTypes.Method, AllBindingFlags, loc);
2189 ArrayList args = new ArrayList (2);
2190 args.Add (new Argument (left, Argument.AType.Expression));
2191 args.Add (new Argument (right, Argument.AType.Expression));
2192 MethodBase method = Invocation.OverloadResolve (ec, ops, args, true, Location.Null);
2193 return new BinaryMethod (type, method, args);
2199 Constant EnumLiftUp (Constant left, Constant right)
2202 case Operator.BitwiseOr:
2203 case Operator.BitwiseAnd:
2204 case Operator.ExclusiveOr:
2205 case Operator.Equality:
2206 case Operator.Inequality:
2207 case Operator.LessThan:
2208 case Operator.LessThanOrEqual:
2209 case Operator.GreaterThan:
2210 case Operator.GreaterThanOrEqual:
2211 if (left is EnumConstant)
2214 if (left.IsZeroInteger)
2215 return new EnumConstant (left, right.Type);
2219 case Operator.Addition:
2220 case Operator.Subtraction:
2223 case Operator.Multiply:
2224 case Operator.Division:
2225 case Operator.Modulus:
2226 case Operator.LeftShift:
2227 case Operator.RightShift:
2228 if (right is EnumConstant || left is EnumConstant)
2232 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2236 public override Expression DoResolve (EmitContext ec)
2241 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2242 left = ((ParenthesizedExpression) left).Expr;
2243 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2247 if (left.eclass == ExprClass.Type) {
2248 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2252 left = left.Resolve (ec);
2257 Constant lc = left as Constant;
2258 if (lc != null && lc.Type == TypeManager.bool_type &&
2259 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2260 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2262 // TODO: make a sense to resolve unreachable expression as we do for statement
2263 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2267 right = right.Resolve (ec);
2271 eclass = ExprClass.Value;
2272 Constant rc = right as Constant;
2274 // The conversion rules are ignored in enum context but why
2275 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2276 left = lc = EnumLiftUp (lc, rc);
2280 right = rc = EnumLiftUp (rc, lc);
2285 if (oper == Operator.BitwiseAnd) {
2286 if (rc != null && rc.IsZeroInteger) {
2287 return lc is EnumConstant ?
2288 new EnumConstant (rc, lc.Type):
2292 if (lc != null && lc.IsZeroInteger) {
2293 return rc is EnumConstant ?
2294 new EnumConstant (lc, rc.Type):
2298 else if (oper == Operator.BitwiseOr) {
2299 if (lc is EnumConstant &&
2300 rc != null && rc.IsZeroInteger)
2302 if (rc is EnumConstant &&
2303 lc != null && lc.IsZeroInteger)
2305 } else if (oper == Operator.LogicalAnd) {
2306 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2308 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2312 if (rc != null && lc != null){
2313 int prev_e = Report.Errors;
2314 Expression e = ConstantFold.BinaryFold (
2315 ec, oper, lc, rc, loc);
2316 if (e != null || Report.Errors != prev_e)
2320 Type ltype = left.Type, rtype = right.Type;
2321 if ((left is NullLiteral || ltype.IsValueType) &&
2322 (right is NullLiteral || rtype.IsValueType) &&
2323 !(left is NullLiteral && right is NullLiteral) &&
2324 (TypeManager.IsNullableType (ltype) || TypeManager.IsNullableType (rtype)))
2325 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2327 // Comparison warnings
2328 if (oper == Operator.Equality || oper == Operator.Inequality ||
2329 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2330 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2331 if (left.Equals (right)) {
2332 Report.Warning (1718, 3, loc, "Comparison made to same variable; did you mean to compare something else?");
2334 CheckUselessComparison (lc, right.Type);
2335 CheckUselessComparison (rc, left.Type);
2338 return ResolveOperator (ec);
2341 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2346 private void CheckUselessComparison (Constant c, Type type)
2348 if (c == null || !IsTypeIntegral (type)
2349 || c is StringConstant
2350 || c is BoolConstant
2351 || c is CharConstant
2352 || c is FloatConstant
2353 || c is DoubleConstant
2354 || c is DecimalConstant
2360 if (c is ULongConstant) {
2361 ulong uvalue = ((ULongConstant) c).Value;
2362 if (uvalue > long.MaxValue) {
2363 if (type == TypeManager.byte_type ||
2364 type == TypeManager.sbyte_type ||
2365 type == TypeManager.short_type ||
2366 type == TypeManager.ushort_type ||
2367 type == TypeManager.int32_type ||
2368 type == TypeManager.uint32_type ||
2369 type == TypeManager.int64_type)
2370 WarnUselessComparison (type);
2373 value = (long) uvalue;
2375 else if (c is ByteConstant)
2376 value = ((ByteConstant) c).Value;
2377 else if (c is SByteConstant)
2378 value = ((SByteConstant) c).Value;
2379 else if (c is ShortConstant)
2380 value = ((ShortConstant) c).Value;
2381 else if (c is UShortConstant)
2382 value = ((UShortConstant) c).Value;
2383 else if (c is IntConstant)
2384 value = ((IntConstant) c).Value;
2385 else if (c is UIntConstant)
2386 value = ((UIntConstant) c).Value;
2387 else if (c is LongConstant)
2388 value = ((LongConstant) c).Value;
2391 if (IsValueOutOfRange (value, type))
2392 WarnUselessComparison (type);
2397 private bool IsValueOutOfRange (long value, Type type)
2399 if (IsTypeUnsigned (type) && value < 0)
2401 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2402 type == TypeManager.byte_type && value >= 0x100 ||
2403 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2404 type == TypeManager.ushort_type && value >= 0x10000 ||
2405 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2406 type == TypeManager.uint32_type && value >= 0x100000000;
2409 private static bool IsTypeIntegral (Type type)
2411 return type == TypeManager.uint64_type ||
2412 type == TypeManager.int64_type ||
2413 type == TypeManager.uint32_type ||
2414 type == TypeManager.int32_type ||
2415 type == TypeManager.ushort_type ||
2416 type == TypeManager.short_type ||
2417 type == TypeManager.sbyte_type ||
2418 type == TypeManager.byte_type;
2421 private static bool IsTypeUnsigned (Type type)
2423 return type == TypeManager.uint64_type ||
2424 type == TypeManager.uint32_type ||
2425 type == TypeManager.ushort_type ||
2426 type == TypeManager.byte_type;
2429 private void WarnUselessComparison (Type type)
2431 Report.Warning (652, 2, loc, "Comparison to integral constant is useless; the constant is outside the range of type `{0}'",
2432 TypeManager.CSharpName (type));
2436 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2437 /// context of a conditional bool expression. This function will return
2438 /// false if it is was possible to use EmitBranchable, or true if it was.
2440 /// The expression's code is generated, and we will generate a branch to `target'
2441 /// if the resulting expression value is equal to isTrue
2443 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2445 ILGenerator ig = ec.ig;
2448 // This is more complicated than it looks, but its just to avoid
2449 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2450 // but on top of that we want for == and != to use a special path
2451 // if we are comparing against null
2453 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2454 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2457 // put the constant on the rhs, for simplicity
2459 if (left is Constant) {
2460 Expression swap = right;
2465 if (((Constant) right).IsZeroInteger) {
2468 ig.Emit (OpCodes.Brtrue, target);
2470 ig.Emit (OpCodes.Brfalse, target);
2473 } else if (right is BoolConstant) {
2475 if (my_on_true != ((BoolConstant) right).Value)
2476 ig.Emit (OpCodes.Brtrue, target);
2478 ig.Emit (OpCodes.Brfalse, target);
2483 } else if (oper == Operator.LogicalAnd) {
2486 Label tests_end = ig.DefineLabel ();
2488 left.EmitBranchable (ec, tests_end, false);
2489 right.EmitBranchable (ec, target, true);
2490 ig.MarkLabel (tests_end);
2492 left.EmitBranchable (ec, target, false);
2493 right.EmitBranchable (ec, target, false);
2498 } else if (oper == Operator.LogicalOr){
2500 left.EmitBranchable (ec, target, true);
2501 right.EmitBranchable (ec, target, true);
2504 Label tests_end = ig.DefineLabel ();
2505 left.EmitBranchable (ec, tests_end, true);
2506 right.EmitBranchable (ec, target, false);
2507 ig.MarkLabel (tests_end);
2512 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2513 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2514 oper == Operator.Equality || oper == Operator.Inequality)) {
2515 base.EmitBranchable (ec, target, onTrue);
2523 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2526 case Operator.Equality:
2528 ig.Emit (OpCodes.Beq, target);
2530 ig.Emit (OpCodes.Bne_Un, target);
2533 case Operator.Inequality:
2535 ig.Emit (OpCodes.Bne_Un, target);
2537 ig.Emit (OpCodes.Beq, target);
2540 case Operator.LessThan:
2543 ig.Emit (OpCodes.Blt_Un, target);
2545 ig.Emit (OpCodes.Blt, target);
2548 ig.Emit (OpCodes.Bge_Un, target);
2550 ig.Emit (OpCodes.Bge, target);
2553 case Operator.GreaterThan:
2556 ig.Emit (OpCodes.Bgt_Un, target);
2558 ig.Emit (OpCodes.Bgt, target);
2561 ig.Emit (OpCodes.Ble_Un, target);
2563 ig.Emit (OpCodes.Ble, target);
2566 case Operator.LessThanOrEqual:
2569 ig.Emit (OpCodes.Ble_Un, target);
2571 ig.Emit (OpCodes.Ble, target);
2574 ig.Emit (OpCodes.Bgt_Un, target);
2576 ig.Emit (OpCodes.Bgt, target);
2580 case Operator.GreaterThanOrEqual:
2583 ig.Emit (OpCodes.Bge_Un, target);
2585 ig.Emit (OpCodes.Bge, target);
2588 ig.Emit (OpCodes.Blt_Un, target);
2590 ig.Emit (OpCodes.Blt, target);
2593 Console.WriteLine (oper);
2594 throw new Exception ("what is THAT");
2598 public override void Emit (EmitContext ec)
2600 ILGenerator ig = ec.ig;
2605 // Handle short-circuit operators differently
2608 if (oper == Operator.LogicalAnd) {
2609 Label load_zero = ig.DefineLabel ();
2610 Label end = ig.DefineLabel ();
2612 left.EmitBranchable (ec, load_zero, false);
2614 ig.Emit (OpCodes.Br, end);
2616 ig.MarkLabel (load_zero);
2617 ig.Emit (OpCodes.Ldc_I4_0);
2620 } else if (oper == Operator.LogicalOr) {
2621 Label load_one = ig.DefineLabel ();
2622 Label end = ig.DefineLabel ();
2624 left.EmitBranchable (ec, load_one, true);
2626 ig.Emit (OpCodes.Br, end);
2628 ig.MarkLabel (load_one);
2629 ig.Emit (OpCodes.Ldc_I4_1);
2637 bool isUnsigned = is_unsigned (left.Type);
2640 case Operator.Multiply:
2642 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2643 opcode = OpCodes.Mul_Ovf;
2644 else if (isUnsigned)
2645 opcode = OpCodes.Mul_Ovf_Un;
2647 opcode = OpCodes.Mul;
2649 opcode = OpCodes.Mul;
2653 case Operator.Division:
2655 opcode = OpCodes.Div_Un;
2657 opcode = OpCodes.Div;
2660 case Operator.Modulus:
2662 opcode = OpCodes.Rem_Un;
2664 opcode = OpCodes.Rem;
2667 case Operator.Addition:
2669 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2670 opcode = OpCodes.Add_Ovf;
2671 else if (isUnsigned)
2672 opcode = OpCodes.Add_Ovf_Un;
2674 opcode = OpCodes.Add;
2676 opcode = OpCodes.Add;
2679 case Operator.Subtraction:
2681 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2682 opcode = OpCodes.Sub_Ovf;
2683 else if (isUnsigned)
2684 opcode = OpCodes.Sub_Ovf_Un;
2686 opcode = OpCodes.Sub;
2688 opcode = OpCodes.Sub;
2691 case Operator.RightShift:
2693 opcode = OpCodes.Shr_Un;
2695 opcode = OpCodes.Shr;
2698 case Operator.LeftShift:
2699 opcode = OpCodes.Shl;
2702 case Operator.Equality:
2703 opcode = OpCodes.Ceq;
2706 case Operator.Inequality:
2707 ig.Emit (OpCodes.Ceq);
2708 ig.Emit (OpCodes.Ldc_I4_0);
2710 opcode = OpCodes.Ceq;
2713 case Operator.LessThan:
2715 opcode = OpCodes.Clt_Un;
2717 opcode = OpCodes.Clt;
2720 case Operator.GreaterThan:
2722 opcode = OpCodes.Cgt_Un;
2724 opcode = OpCodes.Cgt;
2727 case Operator.LessThanOrEqual:
2728 Type lt = left.Type;
2730 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2731 ig.Emit (OpCodes.Cgt_Un);
2733 ig.Emit (OpCodes.Cgt);
2734 ig.Emit (OpCodes.Ldc_I4_0);
2736 opcode = OpCodes.Ceq;
2739 case Operator.GreaterThanOrEqual:
2740 Type le = left.Type;
2742 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2743 ig.Emit (OpCodes.Clt_Un);
2745 ig.Emit (OpCodes.Clt);
2747 ig.Emit (OpCodes.Ldc_I4_0);
2749 opcode = OpCodes.Ceq;
2752 case Operator.BitwiseOr:
2753 opcode = OpCodes.Or;
2756 case Operator.BitwiseAnd:
2757 opcode = OpCodes.And;
2760 case Operator.ExclusiveOr:
2761 opcode = OpCodes.Xor;
2765 throw new Exception ("This should not happen: Operator = "
2766 + oper.ToString ());
2774 // Object created by Binary when the binary operator uses an method instead of being
2775 // a binary operation that maps to a CIL binary operation.
2777 public class BinaryMethod : Expression {
2778 public MethodBase method;
2779 public ArrayList Arguments;
2781 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2786 eclass = ExprClass.Value;
2789 public override Expression DoResolve (EmitContext ec)
2794 public override void Emit (EmitContext ec)
2796 ILGenerator ig = ec.ig;
2798 if (Arguments != null)
2799 Invocation.EmitArguments (ec, method, Arguments, false, null);
2801 if (method is MethodInfo)
2802 ig.Emit (OpCodes.Call, (MethodInfo) method);
2804 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2809 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2810 // b, c, d... may be strings or objects.
2812 public class StringConcat : Expression {
2814 bool invalid = false;
2815 bool emit_conv_done = false;
2817 // Are we also concating objects?
2819 bool is_strings_only = true;
2821 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2824 type = TypeManager.string_type;
2825 eclass = ExprClass.Value;
2827 operands = new ArrayList (2);
2832 public override Expression DoResolve (EmitContext ec)
2840 public void Append (EmitContext ec, Expression operand)
2845 StringConstant sc = operand as StringConstant;
2847 // TODO: it will be better to do this silently as an optimalization
2849 // string s = "" + i;
2850 // because this code has poor performace
2851 // if (sc.Value.Length == 0)
2852 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2854 if (operands.Count != 0) {
2855 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2856 if (last_operand != null) {
2857 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2864 // Conversion to object
2866 if (operand.Type != TypeManager.string_type) {
2867 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2870 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2876 operands.Add (operand);
2879 public override void Emit (EmitContext ec)
2881 MethodInfo concat_method = null;
2884 // Do conversion to arguments; check for strings only
2887 // This can get called multiple times, so we have to deal with that.
2888 if (!emit_conv_done) {
2889 emit_conv_done = true;
2890 for (int i = 0; i < operands.Count; i ++) {
2891 Expression e = (Expression) operands [i];
2892 is_strings_only &= e.Type == TypeManager.string_type;
2895 for (int i = 0; i < operands.Count; i ++) {
2896 Expression e = (Expression) operands [i];
2898 if (! is_strings_only && e.Type == TypeManager.string_type) {
2899 // need to make sure this is an object, because the EmitParams
2900 // method might look at the type of this expression, see it is a
2901 // string and emit a string [] when we want an object [];
2903 e = new EmptyCast (e, TypeManager.object_type);
2905 operands [i] = new Argument (e, Argument.AType.Expression);
2910 // Find the right method
2912 switch (operands.Count) {
2915 // This should not be possible, because simple constant folding
2916 // is taken care of in the Binary code.
2918 throw new Exception ("how did you get here?");
2921 concat_method = is_strings_only ?
2922 TypeManager.string_concat_string_string :
2923 TypeManager.string_concat_object_object ;
2926 concat_method = is_strings_only ?
2927 TypeManager.string_concat_string_string_string :
2928 TypeManager.string_concat_object_object_object ;
2932 // There is not a 4 param overlaod for object (the one that there is
2933 // is actually a varargs methods, and is only in corlib because it was
2934 // introduced there before.).
2936 if (!is_strings_only)
2939 concat_method = TypeManager.string_concat_string_string_string_string;
2942 concat_method = is_strings_only ?
2943 TypeManager.string_concat_string_dot_dot_dot :
2944 TypeManager.string_concat_object_dot_dot_dot ;
2948 Invocation.EmitArguments (ec, concat_method, operands, false, null);
2949 ec.ig.Emit (OpCodes.Call, concat_method);
2954 // Object created with +/= on delegates
2956 public class BinaryDelegate : Expression {
2960 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
2965 eclass = ExprClass.Value;
2968 public override Expression DoResolve (EmitContext ec)
2973 public override void Emit (EmitContext ec)
2975 ILGenerator ig = ec.ig;
2977 Invocation.EmitArguments (ec, method, args, false, null);
2979 ig.Emit (OpCodes.Call, (MethodInfo) method);
2980 ig.Emit (OpCodes.Castclass, type);
2983 public Expression Right {
2985 Argument arg = (Argument) args [1];
2990 public bool IsAddition {
2992 return method == TypeManager.delegate_combine_delegate_delegate;
2998 // User-defined conditional logical operator
2999 public class ConditionalLogicalOperator : Expression {
3000 Expression left, right;
3003 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
3006 eclass = ExprClass.Value;
3010 this.is_and = is_and;
3013 protected void Error19 ()
3015 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3018 protected void Error218 ()
3020 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3021 "declarations of operator true and operator false");
3024 Expression op_true, op_false, op;
3025 LocalTemporary left_temp;
3027 public override Expression DoResolve (EmitContext ec)
3030 Expression operator_group;
3032 operator_group = MethodLookup (ec, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
3033 if (operator_group == null) {
3038 left_temp = new LocalTemporary (type);
3040 ArrayList arguments = new ArrayList ();
3041 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3042 arguments.Add (new Argument (right, Argument.AType.Expression));
3043 method = Invocation.OverloadResolve (
3044 ec, (MethodGroupExpr) operator_group, arguments, false, loc)
3046 if (method == null) {
3051 if (method.ReturnType != type) {
3052 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3053 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3057 op = new StaticCallExpr (method, arguments, loc);
3059 op_true = GetOperatorTrue (ec, left_temp, loc);
3060 op_false = GetOperatorFalse (ec, left_temp, loc);
3061 if ((op_true == null) || (op_false == null)) {
3069 public override void Emit (EmitContext ec)
3071 ILGenerator ig = ec.ig;
3072 Label false_target = ig.DefineLabel ();
3073 Label end_target = ig.DefineLabel ();
3076 left_temp.Store (ec);
3078 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3079 left_temp.Emit (ec);
3080 ig.Emit (OpCodes.Br, end_target);
3081 ig.MarkLabel (false_target);
3083 ig.MarkLabel (end_target);
3085 // We release 'left_temp' here since 'op' may refer to it too
3086 left_temp.Release (ec);
3090 public class PointerArithmetic : Expression {
3091 Expression left, right;
3095 // We assume that `l' is always a pointer
3097 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3103 is_add = is_addition;
3106 public override Expression DoResolve (EmitContext ec)
3108 eclass = ExprClass.Variable;
3110 if (left.Type == TypeManager.void_ptr_type) {
3111 Error (242, "The operation in question is undefined on void pointers");
3118 public override void Emit (EmitContext ec)
3120 Type op_type = left.Type;
3121 ILGenerator ig = ec.ig;
3123 // It must be either array or fixed buffer
3124 Type element = TypeManager.HasElementType (op_type) ?
3125 element = TypeManager.GetElementType (op_type) :
3126 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3128 int size = GetTypeSize (element);
3129 Type rtype = right.Type;
3131 if (rtype.IsPointer){
3133 // handle (pointer - pointer)
3137 ig.Emit (OpCodes.Sub);
3141 ig.Emit (OpCodes.Sizeof, element);
3143 IntLiteral.EmitInt (ig, size);
3144 ig.Emit (OpCodes.Div);
3146 ig.Emit (OpCodes.Conv_I8);
3149 // handle + and - on (pointer op int)
3152 ig.Emit (OpCodes.Conv_I);
3154 Constant right_const = right as Constant;
3155 if (right_const != null && size != 0) {
3156 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3164 ig.Emit (OpCodes.Sizeof, element);
3166 IntLiteral.EmitInt (ig, size);
3167 if (rtype == TypeManager.int64_type)
3168 ig.Emit (OpCodes.Conv_I8);
3169 else if (rtype == TypeManager.uint64_type)
3170 ig.Emit (OpCodes.Conv_U8);
3171 ig.Emit (OpCodes.Mul);
3175 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3176 ig.Emit (OpCodes.Conv_I);
3179 ig.Emit (OpCodes.Add);
3181 ig.Emit (OpCodes.Sub);
3187 /// Implements the ternary conditional operator (?:)
3189 public class Conditional : Expression {
3190 Expression expr, trueExpr, falseExpr;
3192 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3195 this.trueExpr = trueExpr;
3196 this.falseExpr = falseExpr;
3197 this.loc = expr.Location;
3200 public Expression Expr {
3206 public Expression TrueExpr {
3212 public Expression FalseExpr {
3218 public override Expression DoResolve (EmitContext ec)
3220 expr = expr.Resolve (ec);
3225 if (TypeManager.IsNullableValueType (expr.Type))
3226 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
3228 if (expr.Type != TypeManager.bool_type){
3229 expr = Expression.ResolveBoolean (
3236 Assign ass = expr as Assign;
3237 if (ass != null && ass.Source is Constant) {
3238 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3241 trueExpr = trueExpr.Resolve (ec);
3242 falseExpr = falseExpr.Resolve (ec);
3244 if (trueExpr == null || falseExpr == null)
3247 eclass = ExprClass.Value;
3248 if (trueExpr.Type == falseExpr.Type)
3249 type = trueExpr.Type;
3252 Type true_type = trueExpr.Type;
3253 Type false_type = falseExpr.Type;
3256 // First, if an implicit conversion exists from trueExpr
3257 // to falseExpr, then the result type is of type falseExpr.Type
3259 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3262 // Check if both can convert implicitl to each other's type
3264 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3266 "Can not compute type of conditional expression " +
3267 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3268 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3269 "' convert implicitly to each other");
3274 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3278 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3279 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3284 // Dead code optimalization
3285 if (expr is BoolConstant){
3286 BoolConstant bc = (BoolConstant) expr;
3288 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3289 return bc.Value ? trueExpr : falseExpr;
3295 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3300 public override void Emit (EmitContext ec)
3302 ILGenerator ig = ec.ig;
3303 Label false_target = ig.DefineLabel ();
3304 Label end_target = ig.DefineLabel ();
3306 expr.EmitBranchable (ec, false_target, false);
3308 ig.Emit (OpCodes.Br, end_target);
3309 ig.MarkLabel (false_target);
3310 falseExpr.Emit (ec);
3311 ig.MarkLabel (end_target);
3319 public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3320 public readonly string Name;
3321 public readonly Block Block;
3322 public LocalInfo local_info;
3325 LocalTemporary temp;
3327 public LocalVariableReference (Block block, string name, Location l)
3332 eclass = ExprClass.Variable;
3336 // Setting `is_readonly' to false will allow you to create a writable
3337 // reference to a read-only variable. This is used by foreach and using.
3339 public LocalVariableReference (Block block, string name, Location l,
3340 LocalInfo local_info, bool is_readonly)
3341 : this (block, name, l)
3343 this.local_info = local_info;
3344 this.is_readonly = is_readonly;
3347 public VariableInfo VariableInfo {
3348 get { return local_info.VariableInfo; }
3351 public bool IsReadOnly {
3352 get { return is_readonly; }
3355 public bool VerifyAssigned (EmitContext ec)
3357 VariableInfo variable_info = local_info.VariableInfo;
3358 return variable_info == null || variable_info.IsAssigned (ec, loc);
3361 void ResolveLocalInfo ()
3363 if (local_info == null) {
3364 local_info = Block.GetLocalInfo (Name);
3365 is_readonly = local_info.ReadOnly;
3369 protected Expression DoResolveBase (EmitContext ec)
3371 type = local_info.VariableType;
3373 Expression e = Block.GetConstantExpression (Name);
3375 return e.Resolve (ec);
3377 if (!VerifyAssigned (ec))
3380 if (ec.CurrentAnonymousMethod != null){
3382 // If we are referencing a variable from the external block
3383 // flag it for capturing
3385 if ((local_info.Block.Toplevel != ec.CurrentBlock.Toplevel) ||
3386 ec.CurrentAnonymousMethod.IsIterator)
3388 if (local_info.AddressTaken){
3389 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3392 ec.CaptureVariable (local_info);
3399 public override Expression DoResolve (EmitContext ec)
3401 ResolveLocalInfo ();
3402 local_info.Used = true;
3403 return DoResolveBase (ec);
3406 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3408 ResolveLocalInfo ();
3413 if (right_side == EmptyExpression.OutAccess) {
3414 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3415 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3416 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3417 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3418 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3420 Error_CannotAssign (Name, local_info.GetReadOnlyContext ());
3423 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3428 if (right_side == EmptyExpression.OutAccess)
3429 local_info.Used = true;
3431 if (VariableInfo != null)
3432 VariableInfo.SetAssigned (ec);
3434 return DoResolveBase (ec);
3437 public bool VerifyFixed ()
3439 // A local Variable is always fixed.
3443 public override int GetHashCode ()
3445 return Name.GetHashCode ();
3448 public override bool Equals (object obj)
3450 LocalVariableReference lvr = obj as LocalVariableReference;
3454 return Name == lvr.Name && Block == lvr.Block;
3457 public override void Emit (EmitContext ec)
3459 ILGenerator ig = ec.ig;
3461 if (local_info.FieldBuilder == null){
3463 // A local variable on the local CLR stack
3465 ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
3468 // A local variable captured by anonymous methods.
3471 ec.EmitCapturedVariableInstance (local_info);
3473 ig.Emit (OpCodes.Ldfld, local_info.FieldBuilder);
3477 public void Emit (EmitContext ec, bool leave_copy)
3481 ec.ig.Emit (OpCodes.Dup);
3482 if (local_info.FieldBuilder != null){
3483 temp = new LocalTemporary (Type);
3489 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3491 ILGenerator ig = ec.ig;
3492 prepared = prepare_for_load;
3494 if (local_info.FieldBuilder == null){
3496 // A local variable on the local CLR stack
3498 if (local_info.LocalBuilder == null)
3499 throw new Exception ("This should not happen: both Field and Local are null");
3503 ec.ig.Emit (OpCodes.Dup);
3504 ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
3507 // A local variable captured by anonymous methods or itereators.
3509 ec.EmitCapturedVariableInstance (local_info);
3511 if (prepare_for_load)
3512 ig.Emit (OpCodes.Dup);
3515 ig.Emit (OpCodes.Dup);
3516 temp = new LocalTemporary (Type);
3519 ig.Emit (OpCodes.Stfld, local_info.FieldBuilder);
3527 public void AddressOf (EmitContext ec, AddressOp mode)
3529 ILGenerator ig = ec.ig;
3531 if (local_info.FieldBuilder == null){
3533 // A local variable on the local CLR stack
3535 ig.Emit (OpCodes.Ldloca, local_info.LocalBuilder);
3538 // A local variable captured by anonymous methods or iterators
3540 ec.EmitCapturedVariableInstance (local_info);
3541 ig.Emit (OpCodes.Ldflda, local_info.FieldBuilder);
3545 public override string ToString ()
3547 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3552 /// This represents a reference to a parameter in the intermediate
3555 public class ParameterReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3561 public bool is_ref, is_out, prepared;
3575 public string Name {
3581 LocalTemporary temp;
3583 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3586 this.name = par.Name;
3590 eclass = ExprClass.Variable;
3593 public VariableInfo VariableInfo {
3597 public bool VerifyFixed ()
3599 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3600 return par.ModFlags == Parameter.Modifier.NONE;
3603 public bool IsAssigned (EmitContext ec, Location loc)
3605 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3608 Report.Error (269, loc,
3609 "Use of unassigned out parameter `{0}'", par.Name);
3613 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3615 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3618 Report.Error (170, loc,
3619 "Use of possibly unassigned field `" + field_name + "'");
3623 public void SetAssigned (EmitContext ec)
3625 if (is_out && ec.DoFlowAnalysis)
3626 ec.CurrentBranching.SetAssigned (vi);
3629 public void SetFieldAssigned (EmitContext ec, string field_name)
3631 if (is_out && ec.DoFlowAnalysis)
3632 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3635 protected bool DoResolveBase (EmitContext ec)
3637 if (!par.Resolve (ec)) {
3641 type = par.ParameterType;
3642 Parameter.Modifier mod = par.ModFlags;
3643 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3644 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3645 eclass = ExprClass.Variable;
3648 vi = block.ParameterMap [idx];
3650 if (ec.CurrentAnonymousMethod != null){
3651 if (is_ref && !block.Toplevel.IsLocalParameter (name)){
3652 Report.Error (1628, Location, "Cannot use ref or out parameter `{0}' inside an anonymous method block",
3658 // If we are referencing the parameter from the external block
3659 // flag it for capturing
3661 //Console.WriteLine ("Is parameter `{0}' local? {1}", name, block.IsLocalParameter (name));
3662 if (!block.Toplevel.IsLocalParameter (name)){
3663 ec.CaptureParameter (name, type, idx);
3670 public override int GetHashCode()
3672 return name.GetHashCode ();
3675 public override bool Equals (object obj)
3677 ParameterReference pr = obj as ParameterReference;
3681 return name == pr.name && block == pr.block;
3685 // Notice that for ref/out parameters, the type exposed is not the
3686 // same type exposed externally.
3689 // externally we expose "int&"
3690 // here we expose "int".
3692 // We record this in "is_ref". This means that the type system can treat
3693 // the type as it is expected, but when we generate the code, we generate
3694 // the alternate kind of code.
3696 public override Expression DoResolve (EmitContext ec)
3698 if (!DoResolveBase (ec))
3701 if (is_out && ec.DoFlowAnalysis && (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3707 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3709 if (!DoResolveBase (ec))
3717 static public void EmitLdArg (ILGenerator ig, int x)
3721 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3722 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3723 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3724 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3725 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3728 ig.Emit (OpCodes.Ldarg, x);
3732 // This method is used by parameters that are references, that are
3733 // being passed as references: we only want to pass the pointer (that
3734 // is already stored in the parameter, not the address of the pointer,
3735 // and not the value of the variable).
3737 public void EmitLoad (EmitContext ec)
3739 ILGenerator ig = ec.ig;
3742 if (!ec.MethodIsStatic)
3745 EmitLdArg (ig, arg_idx);
3748 // FIXME: Review for anonymous methods
3752 public override void Emit (EmitContext ec)
3757 public void Emit (EmitContext ec, bool leave_copy)
3759 ILGenerator ig = ec.ig;
3762 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3763 ec.EmitParameter (name, leave_copy, prepared, ref temp);
3767 if (!ec.MethodIsStatic)
3770 EmitLdArg (ig, arg_idx);
3774 ec.ig.Emit (OpCodes.Dup);
3777 // If we are a reference, we loaded on the stack a pointer
3778 // Now lets load the real value
3780 LoadFromPtr (ig, type);
3784 ec.ig.Emit (OpCodes.Dup);
3787 temp = new LocalTemporary (type);
3793 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
3795 prepared = prepare_for_load;
3796 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3797 ec.EmitAssignParameter (name, source, leave_copy, prepare_for_load, ref temp);
3801 ILGenerator ig = ec.ig;
3806 if (!ec.MethodIsStatic)
3809 if (is_ref && !prepared)
3810 EmitLdArg (ig, arg_idx);
3815 ec.ig.Emit (OpCodes.Dup);
3819 temp = new LocalTemporary (type);
3823 StoreFromPtr (ig, type);
3831 ig.Emit (OpCodes.Starg_S, (byte) arg_idx);
3833 ig.Emit (OpCodes.Starg, arg_idx);
3837 public void AddressOf (EmitContext ec, AddressOp mode)
3839 if (ec.HaveCaptureInfo && ec.IsParameterCaptured (name)){
3840 ec.EmitAddressOfParameter (name);
3846 if (!ec.MethodIsStatic)
3851 ec.ig.Emit (OpCodes.Ldarg_S, (byte) arg_idx);
3853 ec.ig.Emit (OpCodes.Ldarg, arg_idx);
3856 ec.ig.Emit (OpCodes.Ldarga_S, (byte) arg_idx);
3858 ec.ig.Emit (OpCodes.Ldarga, arg_idx);
3862 public override string ToString ()
3864 return "ParameterReference[" + name + "]";
3869 /// Used for arguments to New(), Invocation()
3871 public class Argument {
3872 public enum AType : byte {
3879 public readonly AType ArgType;
3880 public Expression Expr;
3882 public Argument (Expression expr, AType type)
3885 this.ArgType = type;
3888 public Argument (Expression expr)
3891 this.ArgType = AType.Expression;
3896 if (ArgType == AType.Ref || ArgType == AType.Out)
3897 return TypeManager.GetReferenceType (Expr.Type);
3903 public Parameter.Modifier Modifier
3908 return Parameter.Modifier.OUT;
3911 return Parameter.Modifier.REF;
3914 return Parameter.Modifier.NONE;
3919 public static string FullDesc (Argument a)
3921 if (a.ArgType == AType.ArgList)
3924 return (a.ArgType == AType.Ref ? "ref " :
3925 (a.ArgType == AType.Out ? "out " : "")) +
3926 TypeManager.CSharpName (a.Expr.Type);
3929 public bool ResolveMethodGroup (EmitContext ec)
3931 SimpleName sn = Expr as SimpleName;
3933 Expr = sn.GetMethodGroup ();
3935 // FIXME: csc doesn't report any error if you try to use `ref' or
3936 // `out' in a delegate creation expression.
3937 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3944 public bool Resolve (EmitContext ec, Location loc)
3946 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3947 // Verify that the argument is readable
3948 if (ArgType != AType.Out)
3949 Expr = Expr.Resolve (ec);
3951 // Verify that the argument is writeable
3952 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3953 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3955 return Expr != null;
3959 public void Emit (EmitContext ec)
3961 if (ArgType != AType.Ref && ArgType != AType.Out) {
3966 AddressOp mode = AddressOp.Store;
3967 if (ArgType == AType.Ref)
3968 mode |= AddressOp.Load;
3970 IMemoryLocation ml = (IMemoryLocation) Expr;
3971 ParameterReference pr = ml as ParameterReference;
3974 // ParameterReferences might already be references, so we want
3975 // to pass just the value
3977 if (pr != null && pr.IsRef)
3980 ml.AddressOf (ec, mode);
3985 /// Invocation of methods or delegates.
3987 public class Invocation : ExpressionStatement {
3988 public readonly ArrayList Arguments;
3991 MethodBase method = null;
3994 // arguments is an ArrayList, but we do not want to typecast,
3995 // as it might be null.
3997 // FIXME: only allow expr to be a method invocation or a
3998 // delegate invocation (7.5.5)
4000 public Invocation (Expression expr, ArrayList arguments)
4003 Arguments = arguments;
4004 loc = expr.Location;
4007 public Expression Expr {
4014 /// Determines "better conversion" as specified in 14.4.2.3
4016 /// Returns : p if a->p is better,
4017 /// q if a->q is better,
4018 /// null if neither is better
4020 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q)
4022 Type argument_type = TypeManager.TypeToCoreType (a.Type);
4023 Expression argument_expr = a.Expr;
4025 // p = TypeManager.TypeToCoreType (p);
4026 // q = TypeManager.TypeToCoreType (q);
4028 if (argument_type == null)
4029 throw new Exception ("Expression of type " + a.Expr +
4030 " does not resolve its type");
4032 if (p == null || q == null)
4033 throw new InternalErrorException ("BetterConversion Got a null conversion");
4038 if (argument_expr is NullLiteral) {
4040 // If the argument is null and one of the types to compare is 'object' and
4041 // the other is a reference type, we prefer the other.
4043 // This follows from the usual rules:
4044 // * There is an implicit conversion from 'null' to type 'object'
4045 // * There is an implicit conversion from 'null' to any reference type
4046 // * There is an implicit conversion from any reference type to type 'object'
4047 // * There is no implicit conversion from type 'object' to other reference types
4048 // => Conversion of 'null' to a reference type is better than conversion to 'object'
4050 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
4051 // null type. I think it used to be 'object' and thus needed a special
4052 // case to avoid the immediately following two checks.
4054 if (!p.IsValueType && q == TypeManager.object_type)
4056 if (!q.IsValueType && p == TypeManager.object_type)
4060 if (argument_type == p)
4063 if (argument_type == q)
4066 Expression p_tmp = new EmptyExpression (p);
4067 Expression q_tmp = new EmptyExpression (q);
4069 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
4070 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
4072 if (p_to_q && !q_to_p)
4075 if (q_to_p && !p_to_q)
4078 if (p == TypeManager.sbyte_type)
4079 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
4080 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4082 if (q == TypeManager.sbyte_type)
4083 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
4084 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4087 if (p == TypeManager.short_type)
4088 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
4089 q == TypeManager.uint64_type)
4091 if (q == TypeManager.short_type)
4092 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
4093 p == TypeManager.uint64_type)
4096 if (p == TypeManager.int32_type)
4097 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4099 if (q == TypeManager.int32_type)
4100 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4103 if (p == TypeManager.int64_type)
4104 if (q == TypeManager.uint64_type)
4106 if (q == TypeManager.int64_type)
4107 if (p == TypeManager.uint64_type)
4113 static Type MoreSpecific (Type p, Type q)
4115 if (p.IsGenericParameter && !q.IsGenericParameter)
4117 if (!p.IsGenericParameter && q.IsGenericParameter)
4120 if (TypeManager.HasElementType (p)) {
4121 Type pe = TypeManager.GetElementType (p);
4122 Type qe = TypeManager.GetElementType (q);
4123 Type specific = MoreSpecific (pe, qe);
4128 } else if (p.IsGenericType) {
4129 Type[] pargs = TypeManager.GetTypeArguments (p);
4130 Type[] qargs = TypeManager.GetTypeArguments (q);
4132 bool p_specific_at_least_once = false;
4133 bool q_specific_at_least_once = false;
4135 for (int i = 0; i < pargs.Length; i++) {
4136 Type specific = MoreSpecific (pargs [i], qargs [i]);
4137 if (specific == pargs [i])
4138 p_specific_at_least_once = true;
4139 if (specific == qargs [i])
4140 q_specific_at_least_once = true;
4143 if (p_specific_at_least_once && !q_specific_at_least_once)
4145 if (!p_specific_at_least_once && q_specific_at_least_once)
4153 /// Determines "Better function" between candidate
4154 /// and the current best match
4157 /// Returns a boolean indicating :
4158 /// false if candidate ain't better
4159 /// true if candidate is better than the current best match
4161 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4162 MethodBase candidate, bool candidate_params,
4163 MethodBase best, bool best_params)
4165 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4166 ParameterData best_pd = TypeManager.GetParameterData (best);
4168 bool better_at_least_one = false;
4170 for (int j = 0; j < argument_count; ++j) {
4171 Argument a = (Argument) args [j];
4173 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
4174 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
4176 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4177 if (candidate_params)
4178 ct = TypeManager.GetElementType (ct);
4180 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4182 bt = TypeManager.GetElementType (bt);
4188 Type better = BetterConversion (ec, a, ct, bt);
4190 // for each argument, the conversion to 'ct' should be no worse than
4191 // the conversion to 'bt'.
4195 // for at least one argument, the conversion to 'ct' should be better than
4196 // the conversion to 'bt'.
4198 better_at_least_one = true;
4201 if (better_at_least_one)
4205 // This handles the case
4207 // Add (float f1, float f2, float f3);
4208 // Add (params decimal [] foo);
4210 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4211 // first candidate would've chosen as better.
4217 // The two methods have equal parameter types. Now apply tie-breaking rules
4219 if (TypeManager.IsGenericMethod (best) && !TypeManager.IsGenericMethod (candidate))
4221 if (!TypeManager.IsGenericMethod (best) && TypeManager.IsGenericMethod (candidate))
4225 // This handles the following cases:
4227 // Trim () is better than Trim (params char[] chars)
4228 // Concat (string s1, string s2, string s3) is better than
4229 // Concat (string s1, params string [] srest)
4230 // Foo (int, params int [] rest) is better than Foo (params int [] rest)
4232 if (!candidate_params && best_params)
4234 if (candidate_params && !best_params)
4237 int candidate_param_count = candidate_pd.Count;
4238 int best_param_count = best_pd.Count;
4240 if (candidate_param_count != best_param_count)
4241 // can only happen if (candidate_params && best_params)
4242 return candidate_param_count > best_param_count;
4245 // now, both methods have the same number of parameters, and the parameters have the same types
4246 // Pick the "more specific" signature
4249 MethodBase orig_candidate = TypeManager.DropGenericMethodArguments (candidate);
4250 MethodBase orig_best = TypeManager.DropGenericMethodArguments (best);
4252 ParameterData orig_candidate_pd = TypeManager.GetParameterData (orig_candidate);
4253 ParameterData orig_best_pd = TypeManager.GetParameterData (orig_best);
4255 bool specific_at_least_once = false;
4256 for (int j = 0; j < candidate_param_count; ++j) {
4257 Type ct = TypeManager.TypeToCoreType (orig_candidate_pd.ParameterType (j));
4258 Type bt = TypeManager.TypeToCoreType (orig_best_pd.ParameterType (j));
4261 Type specific = MoreSpecific (ct, bt);
4265 specific_at_least_once = true;
4268 if (specific_at_least_once)
4271 // FIXME: handle lifted operators
4277 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4279 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4282 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4283 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4285 if (cand_pd.Count != base_pd.Count)
4288 for (int j = 0; j < cand_pd.Count; ++j) {
4289 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4290 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4291 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4292 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4294 if (cm != bm || ct != bt)
4301 public static string FullMethodDesc (MethodBase mb)
4307 if (mb is MethodInfo) {
4308 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4312 sb = new StringBuilder ();
4314 sb.Append (TypeManager.CSharpSignature (mb));
4315 return sb.ToString ();
4318 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4320 MemberInfo [] miset;
4321 MethodGroupExpr union;
4326 return (MethodGroupExpr) mg2;
4329 return (MethodGroupExpr) mg1;
4332 MethodGroupExpr left_set = null, right_set = null;
4333 int length1 = 0, length2 = 0;
4335 left_set = (MethodGroupExpr) mg1;
4336 length1 = left_set.Methods.Length;
4338 right_set = (MethodGroupExpr) mg2;
4339 length2 = right_set.Methods.Length;
4341 ArrayList common = new ArrayList ();
4343 foreach (MethodBase r in right_set.Methods){
4344 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4348 miset = new MemberInfo [length1 + length2 - common.Count];
4349 left_set.Methods.CopyTo (miset, 0);
4353 foreach (MethodBase r in right_set.Methods) {
4354 if (!common.Contains (r))
4358 union = new MethodGroupExpr (miset, loc);
4363 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4364 ArrayList arguments, int arg_count,
4365 ref MethodBase candidate)
4367 return IsParamsMethodApplicable (
4368 ec, me, arguments, arg_count, false, ref candidate) ||
4369 IsParamsMethodApplicable (
4370 ec, me, arguments, arg_count, true, ref candidate);
4375 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4376 ArrayList arguments, int arg_count,
4377 bool do_varargs, ref MethodBase candidate)
4379 if (!me.HasTypeArguments &&
4380 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4383 if (TypeManager.IsGenericMethodDefinition (candidate))
4384 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4386 return IsParamsMethodApplicable (
4387 ec, arguments, arg_count, candidate, do_varargs);
4391 /// Determines if the candidate method, if a params method, is applicable
4392 /// in its expanded form to the given set of arguments
4394 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4395 int arg_count, MethodBase candidate,
4398 ParameterData pd = TypeManager.GetParameterData (candidate);
4400 int pd_count = pd.Count;
4404 int count = pd_count - 1;
4406 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4408 if (pd_count != arg_count)
4415 if (count > arg_count)
4418 if (pd_count == 1 && arg_count == 0)
4422 // If we have come this far, the case which
4423 // remains is when the number of parameters is
4424 // less than or equal to the argument count.
4426 for (int i = 0; i < count; ++i) {
4428 Argument a = (Argument) arguments [i];
4430 Parameter.Modifier a_mod = a.Modifier &
4431 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4432 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4433 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4435 if (a_mod == p_mod) {
4437 if (a_mod == Parameter.Modifier.NONE)
4438 if (!Convert.ImplicitConversionExists (ec,
4440 pd.ParameterType (i)))
4443 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4444 Type pt = pd.ParameterType (i);
4447 pt = TypeManager.GetReferenceType (pt);
4458 Argument a = (Argument) arguments [count];
4459 if (!(a.Expr is Arglist))
4465 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
4467 for (int i = pd_count - 1; i < arg_count; i++) {
4468 Argument a = (Argument) arguments [i];
4470 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4477 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4478 ArrayList arguments, int arg_count,
4479 ref MethodBase candidate)
4481 if (!me.HasTypeArguments &&
4482 !TypeManager.InferTypeArguments (arguments, ref candidate))
4485 if (TypeManager.IsGenericMethodDefinition (candidate))
4486 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4488 return IsApplicable (ec, arguments, arg_count, candidate);
4492 /// Determines if the candidate method is applicable (section 14.4.2.1)
4493 /// to the given set of arguments
4495 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4496 MethodBase candidate)
4498 ParameterData pd = TypeManager.GetParameterData (candidate);
4500 if (arg_count != pd.Count)
4503 for (int i = arg_count; i > 0; ) {
4506 Argument a = (Argument) arguments [i];
4508 Parameter.Modifier a_mod = a.Modifier &
4509 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4511 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4512 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4514 if (a_mod == p_mod) {
4515 Type pt = pd.ParameterType (i);
4517 if (a_mod == Parameter.Modifier.NONE) {
4518 if (!TypeManager.IsEqual (a.Type, pt) &&
4519 !Convert.ImplicitConversionExists (ec, a.Expr, pt))
4533 static internal bool IsAncestralType (Type first_type, Type second_type)
4535 return first_type != second_type &&
4536 (TypeManager.IsSubclassOf (second_type, first_type) ||
4537 TypeManager.ImplementsInterface (second_type, first_type));
4541 /// Find the Applicable Function Members (7.4.2.1)
4543 /// me: Method Group expression with the members to select.
4544 /// it might contain constructors or methods (or anything
4545 /// that maps to a method).
4547 /// Arguments: ArrayList containing resolved Argument objects.
4549 /// loc: The location if we want an error to be reported, or a Null
4550 /// location for "probing" purposes.
4552 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4553 /// that is the best match of me on Arguments.
4556 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4557 ArrayList Arguments, bool may_fail,
4560 MethodBase method = null;
4561 bool method_params = false;
4562 Type applicable_type = null;
4564 ArrayList candidates = new ArrayList (2);
4565 ArrayList candidate_overrides = null;
4568 // Used to keep a map between the candidate
4569 // and whether it is being considered in its
4570 // normal or expanded form
4572 // false is normal form, true is expanded form
4574 Hashtable candidate_to_form = null;
4576 if (Arguments != null)
4577 arg_count = Arguments.Count;
4579 if ((me.Name == "Invoke") &&
4580 TypeManager.IsDelegateType (me.DeclaringType)) {
4581 Error_InvokeOnDelegate (loc);
4585 MethodBase[] methods = me.Methods;
4587 int nmethods = methods.Length;
4591 // Methods marked 'override' don't take part in 'applicable_type'
4592 // computation, nor in the actual overload resolution.
4593 // However, they still need to be emitted instead of a base virtual method.
4594 // So, we salt them away into the 'candidate_overrides' array.
4596 // In case of reflected methods, we replace each overriding method with
4597 // its corresponding base virtual method. This is to improve compatibility
4598 // with non-C# libraries which change the visibility of overrides (#75636)
4601 for (int i = 0; i < methods.Length; ++i) {
4602 MethodBase m = methods [i];
4603 Type [] gen_args = m.IsGenericMethod && !m.IsGenericMethodDefinition ? m.GetGenericArguments () : null;
4604 if (TypeManager.IsOverride (m)) {
4605 if (candidate_overrides == null)
4606 candidate_overrides = new ArrayList ();
4607 candidate_overrides.Add (m);
4608 m = TypeManager.TryGetBaseDefinition (m);
4609 if (m != null && gen_args != null) {
4610 if (!m.IsGenericMethodDefinition)
4611 throw new InternalErrorException ("GetBaseDefinition didn't return a GenericMethodDefinition");
4612 m = ((MethodInfo) m).MakeGenericMethod (gen_args);
4621 int applicable_errors = Report.Errors;
4624 // First we construct the set of applicable methods
4626 bool is_sorted = true;
4627 for (int i = 0; i < nmethods; i++){
4628 Type decl_type = methods [i].DeclaringType;
4631 // If we have already found an applicable method
4632 // we eliminate all base types (Section 14.5.5.1)
4634 if (applicable_type != null && IsAncestralType (decl_type, applicable_type))
4638 // Check if candidate is applicable (section 14.4.2.1)
4639 // Is candidate applicable in normal form?
4641 bool is_applicable = IsApplicable (ec, me, Arguments, arg_count, ref methods [i]);
4643 if (!is_applicable && IsParamsMethodApplicable (ec, me, Arguments, arg_count, ref methods [i])) {
4644 MethodBase candidate = methods [i];
4645 if (candidate_to_form == null)
4646 candidate_to_form = new PtrHashtable ();
4647 candidate_to_form [candidate] = candidate;
4648 // Candidate is applicable in expanded form
4649 is_applicable = true;
4655 candidates.Add (methods [i]);
4657 if (applicable_type == null)
4658 applicable_type = decl_type;
4659 else if (applicable_type != decl_type) {
4661 if (IsAncestralType (applicable_type, decl_type))
4662 applicable_type = decl_type;
4666 if (applicable_errors != Report.Errors)
4669 int candidate_top = candidates.Count;
4671 if (applicable_type == null) {
4673 // Okay so we have failed to find anything so we
4674 // return by providing info about the closest match
4676 int errors = Report.Errors;
4677 for (int i = 0; i < nmethods; ++i) {
4678 MethodBase c = (MethodBase) methods [i];
4679 ParameterData pd = TypeManager.GetParameterData (c);
4681 if (pd.Count != arg_count)
4684 if (!TypeManager.InferTypeArguments (Arguments, ref c))
4687 if (TypeManager.IsGenericMethodDefinition (c))
4690 VerifyArgumentsCompat (ec, Arguments, arg_count,
4691 c, false, null, may_fail, loc);
4693 if (!may_fail && errors == Report.Errors)
4694 throw new InternalErrorException (
4695 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4696 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4701 if (!may_fail && errors == Report.Errors) {
4702 string report_name = me.Name;
4703 if (report_name == ".ctor")
4704 report_name = me.DeclaringType.ToString ();
4706 for (int i = 0; i < methods.Length; ++i) {
4707 MethodBase c = methods [i];
4708 ParameterData pd = TypeManager.GetParameterData (c);
4710 if (pd.Count != arg_count)
4713 if (TypeManager.InferTypeArguments (Arguments, ref c))
4717 411, loc, "The type arguments for " +
4718 "method `{0}' cannot be infered from " +
4719 "the usage. Try specifying the type " +
4720 "arguments explicitly.", report_name);
4724 Error_WrongNumArguments (loc, report_name, arg_count);
4732 // At this point, applicable_type is _one_ of the most derived types
4733 // in the set of types containing the methods in this MethodGroup.
4734 // Filter the candidates so that they only contain methods from the
4735 // most derived types.
4738 int finalized = 0; // Number of finalized candidates
4741 // Invariant: applicable_type is a most derived type
4743 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4744 // eliminating all it's base types. At the same time, we'll also move
4745 // every unrelated type to the end of the array, and pick the next
4746 // 'applicable_type'.
4748 Type next_applicable_type = null;
4749 int j = finalized; // where to put the next finalized candidate
4750 int k = finalized; // where to put the next undiscarded candidate
4751 for (int i = finalized; i < candidate_top; ++i) {
4752 MethodBase candidate = (MethodBase) candidates [i];
4753 Type decl_type = candidate.DeclaringType;
4755 if (decl_type == applicable_type) {
4756 candidates [k++] = candidates [j];
4757 candidates [j++] = candidates [i];
4761 if (IsAncestralType (decl_type, applicable_type))
4764 if (next_applicable_type != null &&
4765 IsAncestralType (decl_type, next_applicable_type))
4768 candidates [k++] = candidates [i];
4770 if (next_applicable_type == null ||
4771 IsAncestralType (next_applicable_type, decl_type))
4772 next_applicable_type = decl_type;
4775 applicable_type = next_applicable_type;
4778 } while (applicable_type != null);
4782 // Now we actually find the best method
4785 method = (MethodBase) candidates [0];
4786 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4787 for (int ix = 1; ix < candidate_top; ix++){
4788 MethodBase candidate = (MethodBase) candidates [ix];
4790 if (candidate == method)
4793 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4795 if (BetterFunction (ec, Arguments, arg_count,
4796 candidate, cand_params,
4797 method, method_params)) {
4799 method_params = cand_params;
4803 // Now check that there are no ambiguities i.e the selected method
4804 // should be better than all the others
4806 MethodBase ambiguous = null;
4807 for (int ix = 0; ix < candidate_top; ix++){
4808 MethodBase candidate = (MethodBase) candidates [ix];
4810 if (candidate == method)
4813 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4814 if (!BetterFunction (ec, Arguments, arg_count,
4815 method, method_params,
4816 candidate, cand_params)) {
4817 Report.SymbolRelatedToPreviousError (candidate);
4818 ambiguous = candidate;
4822 if (ambiguous != null) {
4823 Report.SymbolRelatedToPreviousError (method);
4824 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4825 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4830 // If the method is a virtual function, pick an override closer to the LHS type.
4832 if (!me.IsBase && method.IsVirtual) {
4833 if (TypeManager.IsOverride (method))
4834 throw new InternalErrorException (
4835 "Should not happen. An 'override' method took part in overload resolution: " + method);
4837 if (candidate_overrides != null)
4838 foreach (MethodBase candidate in candidate_overrides) {
4839 if (IsOverride (candidate, method))
4845 // And now check if the arguments are all
4846 // compatible, perform conversions if
4847 // necessary etc. and return if everything is
4850 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4851 method_params, null, may_fail, loc))
4857 MethodBase the_method = TypeManager.DropGenericMethodArguments (method);
4858 if (the_method.IsGenericMethodDefinition &&
4859 !ConstraintChecker.CheckConstraints (ec, the_method, method, loc))
4862 IMethodData data = TypeManager.GetMethod (the_method);
4864 data.SetMemberIsUsed ();
4869 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4871 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4872 name, arg_count.ToString ());
4875 static void Error_InvokeOnDelegate (Location loc)
4877 Report.Error (1533, loc,
4878 "Invoke cannot be called directly on a delegate");
4881 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4882 Type delegate_type, Argument a, ParameterData expected_par)
4884 if (delegate_type == null)
4885 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4886 TypeManager.CSharpSignature (method));
4888 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4889 TypeManager.CSharpName (delegate_type));
4891 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4893 string index = (idx + 1).ToString ();
4894 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4895 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4896 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4897 index, Parameter.GetModifierSignature (a.Modifier));
4899 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4900 index, Parameter.GetModifierSignature (mod));
4902 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4903 index, Argument.FullDesc (a), expected_par.ParameterDesc (idx));
4907 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4908 int arg_count, MethodBase method,
4909 bool chose_params_expanded,
4910 Type delegate_type, bool may_fail,
4913 ParameterData pd = TypeManager.GetParameterData (method);
4915 for (j = 0; j < arg_count; j++) {
4916 Argument a = (Argument) Arguments [j];
4917 Expression a_expr = a.Expr;
4918 Type parameter_type = pd.ParameterType (j);
4919 Parameter.Modifier pm = pd.ParameterModifier (j);
4920 Parameter.Modifier am = a.Modifier;
4922 if (pm == Parameter.Modifier.ARGLIST) {
4923 if (!(a.Expr is Arglist))
4928 if (pm == Parameter.Modifier.PARAMS) {
4929 pm = Parameter.Modifier.NONE;
4930 if (chose_params_expanded)
4931 parameter_type = TypeManager.GetElementType (parameter_type);
4937 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4938 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4941 Expression conv = Convert.ImplicitConversion (ec, a_expr, parameter_type, loc);
4945 // Update the argument with the implicit conversion
4950 if (parameter_type.IsPointer && !ec.InUnsafe) {
4960 Error_InvalidArguments (loc, j, method, delegate_type, (Argument) Arguments [j], pd);
4964 private bool resolved = false;
4965 public override Expression DoResolve (EmitContext ec)
4968 return this.method == null ? null : this;
4972 // First, resolve the expression that is used to
4973 // trigger the invocation
4975 SimpleName sn = expr as SimpleName;
4977 expr = sn.GetMethodGroup ();
4979 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4983 if (!(expr is MethodGroupExpr)) {
4984 Type expr_type = expr.Type;
4986 if (expr_type != null){
4987 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4989 return (new DelegateInvocation (
4990 this.expr, Arguments, loc)).Resolve (ec);
4994 if (!(expr is MethodGroupExpr)){
4995 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
5000 // Next, evaluate all the expressions in the argument list
5002 if (Arguments != null){
5003 foreach (Argument a in Arguments){
5004 if (!a.Resolve (ec, loc))
5009 MethodGroupExpr mg = (MethodGroupExpr) expr;
5010 MethodBase method = OverloadResolve (ec, mg, Arguments, false, loc);
5015 MethodInfo mi = method as MethodInfo;
5017 type = TypeManager.TypeToCoreType (mi.ReturnType);
5018 Expression iexpr = mg.InstanceExpression;
5020 if (iexpr == null ||
5021 iexpr is This || iexpr is EmptyExpression ||
5022 mg.IdenticalTypeName) {
5023 mg.InstanceExpression = null;
5025 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
5029 if (iexpr == null || iexpr is EmptyExpression) {
5030 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
5036 if (type.IsPointer){
5044 // Only base will allow this invocation to happen.
5046 if (mg.IsBase && method.IsAbstract){
5047 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
5051 if (Arguments == null && method.Name == "Finalize") {
5053 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
5055 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
5059 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
5063 if (mg.InstanceExpression != null)
5064 mg.InstanceExpression.CheckMarshalByRefAccess ();
5066 eclass = ExprClass.Value;
5067 this.method = method;
5071 bool IsSpecialMethodInvocation (MethodBase method)
5073 IMethodData md = TypeManager.GetMethod (method);
5075 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
5078 if (!TypeManager.IsSpecialMethod (method))
5081 int args = TypeManager.GetParameterData (method).Count;
5082 if (method.Name.StartsWith ("get_") && args > 0)
5084 else if (method.Name.StartsWith ("set_") && args > 2)
5087 // TODO: check operators and events as well ?
5090 Report.SymbolRelatedToPreviousError (method);
5091 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
5092 TypeManager.CSharpSignature (method, true));
5098 // Emits the list of arguments as an array
5100 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
5102 ILGenerator ig = ec.ig;
5103 int count = arguments.Count - idx;
5104 Argument a = (Argument) arguments [idx];
5105 Type t = a.Expr.Type;
5107 IntConstant.EmitInt (ig, count);
5108 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
5110 int top = arguments.Count;
5111 for (int j = idx; j < top; j++){
5112 a = (Argument) arguments [j];
5114 ig.Emit (OpCodes.Dup);
5115 IntConstant.EmitInt (ig, j - idx);
5117 bool is_stobj, has_type_arg;
5118 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
5120 ig.Emit (OpCodes.Ldelema, t);
5132 /// Emits a list of resolved Arguments that are in the arguments
5135 /// The MethodBase argument might be null if the
5136 /// emission of the arguments is known not to contain
5137 /// a `params' field (for example in constructors or other routines
5138 /// that keep their arguments in this structure)
5140 /// if `dup_args' is true, a copy of the arguments will be left
5141 /// on the stack. If `dup_args' is true, you can specify `this_arg'
5142 /// which will be duplicated before any other args. Only EmitCall
5143 /// should be using this interface.
5145 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
5147 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
5148 int top = arguments == null ? 0 : arguments.Count;
5149 LocalTemporary [] temps = null;
5151 if (dup_args && top != 0)
5152 temps = new LocalTemporary [top];
5154 for (int i = 0; i < top; i++){
5155 Argument a = (Argument) arguments [i];
5158 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
5160 // Special case if we are passing the same data as the
5161 // params argument, do not put it in an array.
5163 if (pd.ParameterType (i) == a.Type)
5166 EmitParams (ec, i, arguments);
5173 ec.ig.Emit (OpCodes.Dup);
5174 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
5179 if (this_arg != null)
5182 for (int i = 0; i < top; i ++) {
5183 temps [i].Emit (ec);
5184 temps [i].Release (ec);
5188 if (pd != null && pd.Count > top &&
5189 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
5190 ILGenerator ig = ec.ig;
5192 IntConstant.EmitInt (ig, 0);
5193 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
5197 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
5199 ParameterData pd = TypeManager.GetParameterData (mb);
5201 if (arguments == null)
5202 return new Type [0];
5204 Argument a = (Argument) arguments [pd.Count - 1];
5205 Arglist list = (Arglist) a.Expr;
5207 return list.ArgumentTypes;
5211 /// This checks the ConditionalAttribute on the method
5213 static bool IsMethodExcluded (MethodBase method)
5215 if (method.IsConstructor)
5218 IMethodData md = TypeManager.GetMethod (method);
5220 return md.IsExcluded ();
5222 // For some methods (generated by delegate class) GetMethod returns null
5223 // because they are not included in builder_to_method table
5224 if (method.DeclaringType is TypeBuilder)
5227 return AttributeTester.IsConditionalMethodExcluded (method);
5231 /// is_base tells whether we want to force the use of the `call'
5232 /// opcode instead of using callvirt. Call is required to call
5233 /// a specific method, while callvirt will always use the most
5234 /// recent method in the vtable.
5236 /// is_static tells whether this is an invocation on a static method
5238 /// instance_expr is an expression that represents the instance
5239 /// it must be non-null if is_static is false.
5241 /// method is the method to invoke.
5243 /// Arguments is the list of arguments to pass to the method or constructor.
5245 public static void EmitCall (EmitContext ec, bool is_base,
5246 bool is_static, Expression instance_expr,
5247 MethodBase method, ArrayList Arguments, Location loc)
5249 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
5252 // `dup_args' leaves an extra copy of the arguments on the stack
5253 // `omit_args' does not leave any arguments at all.
5254 // So, basically, you could make one call with `dup_args' set to true,
5255 // and then another with `omit_args' set to true, and the two calls
5256 // would have the same set of arguments. However, each argument would
5257 // only have been evaluated once.
5258 public static void EmitCall (EmitContext ec, bool is_base,
5259 bool is_static, Expression instance_expr,
5260 MethodBase method, ArrayList Arguments, Location loc,
5261 bool dup_args, bool omit_args)
5263 ILGenerator ig = ec.ig;
5264 bool struct_call = false;
5265 bool this_call = false;
5266 LocalTemporary this_arg = null;
5268 Type decl_type = method.DeclaringType;
5270 if (!RootContext.StdLib) {
5271 // Replace any calls to the system's System.Array type with calls to
5272 // the newly created one.
5273 if (method == TypeManager.system_int_array_get_length)
5274 method = TypeManager.int_array_get_length;
5275 else if (method == TypeManager.system_int_array_get_rank)
5276 method = TypeManager.int_array_get_rank;
5277 else if (method == TypeManager.system_object_array_clone)
5278 method = TypeManager.object_array_clone;
5279 else if (method == TypeManager.system_int_array_get_length_int)
5280 method = TypeManager.int_array_get_length_int;
5281 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5282 method = TypeManager.int_array_get_lower_bound_int;
5283 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5284 method = TypeManager.int_array_get_upper_bound_int;
5285 else if (method == TypeManager.system_void_array_copyto_array_int)
5286 method = TypeManager.void_array_copyto_array_int;
5289 if (!ec.IsInObsoleteScope) {
5291 // This checks ObsoleteAttribute on the method and on the declaring type
5293 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5295 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5297 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5299 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5303 if (IsMethodExcluded (method))
5307 if (instance_expr == EmptyExpression.Null) {
5308 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5312 this_call = instance_expr is This;
5313 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5317 // If this is ourselves, push "this"
5321 Type iexpr_type = instance_expr.Type;
5324 // Push the instance expression
5326 if (TypeManager.IsValueType (iexpr_type)) {
5328 // Special case: calls to a function declared in a
5329 // reference-type with a value-type argument need
5330 // to have their value boxed.
5331 if (decl_type.IsValueType ||
5332 iexpr_type.IsGenericParameter) {
5334 // If the expression implements IMemoryLocation, then
5335 // we can optimize and use AddressOf on the
5338 // If not we have to use some temporary storage for
5340 if (instance_expr is IMemoryLocation) {
5341 ((IMemoryLocation)instance_expr).
5342 AddressOf (ec, AddressOp.LoadStore);
5344 LocalTemporary temp = new LocalTemporary (iexpr_type);
5345 instance_expr.Emit (ec);
5347 temp.AddressOf (ec, AddressOp.Load);
5350 // avoid the overhead of doing this all the time.
5352 t = TypeManager.GetReferenceType (iexpr_type);
5354 instance_expr.Emit (ec);
5355 ig.Emit (OpCodes.Box, instance_expr.Type);
5356 t = TypeManager.object_type;
5359 instance_expr.Emit (ec);
5360 t = instance_expr.Type;
5364 ig.Emit (OpCodes.Dup);
5365 if (Arguments != null && Arguments.Count != 0) {
5366 this_arg = new LocalTemporary (t);
5367 this_arg.Store (ec);
5374 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5376 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
5377 ig.Emit (OpCodes.Constrained, instance_expr.Type);
5380 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5381 call_op = OpCodes.Call;
5383 call_op = OpCodes.Callvirt;
5385 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5386 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5387 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5394 // and DoFoo is not virtual, you can omit the callvirt,
5395 // because you don't need the null checking behavior.
5397 if (method is MethodInfo)
5398 ig.Emit (call_op, (MethodInfo) method);
5400 ig.Emit (call_op, (ConstructorInfo) method);
5403 public override void Emit (EmitContext ec)
5405 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5407 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5410 public override void EmitStatement (EmitContext ec)
5415 // Pop the return value if there is one
5417 if (method is MethodInfo){
5418 Type ret = ((MethodInfo)method).ReturnType;
5419 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5420 ec.ig.Emit (OpCodes.Pop);
5425 public class InvocationOrCast : ExpressionStatement
5428 Expression argument;
5430 public InvocationOrCast (Expression expr, Expression argument)
5433 this.argument = argument;
5434 this.loc = expr.Location;
5437 public override Expression DoResolve (EmitContext ec)
5440 // First try to resolve it as a cast.
5442 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5443 if ((te != null) && (te.eclass == ExprClass.Type)) {
5444 Cast cast = new Cast (te, argument, loc);
5445 return cast.Resolve (ec);
5449 // This can either be a type or a delegate invocation.
5450 // Let's just resolve it and see what we'll get.
5452 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5457 // Ok, so it's a Cast.
5459 if (expr.eclass == ExprClass.Type) {
5460 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5461 return cast.Resolve (ec);
5465 // It's a delegate invocation.
5467 if (!TypeManager.IsDelegateType (expr.Type)) {
5468 Error (149, "Method name expected");
5472 ArrayList args = new ArrayList ();
5473 args.Add (new Argument (argument, Argument.AType.Expression));
5474 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5475 return invocation.Resolve (ec);
5480 Error (201, "Only assignment, call, increment, decrement and new object " +
5481 "expressions can be used as a statement");
5484 public override ExpressionStatement ResolveStatement (EmitContext ec)
5487 // First try to resolve it as a cast.
5489 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5490 if ((te != null) && (te.eclass == ExprClass.Type)) {
5496 // This can either be a type or a delegate invocation.
5497 // Let's just resolve it and see what we'll get.
5499 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5500 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5506 // It's a delegate invocation.
5508 if (!TypeManager.IsDelegateType (expr.Type)) {
5509 Error (149, "Method name expected");
5513 ArrayList args = new ArrayList ();
5514 args.Add (new Argument (argument, Argument.AType.Expression));
5515 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5516 return invocation.ResolveStatement (ec);
5519 public override void Emit (EmitContext ec)
5521 throw new Exception ("Cannot happen");
5524 public override void EmitStatement (EmitContext ec)
5526 throw new Exception ("Cannot happen");
5531 // This class is used to "disable" the code generation for the
5532 // temporary variable when initializing value types.
5534 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5535 public void AddressOf (EmitContext ec, AddressOp Mode)
5542 /// Implements the new expression
5544 public class New : ExpressionStatement, IMemoryLocation {
5545 public readonly ArrayList Arguments;
5548 // During bootstrap, it contains the RequestedType,
5549 // but if `type' is not null, it *might* contain a NewDelegate
5550 // (because of field multi-initialization)
5552 public Expression RequestedType;
5554 MethodBase method = null;
5557 // If set, the new expression is for a value_target, and
5558 // we will not leave anything on the stack.
5560 Expression value_target;
5561 bool value_target_set = false;
5562 bool is_type_parameter = false;
5564 public New (Expression requested_type, ArrayList arguments, Location l)
5566 RequestedType = requested_type;
5567 Arguments = arguments;
5571 public bool SetValueTypeVariable (Expression value)
5573 value_target = value;
5574 value_target_set = true;
5575 if (!(value_target is IMemoryLocation)){
5576 Error_UnexpectedKind (null, "variable", loc);
5583 // This function is used to disable the following code sequence for
5584 // value type initialization:
5586 // AddressOf (temporary)
5590 // Instead the provide will have provided us with the address on the
5591 // stack to store the results.
5593 static Expression MyEmptyExpression;
5595 public void DisableTemporaryValueType ()
5597 if (MyEmptyExpression == null)
5598 MyEmptyExpression = new EmptyAddressOf ();
5601 // To enable this, look into:
5602 // test-34 and test-89 and self bootstrapping.
5604 // For instance, we can avoid a copy by using `newobj'
5605 // instead of Call + Push-temp on value types.
5606 // value_target = MyEmptyExpression;
5611 /// Converts complex core type syntax like 'new int ()' to simple constant
5613 public static Constant Constantify (Type t)
5615 if (t == TypeManager.int32_type)
5616 return new IntConstant (0, Location.Null);
5617 if (t == TypeManager.uint32_type)
5618 return new UIntConstant (0, Location.Null);
5619 if (t == TypeManager.int64_type)
5620 return new LongConstant (0, Location.Null);
5621 if (t == TypeManager.uint64_type)
5622 return new ULongConstant (0, Location.Null);
5623 if (t == TypeManager.float_type)
5624 return new FloatConstant (0, Location.Null);
5625 if (t == TypeManager.double_type)
5626 return new DoubleConstant (0, Location.Null);
5627 if (t == TypeManager.short_type)
5628 return new ShortConstant (0, Location.Null);
5629 if (t == TypeManager.ushort_type)
5630 return new UShortConstant (0, Location.Null);
5631 if (t == TypeManager.sbyte_type)
5632 return new SByteConstant (0, Location.Null);
5633 if (t == TypeManager.byte_type)
5634 return new ByteConstant (0, Location.Null);
5635 if (t == TypeManager.char_type)
5636 return new CharConstant ('\0', Location.Null);
5637 if (t == TypeManager.bool_type)
5638 return new BoolConstant (false, Location.Null);
5639 if (t == TypeManager.decimal_type)
5640 return new DecimalConstant (0, Location.Null);
5646 // Checks whether the type is an interface that has the
5647 // [ComImport, CoClass] attributes and must be treated
5650 public Expression CheckComImport (EmitContext ec)
5652 if (!type.IsInterface)
5656 // Turn the call into:
5657 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5659 Type real_class = AttributeTester.GetCoClassAttribute (type);
5660 if (real_class == null)
5663 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5664 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5665 return cast.Resolve (ec);
5668 public override Expression DoResolve (EmitContext ec)
5671 // The New DoResolve might be called twice when initializing field
5672 // expressions (see EmitFieldInitializers, the call to
5673 // GetInitializerExpression will perform a resolve on the expression,
5674 // and later the assign will trigger another resolution
5676 // This leads to bugs (#37014)
5679 if (RequestedType is NewDelegate)
5680 return RequestedType;
5684 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5690 if (Arguments == null) {
5691 Expression c = Constantify (type);
5696 if (TypeManager.IsDelegateType (type)) {
5697 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5698 if (RequestedType != null)
5699 if (!(RequestedType is DelegateCreation))
5700 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5701 return RequestedType;
5704 if (type.IsGenericParameter) {
5705 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5707 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5708 Error (304, String.Format (
5709 "Cannot create an instance of the " +
5710 "variable type '{0}' because it " +
5711 "doesn't have the new() constraint",
5716 if ((Arguments != null) && (Arguments.Count != 0)) {
5717 Error (417, String.Format (
5718 "`{0}': cannot provide arguments " +
5719 "when creating an instance of a " +
5720 "variable type.", type));
5724 is_type_parameter = true;
5725 eclass = ExprClass.Value;
5729 if (type.IsAbstract && type.IsSealed) {
5730 Report.SymbolRelatedToPreviousError (type);
5731 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5735 if (type.IsInterface || type.IsAbstract){
5736 RequestedType = CheckComImport (ec);
5737 if (RequestedType != null)
5738 return RequestedType;
5740 Report.SymbolRelatedToPreviousError (type);
5741 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5745 bool is_struct = type.IsValueType;
5746 eclass = ExprClass.Value;
5749 // SRE returns a match for .ctor () on structs (the object constructor),
5750 // so we have to manually ignore it.
5752 if (is_struct && Arguments == null)
5755 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5756 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5757 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5762 MethodGroupExpr mg = ml as MethodGroupExpr;
5765 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5769 if (Arguments != null){
5770 foreach (Argument a in Arguments){
5771 if (!a.Resolve (ec, loc))
5776 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5777 if (method == null) {
5778 if (almostMatchedMembers.Count != 0)
5779 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5786 bool DoEmitTypeParameter (EmitContext ec)
5788 ILGenerator ig = ec.ig;
5790 ig.Emit (OpCodes.Ldtoken, type);
5791 ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
5792 ig.Emit (OpCodes.Call, TypeManager.activator_create_instance);
5793 ig.Emit (OpCodes.Unbox_Any, type);
5799 // This DoEmit can be invoked in two contexts:
5800 // * As a mechanism that will leave a value on the stack (new object)
5801 // * As one that wont (init struct)
5803 // You can control whether a value is required on the stack by passing
5804 // need_value_on_stack. The code *might* leave a value on the stack
5805 // so it must be popped manually
5807 // If we are dealing with a ValueType, we have a few
5808 // situations to deal with:
5810 // * The target is a ValueType, and we have been provided
5811 // the instance (this is easy, we are being assigned).
5813 // * The target of New is being passed as an argument,
5814 // to a boxing operation or a function that takes a
5817 // In this case, we need to create a temporary variable
5818 // that is the argument of New.
5820 // Returns whether a value is left on the stack
5822 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5824 bool is_value_type = TypeManager.IsValueType (type);
5825 ILGenerator ig = ec.ig;
5830 // Allow DoEmit() to be called multiple times.
5831 // We need to create a new LocalTemporary each time since
5832 // you can't share LocalBuilders among ILGeneators.
5833 if (!value_target_set)
5834 value_target = new LocalTemporary (type);
5836 ml = (IMemoryLocation) value_target;
5837 ml.AddressOf (ec, AddressOp.Store);
5841 Invocation.EmitArguments (ec, method, Arguments, false, null);
5845 ig.Emit (OpCodes.Initobj, type);
5847 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5848 if (need_value_on_stack){
5849 value_target.Emit (ec);
5854 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5859 public override void Emit (EmitContext ec)
5861 if (is_type_parameter)
5862 DoEmitTypeParameter (ec);
5867 public override void EmitStatement (EmitContext ec)
5869 if (is_type_parameter)
5870 throw new InvalidOperationException ();
5872 if (DoEmit (ec, false))
5873 ec.ig.Emit (OpCodes.Pop);
5876 public void AddressOf (EmitContext ec, AddressOp Mode)
5878 if (is_type_parameter)
5879 throw new InvalidOperationException ();
5881 if (!type.IsValueType){
5883 // We throw an exception. So far, I believe we only need to support
5885 // foreach (int j in new StructType ())
5888 throw new Exception ("AddressOf should not be used for classes");
5891 if (!value_target_set)
5892 value_target = new LocalTemporary (type);
5894 IMemoryLocation ml = (IMemoryLocation) value_target;
5895 ml.AddressOf (ec, AddressOp.Store);
5897 Invocation.EmitArguments (ec, method, Arguments, false, null);
5900 ec.ig.Emit (OpCodes.Initobj, type);
5902 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5904 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5909 /// 14.5.10.2: Represents an array creation expression.
5913 /// There are two possible scenarios here: one is an array creation
5914 /// expression that specifies the dimensions and optionally the
5915 /// initialization data and the other which does not need dimensions
5916 /// specified but where initialization data is mandatory.
5918 public class ArrayCreation : Expression {
5919 Expression requested_base_type;
5920 ArrayList initializers;
5923 // The list of Argument types.
5924 // This is used to construct the `newarray' or constructor signature
5926 ArrayList arguments;
5929 // Method used to create the array object.
5931 MethodBase new_method = null;
5933 Type array_element_type;
5934 Type underlying_type;
5935 bool is_one_dimensional = false;
5936 bool is_builtin_type = false;
5937 bool expect_initializers = false;
5938 int num_arguments = 0;
5942 ArrayList array_data;
5946 // The number of constants in array initializers
5947 int const_initializers_count;
5949 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5951 this.requested_base_type = requested_base_type;
5952 this.initializers = initializers;
5956 arguments = new ArrayList ();
5958 foreach (Expression e in exprs) {
5959 arguments.Add (new Argument (e, Argument.AType.Expression));
5964 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5966 this.requested_base_type = requested_base_type;
5967 this.initializers = initializers;
5971 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5973 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5975 //dimensions = tmp.Length - 1;
5976 expect_initializers = true;
5979 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5981 StringBuilder sb = new StringBuilder (rank);
5984 for (int i = 1; i < idx_count; i++)
5989 return new ComposedCast (base_type, sb.ToString (), loc);
5992 void Error_IncorrectArrayInitializer ()
5994 Error (178, "Invalid rank specifier: expected `,' or `]'");
5997 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5999 if (specified_dims) {
6000 Argument a = (Argument) arguments [idx];
6002 if (!a.Resolve (ec, loc))
6005 Constant c = a.Expr as Constant;
6007 c = c.ToType (TypeManager.int32_type, a.Expr.Location);
6011 Report.Error (150, a.Expr.Location, "A constant value is expected");
6015 int value = (int) c.GetValue ();
6017 if (value != probe.Count) {
6018 Error_IncorrectArrayInitializer ();
6022 bounds [idx] = value;
6025 int child_bounds = -1;
6026 for (int i = 0; i < probe.Count; ++i) {
6027 object o = probe [i];
6028 if (o is ArrayList) {
6029 ArrayList sub_probe = o as ArrayList;
6030 int current_bounds = sub_probe.Count;
6032 if (child_bounds == -1)
6033 child_bounds = current_bounds;
6035 else if (child_bounds != current_bounds){
6036 Error_IncorrectArrayInitializer ();
6039 if (idx + 1 >= dimensions){
6040 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
6044 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
6048 if (child_bounds != -1){
6049 Error_IncorrectArrayInitializer ();
6053 Expression tmp = (Expression) o;
6054 tmp = tmp.Resolve (ec);
6058 Expression conv = Convert.ImplicitConversionRequired (
6059 ec, tmp, underlying_type, loc);
6064 // Initializers with the default values can be ignored
6065 Constant c = tmp as Constant;
6067 if (c.IsDefaultInitializer (array_element_type)) {
6071 ++const_initializers_count;
6074 // Used to invalidate static initializer
6075 const_initializers_count = int.MinValue;
6078 array_data.Add (conv);
6085 public void UpdateIndices ()
6088 for (ArrayList probe = initializers; probe != null;) {
6089 if (probe.Count > 0 && probe [0] is ArrayList) {
6090 Expression e = new IntConstant (probe.Count, Location.Null);
6091 arguments.Add (new Argument (e, Argument.AType.Expression));
6093 bounds [i++] = probe.Count;
6095 probe = (ArrayList) probe [0];
6098 Expression e = new IntConstant (probe.Count, Location.Null);
6099 arguments.Add (new Argument (e, Argument.AType.Expression));
6101 bounds [i++] = probe.Count;
6108 bool ResolveInitializers (EmitContext ec)
6110 if (initializers == null) {
6111 return !expect_initializers;
6114 if (underlying_type == null)
6118 // We use this to store all the date values in the order in which we
6119 // will need to store them in the byte blob later
6121 array_data = new ArrayList ();
6122 bounds = new System.Collections.Specialized.HybridDictionary ();
6124 if (arguments != null)
6125 return CheckIndices (ec, initializers, 0, true);
6127 arguments = new ArrayList ();
6129 if (!CheckIndices (ec, initializers, 0, false))
6134 if (arguments.Count != dimensions) {
6135 Error_IncorrectArrayInitializer ();
6143 // Creates the type of the array
6145 bool LookupType (EmitContext ec)
6147 StringBuilder array_qualifier = new StringBuilder (rank);
6150 // `In the first form allocates an array instace of the type that results
6151 // from deleting each of the individual expression from the expression list'
6153 if (num_arguments > 0) {
6154 array_qualifier.Append ("[");
6155 for (int i = num_arguments-1; i > 0; i--)
6156 array_qualifier.Append (",");
6157 array_qualifier.Append ("]");
6163 TypeExpr array_type_expr;
6164 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
6165 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
6166 if (array_type_expr == null)
6169 type = array_type_expr.Type;
6170 underlying_type = TypeManager.GetElementType (type);
6171 dimensions = type.GetArrayRank ();
6176 public override Expression DoResolve (EmitContext ec)
6181 if (!LookupType (ec))
6184 array_element_type = TypeManager.GetElementType (type);
6185 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
6186 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
6191 // First step is to validate the initializers and fill
6192 // in any missing bits
6194 if (!ResolveInitializers (ec))
6198 if (arguments == null)
6201 arg_count = arguments.Count;
6202 foreach (Argument a in arguments){
6203 if (!a.Resolve (ec, loc))
6206 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
6207 if (real_arg == null)
6214 if (arg_count == 1) {
6215 is_one_dimensional = true;
6216 eclass = ExprClass.Value;
6220 is_builtin_type = TypeManager.IsBuiltinType (type);
6222 if (is_builtin_type) {
6225 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
6226 AllBindingFlags, loc);
6228 if (!(ml is MethodGroupExpr)) {
6229 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
6234 Error (-6, "New invocation: Can not find a constructor for " +
6235 "this argument list");
6239 new_method = Invocation.OverloadResolve (
6240 ec, (MethodGroupExpr) ml, arguments, false, loc);
6242 if (new_method == null) {
6243 Error (-6, "New invocation: Can not find a constructor for " +
6244 "this argument list");
6248 eclass = ExprClass.Value;
6251 ModuleBuilder mb = CodeGen.Module.Builder;
6252 ArrayList args = new ArrayList ();
6254 if (arguments != null) {
6255 for (int i = 0; i < arg_count; i++)
6256 args.Add (TypeManager.int32_type);
6259 Type [] arg_types = null;
6262 arg_types = new Type [args.Count];
6264 args.CopyTo (arg_types, 0);
6266 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6269 if (new_method == null) {
6270 Error (-6, "New invocation: Can not find a constructor for " +
6271 "this argument list");
6275 eclass = ExprClass.Value;
6280 byte [] MakeByteBlob ()
6285 int count = array_data.Count;
6287 if (underlying_type.IsEnum)
6288 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
6290 factor = GetTypeSize (underlying_type);
6292 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
6294 data = new byte [(count * factor + 4) & ~3];
6297 for (int i = 0; i < count; ++i) {
6298 object v = array_data [i];
6300 if (v is EnumConstant)
6301 v = ((EnumConstant) v).Child;
6303 if (v is Constant && !(v is StringConstant))
6304 v = ((Constant) v).GetValue ();
6310 if (underlying_type == TypeManager.int64_type){
6311 if (!(v is Expression)){
6312 long val = (long) v;
6314 for (int j = 0; j < factor; ++j) {
6315 data [idx + j] = (byte) (val & 0xFF);
6319 } else if (underlying_type == TypeManager.uint64_type){
6320 if (!(v is Expression)){
6321 ulong val = (ulong) v;
6323 for (int j = 0; j < factor; ++j) {
6324 data [idx + j] = (byte) (val & 0xFF);
6328 } else if (underlying_type == TypeManager.float_type) {
6329 if (!(v is Expression)){
6330 element = BitConverter.GetBytes ((float) v);
6332 for (int j = 0; j < factor; ++j)
6333 data [idx + j] = element [j];
6335 } else if (underlying_type == TypeManager.double_type) {
6336 if (!(v is Expression)){
6337 element = BitConverter.GetBytes ((double) v);
6339 for (int j = 0; j < factor; ++j)
6340 data [idx + j] = element [j];
6342 } else if (underlying_type == TypeManager.char_type){
6343 if (!(v is Expression)){
6344 int val = (int) ((char) v);
6346 data [idx] = (byte) (val & 0xff);
6347 data [idx+1] = (byte) (val >> 8);
6349 } else if (underlying_type == TypeManager.short_type){
6350 if (!(v is Expression)){
6351 int val = (int) ((short) v);
6353 data [idx] = (byte) (val & 0xff);
6354 data [idx+1] = (byte) (val >> 8);
6356 } else if (underlying_type == TypeManager.ushort_type){
6357 if (!(v is Expression)){
6358 int val = (int) ((ushort) v);
6360 data [idx] = (byte) (val & 0xff);
6361 data [idx+1] = (byte) (val >> 8);
6363 } else if (underlying_type == TypeManager.int32_type) {
6364 if (!(v is Expression)){
6367 data [idx] = (byte) (val & 0xff);
6368 data [idx+1] = (byte) ((val >> 8) & 0xff);
6369 data [idx+2] = (byte) ((val >> 16) & 0xff);
6370 data [idx+3] = (byte) (val >> 24);
6372 } else if (underlying_type == TypeManager.uint32_type) {
6373 if (!(v is Expression)){
6374 uint val = (uint) v;
6376 data [idx] = (byte) (val & 0xff);
6377 data [idx+1] = (byte) ((val >> 8) & 0xff);
6378 data [idx+2] = (byte) ((val >> 16) & 0xff);
6379 data [idx+3] = (byte) (val >> 24);
6381 } else if (underlying_type == TypeManager.sbyte_type) {
6382 if (!(v is Expression)){
6383 sbyte val = (sbyte) v;
6384 data [idx] = (byte) val;
6386 } else if (underlying_type == TypeManager.byte_type) {
6387 if (!(v is Expression)){
6388 byte val = (byte) v;
6389 data [idx] = (byte) val;
6391 } else if (underlying_type == TypeManager.bool_type) {
6392 if (!(v is Expression)){
6393 bool val = (bool) v;
6394 data [idx] = (byte) (val ? 1 : 0);
6396 } else if (underlying_type == TypeManager.decimal_type){
6397 if (!(v is Expression)){
6398 int [] bits = Decimal.GetBits ((decimal) v);
6401 // FIXME: For some reason, this doesn't work on the MS runtime.
6402 int [] nbits = new int [4];
6403 nbits [0] = bits [3];
6404 nbits [1] = bits [2];
6405 nbits [2] = bits [0];
6406 nbits [3] = bits [1];
6408 for (int j = 0; j < 4; j++){
6409 data [p++] = (byte) (nbits [j] & 0xff);
6410 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6411 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6412 data [p++] = (byte) (nbits [j] >> 24);
6416 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6425 // Emits the initializers for the array
6427 void EmitStaticInitializers (EmitContext ec)
6430 // First, the static data
6433 ILGenerator ig = ec.ig;
6435 byte [] data = MakeByteBlob ();
6437 fb = RootContext.MakeStaticData (data);
6439 ig.Emit (OpCodes.Dup);
6440 ig.Emit (OpCodes.Ldtoken, fb);
6441 ig.Emit (OpCodes.Call,
6442 TypeManager.void_initializearray_array_fieldhandle);
6446 // Emits pieces of the array that can not be computed at compile
6447 // time (variables and string locations).
6449 // This always expect the top value on the stack to be the array
6451 void EmitDynamicInitializers (EmitContext ec)
6453 ILGenerator ig = ec.ig;
6454 int dims = bounds.Count;
6455 int [] current_pos = new int [dims];
6457 MethodInfo set = null;
6460 Type [] args = new Type [dims + 1];
6462 for (int j = 0; j < dims; j++)
6463 args [j] = TypeManager.int32_type;
6464 args [dims] = array_element_type;
6466 set = CodeGen.Module.Builder.GetArrayMethod (
6468 CallingConventions.HasThis | CallingConventions.Standard,
6469 TypeManager.void_type, args);
6472 for (int i = 0; i < array_data.Count; i++){
6474 Expression e = (Expression)array_data [i];
6477 Type etype = e.Type;
6479 ig.Emit (OpCodes.Dup);
6481 for (int idx = 0; idx < dims; idx++)
6482 IntConstant.EmitInt (ig, current_pos [idx]);
6485 // If we are dealing with a struct, get the
6486 // address of it, so we can store it.
6489 TypeManager.IsValueType (etype) &&
6490 (!TypeManager.IsBuiltinOrEnum (etype) ||
6491 etype == TypeManager.decimal_type)) {
6496 // Let new know that we are providing
6497 // the address where to store the results
6499 n.DisableTemporaryValueType ();
6502 ig.Emit (OpCodes.Ldelema, etype);
6508 bool is_stobj, has_type_arg;
6509 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
6511 ig.Emit (OpCodes.Stobj, etype);
6512 else if (has_type_arg)
6513 ig.Emit (op, etype);
6517 ig.Emit (OpCodes.Call, set);
6524 for (int j = dims - 1; j >= 0; j--){
6526 if (current_pos [j] < (int) bounds [j])
6528 current_pos [j] = 0;
6533 void EmitArrayArguments (EmitContext ec)
6535 ILGenerator ig = ec.ig;
6537 foreach (Argument a in arguments) {
6538 Type atype = a.Type;
6541 if (atype == TypeManager.uint64_type)
6542 ig.Emit (OpCodes.Conv_Ovf_U4);
6543 else if (atype == TypeManager.int64_type)
6544 ig.Emit (OpCodes.Conv_Ovf_I4);
6548 public override void Emit (EmitContext ec)
6550 ILGenerator ig = ec.ig;
6552 EmitArrayArguments (ec);
6553 if (is_one_dimensional)
6554 ig.Emit (OpCodes.Newarr, array_element_type);
6556 if (is_builtin_type)
6557 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6559 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6562 if (initializers == null)
6565 // This is a treshold for static initializers
6566 // I tried to make more accurate but it seems to me that Array.Initialize is
6567 // always slower (managed -> unmanaged switch?)
6568 const int max_automatic_initializers = 200;
6570 if (const_initializers_count > max_automatic_initializers && TypeManager.IsPrimitiveType (array_element_type)) {
6571 EmitStaticInitializers (ec);
6575 EmitDynamicInitializers (ec);
6578 public override bool GetAttributableValue (Type valueType, out object value)
6580 if (!is_one_dimensional){
6581 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6582 return base.GetAttributableValue (null, out value);
6585 if (array_data == null) {
6586 Constant c = (Constant)((Argument)arguments [0]).Expr;
6587 if (c.IsDefaultValue) {
6588 value = Array.CreateInstance (array_element_type, 0);
6591 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6592 return base.GetAttributableValue (null, out value);
6595 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6596 object element_value;
6597 for (int i = 0; i < ret.Length; ++i)
6599 Expression e = (Expression)array_data [i];
6600 if (e == null) // Is null when initializer is optimized away
6601 e = (Expression)initializers [i];
6603 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6607 ret.SetValue (element_value, i);
6614 public sealed class CompilerGeneratedThis : This
6616 public static This Instance = new CompilerGeneratedThis ();
6618 private CompilerGeneratedThis ()
6619 : base (Location.Null)
6623 public override Expression DoResolve (EmitContext ec)
6625 eclass = ExprClass.Variable;
6626 type = ec.ContainerType;
6632 /// Represents the `this' construct
6635 public class This : Expression, IAssignMethod, IMemoryLocation, IVariable {
6638 VariableInfo variable_info;
6640 public This (Block block, Location loc)
6646 public This (Location loc)
6651 public VariableInfo VariableInfo {
6652 get { return variable_info; }
6655 public bool VerifyFixed ()
6657 return !TypeManager.IsValueType (Type);
6660 public bool ResolveBase (EmitContext ec)
6662 eclass = ExprClass.Variable;
6664 if (ec.TypeContainer.CurrentType != null)
6665 type = ec.TypeContainer.CurrentType;
6667 type = ec.ContainerType;
6670 Error (26, "Keyword `this' is not valid in a static property, static method, or static field initializer");
6674 if (block != null && block.Toplevel.ThisVariable != null)
6675 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6677 if (ec.CurrentAnonymousMethod != null)
6683 public override Expression DoResolve (EmitContext ec)
6685 if (!ResolveBase (ec))
6688 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) && !variable_info.IsAssigned (ec)) {
6689 Error (188, "The `this' object cannot be used before all of its fields are assigned to");
6690 variable_info.SetAssigned (ec);
6694 if (ec.IsFieldInitializer) {
6695 Error (27, "Keyword `this' is not available in the current context");
6702 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6704 if (!ResolveBase (ec))
6707 if (variable_info != null)
6708 variable_info.SetAssigned (ec);
6710 if (ec.TypeContainer is Class){
6711 Error (1604, "Cannot assign to 'this' because it is read-only");
6718 public void Emit (EmitContext ec, bool leave_copy)
6722 ec.ig.Emit (OpCodes.Dup);
6725 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
6727 ILGenerator ig = ec.ig;
6729 if (ec.TypeContainer is Struct){
6730 ec.EmitThis (false);
6733 LocalTemporary t = null;
6735 t = new LocalTemporary (type);
6736 ec.ig.Emit (OpCodes.Dup);
6740 ig.Emit (OpCodes.Stobj, type);
6747 throw new Exception ("how did you get here");
6751 public override void Emit (EmitContext ec)
6753 ILGenerator ig = ec.ig;
6755 ec.EmitThis (false);
6756 if (ec.TypeContainer is Struct)
6757 ig.Emit (OpCodes.Ldobj, type);
6760 public override int GetHashCode()
6762 return block.GetHashCode ();
6765 public override bool Equals (object obj)
6767 This t = obj as This;
6771 return block == t.block;
6774 public void AddressOf (EmitContext ec, AddressOp mode)
6779 // FIGURE OUT WHY LDARG_S does not work
6781 // consider: struct X { int val; int P { set { val = value; }}}
6783 // Yes, this looks very bad. Look at `NOTAS' for
6785 // ec.ig.Emit (OpCodes.Ldarga_S, (byte) 0);
6790 /// Represents the `__arglist' construct
6792 public class ArglistAccess : Expression
6794 public ArglistAccess (Location loc)
6799 public override Expression DoResolve (EmitContext ec)
6801 eclass = ExprClass.Variable;
6802 type = TypeManager.runtime_argument_handle_type;
6804 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6806 Error (190, "The __arglist construct is valid only within " +
6807 "a variable argument method");
6814 public override void Emit (EmitContext ec)
6816 ec.ig.Emit (OpCodes.Arglist);
6821 /// Represents the `__arglist (....)' construct
6823 public class Arglist : Expression
6825 public readonly Argument[] Arguments;
6827 public Arglist (Argument[] args, Location l)
6833 public Type[] ArgumentTypes {
6835 Type[] retval = new Type [Arguments.Length];
6836 for (int i = 0; i < Arguments.Length; i++)
6837 retval [i] = Arguments [i].Type;
6842 public override Expression DoResolve (EmitContext ec)
6844 eclass = ExprClass.Variable;
6845 type = TypeManager.runtime_argument_handle_type;
6847 foreach (Argument arg in Arguments) {
6848 if (!arg.Resolve (ec, loc))
6855 public override void Emit (EmitContext ec)
6857 foreach (Argument arg in Arguments)
6863 // This produces the value that renders an instance, used by the iterators code
6865 public class ProxyInstance : Expression, IMemoryLocation {
6866 public override Expression DoResolve (EmitContext ec)
6868 eclass = ExprClass.Variable;
6869 type = ec.ContainerType;
6873 public override void Emit (EmitContext ec)
6875 ec.ig.Emit (OpCodes.Ldarg_0);
6879 public void AddressOf (EmitContext ec, AddressOp mode)
6881 ec.ig.Emit (OpCodes.Ldarg_0);
6886 /// Implements the typeof operator
6888 public class TypeOf : Expression {
6889 readonly Expression QueriedType;
6890 protected Type typearg;
6892 public TypeOf (Expression queried_type, Location l)
6894 QueriedType = queried_type;
6898 public override Expression DoResolve (EmitContext ec)
6900 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6904 typearg = texpr.Type;
6906 if (typearg == TypeManager.void_type) {
6907 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6911 if (typearg.IsPointer && !ec.InUnsafe){
6916 type = TypeManager.type_type;
6917 // Even though what is returned is a type object, it's treated as a value by the compiler.
6918 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6919 eclass = ExprClass.Value;
6923 public override void Emit (EmitContext ec)
6925 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6926 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6929 public override bool GetAttributableValue (Type valueType, out object value)
6931 if (typearg.ContainsGenericParameters) {
6932 Report.SymbolRelatedToPreviousError(typearg);
6933 Report.Error(416, loc, "`{0}': an attribute argument cannot use type parameters",
6934 TypeManager.CSharpName(typearg));
6939 if (valueType == TypeManager.object_type) {
6940 value = (object)typearg;
6947 public Type TypeArgument
6957 /// Implements the `typeof (void)' operator
6959 public class TypeOfVoid : TypeOf {
6960 public TypeOfVoid (Location l) : base (null, l)
6965 public override Expression DoResolve (EmitContext ec)
6967 type = TypeManager.type_type;
6968 typearg = TypeManager.void_type;
6969 // See description in TypeOf.
6970 eclass = ExprClass.Value;
6976 /// Implements the sizeof expression
6978 public class SizeOf : Expression {
6979 public Expression QueriedType;
6982 public SizeOf (Expression queried_type, Location l)
6984 this.QueriedType = queried_type;
6988 public override Expression DoResolve (EmitContext ec)
6990 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6994 if (texpr is TypeParameterExpr){
6995 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
6999 type_queried = texpr.Type;
7000 if (type_queried.IsEnum)
7001 type_queried = TypeManager.EnumToUnderlying (type_queried);
7003 if (type_queried == TypeManager.void_type) {
7004 Expression.Error_VoidInvalidInTheContext (loc);
7008 int size_of = GetTypeSize (type_queried);
7010 return new IntConstant (size_of, loc);
7014 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)",
7015 TypeManager.CSharpName (type_queried));
7019 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
7023 type = TypeManager.int32_type;
7024 eclass = ExprClass.Value;
7028 public override void Emit (EmitContext ec)
7030 int size = GetTypeSize (type_queried);
7033 ec.ig.Emit (OpCodes.Sizeof, type_queried);
7035 IntConstant.EmitInt (ec.ig, size);
7040 /// Implements the qualified-alias-member (::) expression.
7042 public class QualifiedAliasMember : Expression
7044 string alias, identifier;
7046 public QualifiedAliasMember (string alias, string identifier, Location l)
7049 this.identifier = identifier;
7053 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7055 if (alias == "global")
7056 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
7058 int errors = Report.Errors;
7059 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7061 if (errors == Report.Errors)
7062 Report.Error (432, loc, "Alias `{0}' not found", alias);
7065 if (fne.eclass != ExprClass.Namespace) {
7067 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7070 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
7073 public override Expression DoResolve (EmitContext ec)
7075 FullNamedExpression fne;
7076 if (alias == "global") {
7077 fne = RootNamespace.Global;
7079 int errors = Report.Errors;
7080 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7082 if (errors == Report.Errors)
7083 Report.Error (432, loc, "Alias `{0}' not found", alias);
7088 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
7092 if (!(retval is FullNamedExpression)) {
7093 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
7097 // We defer this check till the end to match the behaviour of CSC
7098 if (fne.eclass != ExprClass.Namespace) {
7099 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7105 public override void Emit (EmitContext ec)
7107 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
7111 public override string ToString ()
7113 return alias + "::" + identifier;
7116 public override string GetSignatureForError ()
7123 /// Implements the member access expression
7125 public class MemberAccess : Expression {
7126 public readonly string Identifier;
7130 public MemberAccess (Expression expr, string id)
7131 : this (expr, id, null)
7135 public MemberAccess (Expression expr, string identifier, Location loc, TypeArguments args)
7138 Identifier = identifier;
7143 public MemberAccess (Expression expr, string id, Location loc)
7144 : this (expr, id, loc, null)
7148 public MemberAccess (Expression expr, string id, TypeArguments args)
7149 : this (expr, id, expr.Location, args)
7153 public Expression Expr {
7154 get { return expr; }
7157 // TODO: this method has very poor performace for Enum fields and
7158 // probably for other constants as well
7159 Expression DoResolve (EmitContext ec, Expression right_side)
7162 throw new Exception ();
7165 // Resolve the expression with flow analysis turned off, we'll do the definite
7166 // assignment checks later. This is because we don't know yet what the expression
7167 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7168 // definite assignment check on the actual field and not on the whole struct.
7171 SimpleName original = expr as SimpleName;
7172 Expression new_expr = expr.Resolve (ec,
7173 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7174 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7176 if (new_expr == null)
7179 if (new_expr is Namespace) {
7180 Namespace ns = (Namespace) new_expr;
7181 string lookup_id = MemberName.MakeName (Identifier, args);
7182 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, lookup_id, loc);
7183 if ((retval != null) && (args != null))
7184 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
7186 ns.Error_NamespaceDoesNotExist (loc, Identifier);
7190 Type expr_type = new_expr.Type;
7191 if (expr_type.IsPointer || expr_type == TypeManager.void_type || new_expr is NullLiteral){
7192 Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type);
7195 if (expr_type == TypeManager.anonymous_method_type){
7196 Unary.Error_OperatorCannotBeApplied (loc, ".", "anonymous method");
7201 Expression member_lookup;
7202 member_lookup = MemberLookup (
7203 ec.ContainerType, expr_type, expr_type, Identifier, loc);
7204 if ((member_lookup == null) && (args != null)) {
7205 string lookup_id = MemberName.MakeName (Identifier, args);
7206 member_lookup = MemberLookup (
7207 ec.ContainerType, expr_type, expr_type, lookup_id, loc);
7209 if (member_lookup == null) {
7210 MemberLookupFailed (
7211 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
7215 if (member_lookup is TypeExpr) {
7216 if (!(new_expr is TypeExpr) &&
7217 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
7218 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7219 Identifier, member_lookup.GetSignatureForError ());
7223 ConstructedType ct = new_expr as ConstructedType;
7226 // When looking up a nested type in a generic instance
7227 // via reflection, we always get a generic type definition
7228 // and not a generic instance - so we have to do this here.
7230 // See gtest-172-lib.cs and gtest-172.cs for an example.
7232 ct = new ConstructedType (
7233 member_lookup.Type, ct.TypeArguments, loc);
7235 return ct.ResolveAsTypeStep (ec, false);
7238 return member_lookup;
7241 MemberExpr me = (MemberExpr) member_lookup;
7242 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
7243 if (member_lookup == null)
7247 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
7249 throw new InternalErrorException ();
7251 return mg.ResolveGeneric (ec, args);
7254 if (original != null && !TypeManager.IsValueType (expr_type)) {
7255 me = member_lookup as MemberExpr;
7256 if (me != null && me.IsInstance) {
7257 LocalVariableReference var = new_expr as LocalVariableReference;
7258 if (var != null && !var.VerifyAssigned (ec))
7263 // The following DoResolve/DoResolveLValue will do the definite assignment
7266 if (right_side != null)
7267 return member_lookup.DoResolveLValue (ec, right_side);
7269 return member_lookup.DoResolve (ec);
7272 public override Expression DoResolve (EmitContext ec)
7274 return DoResolve (ec, null);
7277 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7279 return DoResolve (ec, right_side);
7282 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7284 return ResolveNamespaceOrType (ec, silent);
7287 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7289 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
7291 if (new_expr == null)
7294 string lookup_id = MemberName.MakeName (Identifier, args);
7296 if (new_expr is Namespace) {
7297 Namespace ns = (Namespace) new_expr;
7298 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, lookup_id, loc);
7299 if ((retval != null) && (args != null))
7300 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
7301 if (!silent && retval == null)
7302 ns.Error_NamespaceDoesNotExist (loc, Identifier);
7306 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
7307 if (tnew_expr == null)
7310 Type expr_type = tnew_expr.Type;
7312 if (expr_type.IsPointer){
7313 Error (23, "The `.' operator can not be applied to pointer operands (" +
7314 TypeManager.CSharpName (expr_type) + ")");
7318 Expression member_lookup = MemberLookup (
7319 rc.DeclContainer.TypeBuilder, expr_type, expr_type, lookup_id,
7320 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7321 if (member_lookup == null) {
7322 int errors = Report.Errors;
7323 MemberLookupFailed (rc.DeclContainer.TypeBuilder, expr_type, expr_type, lookup_id, null, false, loc);
7325 if (!silent && errors == Report.Errors) {
7326 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7327 Identifier, new_expr.GetSignatureForError ());
7332 if (!(member_lookup is TypeExpr)) {
7333 new_expr.Error_UnexpectedKind (rc.DeclContainer, "type", loc);
7337 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
7341 TypeArguments the_args = args;
7342 if (TypeManager.HasGenericArguments (expr_type)) {
7343 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
7345 TypeArguments new_args = new TypeArguments (loc);
7346 foreach (Type decl in decl_args)
7347 new_args.Add (new TypeExpression (decl, loc));
7350 new_args.Add (args);
7352 the_args = new_args;
7355 if (the_args != null) {
7356 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
7357 return ctype.ResolveAsTypeStep (rc, false);
7363 public override void Emit (EmitContext ec)
7365 throw new Exception ("Should not happen");
7368 public override string ToString ()
7370 return expr + "." + MemberName.MakeName (Identifier, args);
7373 public override string GetSignatureForError ()
7375 return expr.GetSignatureForError () + "." + Identifier;
7380 /// Implements checked expressions
7382 public class CheckedExpr : Expression {
7384 public Expression Expr;
7386 public CheckedExpr (Expression e, Location l)
7392 public override Expression DoResolve (EmitContext ec)
7394 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7395 Expr = Expr.Resolve (ec);
7400 if (Expr is Constant)
7403 eclass = Expr.eclass;
7408 public override void Emit (EmitContext ec)
7410 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7414 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7416 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7417 Expr.EmitBranchable (ec, target, onTrue);
7422 /// Implements the unchecked expression
7424 public class UnCheckedExpr : Expression {
7426 public Expression Expr;
7428 public UnCheckedExpr (Expression e, Location l)
7434 public override Expression DoResolve (EmitContext ec)
7436 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7437 Expr = Expr.Resolve (ec);
7442 if (Expr is Constant)
7445 eclass = Expr.eclass;
7450 public override void Emit (EmitContext ec)
7452 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7456 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7458 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7459 Expr.EmitBranchable (ec, target, onTrue);
7464 /// An Element Access expression.
7466 /// During semantic analysis these are transformed into
7467 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7469 public class ElementAccess : Expression {
7470 public ArrayList Arguments;
7471 public Expression Expr;
7473 public ElementAccess (Expression e, ArrayList e_list)
7482 Arguments = new ArrayList ();
7483 foreach (Expression tmp in e_list)
7484 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7488 bool CommonResolve (EmitContext ec)
7490 Expr = Expr.Resolve (ec);
7495 if (Arguments == null)
7498 foreach (Argument a in Arguments){
7499 if (!a.Resolve (ec, loc))
7506 Expression MakePointerAccess (EmitContext ec, Type t)
7508 if (t == TypeManager.void_ptr_type){
7509 Error (242, "The array index operation is not valid on void pointers");
7512 if (Arguments.Count != 1){
7513 Error (196, "A pointer must be indexed by only one value");
7518 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7521 return new Indirection (p, loc).Resolve (ec);
7524 public override Expression DoResolve (EmitContext ec)
7526 if (!CommonResolve (ec))
7530 // We perform some simple tests, and then to "split" the emit and store
7531 // code we create an instance of a different class, and return that.
7533 // I am experimenting with this pattern.
7537 if (t == TypeManager.array_type){
7538 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7543 return (new ArrayAccess (this, loc)).Resolve (ec);
7545 return MakePointerAccess (ec, Expr.Type);
7547 FieldExpr fe = Expr as FieldExpr;
7549 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7551 return MakePointerAccess (ec, ff.ElementType);
7554 return (new IndexerAccess (this, loc)).Resolve (ec);
7557 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7559 if (!CommonResolve (ec))
7564 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7567 return MakePointerAccess (ec, Expr.Type);
7569 FieldExpr fe = Expr as FieldExpr;
7571 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7573 if (!(fe.InstanceExpression is LocalVariableReference) &&
7574 !(fe.InstanceExpression is This)) {
7575 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7578 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7579 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7582 return MakePointerAccess (ec, ff.ElementType);
7585 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7588 public override void Emit (EmitContext ec)
7590 throw new Exception ("Should never be reached");
7595 /// Implements array access
7597 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7599 // Points to our "data" repository
7603 LocalTemporary temp;
7606 public ArrayAccess (ElementAccess ea_data, Location l)
7609 eclass = ExprClass.Variable;
7613 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7615 return DoResolve (ec);
7618 public override Expression DoResolve (EmitContext ec)
7621 ExprClass eclass = ea.Expr.eclass;
7623 // As long as the type is valid
7624 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7625 eclass == ExprClass.Value)) {
7626 ea.Expr.Error_UnexpectedKind ("variable or value");
7631 Type t = ea.Expr.Type;
7632 if (t.GetArrayRank () != ea.Arguments.Count){
7633 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7634 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7638 type = TypeManager.GetElementType (t);
7639 if (type.IsPointer && !ec.InUnsafe){
7640 UnsafeError (ea.Location);
7644 foreach (Argument a in ea.Arguments){
7645 Type argtype = a.Type;
7647 if (argtype == TypeManager.int32_type ||
7648 argtype == TypeManager.uint32_type ||
7649 argtype == TypeManager.int64_type ||
7650 argtype == TypeManager.uint64_type) {
7651 Constant c = a.Expr as Constant;
7652 if (c != null && c.IsNegative) {
7653 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7659 // Mhm. This is strage, because the Argument.Type is not the same as
7660 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7662 // Wonder if I will run into trouble for this.
7664 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7669 eclass = ExprClass.Variable;
7675 /// Emits the right opcode to load an object of Type `t'
7676 /// from an array of T
7678 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7680 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7681 ig.Emit (OpCodes.Ldelem_U1);
7682 else if (type == TypeManager.sbyte_type)
7683 ig.Emit (OpCodes.Ldelem_I1);
7684 else if (type == TypeManager.short_type)
7685 ig.Emit (OpCodes.Ldelem_I2);
7686 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7687 ig.Emit (OpCodes.Ldelem_U2);
7688 else if (type == TypeManager.int32_type)
7689 ig.Emit (OpCodes.Ldelem_I4);
7690 else if (type == TypeManager.uint32_type)
7691 ig.Emit (OpCodes.Ldelem_U4);
7692 else if (type == TypeManager.uint64_type)
7693 ig.Emit (OpCodes.Ldelem_I8);
7694 else if (type == TypeManager.int64_type)
7695 ig.Emit (OpCodes.Ldelem_I8);
7696 else if (type == TypeManager.float_type)
7697 ig.Emit (OpCodes.Ldelem_R4);
7698 else if (type == TypeManager.double_type)
7699 ig.Emit (OpCodes.Ldelem_R8);
7700 else if (type == TypeManager.intptr_type)
7701 ig.Emit (OpCodes.Ldelem_I);
7702 else if (TypeManager.IsEnumType (type)){
7703 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7704 } else if (type.IsValueType){
7705 ig.Emit (OpCodes.Ldelema, type);
7706 ig.Emit (OpCodes.Ldobj, type);
7707 } else if (type.IsGenericParameter)
7709 ig.Emit (OpCodes.Ldelem, type);
7711 ig.Emit (OpCodes.Ldelem_Any, type);
7713 else if (type.IsPointer)
7714 ig.Emit (OpCodes.Ldelem_I);
7716 ig.Emit (OpCodes.Ldelem_Ref);
7720 /// Returns the right opcode to store an object of Type `t'
7721 /// from an array of T.
7723 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7725 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7726 has_type_arg = false; is_stobj = false;
7727 t = TypeManager.TypeToCoreType (t);
7728 if (TypeManager.IsEnumType (t))
7729 t = TypeManager.EnumToUnderlying (t);
7730 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7731 t == TypeManager.bool_type)
7732 return OpCodes.Stelem_I1;
7733 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7734 t == TypeManager.char_type)
7735 return OpCodes.Stelem_I2;
7736 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7737 return OpCodes.Stelem_I4;
7738 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7739 return OpCodes.Stelem_I8;
7740 else if (t == TypeManager.float_type)
7741 return OpCodes.Stelem_R4;
7742 else if (t == TypeManager.double_type)
7743 return OpCodes.Stelem_R8;
7744 else if (t == TypeManager.intptr_type) {
7745 has_type_arg = true;
7747 return OpCodes.Stobj;
7748 } else if (t.IsValueType) {
7749 has_type_arg = true;
7751 return OpCodes.Stobj;
7752 } else if (t.IsGenericParameter) {
7753 has_type_arg = true;
7755 return OpCodes.Stelem;
7757 return OpCodes.Stelem_Any;
7760 } else if (t.IsPointer)
7761 return OpCodes.Stelem_I;
7763 return OpCodes.Stelem_Ref;
7766 MethodInfo FetchGetMethod ()
7768 ModuleBuilder mb = CodeGen.Module.Builder;
7769 int arg_count = ea.Arguments.Count;
7770 Type [] args = new Type [arg_count];
7773 for (int i = 0; i < arg_count; i++){
7774 //args [i++] = a.Type;
7775 args [i] = TypeManager.int32_type;
7778 get = mb.GetArrayMethod (
7779 ea.Expr.Type, "Get",
7780 CallingConventions.HasThis |
7781 CallingConventions.Standard,
7787 MethodInfo FetchAddressMethod ()
7789 ModuleBuilder mb = CodeGen.Module.Builder;
7790 int arg_count = ea.Arguments.Count;
7791 Type [] args = new Type [arg_count];
7795 ret_type = TypeManager.GetReferenceType (type);
7797 for (int i = 0; i < arg_count; i++){
7798 //args [i++] = a.Type;
7799 args [i] = TypeManager.int32_type;
7802 address = mb.GetArrayMethod (
7803 ea.Expr.Type, "Address",
7804 CallingConventions.HasThis |
7805 CallingConventions.Standard,
7812 // Load the array arguments into the stack.
7814 // If we have been requested to cache the values (cached_locations array
7815 // initialized), then load the arguments the first time and store them
7816 // in locals. otherwise load from local variables.
7818 void LoadArrayAndArguments (EmitContext ec)
7820 ILGenerator ig = ec.ig;
7823 foreach (Argument a in ea.Arguments){
7824 Type argtype = a.Expr.Type;
7828 if (argtype == TypeManager.int64_type)
7829 ig.Emit (OpCodes.Conv_Ovf_I);
7830 else if (argtype == TypeManager.uint64_type)
7831 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7835 public void Emit (EmitContext ec, bool leave_copy)
7837 int rank = ea.Expr.Type.GetArrayRank ();
7838 ILGenerator ig = ec.ig;
7841 LoadArrayAndArguments (ec);
7844 EmitLoadOpcode (ig, type);
7848 method = FetchGetMethod ();
7849 ig.Emit (OpCodes.Call, method);
7852 LoadFromPtr (ec.ig, this.type);
7855 ec.ig.Emit (OpCodes.Dup);
7856 temp = new LocalTemporary (this.type);
7861 public override void Emit (EmitContext ec)
7866 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7868 int rank = ea.Expr.Type.GetArrayRank ();
7869 ILGenerator ig = ec.ig;
7870 Type t = source.Type;
7871 prepared = prepare_for_load;
7873 if (prepare_for_load) {
7874 AddressOf (ec, AddressOp.LoadStore);
7875 ec.ig.Emit (OpCodes.Dup);
7878 ec.ig.Emit (OpCodes.Dup);
7879 temp = new LocalTemporary (this.type);
7882 StoreFromPtr (ec.ig, t);
7892 LoadArrayAndArguments (ec);
7895 bool is_stobj, has_type_arg;
7896 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7899 // The stobj opcode used by value types will need
7900 // an address on the stack, not really an array/array
7904 ig.Emit (OpCodes.Ldelema, t);
7908 ec.ig.Emit (OpCodes.Dup);
7909 temp = new LocalTemporary (this.type);
7914 ig.Emit (OpCodes.Stobj, t);
7915 else if (has_type_arg)
7920 ModuleBuilder mb = CodeGen.Module.Builder;
7921 int arg_count = ea.Arguments.Count;
7922 Type [] args = new Type [arg_count + 1];
7927 ec.ig.Emit (OpCodes.Dup);
7928 temp = new LocalTemporary (this.type);
7932 for (int i = 0; i < arg_count; i++){
7933 //args [i++] = a.Type;
7934 args [i] = TypeManager.int32_type;
7937 args [arg_count] = type;
7939 set = mb.GetArrayMethod (
7940 ea.Expr.Type, "Set",
7941 CallingConventions.HasThis |
7942 CallingConventions.Standard,
7943 TypeManager.void_type, args);
7945 ig.Emit (OpCodes.Call, set);
7954 public void AddressOf (EmitContext ec, AddressOp mode)
7956 int rank = ea.Expr.Type.GetArrayRank ();
7957 ILGenerator ig = ec.ig;
7959 LoadArrayAndArguments (ec);
7962 ig.Emit (OpCodes.Ldelema, type);
7964 MethodInfo address = FetchAddressMethod ();
7965 ig.Emit (OpCodes.Call, address);
7969 public void EmitGetLength (EmitContext ec, int dim)
7971 int rank = ea.Expr.Type.GetArrayRank ();
7972 ILGenerator ig = ec.ig;
7976 ig.Emit (OpCodes.Ldlen);
7977 ig.Emit (OpCodes.Conv_I4);
7979 IntLiteral.EmitInt (ig, dim);
7980 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7986 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7987 public readonly ArrayList Properties;
7988 static Indexers empty;
7990 public struct Indexer {
7991 public readonly PropertyInfo PropertyInfo;
7992 public readonly MethodInfo Getter, Setter;
7994 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7996 this.PropertyInfo = property_info;
8004 empty = new Indexers (null);
8007 Indexers (ArrayList array)
8012 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
8017 foreach (PropertyInfo property in mi){
8018 MethodInfo get, set;
8020 get = property.GetGetMethod (true);
8021 set = property.GetSetMethod (true);
8022 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
8024 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
8026 if (get != null || set != null) {
8028 ix = new Indexers (new ArrayList ());
8029 ix.Properties.Add (new Indexer (property, get, set));
8034 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
8036 string p_name = TypeManager.IndexerPropertyName (lookup_type);
8038 return TypeManager.MemberLookup (
8039 caller_type, caller_type, lookup_type, MemberTypes.Property,
8040 BindingFlags.Public | BindingFlags.Instance |
8041 BindingFlags.DeclaredOnly, p_name, null);
8044 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
8046 Indexers ix = empty;
8048 if (lookup_type.IsGenericParameter) {
8049 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
8053 if (gc.HasClassConstraint)
8054 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
8056 Type[] ifaces = gc.InterfaceConstraints;
8057 foreach (Type itype in ifaces)
8058 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8063 Type copy = lookup_type;
8064 while (copy != TypeManager.object_type && copy != null){
8065 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
8066 copy = copy.BaseType;
8069 if (lookup_type.IsInterface) {
8070 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
8071 if (ifaces != null) {
8072 foreach (Type itype in ifaces)
8073 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8082 /// Expressions that represent an indexer call.
8084 public class IndexerAccess : Expression, IAssignMethod {
8086 // Points to our "data" repository
8088 MethodInfo get, set;
8089 ArrayList set_arguments;
8090 bool is_base_indexer;
8092 protected Type indexer_type;
8093 protected Type current_type;
8094 protected Expression instance_expr;
8095 protected ArrayList arguments;
8097 public IndexerAccess (ElementAccess ea, Location loc)
8098 : this (ea.Expr, false, loc)
8100 this.arguments = ea.Arguments;
8103 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
8106 this.instance_expr = instance_expr;
8107 this.is_base_indexer = is_base_indexer;
8108 this.eclass = ExprClass.Value;
8112 protected virtual bool CommonResolve (EmitContext ec)
8114 indexer_type = instance_expr.Type;
8115 current_type = ec.ContainerType;
8120 public override Expression DoResolve (EmitContext ec)
8122 if (!CommonResolve (ec))
8126 // Step 1: Query for all `Item' *properties*. Notice
8127 // that the actual methods are pointed from here.
8129 // This is a group of properties, piles of them.
8131 ArrayList AllGetters = null;
8133 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8134 if (ilist.Properties != null) {
8135 AllGetters = new ArrayList(ilist.Properties.Count);
8136 foreach (Indexers.Indexer ix in ilist.Properties) {
8137 if (ix.Getter != null)
8138 AllGetters.Add (ix.Getter);
8142 if (AllGetters == null) {
8143 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8144 TypeManager.CSharpName (indexer_type));
8148 if (AllGetters.Count == 0) {
8149 // FIXME: we cannot simply select first one as the error message is missleading when
8150 // multiple indexers exist
8151 Indexers.Indexer first_indexer = (Indexers.Indexer)ilist.Properties[ilist.Properties.Count - 1];
8152 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
8153 TypeManager.GetFullNameSignature (first_indexer.PropertyInfo));
8157 get = (MethodInfo)Invocation.OverloadResolve (ec, new MethodGroupExpr (AllGetters, loc),
8158 arguments, false, loc);
8161 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8166 // Only base will allow this invocation to happen.
8168 if (get.IsAbstract && this is BaseIndexerAccess){
8169 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
8173 type = get.ReturnType;
8174 if (type.IsPointer && !ec.InUnsafe){
8179 instance_expr.CheckMarshalByRefAccess ();
8181 eclass = ExprClass.IndexerAccess;
8185 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8187 if (right_side == EmptyExpression.OutAccess) {
8188 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
8189 GetSignatureForError ());
8193 // if the indexer returns a value type, and we try to set a field in it
8194 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
8195 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
8196 GetSignatureForError ());
8200 ArrayList AllSetters = new ArrayList();
8201 if (!CommonResolve (ec))
8204 bool found_any = false, found_any_setters = false;
8206 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8207 if (ilist.Properties != null) {
8209 foreach (Indexers.Indexer ix in ilist.Properties) {
8210 if (ix.Setter != null)
8211 AllSetters.Add (ix.Setter);
8214 if (AllSetters.Count > 0) {
8215 found_any_setters = true;
8216 set_arguments = (ArrayList) arguments.Clone ();
8217 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
8218 set = (MethodInfo) Invocation.OverloadResolve (
8219 ec, new MethodGroupExpr (AllSetters, loc),
8220 set_arguments, false, loc);
8224 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8225 TypeManager.CSharpName (indexer_type));
8229 if (!found_any_setters) {
8230 Error (154, "indexer can not be used in this context, because " +
8231 "it lacks a `set' accessor");
8236 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8241 // Only base will allow this invocation to happen.
8243 if (set.IsAbstract && this is BaseIndexerAccess){
8244 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
8249 // Now look for the actual match in the list of indexers to set our "return" type
8251 type = TypeManager.void_type; // default value
8252 foreach (Indexers.Indexer ix in ilist.Properties){
8253 if (ix.Setter == set){
8254 type = ix.PropertyInfo.PropertyType;
8259 instance_expr.CheckMarshalByRefAccess ();
8261 eclass = ExprClass.IndexerAccess;
8265 bool prepared = false;
8266 LocalTemporary temp;
8268 public void Emit (EmitContext ec, bool leave_copy)
8270 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
8272 ec.ig.Emit (OpCodes.Dup);
8273 temp = new LocalTemporary (Type);
8279 // source is ignored, because we already have a copy of it from the
8280 // LValue resolution and we have already constructed a pre-cached
8281 // version of the arguments (ea.set_arguments);
8283 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8285 prepared = prepare_for_load;
8286 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
8291 ec.ig.Emit (OpCodes.Dup);
8292 temp = new LocalTemporary (Type);
8295 } else if (leave_copy) {
8296 temp = new LocalTemporary (Type);
8302 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
8311 public override void Emit (EmitContext ec)
8316 public override string GetSignatureForError ()
8318 // FIXME: print the argument list of the indexer
8319 return instance_expr.GetSignatureForError () + ".this[...]";
8324 /// The base operator for method names
8326 public class BaseAccess : Expression {
8327 public readonly string Identifier;
8330 public BaseAccess (string member, TypeArguments args, Location l)
8332 this.Identifier = member;
8337 public override Expression DoResolve (EmitContext ec)
8339 Expression c = CommonResolve (ec);
8345 // MethodGroups use this opportunity to flag an error on lacking ()
8347 if (!(c is MethodGroupExpr))
8348 return c.Resolve (ec);
8352 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8354 Expression c = CommonResolve (ec);
8360 // MethodGroups use this opportunity to flag an error on lacking ()
8362 if (! (c is MethodGroupExpr))
8363 return c.DoResolveLValue (ec, right_side);
8368 Expression CommonResolve (EmitContext ec)
8370 Expression member_lookup;
8371 Type current_type = ec.ContainerType;
8372 Type base_type = current_type.BaseType;
8375 Error (1511, "Keyword `base' is not available in a static method");
8379 if (ec.IsFieldInitializer){
8380 Error (1512, "Keyword `base' is not available in the current context");
8384 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8385 AllMemberTypes, AllBindingFlags, loc);
8386 if (member_lookup == null) {
8387 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8394 left = new TypeExpression (base_type, loc);
8396 left = ec.GetThis (loc);
8398 MemberExpr me = (MemberExpr) member_lookup;
8400 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8402 if (e is PropertyExpr) {
8403 PropertyExpr pe = (PropertyExpr) e;
8408 MethodGroupExpr mg = e as MethodGroupExpr;
8414 return mg.ResolveGeneric (ec, args);
8416 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8424 public override void Emit (EmitContext ec)
8426 throw new Exception ("Should never be called");
8431 /// The base indexer operator
8433 public class BaseIndexerAccess : IndexerAccess {
8434 public BaseIndexerAccess (ArrayList args, Location loc)
8435 : base (null, true, loc)
8437 arguments = new ArrayList ();
8438 foreach (Expression tmp in args)
8439 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8442 protected override bool CommonResolve (EmitContext ec)
8444 instance_expr = ec.GetThis (loc);
8446 current_type = ec.ContainerType.BaseType;
8447 indexer_type = current_type;
8449 foreach (Argument a in arguments){
8450 if (!a.Resolve (ec, loc))
8459 /// This class exists solely to pass the Type around and to be a dummy
8460 /// that can be passed to the conversion functions (this is used by
8461 /// foreach implementation to typecast the object return value from
8462 /// get_Current into the proper type. All code has been generated and
8463 /// we only care about the side effect conversions to be performed
8465 /// This is also now used as a placeholder where a no-action expression
8466 /// is needed (the `New' class).
8468 public class EmptyExpression : Expression {
8469 public static readonly EmptyExpression Null = new EmptyExpression ();
8471 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8472 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8473 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8475 static EmptyExpression temp = new EmptyExpression ();
8476 public static EmptyExpression Grab ()
8479 throw new InternalErrorException ("Nested Grab");
8480 EmptyExpression retval = temp;
8485 public static void Release (EmptyExpression e)
8488 throw new InternalErrorException ("Already released");
8492 // TODO: should be protected
8493 public EmptyExpression ()
8495 type = TypeManager.object_type;
8496 eclass = ExprClass.Value;
8497 loc = Location.Null;
8500 public EmptyExpression (Type t)
8503 eclass = ExprClass.Value;
8504 loc = Location.Null;
8507 public override Expression DoResolve (EmitContext ec)
8512 public override void Emit (EmitContext ec)
8514 // nothing, as we only exist to not do anything.
8518 // This is just because we might want to reuse this bad boy
8519 // instead of creating gazillions of EmptyExpressions.
8520 // (CanImplicitConversion uses it)
8522 public void SetType (Type t)
8528 public class UserCast : Expression {
8532 public UserCast (MethodInfo method, Expression source, Location l)
8534 this.method = method;
8535 this.source = source;
8536 type = method.ReturnType;
8537 eclass = ExprClass.Value;
8541 public Expression Source {
8547 public override Expression DoResolve (EmitContext ec)
8550 // We are born fully resolved
8555 public override void Emit (EmitContext ec)
8557 ILGenerator ig = ec.ig;
8561 if (method is MethodInfo)
8562 ig.Emit (OpCodes.Call, (MethodInfo) method);
8564 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8570 // This class is used to "construct" the type during a typecast
8571 // operation. Since the Type.GetType class in .NET can parse
8572 // the type specification, we just use this to construct the type
8573 // one bit at a time.
8575 public class ComposedCast : TypeExpr {
8579 public ComposedCast (Expression left, string dim)
8580 : this (left, dim, left.Location)
8584 public ComposedCast (Expression left, string dim, Location l)
8591 public Expression RemoveNullable ()
8593 if (dim.EndsWith ("?")) {
8594 dim = dim.Substring (0, dim.Length - 1);
8602 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8604 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8608 Type ltype = lexpr.Type;
8609 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8610 Error_VoidInvalidInTheContext (loc);
8614 if ((dim.Length > 0) && (dim [0] == '?')) {
8615 TypeExpr nullable = new NullableType (left, loc);
8617 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8618 return nullable.ResolveAsTypeTerminal (ec, false);
8621 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc)) {
8626 type = TypeManager.GetConstructedType (ltype, dim);
8631 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8634 if (type.IsPointer && !ec.IsInUnsafeScope){
8639 if (type.IsArray && (type.GetElementType () == TypeManager.arg_iterator_type ||
8640 type.GetElementType () == TypeManager.typed_reference_type)) {
8641 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (type.GetElementType ()));
8645 eclass = ExprClass.Type;
8649 public override string Name {
8650 get { return left + dim; }
8653 public override string FullName {
8654 get { return type.FullName; }
8657 public override string GetSignatureForError ()
8659 return left.GetSignatureForError () + dim;
8663 public class FixedBufferPtr : Expression {
8666 public FixedBufferPtr (Expression array, Type array_type, Location l)
8671 type = TypeManager.GetPointerType (array_type);
8672 eclass = ExprClass.Value;
8675 public override void Emit(EmitContext ec)
8680 public override Expression DoResolve (EmitContext ec)
8683 // We are born fully resolved
8691 // This class is used to represent the address of an array, used
8692 // only by the Fixed statement, this generates "&a [0]" construct
8693 // for fixed (char *pa = a)
8695 public class ArrayPtr : FixedBufferPtr {
8698 public ArrayPtr (Expression array, Type array_type, Location l):
8699 base (array, array_type, l)
8701 this.array_type = array_type;
8704 public override void Emit (EmitContext ec)
8708 ILGenerator ig = ec.ig;
8709 IntLiteral.EmitInt (ig, 0);
8710 ig.Emit (OpCodes.Ldelema, array_type);
8715 // Used by the fixed statement
8717 public class StringPtr : Expression {
8720 public StringPtr (LocalBuilder b, Location l)
8723 eclass = ExprClass.Value;
8724 type = TypeManager.char_ptr_type;
8728 public override Expression DoResolve (EmitContext ec)
8730 // This should never be invoked, we are born in fully
8731 // initialized state.
8736 public override void Emit (EmitContext ec)
8738 ILGenerator ig = ec.ig;
8740 ig.Emit (OpCodes.Ldloc, b);
8741 ig.Emit (OpCodes.Conv_I);
8742 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8743 ig.Emit (OpCodes.Add);
8748 // Implements the `stackalloc' keyword
8750 public class StackAlloc : Expression {
8755 public StackAlloc (Expression type, Expression count, Location l)
8762 public override Expression DoResolve (EmitContext ec)
8764 count = count.Resolve (ec);
8768 if (count.Type != TypeManager.int32_type){
8769 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8774 Constant c = count as Constant;
8775 if (c != null && c.IsNegative) {
8776 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8780 if (ec.InCatch || ec.InFinally) {
8781 Error (255, "Cannot use stackalloc in finally or catch");
8785 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8791 if (!TypeManager.VerifyUnManaged (otype, loc))
8794 type = TypeManager.GetPointerType (otype);
8795 eclass = ExprClass.Value;
8800 public override void Emit (EmitContext ec)
8802 int size = GetTypeSize (otype);
8803 ILGenerator ig = ec.ig;
8806 ig.Emit (OpCodes.Sizeof, otype);
8808 IntConstant.EmitInt (ig, size);
8810 ig.Emit (OpCodes.Mul);
8811 ig.Emit (OpCodes.Localloc);