2 // expression.cs: Expression representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@seznam.cz)
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2003, 2004 Novell, Inc.
13 namespace Mono.CSharp {
15 using System.Collections;
16 using System.Reflection;
17 using System.Reflection.Emit;
21 /// This is just a helper class, it is generated by Unary, UnaryMutator
22 /// when an overloaded method has been found. It just emits the code for a
25 public class StaticCallExpr : ExpressionStatement {
29 public StaticCallExpr (MethodInfo m, ArrayList a, Location l)
35 eclass = ExprClass.Value;
39 public override Expression DoResolve (EmitContext ec)
42 // We are born fully resolved
47 public override void Emit (EmitContext ec)
50 Invocation.EmitArguments (ec, mi, args, false, null);
52 ec.ig.Emit (OpCodes.Call, mi);
56 static public StaticCallExpr MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
57 Expression e, Location loc)
62 args = new ArrayList (1);
63 Argument a = new Argument (e, Argument.AType.Expression);
65 // We need to resolve the arguments before sending them in !
66 if (!a.Resolve (ec, loc))
70 method = Invocation.OverloadResolve (
71 ec, (MethodGroupExpr) mg, args, false, loc);
76 return new StaticCallExpr ((MethodInfo) method, args, loc);
79 public override void EmitStatement (EmitContext ec)
82 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
83 ec.ig.Emit (OpCodes.Pop);
86 public MethodInfo Method {
91 public class ParenthesizedExpression : Expression
93 public Expression Expr;
95 public ParenthesizedExpression (Expression expr)
100 public override Expression DoResolve (EmitContext ec)
102 Expr = Expr.Resolve (ec);
106 public override void Emit (EmitContext ec)
108 throw new Exception ("Should not happen");
111 public override Location Location
114 return Expr.Location;
120 /// Unary expressions.
124 /// Unary implements unary expressions. It derives from
125 /// ExpressionStatement becuase the pre/post increment/decrement
126 /// operators can be used in a statement context.
128 public class Unary : Expression {
129 public enum Operator : byte {
130 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
131 Indirection, AddressOf, TOP
134 public Operator Oper;
135 public Expression Expr;
137 public Unary (Operator op, Expression expr, Location loc)
145 /// Returns a stringified representation of the Operator
147 static public string OperName (Operator oper)
150 case Operator.UnaryPlus:
152 case Operator.UnaryNegation:
154 case Operator.LogicalNot:
156 case Operator.OnesComplement:
158 case Operator.AddressOf:
160 case Operator.Indirection:
164 return oper.ToString ();
167 public static readonly string [] oper_names;
171 oper_names = new string [(int)Operator.TOP];
173 oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus";
174 oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation";
175 oper_names [(int) Operator.LogicalNot] = "op_LogicalNot";
176 oper_names [(int) Operator.OnesComplement] = "op_OnesComplement";
177 oper_names [(int) Operator.Indirection] = "op_Indirection";
178 oper_names [(int) Operator.AddressOf] = "op_AddressOf";
181 public static void Error_OperatorCannotBeApplied (Location loc, string oper, Type t)
183 Error_OperatorCannotBeApplied (loc, oper, TypeManager.CSharpName (t));
186 public static void Error_OperatorCannotBeApplied (Location loc, string oper, string type)
188 Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'",
192 void Error23 (Type t)
194 Error_OperatorCannotBeApplied (loc, OperName (Oper), t);
198 /// The result has been already resolved:
200 /// FIXME: a minus constant -128 sbyte cant be turned into a
203 static Expression TryReduceNegative (Constant expr)
207 if (expr is IntConstant)
208 e = new IntConstant (-((IntConstant) expr).Value, expr.Location);
209 else if (expr is UIntConstant){
210 uint value = ((UIntConstant) expr).Value;
212 if (value < 2147483649)
213 return new IntConstant (-(int)value, expr.Location);
215 e = new LongConstant (-value, expr.Location);
217 else if (expr is LongConstant)
218 e = new LongConstant (-((LongConstant) expr).Value, expr.Location);
219 else if (expr is ULongConstant){
220 ulong value = ((ULongConstant) expr).Value;
222 if (value < 9223372036854775809)
223 return new LongConstant(-(long)value, expr.Location);
225 else if (expr is FloatConstant)
226 e = new FloatConstant (-((FloatConstant) expr).Value, expr.Location);
227 else if (expr is DoubleConstant)
228 e = new DoubleConstant (-((DoubleConstant) expr).Value, expr.Location);
229 else if (expr is DecimalConstant)
230 e = new DecimalConstant (-((DecimalConstant) expr).Value, expr.Location);
231 else if (expr is ShortConstant)
232 e = new IntConstant (-((ShortConstant) expr).Value, expr.Location);
233 else if (expr is UShortConstant)
234 e = new IntConstant (-((UShortConstant) expr).Value, expr.Location);
235 else if (expr is SByteConstant)
236 e = new IntConstant (-((SByteConstant) expr).Value, expr.Location);
237 else if (expr is ByteConstant)
238 e = new IntConstant (-((ByteConstant) expr).Value, expr.Location);
243 // This routine will attempt to simplify the unary expression when the
244 // argument is a constant. The result is returned in `result' and the
245 // function returns true or false depending on whether a reduction
246 // was performed or not
248 bool Reduce (EmitContext ec, Constant e, out Expression result)
250 Type expr_type = e.Type;
253 case Operator.UnaryPlus:
254 if (expr_type == TypeManager.bool_type){
263 case Operator.UnaryNegation:
264 result = TryReduceNegative (e);
265 return result != null;
267 case Operator.LogicalNot:
268 if (expr_type != TypeManager.bool_type) {
274 BoolConstant b = (BoolConstant) e;
275 result = new BoolConstant (!(b.Value), b.Location);
278 case Operator.OnesComplement:
279 if (!((expr_type == TypeManager.int32_type) ||
280 (expr_type == TypeManager.uint32_type) ||
281 (expr_type == TypeManager.int64_type) ||
282 (expr_type == TypeManager.uint64_type) ||
283 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
286 if (Convert.ImplicitConversionExists (ec, e, TypeManager.int32_type)){
287 result = new Cast (new TypeExpression (TypeManager.int32_type, loc), e, loc);
288 result = result.Resolve (ec);
289 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.uint32_type)){
290 result = new Cast (new TypeExpression (TypeManager.uint32_type, loc), e, loc);
291 result = result.Resolve (ec);
292 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.int64_type)){
293 result = new Cast (new TypeExpression (TypeManager.int64_type, loc), e, loc);
294 result = result.Resolve (ec);
295 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.uint64_type)){
296 result = new Cast (new TypeExpression (TypeManager.uint64_type, loc), e, loc);
297 result = result.Resolve (ec);
300 if (result == null || !(result is Constant)){
306 expr_type = result.Type;
307 e = (Constant) result;
310 if (e is EnumConstant){
311 EnumConstant enum_constant = (EnumConstant) e;
314 if (Reduce (ec, enum_constant.Child, out reduced)){
315 result = new EnumConstant ((Constant) reduced, enum_constant.Type);
323 if (expr_type == TypeManager.int32_type){
324 result = new IntConstant (~ ((IntConstant) e).Value, e.Location);
325 } else if (expr_type == TypeManager.uint32_type){
326 result = new UIntConstant (~ ((UIntConstant) e).Value, e.Location);
327 } else if (expr_type == TypeManager.int64_type){
328 result = new LongConstant (~ ((LongConstant) e).Value, e.Location);
329 } else if (expr_type == TypeManager.uint64_type){
330 result = new ULongConstant (~ ((ULongConstant) e).Value, e.Location);
338 case Operator.AddressOf:
342 case Operator.Indirection:
346 throw new Exception ("Can not constant fold: " + Oper.ToString());
349 Expression ResolveOperator (EmitContext ec)
352 // Step 1: Default operations on CLI native types.
355 // Attempt to use a constant folding operation.
356 if (Expr is Constant){
359 if (Reduce (ec, (Constant) Expr, out result))
364 // Step 2: Perform Operator Overload location
366 Type expr_type = Expr.Type;
370 op_name = oper_names [(int) Oper];
372 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
375 Expression e = StaticCallExpr.MakeSimpleCall (
376 ec, (MethodGroupExpr) mg, Expr, loc);
386 // Only perform numeric promotions on:
389 if (expr_type == null)
393 case Operator.LogicalNot:
394 if (expr_type != TypeManager.bool_type) {
395 Expr = ResolveBoolean (ec, Expr, loc);
402 type = TypeManager.bool_type;
405 case Operator.OnesComplement:
406 if (!((expr_type == TypeManager.int32_type) ||
407 (expr_type == TypeManager.uint32_type) ||
408 (expr_type == TypeManager.int64_type) ||
409 (expr_type == TypeManager.uint64_type) ||
410 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
413 e = Convert.ImplicitConversion (ec, Expr, TypeManager.int32_type, loc);
416 e = Convert.ImplicitConversion (ec, Expr, TypeManager.uint32_type, loc);
419 e = Convert.ImplicitConversion (ec, Expr, TypeManager.int64_type, loc);
422 e = Convert.ImplicitConversion (ec, Expr, TypeManager.uint64_type, loc);
435 case Operator.AddressOf:
441 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){
445 IVariable variable = Expr as IVariable;
446 bool is_fixed = variable != null && variable.VerifyFixed ();
448 if (!ec.InFixedInitializer && !is_fixed) {
449 Error (212, "You can only take the address of unfixed expression inside " +
450 "of a fixed statement initializer");
454 if (ec.InFixedInitializer && is_fixed) {
455 Error (213, "You cannot use the fixed statement to take the address of an already fixed expression");
459 LocalVariableReference lr = Expr as LocalVariableReference;
461 if (lr.local_info.IsCaptured){
462 AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc);
465 lr.local_info.AddressTaken = true;
466 lr.local_info.Used = true;
469 ParameterReference pr = Expr as ParameterReference;
470 if ((pr != null) && pr.Parameter.IsCaptured) {
471 AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc);
475 // According to the specs, a variable is considered definitely assigned if you take
477 if ((variable != null) && (variable.VariableInfo != null)){
478 variable.VariableInfo.SetAssigned (ec);
481 type = TypeManager.GetPointerType (Expr.Type);
484 case Operator.Indirection:
490 if (!expr_type.IsPointer){
491 Error (193, "The * or -> operator must be applied to a pointer");
496 // We create an Indirection expression, because
497 // it can implement the IMemoryLocation.
499 return new Indirection (Expr, loc);
501 case Operator.UnaryPlus:
503 // A plus in front of something is just a no-op, so return the child.
507 case Operator.UnaryNegation:
509 // Deals with -literals
510 // int operator- (int x)
511 // long operator- (long x)
512 // float operator- (float f)
513 // double operator- (double d)
514 // decimal operator- (decimal d)
516 Expression expr = null;
519 // transform - - expr into expr
522 Unary unary = (Unary) Expr;
524 if (unary.Oper == Operator.UnaryNegation)
529 // perform numeric promotions to int,
533 // The following is inneficient, because we call
534 // ImplicitConversion too many times.
536 // It is also not clear if we should convert to Float
537 // or Double initially.
539 if (expr_type == TypeManager.uint32_type){
541 // FIXME: handle exception to this rule that
542 // permits the int value -2147483648 (-2^31) to
543 // bt wrote as a decimal interger literal
545 type = TypeManager.int64_type;
546 Expr = Convert.ImplicitConversion (ec, Expr, type, loc);
550 if (expr_type == TypeManager.uint64_type){
552 // FIXME: Handle exception of `long value'
553 // -92233720368547758087 (-2^63) to be wrote as
554 // decimal integer literal.
560 if (expr_type == TypeManager.float_type){
565 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int32_type, loc);
572 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int64_type, loc);
579 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.double_type, loc);
590 Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" +
591 TypeManager.CSharpName (expr_type) + "'");
595 public override Expression DoResolve (EmitContext ec)
597 if (Oper == Operator.AddressOf) {
598 Expr = Expr.DoResolveLValue (ec, new EmptyExpression ());
600 if (Expr == null || Expr.eclass != ExprClass.Variable){
601 Error (211, "Cannot take the address of the given expression");
606 Expr = Expr.Resolve (ec);
612 if (TypeManager.IsNullableValueType (Expr.Type))
613 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
616 eclass = ExprClass.Value;
617 return ResolveOperator (ec);
620 public override Expression DoResolveLValue (EmitContext ec, Expression right)
622 if (Oper == Operator.Indirection)
623 return DoResolve (ec);
628 public override void Emit (EmitContext ec)
630 ILGenerator ig = ec.ig;
633 case Operator.UnaryPlus:
634 throw new Exception ("This should be caught by Resolve");
636 case Operator.UnaryNegation:
637 if (ec.CheckState && type != TypeManager.float_type && type != TypeManager.double_type) {
638 ig.Emit (OpCodes.Ldc_I4_0);
639 if (type == TypeManager.int64_type)
640 ig.Emit (OpCodes.Conv_U8);
642 ig.Emit (OpCodes.Sub_Ovf);
645 ig.Emit (OpCodes.Neg);
650 case Operator.LogicalNot:
652 ig.Emit (OpCodes.Ldc_I4_0);
653 ig.Emit (OpCodes.Ceq);
656 case Operator.OnesComplement:
658 ig.Emit (OpCodes.Not);
661 case Operator.AddressOf:
662 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
666 throw new Exception ("This should not happen: Operator = "
671 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
673 if (Oper == Operator.LogicalNot)
674 Expr.EmitBranchable (ec, target, !onTrue);
676 base.EmitBranchable (ec, target, onTrue);
679 public override string ToString ()
681 return "Unary (" + Oper + ", " + Expr + ")";
687 // Unary operators are turned into Indirection expressions
688 // after semantic analysis (this is so we can take the address
689 // of an indirection).
691 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
693 LocalTemporary temporary;
696 public Indirection (Expression expr, Location l)
699 type = TypeManager.HasElementType (expr.Type) ? TypeManager.GetElementType (expr.Type) : expr.Type;
700 eclass = ExprClass.Variable;
704 public override void Emit (EmitContext ec)
709 LoadFromPtr (ec.ig, Type);
712 public void Emit (EmitContext ec, bool leave_copy)
716 ec.ig.Emit (OpCodes.Dup);
717 temporary = new LocalTemporary (expr.Type);
718 temporary.Store (ec);
722 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
724 prepared = prepare_for_load;
728 if (prepare_for_load)
729 ec.ig.Emit (OpCodes.Dup);
733 ec.ig.Emit (OpCodes.Dup);
734 temporary = new LocalTemporary (expr.Type);
735 temporary.Store (ec);
738 StoreFromPtr (ec.ig, type);
740 if (temporary != null) {
742 temporary.Release (ec);
746 public void AddressOf (EmitContext ec, AddressOp Mode)
751 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
753 return DoResolve (ec);
756 public override Expression DoResolve (EmitContext ec)
759 // Born fully resolved
764 public override string ToString ()
766 return "*(" + expr + ")";
769 #region IVariable Members
771 public VariableInfo VariableInfo {
775 public bool VerifyFixed ()
777 // A pointer-indirection is always fixed.
785 /// Unary Mutator expressions (pre and post ++ and --)
789 /// UnaryMutator implements ++ and -- expressions. It derives from
790 /// ExpressionStatement becuase the pre/post increment/decrement
791 /// operators can be used in a statement context.
793 /// FIXME: Idea, we could split this up in two classes, one simpler
794 /// for the common case, and one with the extra fields for more complex
795 /// classes (indexers require temporary access; overloaded require method)
798 public class UnaryMutator : ExpressionStatement {
800 public enum Mode : byte {
807 PreDecrement = IsDecrement,
808 PostIncrement = IsPost,
809 PostDecrement = IsPost | IsDecrement
813 bool is_expr = false;
814 bool recurse = false;
819 // This is expensive for the simplest case.
821 StaticCallExpr method;
823 public UnaryMutator (Mode m, Expression e, Location l)
830 static string OperName (Mode mode)
832 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
837 /// Returns whether an object of type `t' can be incremented
838 /// or decremented with add/sub (ie, basically whether we can
839 /// use pre-post incr-decr operations on it, but it is not a
840 /// System.Decimal, which we require operator overloading to catch)
842 static bool IsIncrementableNumber (Type t)
844 return (t == TypeManager.sbyte_type) ||
845 (t == TypeManager.byte_type) ||
846 (t == TypeManager.short_type) ||
847 (t == TypeManager.ushort_type) ||
848 (t == TypeManager.int32_type) ||
849 (t == TypeManager.uint32_type) ||
850 (t == TypeManager.int64_type) ||
851 (t == TypeManager.uint64_type) ||
852 (t == TypeManager.char_type) ||
853 (t.IsSubclassOf (TypeManager.enum_type)) ||
854 (t == TypeManager.float_type) ||
855 (t == TypeManager.double_type) ||
856 (t.IsPointer && t != TypeManager.void_ptr_type);
859 Expression ResolveOperator (EmitContext ec)
861 Type expr_type = expr.Type;
864 // Step 1: Perform Operator Overload location
869 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
870 op_name = "op_Increment";
872 op_name = "op_Decrement";
874 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
877 method = StaticCallExpr.MakeSimpleCall (
878 ec, (MethodGroupExpr) mg, expr, loc);
881 } else if (!IsIncrementableNumber (expr_type)) {
882 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
883 TypeManager.CSharpName (expr_type) + "'");
888 // The operand of the prefix/postfix increment decrement operators
889 // should be an expression that is classified as a variable,
890 // a property access or an indexer access
893 if (expr.eclass == ExprClass.Variable){
894 LocalVariableReference var = expr as LocalVariableReference;
895 if ((var != null) && var.IsReadOnly) {
896 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
899 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
900 expr = expr.ResolveLValue (ec, this, Location);
904 expr.Error_UnexpectedKind (ec.DeclContainer, "variable, indexer or property access", loc);
911 public override Expression DoResolve (EmitContext ec)
913 expr = expr.Resolve (ec);
918 eclass = ExprClass.Value;
921 if (TypeManager.IsNullableValueType (expr.Type))
922 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
925 return ResolveOperator (ec);
928 static int PtrTypeSize (Type t)
930 return GetTypeSize (TypeManager.GetElementType (t));
934 // Loads the proper "1" into the stack based on the type, then it emits the
935 // opcode for the operation requested
937 void LoadOneAndEmitOp (EmitContext ec, Type t)
940 // Measure if getting the typecode and using that is more/less efficient
941 // that comparing types. t.GetTypeCode() is an internal call.
943 ILGenerator ig = ec.ig;
945 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
946 LongConstant.EmitLong (ig, 1);
947 else if (t == TypeManager.double_type)
948 ig.Emit (OpCodes.Ldc_R8, 1.0);
949 else if (t == TypeManager.float_type)
950 ig.Emit (OpCodes.Ldc_R4, 1.0F);
951 else if (t.IsPointer){
952 int n = PtrTypeSize (t);
955 ig.Emit (OpCodes.Sizeof, t);
957 IntConstant.EmitInt (ig, n);
959 ig.Emit (OpCodes.Ldc_I4_1);
962 // Now emit the operation
965 if (t == TypeManager.int32_type ||
966 t == TypeManager.int64_type){
967 if ((mode & Mode.IsDecrement) != 0)
968 ig.Emit (OpCodes.Sub_Ovf);
970 ig.Emit (OpCodes.Add_Ovf);
971 } else if (t == TypeManager.uint32_type ||
972 t == TypeManager.uint64_type){
973 if ((mode & Mode.IsDecrement) != 0)
974 ig.Emit (OpCodes.Sub_Ovf_Un);
976 ig.Emit (OpCodes.Add_Ovf_Un);
978 if ((mode & Mode.IsDecrement) != 0)
979 ig.Emit (OpCodes.Sub_Ovf);
981 ig.Emit (OpCodes.Add_Ovf);
984 if ((mode & Mode.IsDecrement) != 0)
985 ig.Emit (OpCodes.Sub);
987 ig.Emit (OpCodes.Add);
990 if (t == TypeManager.sbyte_type){
992 ig.Emit (OpCodes.Conv_Ovf_I1);
994 ig.Emit (OpCodes.Conv_I1);
995 } else if (t == TypeManager.byte_type){
997 ig.Emit (OpCodes.Conv_Ovf_U1);
999 ig.Emit (OpCodes.Conv_U1);
1000 } else if (t == TypeManager.short_type){
1002 ig.Emit (OpCodes.Conv_Ovf_I2);
1004 ig.Emit (OpCodes.Conv_I2);
1005 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
1007 ig.Emit (OpCodes.Conv_Ovf_U2);
1009 ig.Emit (OpCodes.Conv_U2);
1014 void EmitCode (EmitContext ec, bool is_expr)
1017 this.is_expr = is_expr;
1018 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1021 public override void Emit (EmitContext ec)
1024 // We use recurse to allow ourselfs to be the source
1025 // of an assignment. This little hack prevents us from
1026 // having to allocate another expression
1029 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1031 LoadOneAndEmitOp (ec, expr.Type);
1033 ec.ig.Emit (OpCodes.Call, method.Method);
1038 EmitCode (ec, true);
1041 public override void EmitStatement (EmitContext ec)
1043 EmitCode (ec, false);
1048 /// Base class for the `Is' and `As' classes.
1052 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1055 public abstract class Probe : Expression {
1056 public Expression ProbeType;
1057 protected Expression expr;
1058 protected TypeExpr probe_type_expr;
1060 public Probe (Expression expr, Expression probe_type, Location l)
1062 ProbeType = probe_type;
1067 public Expression Expr {
1073 public override Expression DoResolve (EmitContext ec)
1075 probe_type_expr = ProbeType.ResolveAsTypeTerminal (ec, false);
1076 if (probe_type_expr == null)
1079 expr = expr.Resolve (ec);
1083 if (expr.Type.IsPointer) {
1084 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1092 /// Implementation of the `is' operator.
1094 public class Is : Probe {
1095 public Is (Expression expr, Expression probe_type, Location l)
1096 : base (expr, probe_type, l)
1101 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1106 public override void Emit (EmitContext ec)
1108 ILGenerator ig = ec.ig;
1113 case Action.AlwaysFalse:
1114 ig.Emit (OpCodes.Pop);
1115 IntConstant.EmitInt (ig, 0);
1117 case Action.AlwaysTrue:
1118 ig.Emit (OpCodes.Pop);
1119 IntConstant.EmitInt (ig, 1);
1121 case Action.LeaveOnStack:
1122 // the `e != null' rule.
1123 ig.Emit (OpCodes.Ldnull);
1124 ig.Emit (OpCodes.Ceq);
1125 ig.Emit (OpCodes.Ldc_I4_0);
1126 ig.Emit (OpCodes.Ceq);
1129 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1130 ig.Emit (OpCodes.Ldnull);
1131 ig.Emit (OpCodes.Cgt_Un);
1134 throw new Exception ("never reached");
1137 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1139 ILGenerator ig = ec.ig;
1142 case Action.AlwaysFalse:
1144 ig.Emit (OpCodes.Br, target);
1147 case Action.AlwaysTrue:
1149 ig.Emit (OpCodes.Br, target);
1152 case Action.LeaveOnStack:
1153 // the `e != null' rule.
1155 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1159 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1160 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1163 throw new Exception ("never reached");
1166 public override Expression DoResolve (EmitContext ec)
1168 Expression e = base.DoResolve (ec);
1170 if ((e == null) || (expr == null))
1173 Type etype = expr.Type;
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);
1185 if (etype.IsValueType)
1186 action = Action.AlwaysTrue;
1188 action = Action.LeaveOnStack;
1190 Constant c = e as Constant;
1191 if (c != null && c.GetValue () == null) {
1192 action = Action.AlwaysFalse;
1193 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
1194 TypeManager.CSharpName (probe_type));
1196 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
1197 TypeManager.CSharpName (probe_type));
1202 if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1203 if (TypeManager.IsGenericParameter (etype))
1204 expr = new BoxedCast (expr, etype);
1207 // Second case: explicit reference convresion
1209 if (expr is NullLiteral)
1210 action = Action.AlwaysFalse;
1212 action = Action.Probe;
1213 } else if (TypeManager.ContainsGenericParameters (etype) ||
1214 TypeManager.ContainsGenericParameters (probe_type)) {
1215 expr = new BoxedCast (expr, etype);
1216 action = Action.Probe;
1218 action = Action.AlwaysFalse;
1219 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1220 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1228 /// Implementation of the `as' operator.
1230 public class As : Probe {
1231 public As (Expression expr, Expression probe_type, Location l)
1232 : base (expr, probe_type, l)
1236 bool do_isinst = false;
1237 Expression resolved_type;
1239 public override void Emit (EmitContext ec)
1241 ILGenerator ig = ec.ig;
1246 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1249 static void Error_CannotConvertType (Type source, Type target, Location loc)
1251 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1252 TypeManager.CSharpName (source),
1253 TypeManager.CSharpName (target));
1256 public override Expression DoResolve (EmitContext ec)
1258 if (resolved_type == null) {
1259 resolved_type = base.DoResolve (ec);
1261 if (resolved_type == null)
1265 type = probe_type_expr.Type;
1266 eclass = ExprClass.Value;
1267 Type etype = expr.Type;
1269 if (type.IsValueType) {
1270 Report.Error (77, loc, "The as operator must be used with a reference type (`" +
1271 TypeManager.CSharpName (type) + "' is a value type)");
1278 // If the type is a type parameter, ensure
1279 // that it is constrained by a class
1281 TypeParameterExpr tpe = probe_type_expr as TypeParameterExpr;
1283 Constraints constraints = tpe.TypeParameter.Constraints;
1286 if (constraints == null)
1289 if (!constraints.HasClassConstraint)
1290 if ((constraints.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == 0)
1294 Report.Error (413, loc,
1295 "The as operator requires that the `{0}' type parameter be constrained by a class",
1296 probe_type_expr.GetSignatureForError ());
1302 Expression e = Convert.ImplicitConversion (ec, expr, type, loc);
1309 if (Convert.ExplicitReferenceConversionExists (etype, type)){
1310 if (TypeManager.IsGenericParameter (etype))
1311 expr = new BoxedCast (expr, etype);
1317 if (TypeManager.ContainsGenericParameters (etype) ||
1318 TypeManager.ContainsGenericParameters (type)) {
1319 expr = new BoxedCast (expr, etype);
1324 Error_CannotConvertType (etype, type, loc);
1328 public override bool GetAttributableValue (Type valueType, out object value)
1330 return expr.GetAttributableValue (valueType, out value);
1335 /// This represents a typecast in the source language.
1337 /// FIXME: Cast expressions have an unusual set of parsing
1338 /// rules, we need to figure those out.
1340 public class Cast : Expression {
1341 Expression target_type;
1344 public Cast (Expression cast_type, Expression expr)
1345 : this (cast_type, expr, cast_type.Location)
1349 public Cast (Expression cast_type, Expression expr, Location loc)
1351 this.target_type = cast_type;
1355 if (target_type == TypeManager.system_void_expr)
1356 Error_VoidInvalidInTheContext (loc);
1359 public Expression TargetType {
1360 get { return target_type; }
1363 public Expression Expr {
1364 get { return expr; }
1365 set { expr = value; }
1368 public override Expression DoResolve (EmitContext ec)
1370 expr = expr.Resolve (ec);
1374 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1380 if (type.IsAbstract && type.IsSealed) {
1381 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1385 eclass = ExprClass.Value;
1387 Constant c = expr as Constant;
1390 c = c.TryReduce (ec, type, loc);
1394 catch (OverflowException) {
1399 if (type.IsPointer && !ec.InUnsafe) {
1403 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1407 public override void Emit (EmitContext ec)
1409 throw new Exception ("Should not happen");
1414 /// Binary operators
1416 public class Binary : Expression {
1417 public enum Operator : byte {
1418 Multiply, Division, Modulus,
1419 Addition, Subtraction,
1420 LeftShift, RightShift,
1421 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1422 Equality, Inequality,
1432 Expression left, right;
1434 // This must be kept in sync with Operator!!!
1435 public static readonly string [] oper_names;
1439 oper_names = new string [(int) Operator.TOP];
1441 oper_names [(int) Operator.Multiply] = "op_Multiply";
1442 oper_names [(int) Operator.Division] = "op_Division";
1443 oper_names [(int) Operator.Modulus] = "op_Modulus";
1444 oper_names [(int) Operator.Addition] = "op_Addition";
1445 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1446 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1447 oper_names [(int) Operator.RightShift] = "op_RightShift";
1448 oper_names [(int) Operator.LessThan] = "op_LessThan";
1449 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1450 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1451 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1452 oper_names [(int) Operator.Equality] = "op_Equality";
1453 oper_names [(int) Operator.Inequality] = "op_Inequality";
1454 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1455 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1456 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1457 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1458 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1461 public Binary (Operator oper, Expression left, Expression right)
1466 this.loc = left.Location;
1469 public Operator Oper {
1478 public Expression Left {
1487 public Expression Right {
1498 /// Returns a stringified representation of the Operator
1500 public static string OperName (Operator oper)
1503 case Operator.Multiply:
1505 case Operator.Division:
1507 case Operator.Modulus:
1509 case Operator.Addition:
1511 case Operator.Subtraction:
1513 case Operator.LeftShift:
1515 case Operator.RightShift:
1517 case Operator.LessThan:
1519 case Operator.GreaterThan:
1521 case Operator.LessThanOrEqual:
1523 case Operator.GreaterThanOrEqual:
1525 case Operator.Equality:
1527 case Operator.Inequality:
1529 case Operator.BitwiseAnd:
1531 case Operator.BitwiseOr:
1533 case Operator.ExclusiveOr:
1535 case Operator.LogicalOr:
1537 case Operator.LogicalAnd:
1541 return oper.ToString ();
1544 public override string ToString ()
1546 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1547 right.ToString () + ")";
1550 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1552 if (expr.Type == target_type)
1555 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1558 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1561 34, loc, "Operator `" + OperName (oper)
1562 + "' is ambiguous on operands of type `"
1563 + TypeManager.CSharpName (l) + "' "
1564 + "and `" + TypeManager.CSharpName (r)
1568 bool IsConvertible (EmitContext ec, Expression le, Expression re, Type t)
1570 return Convert.ImplicitConversionExists (ec, le, t) && Convert.ImplicitConversionExists (ec, re, t);
1573 bool VerifyApplicable_Predefined (EmitContext ec, Type t)
1575 if (!IsConvertible (ec, left, right, t))
1577 left = ForceConversion (ec, left, t);
1578 right = ForceConversion (ec, right, t);
1583 bool IsApplicable_String (EmitContext ec, Expression le, Expression re, Operator oper)
1585 bool l = Convert.ImplicitConversionExists (ec, le, TypeManager.string_type);
1586 bool r = Convert.ImplicitConversionExists (ec, re, TypeManager.string_type);
1588 if (oper == Operator.Equality || oper == Operator.Inequality)
1590 if (oper == Operator.Addition)
1595 bool OverloadResolve_PredefinedString (EmitContext ec, Operator oper)
1597 if (!IsApplicable_String (ec, left, right, oper))
1599 Type t = TypeManager.string_type;
1600 if (Convert.ImplicitConversionExists (ec, left, t))
1601 left = ForceConversion (ec, left, t);
1602 if (Convert.ImplicitConversionExists (ec, right, t))
1603 right = ForceConversion (ec, right, t);
1608 bool OverloadResolve_PredefinedIntegral (EmitContext ec)
1610 return VerifyApplicable_Predefined (ec, TypeManager.int32_type) ||
1611 VerifyApplicable_Predefined (ec, TypeManager.uint32_type) ||
1612 VerifyApplicable_Predefined (ec, TypeManager.int64_type) ||
1613 VerifyApplicable_Predefined (ec, TypeManager.uint64_type) ||
1617 bool OverloadResolve_PredefinedFloating (EmitContext ec)
1619 return VerifyApplicable_Predefined (ec, TypeManager.float_type) ||
1620 VerifyApplicable_Predefined (ec, TypeManager.double_type) ||
1624 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1626 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1629 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1631 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1635 void Error_OperatorCannotBeApplied ()
1637 Error_OperatorCannotBeApplied (Location, OperName (oper), TypeManager.CSharpName (left.Type),
1638 TypeManager.CSharpName(right.Type));
1641 static bool is_unsigned (Type t)
1643 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1644 t == TypeManager.short_type || t == TypeManager.byte_type);
1647 Expression Make32or64 (EmitContext ec, Expression e)
1651 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1652 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1654 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1657 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1660 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1663 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1669 Expression CheckShiftArguments (EmitContext ec)
1671 Expression new_left = Make32or64 (ec, left);
1672 Expression new_right = ForceConversion (ec, right, TypeManager.int32_type);
1673 if (new_left == null || new_right == null) {
1674 Error_OperatorCannotBeApplied ();
1677 type = new_left.Type;
1678 int shiftmask = (type == TypeManager.int32_type || type == TypeManager.uint32_type) ? 31 : 63;
1680 right = new Binary (Binary.Operator.BitwiseAnd, new_right, new IntConstant (shiftmask, loc)).DoResolve (ec);
1685 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1686 // i.e., not invoke op_Equality.
1688 static bool EqualsNullIsReferenceEquals (Type t)
1690 return t == TypeManager.object_type || t == TypeManager.string_type ||
1691 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1694 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1696 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1697 "Possible unintended reference comparison; to get a value comparison, " +
1698 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1701 Expression ResolveOperator (EmitContext ec)
1704 Type r = right.Type;
1706 if (oper == Operator.Equality || oper == Operator.Inequality){
1707 if (TypeManager.IsGenericParameter (l) && (right is NullLiteral)) {
1708 if (l.BaseType == TypeManager.value_type) {
1709 Error_OperatorCannotBeApplied ();
1713 left = new BoxedCast (left, TypeManager.object_type);
1714 Type = TypeManager.bool_type;
1718 if (TypeManager.IsGenericParameter (r) && (left is NullLiteral)) {
1719 if (r.BaseType == TypeManager.value_type) {
1720 Error_OperatorCannotBeApplied ();
1724 right = new BoxedCast (right, TypeManager.object_type);
1725 Type = TypeManager.bool_type;
1730 // Optimize out call to op_Equality in a few cases.
1732 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1733 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1734 Type = TypeManager.bool_type;
1739 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1740 Type = TypeManager.bool_type;
1746 // Do not perform operator overload resolution when both sides are
1749 Expression left_operators = null, right_operators = null;
1750 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))) {
1752 // Step 1: Perform Operator Overload location
1754 string op = oper_names [(int) oper];
1756 MethodGroupExpr union;
1757 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc);
1759 right_operators = MemberLookup (
1760 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc);
1761 union = Invocation.MakeUnionSet (left_operators, right_operators, loc);
1763 union = (MethodGroupExpr) left_operators;
1765 if (union != null) {
1766 ArrayList args = new ArrayList (2);
1767 args.Add (new Argument (left, Argument.AType.Expression));
1768 args.Add (new Argument (right, Argument.AType.Expression));
1770 MethodBase method = Invocation.OverloadResolve (ec, union, args, true, Location.Null);
1772 if (method != null) {
1773 MethodInfo mi = (MethodInfo) method;
1774 return new BinaryMethod (mi.ReturnType, method, args);
1780 // Step 0: String concatenation (because overloading will get this wrong)
1782 if (oper == Operator.Addition){
1784 // If any of the arguments is a string, cast to string
1787 // Simple constant folding
1788 if (left is StringConstant && right is StringConstant)
1789 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1791 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1793 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1794 Error_OperatorCannotBeApplied ();
1798 // try to fold it in on the left
1799 if (left is StringConcat) {
1802 // We have to test here for not-null, since we can be doubly-resolved
1803 // take care of not appending twice
1806 type = TypeManager.string_type;
1807 ((StringConcat) left).Append (ec, right);
1808 return left.Resolve (ec);
1814 // Otherwise, start a new concat expression
1815 return new StringConcat (ec, loc, left, right).Resolve (ec);
1819 // Transform a + ( - b) into a - b
1821 if (right is Unary){
1822 Unary right_unary = (Unary) right;
1824 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1825 oper = Operator.Subtraction;
1826 right = right_unary.Expr;
1832 if (oper == Operator.Equality || oper == Operator.Inequality){
1833 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1834 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1835 Error_OperatorCannotBeApplied ();
1839 type = TypeManager.bool_type;
1843 if (l.IsPointer || r.IsPointer) {
1844 if (l.IsPointer && r.IsPointer) {
1845 type = TypeManager.bool_type;
1849 if (l.IsPointer && r == TypeManager.null_type) {
1850 right = new EmptyCast (NullPointer.Null, l);
1851 type = TypeManager.bool_type;
1855 if (r.IsPointer && l == TypeManager.null_type) {
1856 left = new EmptyCast (NullPointer.Null, r);
1857 type = TypeManager.bool_type;
1863 if (l.IsGenericParameter && r.IsGenericParameter) {
1864 GenericConstraints l_gc, r_gc;
1866 l_gc = TypeManager.GetTypeParameterConstraints (l);
1867 r_gc = TypeManager.GetTypeParameterConstraints (r);
1869 if ((l_gc == null) || (r_gc == null) ||
1870 !(l_gc.HasReferenceTypeConstraint || l_gc.HasClassConstraint) ||
1871 !(r_gc.HasReferenceTypeConstraint || r_gc.HasClassConstraint)) {
1872 Error_OperatorCannotBeApplied ();
1880 // operator != (object a, object b)
1881 // operator == (object a, object b)
1883 // For this to be used, both arguments have to be reference-types.
1884 // Read the rationale on the spec (14.9.6)
1886 if (!(l.IsValueType || r.IsValueType)){
1887 type = TypeManager.bool_type;
1893 // Also, a standard conversion must exist from either one
1895 bool left_to_right =
1896 Convert.ImplicitStandardConversionExists (left, r);
1897 bool right_to_left = !left_to_right &&
1898 Convert.ImplicitStandardConversionExists (right, l);
1900 if (!left_to_right && !right_to_left) {
1901 Error_OperatorCannotBeApplied ();
1905 if (left_to_right && left_operators != null &&
1906 RootContext.WarningLevel >= 2) {
1907 ArrayList args = new ArrayList (2);
1908 args.Add (new Argument (left, Argument.AType.Expression));
1909 args.Add (new Argument (left, Argument.AType.Expression));
1910 MethodBase method = Invocation.OverloadResolve (
1911 ec, (MethodGroupExpr) left_operators, args, true, Location.Null);
1913 Warning_UnintendedReferenceComparison (loc, "right", l);
1916 if (right_to_left && right_operators != null &&
1917 RootContext.WarningLevel >= 2) {
1918 ArrayList args = new ArrayList (2);
1919 args.Add (new Argument (right, Argument.AType.Expression));
1920 args.Add (new Argument (right, Argument.AType.Expression));
1921 MethodBase method = Invocation.OverloadResolve (
1922 ec, (MethodGroupExpr) right_operators, args, true, Location.Null);
1924 Warning_UnintendedReferenceComparison (loc, "left", r);
1928 // We are going to have to convert to an object to compare
1930 if (l != TypeManager.object_type)
1931 left = new EmptyCast (left, TypeManager.object_type);
1932 if (r != TypeManager.object_type)
1933 right = new EmptyCast (right, TypeManager.object_type);
1939 // Only perform numeric promotions on:
1940 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
1942 if (oper == Operator.Addition || oper == Operator.Subtraction) {
1943 if (TypeManager.IsDelegateType (l)){
1944 if (((right.eclass == ExprClass.MethodGroup) ||
1945 (r == TypeManager.anonymous_method_type))){
1946 if ((RootContext.Version != LanguageVersion.ISO_1)){
1947 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
1955 if (TypeManager.IsDelegateType (r) || right is NullLiteral){
1957 ArrayList args = new ArrayList (2);
1959 args = new ArrayList (2);
1960 args.Add (new Argument (left, Argument.AType.Expression));
1961 args.Add (new Argument (right, Argument.AType.Expression));
1963 if (oper == Operator.Addition)
1964 method = TypeManager.delegate_combine_delegate_delegate;
1966 method = TypeManager.delegate_remove_delegate_delegate;
1968 if (!TypeManager.IsEqual (l, r) && !(right is NullLiteral)) {
1969 Error_OperatorCannotBeApplied ();
1973 return new BinaryDelegate (l, method, args);
1978 // Pointer arithmetic:
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 // T* operator + (int y, T* x);
1986 // T* operator + (uint y, T *x);
1987 // T* operator + (long y, T *x);
1988 // T* operator + (ulong y, T *x);
1990 // T* operator - (T* x, int y);
1991 // T* operator - (T* x, uint y);
1992 // T* operator - (T* x, long y);
1993 // T* operator - (T* x, ulong y);
1995 // long operator - (T* x, T *y)
1998 if (r.IsPointer && oper == Operator.Subtraction){
2000 return new PointerArithmetic (
2001 false, left, right, TypeManager.int64_type,
2004 Expression t = Make32or64 (ec, right);
2006 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
2008 } else if (r.IsPointer && oper == Operator.Addition){
2009 Expression t = Make32or64 (ec, left);
2011 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
2016 // Enumeration operators
2018 bool lie = TypeManager.IsEnumType (l);
2019 bool rie = TypeManager.IsEnumType (r);
2023 // U operator - (E e, E f)
2025 if (oper == Operator.Subtraction){
2027 type = TypeManager.EnumToUnderlying (l);
2030 Error_OperatorCannotBeApplied ();
2036 // operator + (E e, U x)
2037 // operator - (E e, U x)
2039 if (oper == Operator.Addition || oper == Operator.Subtraction){
2040 Type enum_type = lie ? l : r;
2041 Type other_type = lie ? r : l;
2042 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2044 if (underlying_type != other_type){
2045 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2055 Error_OperatorCannotBeApplied ();
2064 temp = Convert.ImplicitConversion (ec, right, l, loc);
2068 Error_OperatorCannotBeApplied ();
2072 temp = Convert.ImplicitConversion (ec, left, r, loc);
2077 Error_OperatorCannotBeApplied ();
2082 if (oper == Operator.Equality || oper == Operator.Inequality ||
2083 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2084 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2085 if (left.Type != right.Type){
2086 Error_OperatorCannotBeApplied ();
2089 type = TypeManager.bool_type;
2093 if (oper == Operator.BitwiseAnd ||
2094 oper == Operator.BitwiseOr ||
2095 oper == Operator.ExclusiveOr){
2096 if (left.Type != right.Type){
2097 Error_OperatorCannotBeApplied ();
2103 Error_OperatorCannotBeApplied ();
2107 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2108 return CheckShiftArguments (ec);
2110 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2111 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2112 type = TypeManager.bool_type;
2117 Error_OperatorCannotBeApplied ();
2121 Expression e = new ConditionalLogicalOperator (
2122 oper == Operator.LogicalAnd, left, right, l, loc);
2123 return e.Resolve (ec);
2126 Expression orig_left = left;
2127 Expression orig_right = right;
2130 // operator & (bool x, bool y)
2131 // operator | (bool x, bool y)
2132 // operator ^ (bool x, bool y)
2134 if (oper == Operator.BitwiseAnd ||
2135 oper == Operator.BitwiseOr ||
2136 oper == Operator.ExclusiveOr) {
2137 if (OverloadResolve_PredefinedIntegral (ec)) {
2138 if (IsConvertible (ec, orig_left, orig_right, TypeManager.bool_type)) {
2139 Error_OperatorAmbiguous (loc, oper, l, r);
2143 if (oper == Operator.BitwiseOr && l != r && !(orig_right is Constant) && right is OpcodeCast &&
2144 (r == TypeManager.sbyte_type || r == TypeManager.short_type ||
2145 r == TypeManager.int32_type || r == TypeManager.int64_type)) {
2146 Report.Warning (675, 3, loc, "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first",
2147 TypeManager.CSharpName (r));
2150 } else if (!VerifyApplicable_Predefined (ec, TypeManager.bool_type)) {
2151 Error_OperatorCannotBeApplied ();
2158 // Pointer comparison
2160 if (l.IsPointer && r.IsPointer){
2161 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2162 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2163 type = TypeManager.bool_type;
2168 if (OverloadResolve_PredefinedIntegral (ec)) {
2169 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2170 Error_OperatorAmbiguous (loc, oper, l, r);
2173 } else if (OverloadResolve_PredefinedFloating (ec)) {
2174 if (IsConvertible (ec, orig_left, orig_right, TypeManager.decimal_type) ||
2175 IsApplicable_String (ec, orig_left, orig_right, oper)) {
2176 Error_OperatorAmbiguous (loc, oper, l, r);
2179 } else if (VerifyApplicable_Predefined (ec, TypeManager.decimal_type)) {
2180 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2181 Error_OperatorAmbiguous (loc, oper, l, r);
2184 } else if (!OverloadResolve_PredefinedString (ec, oper)) {
2185 Error_OperatorCannotBeApplied ();
2189 if (oper == Operator.Equality ||
2190 oper == Operator.Inequality ||
2191 oper == Operator.LessThanOrEqual ||
2192 oper == Operator.LessThan ||
2193 oper == Operator.GreaterThanOrEqual ||
2194 oper == Operator.GreaterThan)
2195 type = TypeManager.bool_type;
2200 if (l == TypeManager.decimal_type || l == TypeManager.string_type || r == TypeManager.string_type) {
2202 if (r == TypeManager.string_type)
2204 MethodGroupExpr ops = (MethodGroupExpr) MemberLookup (
2205 ec.ContainerType, lookup, oper_names [(int) oper],
2206 MemberTypes.Method, AllBindingFlags, loc);
2207 ArrayList args = new ArrayList (2);
2208 args.Add (new Argument (left, Argument.AType.Expression));
2209 args.Add (new Argument (right, Argument.AType.Expression));
2210 MethodBase method = Invocation.OverloadResolve (ec, ops, args, true, Location.Null);
2211 return new BinaryMethod (type, method, args);
2217 Constant EnumLiftUp (Constant left, Constant right)
2220 case Operator.BitwiseOr:
2221 case Operator.BitwiseAnd:
2222 case Operator.ExclusiveOr:
2223 case Operator.Equality:
2224 case Operator.Inequality:
2225 case Operator.LessThan:
2226 case Operator.LessThanOrEqual:
2227 case Operator.GreaterThan:
2228 case Operator.GreaterThanOrEqual:
2229 if (left is EnumConstant)
2232 if (left.IsZeroInteger)
2233 return new EnumConstant (left, right.Type);
2237 case Operator.Addition:
2238 case Operator.Subtraction:
2241 case Operator.Multiply:
2242 case Operator.Division:
2243 case Operator.Modulus:
2244 case Operator.LeftShift:
2245 case Operator.RightShift:
2246 if (right is EnumConstant || left is EnumConstant)
2250 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2254 public override Expression DoResolve (EmitContext ec)
2259 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2260 left = ((ParenthesizedExpression) left).Expr;
2261 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2265 if (left.eclass == ExprClass.Type) {
2266 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2270 left = left.Resolve (ec);
2275 Constant lc = left as Constant;
2276 if (lc != null && lc.Type == TypeManager.bool_type &&
2277 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2278 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2280 // TODO: make a sense to resolve unreachable expression as we do for statement
2281 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2285 right = right.Resolve (ec);
2289 eclass = ExprClass.Value;
2290 Constant rc = right as Constant;
2292 // The conversion rules are ignored in enum context but why
2293 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2294 left = lc = EnumLiftUp (lc, rc);
2298 right = rc = EnumLiftUp (rc, lc);
2303 if (oper == Operator.BitwiseAnd) {
2304 if (rc != null && rc.IsZeroInteger) {
2305 return lc is EnumConstant ?
2306 new EnumConstant (rc, lc.Type):
2310 if (lc != null && lc.IsZeroInteger) {
2311 return rc is EnumConstant ?
2312 new EnumConstant (lc, rc.Type):
2316 else if (oper == Operator.BitwiseOr) {
2317 if (lc is EnumConstant &&
2318 rc != null && rc.IsZeroInteger)
2320 if (rc is EnumConstant &&
2321 lc != null && lc.IsZeroInteger)
2323 } else if (oper == Operator.LogicalAnd) {
2324 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2326 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2330 if (rc != null && lc != null){
2331 int prev_e = Report.Errors;
2332 Expression e = ConstantFold.BinaryFold (
2333 ec, oper, lc, rc, loc);
2334 if (e != null || Report.Errors != prev_e)
2339 if ((left is NullLiteral || left.Type.IsValueType) &&
2340 (right is NullLiteral || right.Type.IsValueType) &&
2341 !(left is NullLiteral && right is NullLiteral) &&
2342 (TypeManager.IsNullableType (left.Type) || TypeManager.IsNullableType (right.Type)))
2343 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2346 // Comparison warnings
2347 if (oper == Operator.Equality || oper == Operator.Inequality ||
2348 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2349 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2350 if (left.Equals (right)) {
2351 Report.Warning (1718, 3, loc, "Comparison made to same variable; did you mean to compare something else?");
2353 CheckUselessComparison (lc, right.Type);
2354 CheckUselessComparison (rc, left.Type);
2357 return ResolveOperator (ec);
2360 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2365 private void CheckUselessComparison (Constant c, Type type)
2367 if (c == null || !IsTypeIntegral (type)
2368 || c is StringConstant
2369 || c is BoolConstant
2370 || c is CharConstant
2371 || c is FloatConstant
2372 || c is DoubleConstant
2373 || c is DecimalConstant
2379 if (c is ULongConstant) {
2380 ulong uvalue = ((ULongConstant) c).Value;
2381 if (uvalue > long.MaxValue) {
2382 if (type == TypeManager.byte_type ||
2383 type == TypeManager.sbyte_type ||
2384 type == TypeManager.short_type ||
2385 type == TypeManager.ushort_type ||
2386 type == TypeManager.int32_type ||
2387 type == TypeManager.uint32_type ||
2388 type == TypeManager.int64_type)
2389 WarnUselessComparison (type);
2392 value = (long) uvalue;
2394 else if (c is ByteConstant)
2395 value = ((ByteConstant) c).Value;
2396 else if (c is SByteConstant)
2397 value = ((SByteConstant) c).Value;
2398 else if (c is ShortConstant)
2399 value = ((ShortConstant) c).Value;
2400 else if (c is UShortConstant)
2401 value = ((UShortConstant) c).Value;
2402 else if (c is IntConstant)
2403 value = ((IntConstant) c).Value;
2404 else if (c is UIntConstant)
2405 value = ((UIntConstant) c).Value;
2406 else if (c is LongConstant)
2407 value = ((LongConstant) c).Value;
2410 if (IsValueOutOfRange (value, type))
2411 WarnUselessComparison (type);
2416 private bool IsValueOutOfRange (long value, Type type)
2418 if (IsTypeUnsigned (type) && value < 0)
2420 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2421 type == TypeManager.byte_type && value >= 0x100 ||
2422 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2423 type == TypeManager.ushort_type && value >= 0x10000 ||
2424 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2425 type == TypeManager.uint32_type && value >= 0x100000000;
2428 private static bool IsTypeIntegral (Type type)
2430 return type == TypeManager.uint64_type ||
2431 type == TypeManager.int64_type ||
2432 type == TypeManager.uint32_type ||
2433 type == TypeManager.int32_type ||
2434 type == TypeManager.ushort_type ||
2435 type == TypeManager.short_type ||
2436 type == TypeManager.sbyte_type ||
2437 type == TypeManager.byte_type;
2440 private static bool IsTypeUnsigned (Type type)
2442 return type == TypeManager.uint64_type ||
2443 type == TypeManager.uint32_type ||
2444 type == TypeManager.ushort_type ||
2445 type == TypeManager.byte_type;
2448 private void WarnUselessComparison (Type type)
2450 Report.Warning (652, 2, loc, "Comparison to integral constant is useless; the constant is outside the range of type `{0}'",
2451 TypeManager.CSharpName (type));
2455 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2456 /// context of a conditional bool expression. This function will return
2457 /// false if it is was possible to use EmitBranchable, or true if it was.
2459 /// The expression's code is generated, and we will generate a branch to `target'
2460 /// if the resulting expression value is equal to isTrue
2462 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2464 ILGenerator ig = ec.ig;
2467 // This is more complicated than it looks, but its just to avoid
2468 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2469 // but on top of that we want for == and != to use a special path
2470 // if we are comparing against null
2472 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2473 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2476 // put the constant on the rhs, for simplicity
2478 if (left is Constant) {
2479 Expression swap = right;
2484 if (((Constant) right).IsZeroInteger) {
2487 ig.Emit (OpCodes.Brtrue, target);
2489 ig.Emit (OpCodes.Brfalse, target);
2492 } else if (right is BoolConstant) {
2494 if (my_on_true != ((BoolConstant) right).Value)
2495 ig.Emit (OpCodes.Brtrue, target);
2497 ig.Emit (OpCodes.Brfalse, target);
2502 } else if (oper == Operator.LogicalAnd) {
2505 Label tests_end = ig.DefineLabel ();
2507 left.EmitBranchable (ec, tests_end, false);
2508 right.EmitBranchable (ec, target, true);
2509 ig.MarkLabel (tests_end);
2511 left.EmitBranchable (ec, target, false);
2512 right.EmitBranchable (ec, target, false);
2517 } else if (oper == Operator.LogicalOr){
2519 left.EmitBranchable (ec, target, true);
2520 right.EmitBranchable (ec, target, true);
2523 Label tests_end = ig.DefineLabel ();
2524 left.EmitBranchable (ec, tests_end, true);
2525 right.EmitBranchable (ec, target, false);
2526 ig.MarkLabel (tests_end);
2531 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2532 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2533 oper == Operator.Equality || oper == Operator.Inequality)) {
2534 base.EmitBranchable (ec, target, onTrue);
2542 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2545 case Operator.Equality:
2547 ig.Emit (OpCodes.Beq, target);
2549 ig.Emit (OpCodes.Bne_Un, target);
2552 case Operator.Inequality:
2554 ig.Emit (OpCodes.Bne_Un, target);
2556 ig.Emit (OpCodes.Beq, target);
2559 case Operator.LessThan:
2562 ig.Emit (OpCodes.Blt_Un, target);
2564 ig.Emit (OpCodes.Blt, target);
2567 ig.Emit (OpCodes.Bge_Un, target);
2569 ig.Emit (OpCodes.Bge, target);
2572 case Operator.GreaterThan:
2575 ig.Emit (OpCodes.Bgt_Un, target);
2577 ig.Emit (OpCodes.Bgt, target);
2580 ig.Emit (OpCodes.Ble_Un, target);
2582 ig.Emit (OpCodes.Ble, target);
2585 case Operator.LessThanOrEqual:
2588 ig.Emit (OpCodes.Ble_Un, target);
2590 ig.Emit (OpCodes.Ble, target);
2593 ig.Emit (OpCodes.Bgt_Un, target);
2595 ig.Emit (OpCodes.Bgt, target);
2599 case Operator.GreaterThanOrEqual:
2602 ig.Emit (OpCodes.Bge_Un, target);
2604 ig.Emit (OpCodes.Bge, target);
2607 ig.Emit (OpCodes.Blt_Un, target);
2609 ig.Emit (OpCodes.Blt, target);
2612 Console.WriteLine (oper);
2613 throw new Exception ("what is THAT");
2617 public override void Emit (EmitContext ec)
2619 ILGenerator ig = ec.ig;
2624 // Handle short-circuit operators differently
2627 if (oper == Operator.LogicalAnd) {
2628 Label load_zero = ig.DefineLabel ();
2629 Label end = ig.DefineLabel ();
2631 left.EmitBranchable (ec, load_zero, false);
2633 ig.Emit (OpCodes.Br, end);
2635 ig.MarkLabel (load_zero);
2636 ig.Emit (OpCodes.Ldc_I4_0);
2639 } else if (oper == Operator.LogicalOr) {
2640 Label load_one = ig.DefineLabel ();
2641 Label end = ig.DefineLabel ();
2643 left.EmitBranchable (ec, load_one, true);
2645 ig.Emit (OpCodes.Br, end);
2647 ig.MarkLabel (load_one);
2648 ig.Emit (OpCodes.Ldc_I4_1);
2656 bool isUnsigned = is_unsigned (left.Type);
2659 case Operator.Multiply:
2661 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2662 opcode = OpCodes.Mul_Ovf;
2663 else if (isUnsigned)
2664 opcode = OpCodes.Mul_Ovf_Un;
2666 opcode = OpCodes.Mul;
2668 opcode = OpCodes.Mul;
2672 case Operator.Division:
2674 opcode = OpCodes.Div_Un;
2676 opcode = OpCodes.Div;
2679 case Operator.Modulus:
2681 opcode = OpCodes.Rem_Un;
2683 opcode = OpCodes.Rem;
2686 case Operator.Addition:
2688 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2689 opcode = OpCodes.Add_Ovf;
2690 else if (isUnsigned)
2691 opcode = OpCodes.Add_Ovf_Un;
2693 opcode = OpCodes.Add;
2695 opcode = OpCodes.Add;
2698 case Operator.Subtraction:
2700 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2701 opcode = OpCodes.Sub_Ovf;
2702 else if (isUnsigned)
2703 opcode = OpCodes.Sub_Ovf_Un;
2705 opcode = OpCodes.Sub;
2707 opcode = OpCodes.Sub;
2710 case Operator.RightShift:
2712 opcode = OpCodes.Shr_Un;
2714 opcode = OpCodes.Shr;
2717 case Operator.LeftShift:
2718 opcode = OpCodes.Shl;
2721 case Operator.Equality:
2722 opcode = OpCodes.Ceq;
2725 case Operator.Inequality:
2726 ig.Emit (OpCodes.Ceq);
2727 ig.Emit (OpCodes.Ldc_I4_0);
2729 opcode = OpCodes.Ceq;
2732 case Operator.LessThan:
2734 opcode = OpCodes.Clt_Un;
2736 opcode = OpCodes.Clt;
2739 case Operator.GreaterThan:
2741 opcode = OpCodes.Cgt_Un;
2743 opcode = OpCodes.Cgt;
2746 case Operator.LessThanOrEqual:
2747 Type lt = left.Type;
2749 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2750 ig.Emit (OpCodes.Cgt_Un);
2752 ig.Emit (OpCodes.Cgt);
2753 ig.Emit (OpCodes.Ldc_I4_0);
2755 opcode = OpCodes.Ceq;
2758 case Operator.GreaterThanOrEqual:
2759 Type le = left.Type;
2761 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2762 ig.Emit (OpCodes.Clt_Un);
2764 ig.Emit (OpCodes.Clt);
2766 ig.Emit (OpCodes.Ldc_I4_0);
2768 opcode = OpCodes.Ceq;
2771 case Operator.BitwiseOr:
2772 opcode = OpCodes.Or;
2775 case Operator.BitwiseAnd:
2776 opcode = OpCodes.And;
2779 case Operator.ExclusiveOr:
2780 opcode = OpCodes.Xor;
2784 throw new Exception ("This should not happen: Operator = "
2785 + oper.ToString ());
2793 // Object created by Binary when the binary operator uses an method instead of being
2794 // a binary operation that maps to a CIL binary operation.
2796 public class BinaryMethod : Expression {
2797 public MethodBase method;
2798 public ArrayList Arguments;
2800 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2805 eclass = ExprClass.Value;
2808 public override Expression DoResolve (EmitContext ec)
2813 public override void Emit (EmitContext ec)
2815 ILGenerator ig = ec.ig;
2817 if (Arguments != null)
2818 Invocation.EmitArguments (ec, method, Arguments, false, null);
2820 if (method is MethodInfo)
2821 ig.Emit (OpCodes.Call, (MethodInfo) method);
2823 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2828 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2829 // b, c, d... may be strings or objects.
2831 public class StringConcat : Expression {
2833 bool invalid = false;
2834 bool emit_conv_done = false;
2836 // Are we also concating objects?
2838 bool is_strings_only = true;
2840 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2843 type = TypeManager.string_type;
2844 eclass = ExprClass.Value;
2846 operands = new ArrayList (2);
2851 public override Expression DoResolve (EmitContext ec)
2859 public void Append (EmitContext ec, Expression operand)
2864 StringConstant sc = operand as StringConstant;
2866 // TODO: it will be better to do this silently as an optimalization
2868 // string s = "" + i;
2869 // because this code has poor performace
2870 // if (sc.Value.Length == 0)
2871 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2873 if (operands.Count != 0) {
2874 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2875 if (last_operand != null) {
2876 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2883 // Conversion to object
2885 if (operand.Type != TypeManager.string_type) {
2886 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2889 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2895 operands.Add (operand);
2898 public override void Emit (EmitContext ec)
2900 MethodInfo concat_method = null;
2903 // Do conversion to arguments; check for strings only
2906 // This can get called multiple times, so we have to deal with that.
2907 if (!emit_conv_done) {
2908 emit_conv_done = true;
2909 for (int i = 0; i < operands.Count; i ++) {
2910 Expression e = (Expression) operands [i];
2911 is_strings_only &= e.Type == TypeManager.string_type;
2914 for (int i = 0; i < operands.Count; i ++) {
2915 Expression e = (Expression) operands [i];
2917 if (! is_strings_only && e.Type == TypeManager.string_type) {
2918 // need to make sure this is an object, because the EmitParams
2919 // method might look at the type of this expression, see it is a
2920 // string and emit a string [] when we want an object [];
2922 e = new EmptyCast (e, TypeManager.object_type);
2924 operands [i] = new Argument (e, Argument.AType.Expression);
2929 // Find the right method
2931 switch (operands.Count) {
2934 // This should not be possible, because simple constant folding
2935 // is taken care of in the Binary code.
2937 throw new Exception ("how did you get here?");
2940 concat_method = is_strings_only ?
2941 TypeManager.string_concat_string_string :
2942 TypeManager.string_concat_object_object ;
2945 concat_method = is_strings_only ?
2946 TypeManager.string_concat_string_string_string :
2947 TypeManager.string_concat_object_object_object ;
2951 // There is not a 4 param overlaod for object (the one that there is
2952 // is actually a varargs methods, and is only in corlib because it was
2953 // introduced there before.).
2955 if (!is_strings_only)
2958 concat_method = TypeManager.string_concat_string_string_string_string;
2961 concat_method = is_strings_only ?
2962 TypeManager.string_concat_string_dot_dot_dot :
2963 TypeManager.string_concat_object_dot_dot_dot ;
2967 Invocation.EmitArguments (ec, concat_method, operands, false, null);
2968 ec.ig.Emit (OpCodes.Call, concat_method);
2973 // Object created with +/= on delegates
2975 public class BinaryDelegate : Expression {
2979 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
2984 eclass = ExprClass.Value;
2987 public override Expression DoResolve (EmitContext ec)
2992 public override void Emit (EmitContext ec)
2994 ILGenerator ig = ec.ig;
2996 Invocation.EmitArguments (ec, method, args, false, null);
2998 ig.Emit (OpCodes.Call, (MethodInfo) method);
2999 ig.Emit (OpCodes.Castclass, type);
3002 public Expression Right {
3004 Argument arg = (Argument) args [1];
3009 public bool IsAddition {
3011 return method == TypeManager.delegate_combine_delegate_delegate;
3017 // User-defined conditional logical operator
3018 public class ConditionalLogicalOperator : Expression {
3019 Expression left, right;
3022 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
3025 eclass = ExprClass.Value;
3029 this.is_and = is_and;
3032 protected void Error19 ()
3034 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3037 protected void Error218 ()
3039 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3040 "declarations of operator true and operator false");
3043 Expression op_true, op_false, op;
3044 LocalTemporary left_temp;
3046 public override Expression DoResolve (EmitContext ec)
3049 Expression operator_group;
3051 operator_group = MethodLookup (ec, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
3052 if (operator_group == null) {
3057 left_temp = new LocalTemporary (type);
3059 ArrayList arguments = new ArrayList ();
3060 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3061 arguments.Add (new Argument (right, Argument.AType.Expression));
3062 method = Invocation.OverloadResolve (
3063 ec, (MethodGroupExpr) operator_group, arguments, false, loc)
3065 if (method == null) {
3070 if (method.ReturnType != type) {
3071 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3072 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3076 op = new StaticCallExpr (method, arguments, loc);
3078 op_true = GetOperatorTrue (ec, left_temp, loc);
3079 op_false = GetOperatorFalse (ec, left_temp, loc);
3080 if ((op_true == null) || (op_false == null)) {
3088 public override void Emit (EmitContext ec)
3090 ILGenerator ig = ec.ig;
3091 Label false_target = ig.DefineLabel ();
3092 Label end_target = ig.DefineLabel ();
3095 left_temp.Store (ec);
3097 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3098 left_temp.Emit (ec);
3099 ig.Emit (OpCodes.Br, end_target);
3100 ig.MarkLabel (false_target);
3102 ig.MarkLabel (end_target);
3104 // We release 'left_temp' here since 'op' may refer to it too
3105 left_temp.Release (ec);
3109 public class PointerArithmetic : Expression {
3110 Expression left, right;
3114 // We assume that `l' is always a pointer
3116 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3122 is_add = is_addition;
3125 public override Expression DoResolve (EmitContext ec)
3127 eclass = ExprClass.Variable;
3129 if (left.Type == TypeManager.void_ptr_type) {
3130 Error (242, "The operation in question is undefined on void pointers");
3137 public override void Emit (EmitContext ec)
3139 Type op_type = left.Type;
3140 ILGenerator ig = ec.ig;
3142 // It must be either array or fixed buffer
3143 Type element = TypeManager.HasElementType (op_type) ?
3144 element = TypeManager.GetElementType (op_type) :
3145 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3147 int size = GetTypeSize (element);
3148 Type rtype = right.Type;
3150 if (rtype.IsPointer){
3152 // handle (pointer - pointer)
3156 ig.Emit (OpCodes.Sub);
3160 ig.Emit (OpCodes.Sizeof, element);
3162 IntLiteral.EmitInt (ig, size);
3163 ig.Emit (OpCodes.Div);
3165 ig.Emit (OpCodes.Conv_I8);
3168 // handle + and - on (pointer op int)
3171 ig.Emit (OpCodes.Conv_I);
3173 Constant right_const = right as Constant;
3174 if (right_const != null && size != 0) {
3175 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3183 ig.Emit (OpCodes.Sizeof, element);
3185 IntLiteral.EmitInt (ig, size);
3186 if (rtype == TypeManager.int64_type)
3187 ig.Emit (OpCodes.Conv_I8);
3188 else if (rtype == TypeManager.uint64_type)
3189 ig.Emit (OpCodes.Conv_U8);
3190 ig.Emit (OpCodes.Mul);
3194 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3195 ig.Emit (OpCodes.Conv_I);
3198 ig.Emit (OpCodes.Add);
3200 ig.Emit (OpCodes.Sub);
3206 /// Implements the ternary conditional operator (?:)
3208 public class Conditional : Expression {
3209 Expression expr, trueExpr, falseExpr;
3211 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3214 this.trueExpr = trueExpr;
3215 this.falseExpr = falseExpr;
3216 this.loc = expr.Location;
3219 public Expression Expr {
3225 public Expression TrueExpr {
3231 public Expression FalseExpr {
3237 public override Expression DoResolve (EmitContext ec)
3239 expr = expr.Resolve (ec);
3245 if (TypeManager.IsNullableValueType (expr.Type))
3246 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
3249 if (expr.Type != TypeManager.bool_type){
3250 expr = Expression.ResolveBoolean (
3257 Assign ass = expr as Assign;
3258 if (ass != null && ass.Source is Constant) {
3259 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3262 trueExpr = trueExpr.Resolve (ec);
3263 falseExpr = falseExpr.Resolve (ec);
3265 if (trueExpr == null || falseExpr == null)
3268 eclass = ExprClass.Value;
3269 if (trueExpr.Type == falseExpr.Type) {
3270 type = trueExpr.Type;
3271 if (type == TypeManager.null_type) {
3272 // TODO: probably will have to implement ConditionalConstant
3273 // to call method without return constant as well
3274 Report.Warning (-101, 1, loc, "Conditional expression will always return same value");
3279 Type true_type = trueExpr.Type;
3280 Type false_type = falseExpr.Type;
3283 // First, if an implicit conversion exists from trueExpr
3284 // to falseExpr, then the result type is of type falseExpr.Type
3286 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3289 // Check if both can convert implicitl to each other's type
3291 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3293 "Can not compute type of conditional expression " +
3294 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3295 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3296 "' convert implicitly to each other");
3301 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3305 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3306 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3311 // Dead code optimalization
3312 if (expr is BoolConstant){
3313 BoolConstant bc = (BoolConstant) expr;
3315 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3316 return bc.Value ? trueExpr : falseExpr;
3322 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3327 public override void Emit (EmitContext ec)
3329 ILGenerator ig = ec.ig;
3330 Label false_target = ig.DefineLabel ();
3331 Label end_target = ig.DefineLabel ();
3333 expr.EmitBranchable (ec, false_target, false);
3335 ig.Emit (OpCodes.Br, end_target);
3336 ig.MarkLabel (false_target);
3337 falseExpr.Emit (ec);
3338 ig.MarkLabel (end_target);
3343 public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation {
3345 LocalTemporary temp;
3347 public abstract Variable Variable {
3351 public abstract bool IsRef {
3355 public override void Emit (EmitContext ec)
3361 // This method is used by parameters that are references, that are
3362 // being passed as references: we only want to pass the pointer (that
3363 // is already stored in the parameter, not the address of the pointer,
3364 // and not the value of the variable).
3366 public void EmitLoad (EmitContext ec)
3368 Report.Debug (64, "VARIABLE EMIT LOAD", this, Variable, type, loc);
3370 Variable.EmitInstance (ec);
3374 public void Emit (EmitContext ec, bool leave_copy)
3376 Report.Debug (64, "VARIABLE EMIT", this, Variable, type, IsRef, loc);
3382 ec.ig.Emit (OpCodes.Dup);
3385 // If we are a reference, we loaded on the stack a pointer
3386 // Now lets load the real value
3388 LoadFromPtr (ec.ig, type);
3392 ec.ig.Emit (OpCodes.Dup);
3394 if (IsRef || Variable.NeedsTemporary) {
3395 temp = new LocalTemporary (Type);
3401 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
3402 bool prepare_for_load)
3404 Report.Debug (64, "VARIABLE EMIT ASSIGN", this, Variable, type, IsRef,
3407 ILGenerator ig = ec.ig;
3408 prepared = prepare_for_load;
3410 Variable.EmitInstance (ec);
3411 if (prepare_for_load && Variable.HasInstance)
3412 ig.Emit (OpCodes.Dup);
3413 else if (IsRef && !prepared)
3419 ig.Emit (OpCodes.Dup);
3420 if (IsRef || Variable.NeedsTemporary) {
3421 temp = new LocalTemporary (Type);
3427 StoreFromPtr (ig, type);
3429 Variable.EmitAssign (ec);
3437 public void AddressOf (EmitContext ec, AddressOp mode)
3439 Variable.EmitInstance (ec);
3440 Variable.EmitAddressOf (ec);
3447 public class LocalVariableReference : VariableReference, IVariable {
3448 public readonly string Name;
3449 public readonly Block Block;
3450 public LocalInfo local_info;
3454 public LocalVariableReference (Block block, string name, Location l)
3459 eclass = ExprClass.Variable;
3463 // Setting `is_readonly' to false will allow you to create a writable
3464 // reference to a read-only variable. This is used by foreach and using.
3466 public LocalVariableReference (Block block, string name, Location l,
3467 LocalInfo local_info, bool is_readonly)
3468 : this (block, name, l)
3470 this.local_info = local_info;
3471 this.is_readonly = is_readonly;
3474 public VariableInfo VariableInfo {
3475 get { return local_info.VariableInfo; }
3478 public override bool IsRef {
3479 get { return false; }
3482 public bool IsReadOnly {
3483 get { return is_readonly; }
3486 public bool VerifyAssigned (EmitContext ec)
3488 VariableInfo variable_info = local_info.VariableInfo;
3489 return variable_info == null || variable_info.IsAssigned (ec, loc);
3492 void ResolveLocalInfo ()
3494 if (local_info == null) {
3495 local_info = Block.GetLocalInfo (Name);
3496 is_readonly = local_info.ReadOnly;
3500 protected Expression DoResolveBase (EmitContext ec)
3502 type = local_info.VariableType;
3504 Expression e = Block.GetConstantExpression (Name);
3506 return e.Resolve (ec);
3508 if (!VerifyAssigned (ec))
3512 // If we are referencing a variable from the external block
3513 // flag it for capturing
3515 if (ec.MustCaptureVariable (local_info)) {
3516 if (local_info.AddressTaken){
3517 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3521 ScopeInfo scope = local_info.Block.CreateScopeInfo ();
3522 variable = scope.AddLocal (local_info);
3523 type = variable.Type;
3529 public override Expression DoResolve (EmitContext ec)
3531 ResolveLocalInfo ();
3532 local_info.Used = true;
3533 return DoResolveBase (ec);
3536 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3538 ResolveLocalInfo ();
3543 if (right_side == EmptyExpression.OutAccess) {
3544 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3545 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3546 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3547 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3548 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3550 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
3552 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3557 if (right_side == EmptyExpression.OutAccess)
3558 local_info.Used = true;
3560 if (VariableInfo != null)
3561 VariableInfo.SetAssigned (ec);
3563 return DoResolveBase (ec);
3566 public bool VerifyFixed ()
3568 // A local Variable is always fixed.
3572 public override int GetHashCode ()
3574 return Name.GetHashCode ();
3577 public override bool Equals (object obj)
3579 LocalVariableReference lvr = obj as LocalVariableReference;
3583 return Name == lvr.Name && Block == lvr.Block;
3586 public override Variable Variable {
3587 get { return variable != null ? variable : local_info.Variable; }
3590 public override string ToString ()
3592 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3597 /// This represents a reference to a parameter in the intermediate
3600 public class ParameterReference : VariableReference, IVariable {
3606 public bool is_ref, is_out;
3614 public override bool IsRef {
3620 public string Name {
3626 public Parameter Parameter {
3634 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3637 this.name = par.Name;
3641 eclass = ExprClass.Variable;
3644 public VariableInfo VariableInfo {
3648 public override Variable Variable {
3649 get { return variable != null ? variable : par.Variable; }
3652 public bool VerifyFixed ()
3654 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3655 return par.ModFlags == Parameter.Modifier.NONE;
3658 public bool IsAssigned (EmitContext ec, Location loc)
3660 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3663 Report.Error (269, loc,
3664 "Use of unassigned out parameter `{0}'", par.Name);
3668 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3670 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3673 Report.Error (170, loc,
3674 "Use of possibly unassigned field `" + field_name + "'");
3678 public void SetAssigned (EmitContext ec)
3680 if (is_out && ec.DoFlowAnalysis)
3681 ec.CurrentBranching.SetAssigned (vi);
3684 public void SetFieldAssigned (EmitContext ec, string field_name)
3686 if (is_out && ec.DoFlowAnalysis)
3687 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3690 protected bool DoResolveBase (EmitContext ec)
3692 if (!par.Resolve (ec)) {
3696 type = par.ParameterType;
3697 Parameter.Modifier mod = par.ModFlags;
3698 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3699 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3700 eclass = ExprClass.Variable;
3703 vi = block.ParameterMap [idx];
3705 AnonymousContainer am = ec.CurrentAnonymousMethod;
3709 if (is_ref && !block.Toplevel.IsLocalParameter (name)){
3710 Report.Error (1628, Location,
3711 "Cannot use ref or out parameter `{0}' inside an " +
3712 "anonymous method block", par.Name);
3716 if (!am.IsIterator && block.Toplevel.IsLocalParameter (name))
3719 AnonymousMethodHost host = null;
3720 ToplevelBlock toplevel = block.Toplevel;
3721 while (toplevel != null) {
3722 if (toplevel.IsLocalParameter (name)) {
3723 host = toplevel.AnonymousMethodHost;
3727 toplevel = toplevel.Container;
3730 variable = host.AddParameter (par, idx);
3731 type = variable.Type;
3735 public override int GetHashCode()
3737 return name.GetHashCode ();
3740 public override bool Equals (object obj)
3742 ParameterReference pr = obj as ParameterReference;
3746 return name == pr.name && block == pr.block;
3750 // Notice that for ref/out parameters, the type exposed is not the
3751 // same type exposed externally.
3754 // externally we expose "int&"
3755 // here we expose "int".
3757 // We record this in "is_ref". This means that the type system can treat
3758 // the type as it is expected, but when we generate the code, we generate
3759 // the alternate kind of code.
3761 public override Expression DoResolve (EmitContext ec)
3763 if (!DoResolveBase (ec))
3766 if (is_out && ec.DoFlowAnalysis &&
3767 (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3773 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3775 if (!DoResolveBase (ec))
3783 static public void EmitLdArg (ILGenerator ig, int x)
3787 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3788 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3789 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3790 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3791 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3794 ig.Emit (OpCodes.Ldarg, x);
3797 public override string ToString ()
3799 return "ParameterReference[" + name + "]";
3804 /// Used for arguments to New(), Invocation()
3806 public class Argument {
3807 public enum AType : byte {
3814 public readonly AType ArgType;
3815 public Expression Expr;
3817 public Argument (Expression expr, AType type)
3820 this.ArgType = type;
3823 public Argument (Expression expr)
3826 this.ArgType = AType.Expression;
3831 if (ArgType == AType.Ref || ArgType == AType.Out)
3832 return TypeManager.GetReferenceType (Expr.Type);
3838 public Parameter.Modifier Modifier
3843 return Parameter.Modifier.OUT;
3846 return Parameter.Modifier.REF;
3849 return Parameter.Modifier.NONE;
3854 public static string FullDesc (Argument a)
3856 if (a.ArgType == AType.ArgList)
3859 return (a.ArgType == AType.Ref ? "ref " :
3860 (a.ArgType == AType.Out ? "out " : "")) +
3861 TypeManager.CSharpName (a.Expr.Type);
3864 public bool ResolveMethodGroup (EmitContext ec)
3866 SimpleName sn = Expr as SimpleName;
3868 Expr = sn.GetMethodGroup ();
3870 // FIXME: csc doesn't report any error if you try to use `ref' or
3871 // `out' in a delegate creation expression.
3872 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3879 public bool Resolve (EmitContext ec, Location loc)
3881 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3882 // Verify that the argument is readable
3883 if (ArgType != AType.Out)
3884 Expr = Expr.Resolve (ec);
3886 // Verify that the argument is writeable
3887 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3888 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3890 return Expr != null;
3894 public void Emit (EmitContext ec)
3896 if (ArgType != AType.Ref && ArgType != AType.Out) {
3901 AddressOp mode = AddressOp.Store;
3902 if (ArgType == AType.Ref)
3903 mode |= AddressOp.Load;
3905 IMemoryLocation ml = (IMemoryLocation) Expr;
3906 ParameterReference pr = ml as ParameterReference;
3909 // ParameterReferences might already be references, so we want
3910 // to pass just the value
3912 if (pr != null && pr.IsRef)
3915 ml.AddressOf (ec, mode);
3920 /// Invocation of methods or delegates.
3922 public class Invocation : ExpressionStatement {
3923 public readonly ArrayList Arguments;
3926 MethodBase method = null;
3929 // arguments is an ArrayList, but we do not want to typecast,
3930 // as it might be null.
3932 // FIXME: only allow expr to be a method invocation or a
3933 // delegate invocation (7.5.5)
3935 public Invocation (Expression expr, ArrayList arguments)
3938 Arguments = arguments;
3939 loc = expr.Location;
3942 public Expression Expr {
3949 /// Determines "better conversion" as specified in 14.4.2.3
3951 /// Returns : p if a->p is better,
3952 /// q if a->q is better,
3953 /// null if neither is better
3955 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q)
3957 Type argument_type = TypeManager.TypeToCoreType (a.Type);
3958 Expression argument_expr = a.Expr;
3960 if (argument_type == null)
3961 throw new Exception ("Expression of type " + a.Expr +
3962 " does not resolve its type");
3964 if (p == null || q == null)
3965 throw new InternalErrorException ("BetterConversion Got a null conversion");
3970 if (argument_expr is NullLiteral) {
3972 // If the argument is null and one of the types to compare is 'object' and
3973 // the other is a reference type, we prefer the other.
3975 // This follows from the usual rules:
3976 // * There is an implicit conversion from 'null' to type 'object'
3977 // * There is an implicit conversion from 'null' to any reference type
3978 // * There is an implicit conversion from any reference type to type 'object'
3979 // * There is no implicit conversion from type 'object' to other reference types
3980 // => Conversion of 'null' to a reference type is better than conversion to 'object'
3982 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
3983 // null type. I think it used to be 'object' and thus needed a special
3984 // case to avoid the immediately following two checks.
3986 if (!p.IsValueType && q == TypeManager.object_type)
3988 if (!q.IsValueType && p == TypeManager.object_type)
3992 if (argument_type == p)
3995 if (argument_type == q)
3998 Expression p_tmp = new EmptyExpression (p);
3999 Expression q_tmp = new EmptyExpression (q);
4001 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
4002 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
4004 if (p_to_q && !q_to_p)
4007 if (q_to_p && !p_to_q)
4010 if (p == TypeManager.sbyte_type)
4011 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
4012 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4014 if (q == TypeManager.sbyte_type)
4015 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
4016 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4019 if (p == TypeManager.short_type)
4020 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
4021 q == TypeManager.uint64_type)
4023 if (q == TypeManager.short_type)
4024 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
4025 p == TypeManager.uint64_type)
4028 if (p == TypeManager.int32_type)
4029 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4031 if (q == TypeManager.int32_type)
4032 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4035 if (p == TypeManager.int64_type)
4036 if (q == TypeManager.uint64_type)
4038 if (q == TypeManager.int64_type)
4039 if (p == TypeManager.uint64_type)
4045 static Type MoreSpecific (Type p, Type q)
4047 if (TypeManager.IsGenericParameter (p) && !TypeManager.IsGenericParameter (q))
4049 if (!TypeManager.IsGenericParameter (p) && TypeManager.IsGenericParameter (q))
4052 if (TypeManager.HasElementType (p)) {
4053 Type pe = TypeManager.GetElementType (p);
4054 Type qe = TypeManager.GetElementType (q);
4055 Type specific = MoreSpecific (pe, qe);
4060 } else if (TypeManager.IsGenericType (p)) {
4061 Type[] pargs = TypeManager.GetTypeArguments (p);
4062 Type[] qargs = TypeManager.GetTypeArguments (q);
4064 bool p_specific_at_least_once = false;
4065 bool q_specific_at_least_once = false;
4067 for (int i = 0; i < pargs.Length; i++) {
4068 Type specific = MoreSpecific (pargs [i], qargs [i]);
4069 if (specific == pargs [i])
4070 p_specific_at_least_once = true;
4071 if (specific == qargs [i])
4072 q_specific_at_least_once = true;
4075 if (p_specific_at_least_once && !q_specific_at_least_once)
4077 if (!p_specific_at_least_once && q_specific_at_least_once)
4085 /// Determines "Better function" between candidate
4086 /// and the current best match
4089 /// Returns a boolean indicating :
4090 /// false if candidate ain't better
4091 /// true if candidate is better than the current best match
4093 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4094 MethodBase candidate, bool candidate_params,
4095 MethodBase best, bool best_params)
4097 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4098 ParameterData best_pd = TypeManager.GetParameterData (best);
4100 bool better_at_least_one = false;
4102 for (int j = 0; j < argument_count; ++j) {
4103 Argument a = (Argument) args [j];
4105 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
4106 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
4108 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4109 if (candidate_params)
4110 ct = TypeManager.GetElementType (ct);
4112 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4114 bt = TypeManager.GetElementType (bt);
4120 Type better = BetterConversion (ec, a, ct, bt);
4122 // for each argument, the conversion to 'ct' should be no worse than
4123 // the conversion to 'bt'.
4127 // for at least one argument, the conversion to 'ct' should be better than
4128 // the conversion to 'bt'.
4130 better_at_least_one = true;
4133 if (better_at_least_one)
4137 // This handles the case
4139 // Add (float f1, float f2, float f3);
4140 // Add (params decimal [] foo);
4142 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4143 // first candidate would've chosen as better.
4149 // The two methods have equal parameter types. Now apply tie-breaking rules
4151 if (TypeManager.IsGenericMethod (best) && !TypeManager.IsGenericMethod (candidate))
4153 if (!TypeManager.IsGenericMethod (best) && TypeManager.IsGenericMethod (candidate))
4157 // This handles the following cases:
4159 // Trim () is better than Trim (params char[] chars)
4160 // Concat (string s1, string s2, string s3) is better than
4161 // Concat (string s1, params string [] srest)
4162 // Foo (int, params int [] rest) is better than Foo (params int [] rest)
4164 if (!candidate_params && best_params)
4166 if (candidate_params && !best_params)
4169 int candidate_param_count = candidate_pd.Count;
4170 int best_param_count = best_pd.Count;
4172 if (candidate_param_count != best_param_count)
4173 // can only happen if (candidate_params && best_params)
4174 return candidate_param_count > best_param_count;
4177 // now, both methods have the same number of parameters, and the parameters have the same types
4178 // Pick the "more specific" signature
4181 MethodBase orig_candidate = TypeManager.DropGenericMethodArguments (candidate);
4182 MethodBase orig_best = TypeManager.DropGenericMethodArguments (best);
4184 ParameterData orig_candidate_pd = TypeManager.GetParameterData (orig_candidate);
4185 ParameterData orig_best_pd = TypeManager.GetParameterData (orig_best);
4187 bool specific_at_least_once = false;
4188 for (int j = 0; j < candidate_param_count; ++j) {
4189 Type ct = TypeManager.TypeToCoreType (orig_candidate_pd.ParameterType (j));
4190 Type bt = TypeManager.TypeToCoreType (orig_best_pd.ParameterType (j));
4193 Type specific = MoreSpecific (ct, bt);
4197 specific_at_least_once = true;
4200 if (specific_at_least_once)
4203 // FIXME: handle lifted operators
4209 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4211 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4214 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4215 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4217 if (cand_pd.Count != base_pd.Count)
4220 for (int j = 0; j < cand_pd.Count; ++j) {
4221 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4222 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4223 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4224 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4226 if (cm != bm || ct != bt)
4233 public static string FullMethodDesc (MethodBase mb)
4239 if (mb is MethodInfo) {
4240 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4244 sb = new StringBuilder ();
4246 sb.Append (TypeManager.CSharpSignature (mb));
4247 return sb.ToString ();
4250 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4252 MemberInfo [] miset;
4253 MethodGroupExpr union;
4258 return (MethodGroupExpr) mg2;
4261 return (MethodGroupExpr) mg1;
4264 MethodGroupExpr left_set = null, right_set = null;
4265 int length1 = 0, length2 = 0;
4267 left_set = (MethodGroupExpr) mg1;
4268 length1 = left_set.Methods.Length;
4270 right_set = (MethodGroupExpr) mg2;
4271 length2 = right_set.Methods.Length;
4273 ArrayList common = new ArrayList ();
4275 foreach (MethodBase r in right_set.Methods){
4276 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4280 miset = new MemberInfo [length1 + length2 - common.Count];
4281 left_set.Methods.CopyTo (miset, 0);
4285 foreach (MethodBase r in right_set.Methods) {
4286 if (!common.Contains (r))
4290 union = new MethodGroupExpr (miset, loc);
4295 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4296 ArrayList arguments, int arg_count,
4297 ref MethodBase candidate)
4299 return IsParamsMethodApplicable (
4300 ec, me, arguments, arg_count, false, ref candidate) ||
4301 IsParamsMethodApplicable (
4302 ec, me, arguments, arg_count, true, ref candidate);
4307 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4308 ArrayList arguments, int arg_count,
4309 bool do_varargs, ref MethodBase candidate)
4312 if (!me.HasTypeArguments &&
4313 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4316 if (TypeManager.IsGenericMethodDefinition (candidate))
4317 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4320 return IsParamsMethodApplicable (
4321 ec, arguments, arg_count, candidate, do_varargs);
4325 /// Determines if the candidate method, if a params method, is applicable
4326 /// in its expanded form to the given set of arguments
4328 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4329 int arg_count, MethodBase candidate,
4332 ParameterData pd = TypeManager.GetParameterData (candidate);
4334 int pd_count = pd.Count;
4338 int count = pd_count - 1;
4340 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4342 if (pd_count != arg_count)
4349 if (count > arg_count)
4352 if (pd_count == 1 && arg_count == 0)
4356 // If we have come this far, the case which
4357 // remains is when the number of parameters is
4358 // less than or equal to the argument count.
4360 for (int i = 0; i < count; ++i) {
4362 Argument a = (Argument) arguments [i];
4364 Parameter.Modifier a_mod = a.Modifier &
4365 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4366 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4367 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4369 if (a_mod == p_mod) {
4371 if (a_mod == Parameter.Modifier.NONE)
4372 if (!Convert.ImplicitConversionExists (ec,
4374 pd.ParameterType (i)))
4377 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4378 Type pt = pd.ParameterType (i);
4381 pt = TypeManager.GetReferenceType (pt);
4392 Argument a = (Argument) arguments [count];
4393 if (!(a.Expr is Arglist))
4399 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
4401 for (int i = pd_count - 1; i < arg_count; i++) {
4402 Argument a = (Argument) arguments [i];
4404 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4411 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4412 ArrayList arguments, int arg_count,
4413 ref MethodBase candidate)
4416 if (!me.HasTypeArguments &&
4417 !TypeManager.InferTypeArguments (arguments, ref candidate))
4420 if (TypeManager.IsGenericMethodDefinition (candidate))
4421 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4424 return IsApplicable (ec, arguments, arg_count, candidate);
4428 /// Determines if the candidate method is applicable (section 14.4.2.1)
4429 /// to the given set of arguments
4431 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4432 MethodBase candidate)
4434 ParameterData pd = TypeManager.GetParameterData (candidate);
4436 if (arg_count != pd.Count)
4439 for (int i = arg_count; i > 0; ) {
4442 Argument a = (Argument) arguments [i];
4444 Parameter.Modifier a_mod = a.Modifier &
4445 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4447 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4448 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4450 if (a_mod == p_mod) {
4451 Type pt = pd.ParameterType (i);
4453 if (a_mod == Parameter.Modifier.NONE) {
4454 if (!TypeManager.IsEqual (a.Type, pt) &&
4455 !Convert.ImplicitConversionExists (ec, a.Expr, pt))
4469 static internal bool IsAncestralType (Type first_type, Type second_type)
4471 return first_type != second_type &&
4472 (TypeManager.IsSubclassOf (second_type, first_type) ||
4473 TypeManager.ImplementsInterface (second_type, first_type));
4477 /// Find the Applicable Function Members (7.4.2.1)
4479 /// me: Method Group expression with the members to select.
4480 /// it might contain constructors or methods (or anything
4481 /// that maps to a method).
4483 /// Arguments: ArrayList containing resolved Argument objects.
4485 /// loc: The location if we want an error to be reported, or a Null
4486 /// location for "probing" purposes.
4488 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4489 /// that is the best match of me on Arguments.
4492 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4493 ArrayList Arguments, bool may_fail,
4496 MethodBase method = null;
4497 bool method_params = false;
4498 Type applicable_type = null;
4500 ArrayList candidates = new ArrayList (2);
4501 ArrayList candidate_overrides = null;
4504 // Used to keep a map between the candidate
4505 // and whether it is being considered in its
4506 // normal or expanded form
4508 // false is normal form, true is expanded form
4510 Hashtable candidate_to_form = null;
4512 if (Arguments != null)
4513 arg_count = Arguments.Count;
4515 if ((me.Name == "Invoke") &&
4516 TypeManager.IsDelegateType (me.DeclaringType)) {
4517 Error_InvokeOnDelegate (loc);
4521 MethodBase[] methods = me.Methods;
4523 int nmethods = methods.Length;
4527 // Methods marked 'override' don't take part in 'applicable_type'
4528 // computation, nor in the actual overload resolution.
4529 // However, they still need to be emitted instead of a base virtual method.
4530 // So, we salt them away into the 'candidate_overrides' array.
4532 // In case of reflected methods, we replace each overriding method with
4533 // its corresponding base virtual method. This is to improve compatibility
4534 // with non-C# libraries which change the visibility of overrides (#75636)
4537 for (int i = 0; i < methods.Length; ++i) {
4538 MethodBase m = methods [i];
4540 Type [] gen_args = null;
4541 if (m.IsGenericMethod && !m.IsGenericMethodDefinition)
4542 gen_args = m.GetGenericArguments ();
4544 if (TypeManager.IsOverride (m)) {
4545 if (candidate_overrides == null)
4546 candidate_overrides = new ArrayList ();
4547 candidate_overrides.Add (m);
4548 m = TypeManager.TryGetBaseDefinition (m);
4550 if (m != null && gen_args != null) {
4551 if (!m.IsGenericMethodDefinition)
4552 throw new InternalErrorException ("GetBaseDefinition didn't return a GenericMethodDefinition");
4553 m = ((MethodInfo) m).MakeGenericMethod (gen_args);
4563 int applicable_errors = Report.Errors;
4566 // First we construct the set of applicable methods
4568 bool is_sorted = true;
4569 for (int i = 0; i < nmethods; i++){
4570 Type decl_type = methods [i].DeclaringType;
4573 // If we have already found an applicable method
4574 // we eliminate all base types (Section 14.5.5.1)
4576 if (applicable_type != null && IsAncestralType (decl_type, applicable_type))
4580 // Check if candidate is applicable (section 14.4.2.1)
4581 // Is candidate applicable in normal form?
4583 bool is_applicable = IsApplicable (ec, me, Arguments, arg_count, ref methods [i]);
4585 if (!is_applicable && IsParamsMethodApplicable (ec, me, Arguments, arg_count, ref methods [i])) {
4586 MethodBase candidate = methods [i];
4587 if (candidate_to_form == null)
4588 candidate_to_form = new PtrHashtable ();
4589 candidate_to_form [candidate] = candidate;
4590 // Candidate is applicable in expanded form
4591 is_applicable = true;
4597 candidates.Add (methods [i]);
4599 if (applicable_type == null)
4600 applicable_type = decl_type;
4601 else if (applicable_type != decl_type) {
4603 if (IsAncestralType (applicable_type, decl_type))
4604 applicable_type = decl_type;
4608 if (applicable_errors != Report.Errors)
4611 int candidate_top = candidates.Count;
4613 if (applicable_type == null) {
4615 // Okay so we have failed to find anything so we
4616 // return by providing info about the closest match
4618 int errors = Report.Errors;
4619 for (int i = 0; i < nmethods; ++i) {
4620 MethodBase c = (MethodBase) methods [i];
4621 ParameterData pd = TypeManager.GetParameterData (c);
4623 if (pd.Count != arg_count)
4627 if (!TypeManager.InferTypeArguments (Arguments, ref c))
4629 if (TypeManager.IsGenericMethodDefinition (c))
4633 VerifyArgumentsCompat (ec, Arguments, arg_count,
4634 c, false, null, may_fail, loc);
4636 if (!may_fail && errors == Report.Errors)
4637 throw new InternalErrorException (
4638 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4639 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4644 if (!may_fail && errors == Report.Errors) {
4645 string report_name = me.Name;
4646 if (report_name == ".ctor")
4647 report_name = me.DeclaringType.ToString ();
4653 for (int i = 0; i < methods.Length; ++i) {
4654 MethodBase c = methods [i];
4655 ParameterData pd = TypeManager.GetParameterData (c);
4657 if (pd.Count != arg_count)
4660 if (TypeManager.InferTypeArguments (Arguments, ref c))
4664 411, loc, "The type arguments for " +
4665 "method `{0}' cannot be infered from " +
4666 "the usage. Try specifying the type " +
4667 "arguments explicitly.", report_name);
4672 Error_WrongNumArguments (loc, report_name, arg_count);
4680 // At this point, applicable_type is _one_ of the most derived types
4681 // in the set of types containing the methods in this MethodGroup.
4682 // Filter the candidates so that they only contain methods from the
4683 // most derived types.
4686 int finalized = 0; // Number of finalized candidates
4689 // Invariant: applicable_type is a most derived type
4691 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4692 // eliminating all it's base types. At the same time, we'll also move
4693 // every unrelated type to the end of the array, and pick the next
4694 // 'applicable_type'.
4696 Type next_applicable_type = null;
4697 int j = finalized; // where to put the next finalized candidate
4698 int k = finalized; // where to put the next undiscarded candidate
4699 for (int i = finalized; i < candidate_top; ++i) {
4700 MethodBase candidate = (MethodBase) candidates [i];
4701 Type decl_type = candidate.DeclaringType;
4703 if (decl_type == applicable_type) {
4704 candidates [k++] = candidates [j];
4705 candidates [j++] = candidates [i];
4709 if (IsAncestralType (decl_type, applicable_type))
4712 if (next_applicable_type != null &&
4713 IsAncestralType (decl_type, next_applicable_type))
4716 candidates [k++] = candidates [i];
4718 if (next_applicable_type == null ||
4719 IsAncestralType (next_applicable_type, decl_type))
4720 next_applicable_type = decl_type;
4723 applicable_type = next_applicable_type;
4726 } while (applicable_type != null);
4730 // Now we actually find the best method
4733 method = (MethodBase) candidates [0];
4734 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4735 for (int ix = 1; ix < candidate_top; ix++){
4736 MethodBase candidate = (MethodBase) candidates [ix];
4738 if (candidate == method)
4741 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4743 if (BetterFunction (ec, Arguments, arg_count,
4744 candidate, cand_params,
4745 method, method_params)) {
4747 method_params = cand_params;
4751 // Now check that there are no ambiguities i.e the selected method
4752 // should be better than all the others
4754 MethodBase ambiguous = null;
4755 for (int ix = 0; ix < candidate_top; ix++){
4756 MethodBase candidate = (MethodBase) candidates [ix];
4758 if (candidate == method)
4761 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4762 if (!BetterFunction (ec, Arguments, arg_count,
4763 method, method_params,
4764 candidate, cand_params)) {
4765 Report.SymbolRelatedToPreviousError (candidate);
4766 ambiguous = candidate;
4770 if (ambiguous != null) {
4771 Report.SymbolRelatedToPreviousError (method);
4772 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4773 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4778 // If the method is a virtual function, pick an override closer to the LHS type.
4780 if (!me.IsBase && method.IsVirtual) {
4781 if (TypeManager.IsOverride (method))
4782 throw new InternalErrorException (
4783 "Should not happen. An 'override' method took part in overload resolution: " + method);
4785 if (candidate_overrides != null)
4786 foreach (MethodBase candidate in candidate_overrides) {
4787 if (IsOverride (candidate, method))
4793 // And now check if the arguments are all
4794 // compatible, perform conversions if
4795 // necessary etc. and return if everything is
4798 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4799 method_params, null, may_fail, loc))
4805 MethodBase the_method = TypeManager.DropGenericMethodArguments (method);
4807 if (the_method.IsGenericMethodDefinition &&
4808 !ConstraintChecker.CheckConstraints (ec, the_method, method, loc))
4812 IMethodData data = TypeManager.GetMethod (the_method);
4814 data.SetMemberIsUsed ();
4819 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4821 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4822 name, arg_count.ToString ());
4825 static void Error_InvokeOnDelegate (Location loc)
4827 Report.Error (1533, loc,
4828 "Invoke cannot be called directly on a delegate");
4831 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4832 Type delegate_type, Argument a, ParameterData expected_par)
4834 if (delegate_type == null)
4835 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4836 TypeManager.CSharpSignature (method));
4838 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4839 TypeManager.CSharpName (delegate_type));
4841 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4843 string index = (idx + 1).ToString ();
4844 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4845 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4846 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4847 index, Parameter.GetModifierSignature (a.Modifier));
4849 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4850 index, Parameter.GetModifierSignature (mod));
4852 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4853 index, Argument.FullDesc (a), expected_par.ParameterDesc (idx));
4857 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4858 int arg_count, MethodBase method,
4859 bool chose_params_expanded,
4860 Type delegate_type, bool may_fail,
4863 ParameterData pd = TypeManager.GetParameterData (method);
4865 for (j = 0; j < arg_count; j++) {
4866 Argument a = (Argument) Arguments [j];
4867 Expression a_expr = a.Expr;
4868 Type parameter_type = pd.ParameterType (j);
4869 Parameter.Modifier pm = pd.ParameterModifier (j);
4870 Parameter.Modifier am = a.Modifier;
4872 if (pm == Parameter.Modifier.ARGLIST) {
4873 if (!(a.Expr is Arglist))
4878 if (pm == Parameter.Modifier.PARAMS) {
4879 pm = Parameter.Modifier.NONE;
4880 if (chose_params_expanded)
4881 parameter_type = TypeManager.GetElementType (parameter_type);
4887 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4888 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4891 Expression conv = Convert.ImplicitConversion (ec, a_expr, parameter_type, loc);
4895 // Update the argument with the implicit conversion
4900 if (parameter_type.IsPointer && !ec.InUnsafe) {
4910 Error_InvalidArguments (loc, j, method, delegate_type, (Argument) Arguments [j], pd);
4914 private bool resolved = false;
4915 public override Expression DoResolve (EmitContext ec)
4918 return this.method == null ? null : this;
4922 // First, resolve the expression that is used to
4923 // trigger the invocation
4925 SimpleName sn = expr as SimpleName;
4927 expr = sn.GetMethodGroup ();
4929 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4933 if (!(expr is MethodGroupExpr)) {
4934 Type expr_type = expr.Type;
4936 if (expr_type != null){
4937 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4939 return (new DelegateInvocation (
4940 this.expr, Arguments, loc)).Resolve (ec);
4944 if (!(expr is MethodGroupExpr)){
4945 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4950 // Next, evaluate all the expressions in the argument list
4952 if (Arguments != null){
4953 foreach (Argument a in Arguments){
4954 if (!a.Resolve (ec, loc))
4959 MethodGroupExpr mg = (MethodGroupExpr) expr;
4960 MethodBase method = OverloadResolve (ec, mg, Arguments, false, loc);
4965 MethodInfo mi = method as MethodInfo;
4967 type = TypeManager.TypeToCoreType (mi.ReturnType);
4968 Expression iexpr = mg.InstanceExpression;
4970 if (iexpr == null ||
4971 iexpr is This || iexpr is EmptyExpression ||
4972 mg.IdenticalTypeName) {
4973 mg.InstanceExpression = null;
4975 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
4979 if (iexpr == null || iexpr is EmptyExpression) {
4980 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
4986 if (type.IsPointer){
4994 // Only base will allow this invocation to happen.
4996 if (mg.IsBase && method.IsAbstract){
4997 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
5001 if (Arguments == null && method.Name == "Finalize") {
5003 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
5005 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
5009 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
5013 if (mg.InstanceExpression != null)
5014 mg.InstanceExpression.CheckMarshalByRefAccess ();
5016 eclass = ExprClass.Value;
5017 this.method = method;
5021 bool IsSpecialMethodInvocation (MethodBase method)
5023 IMethodData md = TypeManager.GetMethod (method);
5025 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
5028 if (!TypeManager.IsSpecialMethod (method))
5031 int args = TypeManager.GetParameterData (method).Count;
5032 if (method.Name.StartsWith ("get_") && args > 0)
5034 else if (method.Name.StartsWith ("set_") && args > 2)
5037 // TODO: check operators and events as well ?
5040 Report.SymbolRelatedToPreviousError (method);
5041 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
5042 TypeManager.CSharpSignature (method, true));
5048 // Emits the list of arguments as an array
5050 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
5052 ILGenerator ig = ec.ig;
5053 int count = arguments.Count - idx;
5054 Argument a = (Argument) arguments [idx];
5055 Type t = a.Expr.Type;
5057 IntConstant.EmitInt (ig, count);
5058 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
5060 int top = arguments.Count;
5061 for (int j = idx; j < top; j++){
5062 a = (Argument) arguments [j];
5064 ig.Emit (OpCodes.Dup);
5065 IntConstant.EmitInt (ig, j - idx);
5067 bool is_stobj, has_type_arg;
5068 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
5070 ig.Emit (OpCodes.Ldelema, t);
5082 /// Emits a list of resolved Arguments that are in the arguments
5085 /// The MethodBase argument might be null if the
5086 /// emission of the arguments is known not to contain
5087 /// a `params' field (for example in constructors or other routines
5088 /// that keep their arguments in this structure)
5090 /// if `dup_args' is true, a copy of the arguments will be left
5091 /// on the stack. If `dup_args' is true, you can specify `this_arg'
5092 /// which will be duplicated before any other args. Only EmitCall
5093 /// should be using this interface.
5095 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
5097 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
5098 int top = arguments == null ? 0 : arguments.Count;
5099 LocalTemporary [] temps = null;
5101 if (dup_args && top != 0)
5102 temps = new LocalTemporary [top];
5104 for (int i = 0; i < top; i++){
5105 Argument a = (Argument) arguments [i];
5108 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
5110 // Special case if we are passing the same data as the
5111 // params argument, do not put it in an array.
5113 if (pd.ParameterType (i) == a.Type)
5116 EmitParams (ec, i, arguments);
5123 ec.ig.Emit (OpCodes.Dup);
5124 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
5129 if (this_arg != null)
5132 for (int i = 0; i < top; i ++) {
5133 temps [i].Emit (ec);
5134 temps [i].Release (ec);
5138 if (pd != null && pd.Count > top &&
5139 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
5140 ILGenerator ig = ec.ig;
5142 IntConstant.EmitInt (ig, 0);
5143 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
5147 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
5149 ParameterData pd = TypeManager.GetParameterData (mb);
5151 if (arguments == null)
5152 return new Type [0];
5154 Argument a = (Argument) arguments [pd.Count - 1];
5155 Arglist list = (Arglist) a.Expr;
5157 return list.ArgumentTypes;
5161 /// This checks the ConditionalAttribute on the method
5163 static bool IsMethodExcluded (MethodBase method)
5165 if (method.IsConstructor)
5168 IMethodData md = TypeManager.GetMethod (method);
5170 return md.IsExcluded ();
5172 // For some methods (generated by delegate class) GetMethod returns null
5173 // because they are not included in builder_to_method table
5174 if (method.DeclaringType is TypeBuilder)
5177 return AttributeTester.IsConditionalMethodExcluded (method);
5181 /// is_base tells whether we want to force the use of the `call'
5182 /// opcode instead of using callvirt. Call is required to call
5183 /// a specific method, while callvirt will always use the most
5184 /// recent method in the vtable.
5186 /// is_static tells whether this is an invocation on a static method
5188 /// instance_expr is an expression that represents the instance
5189 /// it must be non-null if is_static is false.
5191 /// method is the method to invoke.
5193 /// Arguments is the list of arguments to pass to the method or constructor.
5195 public static void EmitCall (EmitContext ec, bool is_base,
5196 bool is_static, Expression instance_expr,
5197 MethodBase method, ArrayList Arguments, Location loc)
5199 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
5202 // `dup_args' leaves an extra copy of the arguments on the stack
5203 // `omit_args' does not leave any arguments at all.
5204 // So, basically, you could make one call with `dup_args' set to true,
5205 // and then another with `omit_args' set to true, and the two calls
5206 // would have the same set of arguments. However, each argument would
5207 // only have been evaluated once.
5208 public static void EmitCall (EmitContext ec, bool is_base,
5209 bool is_static, Expression instance_expr,
5210 MethodBase method, ArrayList Arguments, Location loc,
5211 bool dup_args, bool omit_args)
5213 ILGenerator ig = ec.ig;
5214 bool struct_call = false;
5215 bool this_call = false;
5216 LocalTemporary this_arg = null;
5218 Type decl_type = method.DeclaringType;
5220 if (!RootContext.StdLib) {
5221 // Replace any calls to the system's System.Array type with calls to
5222 // the newly created one.
5223 if (method == TypeManager.system_int_array_get_length)
5224 method = TypeManager.int_array_get_length;
5225 else if (method == TypeManager.system_int_array_get_rank)
5226 method = TypeManager.int_array_get_rank;
5227 else if (method == TypeManager.system_object_array_clone)
5228 method = TypeManager.object_array_clone;
5229 else if (method == TypeManager.system_int_array_get_length_int)
5230 method = TypeManager.int_array_get_length_int;
5231 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5232 method = TypeManager.int_array_get_lower_bound_int;
5233 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5234 method = TypeManager.int_array_get_upper_bound_int;
5235 else if (method == TypeManager.system_void_array_copyto_array_int)
5236 method = TypeManager.void_array_copyto_array_int;
5239 if (!ec.IsInObsoleteScope) {
5241 // This checks ObsoleteAttribute on the method and on the declaring type
5243 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5245 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5247 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5249 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5253 if (IsMethodExcluded (method))
5257 if (instance_expr == EmptyExpression.Null) {
5258 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5262 this_call = instance_expr is This;
5263 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5267 // If this is ourselves, push "this"
5271 Type iexpr_type = instance_expr.Type;
5274 // Push the instance expression
5276 if (TypeManager.IsValueType (iexpr_type)) {
5278 // Special case: calls to a function declared in a
5279 // reference-type with a value-type argument need
5280 // to have their value boxed.
5281 if (decl_type.IsValueType ||
5282 TypeManager.IsGenericParameter (iexpr_type)) {
5284 // If the expression implements IMemoryLocation, then
5285 // we can optimize and use AddressOf on the
5288 // If not we have to use some temporary storage for
5290 if (instance_expr is IMemoryLocation) {
5291 ((IMemoryLocation)instance_expr).
5292 AddressOf (ec, AddressOp.LoadStore);
5294 LocalTemporary temp = new LocalTemporary (iexpr_type);
5295 instance_expr.Emit (ec);
5297 temp.AddressOf (ec, AddressOp.Load);
5300 // avoid the overhead of doing this all the time.
5302 t = TypeManager.GetReferenceType (iexpr_type);
5304 instance_expr.Emit (ec);
5305 ig.Emit (OpCodes.Box, instance_expr.Type);
5306 t = TypeManager.object_type;
5309 instance_expr.Emit (ec);
5310 t = instance_expr.Type;
5314 ig.Emit (OpCodes.Dup);
5315 if (Arguments != null && Arguments.Count != 0) {
5316 this_arg = new LocalTemporary (t);
5317 this_arg.Store (ec);
5324 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5327 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
5328 ig.Emit (OpCodes.Constrained, instance_expr.Type);
5332 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5333 call_op = OpCodes.Call;
5335 call_op = OpCodes.Callvirt;
5337 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5338 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5339 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5346 // and DoFoo is not virtual, you can omit the callvirt,
5347 // because you don't need the null checking behavior.
5349 if (method is MethodInfo)
5350 ig.Emit (call_op, (MethodInfo) method);
5352 ig.Emit (call_op, (ConstructorInfo) method);
5355 public override void Emit (EmitContext ec)
5357 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5359 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5362 public override void EmitStatement (EmitContext ec)
5367 // Pop the return value if there is one
5369 if (method is MethodInfo){
5370 Type ret = ((MethodInfo)method).ReturnType;
5371 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5372 ec.ig.Emit (OpCodes.Pop);
5377 public class InvocationOrCast : ExpressionStatement
5380 Expression argument;
5382 public InvocationOrCast (Expression expr, Expression argument)
5385 this.argument = argument;
5386 this.loc = expr.Location;
5389 public override Expression DoResolve (EmitContext ec)
5392 // First try to resolve it as a cast.
5394 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5395 if ((te != null) && (te.eclass == ExprClass.Type)) {
5396 Cast cast = new Cast (te, argument, loc);
5397 return cast.Resolve (ec);
5401 // This can either be a type or a delegate invocation.
5402 // Let's just resolve it and see what we'll get.
5404 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5409 // Ok, so it's a Cast.
5411 if (expr.eclass == ExprClass.Type) {
5412 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5413 return cast.Resolve (ec);
5417 // It's a delegate invocation.
5419 if (!TypeManager.IsDelegateType (expr.Type)) {
5420 Error (149, "Method name expected");
5424 ArrayList args = new ArrayList ();
5425 args.Add (new Argument (argument, Argument.AType.Expression));
5426 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5427 return invocation.Resolve (ec);
5432 Error (201, "Only assignment, call, increment, decrement and new object " +
5433 "expressions can be used as a statement");
5436 public override ExpressionStatement ResolveStatement (EmitContext ec)
5439 // First try to resolve it as a cast.
5441 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5442 if ((te != null) && (te.eclass == ExprClass.Type)) {
5448 // This can either be a type or a delegate invocation.
5449 // Let's just resolve it and see what we'll get.
5451 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5452 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5458 // It's a delegate invocation.
5460 if (!TypeManager.IsDelegateType (expr.Type)) {
5461 Error (149, "Method name expected");
5465 ArrayList args = new ArrayList ();
5466 args.Add (new Argument (argument, Argument.AType.Expression));
5467 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5468 return invocation.ResolveStatement (ec);
5471 public override void Emit (EmitContext ec)
5473 throw new Exception ("Cannot happen");
5476 public override void EmitStatement (EmitContext ec)
5478 throw new Exception ("Cannot happen");
5483 // This class is used to "disable" the code generation for the
5484 // temporary variable when initializing value types.
5486 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5487 public void AddressOf (EmitContext ec, AddressOp Mode)
5494 /// Implements the new expression
5496 public class New : ExpressionStatement, IMemoryLocation {
5497 public readonly ArrayList Arguments;
5500 // During bootstrap, it contains the RequestedType,
5501 // but if `type' is not null, it *might* contain a NewDelegate
5502 // (because of field multi-initialization)
5504 public Expression RequestedType;
5506 MethodBase method = null;
5509 // If set, the new expression is for a value_target, and
5510 // we will not leave anything on the stack.
5512 Expression value_target;
5513 bool value_target_set = false;
5514 bool is_type_parameter = false;
5516 public New (Expression requested_type, ArrayList arguments, Location l)
5518 RequestedType = requested_type;
5519 Arguments = arguments;
5523 public bool SetValueTypeVariable (Expression value)
5525 value_target = value;
5526 value_target_set = true;
5527 if (!(value_target is IMemoryLocation)){
5528 Error_UnexpectedKind (null, "variable", loc);
5535 // This function is used to disable the following code sequence for
5536 // value type initialization:
5538 // AddressOf (temporary)
5542 // Instead the provide will have provided us with the address on the
5543 // stack to store the results.
5545 static Expression MyEmptyExpression;
5547 public void DisableTemporaryValueType ()
5549 if (MyEmptyExpression == null)
5550 MyEmptyExpression = new EmptyAddressOf ();
5553 // To enable this, look into:
5554 // test-34 and test-89 and self bootstrapping.
5556 // For instance, we can avoid a copy by using `newobj'
5557 // instead of Call + Push-temp on value types.
5558 // value_target = MyEmptyExpression;
5563 /// Converts complex core type syntax like 'new int ()' to simple constant
5565 public static Constant Constantify (Type t)
5567 if (t == TypeManager.int32_type)
5568 return new IntConstant (0, Location.Null);
5569 if (t == TypeManager.uint32_type)
5570 return new UIntConstant (0, Location.Null);
5571 if (t == TypeManager.int64_type)
5572 return new LongConstant (0, Location.Null);
5573 if (t == TypeManager.uint64_type)
5574 return new ULongConstant (0, Location.Null);
5575 if (t == TypeManager.float_type)
5576 return new FloatConstant (0, Location.Null);
5577 if (t == TypeManager.double_type)
5578 return new DoubleConstant (0, Location.Null);
5579 if (t == TypeManager.short_type)
5580 return new ShortConstant (0, Location.Null);
5581 if (t == TypeManager.ushort_type)
5582 return new UShortConstant (0, Location.Null);
5583 if (t == TypeManager.sbyte_type)
5584 return new SByteConstant (0, Location.Null);
5585 if (t == TypeManager.byte_type)
5586 return new ByteConstant (0, Location.Null);
5587 if (t == TypeManager.char_type)
5588 return new CharConstant ('\0', Location.Null);
5589 if (t == TypeManager.bool_type)
5590 return new BoolConstant (false, Location.Null);
5591 if (t == TypeManager.decimal_type)
5592 return new DecimalConstant (0, Location.Null);
5593 if (TypeManager.IsEnumType (t))
5594 return new EnumConstant (Constantify (TypeManager.EnumToUnderlying (t)), t);
5600 // Checks whether the type is an interface that has the
5601 // [ComImport, CoClass] attributes and must be treated
5604 public Expression CheckComImport (EmitContext ec)
5606 if (!type.IsInterface)
5610 // Turn the call into:
5611 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5613 Type real_class = AttributeTester.GetCoClassAttribute (type);
5614 if (real_class == null)
5617 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5618 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5619 return cast.Resolve (ec);
5622 public override Expression DoResolve (EmitContext ec)
5625 // The New DoResolve might be called twice when initializing field
5626 // expressions (see EmitFieldInitializers, the call to
5627 // GetInitializerExpression will perform a resolve on the expression,
5628 // and later the assign will trigger another resolution
5630 // This leads to bugs (#37014)
5633 if (RequestedType is NewDelegate)
5634 return RequestedType;
5638 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5644 if (type == TypeManager.void_type) {
5645 Error_VoidInvalidInTheContext (loc);
5649 if (Arguments == null) {
5650 Expression c = Constantify (type);
5655 if (TypeManager.IsDelegateType (type)) {
5656 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5657 if (RequestedType != null)
5658 if (!(RequestedType is DelegateCreation))
5659 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5660 return RequestedType;
5664 if (type.IsGenericParameter) {
5665 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5667 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5668 Error (304, String.Format (
5669 "Cannot create an instance of the " +
5670 "variable type '{0}' because it " +
5671 "doesn't have the new() constraint",
5676 if ((Arguments != null) && (Arguments.Count != 0)) {
5677 Error (417, String.Format (
5678 "`{0}': cannot provide arguments " +
5679 "when creating an instance of a " +
5680 "variable type.", type));
5684 is_type_parameter = true;
5685 eclass = ExprClass.Value;
5690 if (type.IsAbstract && type.IsSealed) {
5691 Report.SymbolRelatedToPreviousError (type);
5692 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5696 if (type.IsInterface || type.IsAbstract){
5697 RequestedType = CheckComImport (ec);
5698 if (RequestedType != null)
5699 return RequestedType;
5701 Report.SymbolRelatedToPreviousError (type);
5702 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5706 bool is_struct = type.IsValueType;
5707 eclass = ExprClass.Value;
5710 // SRE returns a match for .ctor () on structs (the object constructor),
5711 // so we have to manually ignore it.
5713 if (is_struct && Arguments == null)
5716 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5717 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5718 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5723 MethodGroupExpr mg = ml as MethodGroupExpr;
5726 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5730 if (Arguments != null){
5731 foreach (Argument a in Arguments){
5732 if (!a.Resolve (ec, loc))
5737 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5738 if (method == null) {
5739 if (almostMatchedMembers.Count != 0)
5740 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5747 bool DoEmitTypeParameter (EmitContext ec)
5750 ILGenerator ig = ec.ig;
5752 ig.Emit (OpCodes.Ldtoken, type);
5753 ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
5754 ig.Emit (OpCodes.Call, TypeManager.activator_create_instance);
5755 ig.Emit (OpCodes.Unbox_Any, type);
5758 throw new InternalErrorException ();
5763 // This DoEmit can be invoked in two contexts:
5764 // * As a mechanism that will leave a value on the stack (new object)
5765 // * As one that wont (init struct)
5767 // You can control whether a value is required on the stack by passing
5768 // need_value_on_stack. The code *might* leave a value on the stack
5769 // so it must be popped manually
5771 // If we are dealing with a ValueType, we have a few
5772 // situations to deal with:
5774 // * The target is a ValueType, and we have been provided
5775 // the instance (this is easy, we are being assigned).
5777 // * The target of New is being passed as an argument,
5778 // to a boxing operation or a function that takes a
5781 // In this case, we need to create a temporary variable
5782 // that is the argument of New.
5784 // Returns whether a value is left on the stack
5786 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5788 bool is_value_type = TypeManager.IsValueType (type);
5789 ILGenerator ig = ec.ig;
5794 // Allow DoEmit() to be called multiple times.
5795 // We need to create a new LocalTemporary each time since
5796 // you can't share LocalBuilders among ILGeneators.
5797 if (!value_target_set)
5798 value_target = new LocalTemporary (type);
5800 ml = (IMemoryLocation) value_target;
5801 ml.AddressOf (ec, AddressOp.Store);
5805 Invocation.EmitArguments (ec, method, Arguments, false, null);
5809 ig.Emit (OpCodes.Initobj, type);
5811 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5812 if (need_value_on_stack){
5813 value_target.Emit (ec);
5818 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5823 public override void Emit (EmitContext ec)
5825 if (is_type_parameter)
5826 DoEmitTypeParameter (ec);
5831 public override void EmitStatement (EmitContext ec)
5833 if (is_type_parameter)
5834 throw new InvalidOperationException ();
5836 if (DoEmit (ec, false))
5837 ec.ig.Emit (OpCodes.Pop);
5840 public void AddressOf (EmitContext ec, AddressOp Mode)
5842 if (is_type_parameter)
5843 throw new InvalidOperationException ();
5845 if (!type.IsValueType){
5847 // We throw an exception. So far, I believe we only need to support
5849 // foreach (int j in new StructType ())
5852 throw new Exception ("AddressOf should not be used for classes");
5855 if (!value_target_set)
5856 value_target = new LocalTemporary (type);
5858 IMemoryLocation ml = (IMemoryLocation) value_target;
5859 ml.AddressOf (ec, AddressOp.Store);
5861 Invocation.EmitArguments (ec, method, Arguments, false, null);
5864 ec.ig.Emit (OpCodes.Initobj, type);
5866 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5868 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5873 /// 14.5.10.2: Represents an array creation expression.
5877 /// There are two possible scenarios here: one is an array creation
5878 /// expression that specifies the dimensions and optionally the
5879 /// initialization data and the other which does not need dimensions
5880 /// specified but where initialization data is mandatory.
5882 public class ArrayCreation : Expression {
5883 Expression requested_base_type;
5884 ArrayList initializers;
5887 // The list of Argument types.
5888 // This is used to construct the `newarray' or constructor signature
5890 ArrayList arguments;
5893 // Method used to create the array object.
5895 MethodBase new_method = null;
5897 Type array_element_type;
5898 Type underlying_type;
5899 bool is_one_dimensional = false;
5900 bool is_builtin_type = false;
5901 bool expect_initializers = false;
5902 int num_arguments = 0;
5906 ArrayList array_data;
5910 // The number of constants in array initializers
5911 int const_initializers_count;
5913 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5915 this.requested_base_type = requested_base_type;
5916 this.initializers = initializers;
5920 arguments = new ArrayList ();
5922 foreach (Expression e in exprs) {
5923 arguments.Add (new Argument (e, Argument.AType.Expression));
5928 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5930 this.requested_base_type = requested_base_type;
5931 this.initializers = initializers;
5935 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5937 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5939 //dimensions = tmp.Length - 1;
5940 expect_initializers = true;
5943 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5945 StringBuilder sb = new StringBuilder (rank);
5948 for (int i = 1; i < idx_count; i++)
5953 return new ComposedCast (base_type, sb.ToString (), loc);
5956 void Error_IncorrectArrayInitializer ()
5958 Error (178, "Invalid rank specifier: expected `,' or `]'");
5961 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5963 if (specified_dims) {
5964 Argument a = (Argument) arguments [idx];
5966 if (!a.Resolve (ec, loc))
5969 Constant c = a.Expr as Constant;
5971 c = c.ImplicitConversionRequired (TypeManager.int32_type, a.Expr.Location);
5975 Report.Error (150, a.Expr.Location, "A constant value is expected");
5979 int value = (int) c.GetValue ();
5981 if (value != probe.Count) {
5982 Error_IncorrectArrayInitializer ();
5986 bounds [idx] = value;
5989 int child_bounds = -1;
5990 for (int i = 0; i < probe.Count; ++i) {
5991 object o = probe [i];
5992 if (o is ArrayList) {
5993 ArrayList sub_probe = o as ArrayList;
5994 int current_bounds = sub_probe.Count;
5996 if (child_bounds == -1)
5997 child_bounds = current_bounds;
5999 else if (child_bounds != current_bounds){
6000 Error_IncorrectArrayInitializer ();
6003 if (idx + 1 >= dimensions){
6004 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
6008 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
6012 if (child_bounds != -1){
6013 Error_IncorrectArrayInitializer ();
6017 Expression tmp = (Expression) o;
6018 tmp = tmp.Resolve (ec);
6022 Expression conv = Convert.ImplicitConversionRequired (
6023 ec, tmp, underlying_type, loc);
6028 // Initializers with the default values can be ignored
6029 Constant c = tmp as Constant;
6031 if (c.IsDefaultInitializer (array_element_type)) {
6035 ++const_initializers_count;
6038 // Used to invalidate static initializer
6039 const_initializers_count = int.MinValue;
6042 array_data.Add (conv);
6049 public void UpdateIndices ()
6052 for (ArrayList probe = initializers; probe != null;) {
6053 if (probe.Count > 0 && probe [0] is ArrayList) {
6054 Expression e = new IntConstant (probe.Count, Location.Null);
6055 arguments.Add (new Argument (e, Argument.AType.Expression));
6057 bounds [i++] = probe.Count;
6059 probe = (ArrayList) probe [0];
6062 Expression e = new IntConstant (probe.Count, Location.Null);
6063 arguments.Add (new Argument (e, Argument.AType.Expression));
6065 bounds [i++] = probe.Count;
6072 bool ResolveInitializers (EmitContext ec)
6074 if (initializers == null) {
6075 return !expect_initializers;
6078 if (underlying_type == null)
6082 // We use this to store all the date values in the order in which we
6083 // will need to store them in the byte blob later
6085 array_data = new ArrayList ();
6086 bounds = new System.Collections.Specialized.HybridDictionary ();
6088 if (arguments != null)
6089 return CheckIndices (ec, initializers, 0, true);
6091 arguments = new ArrayList ();
6093 if (!CheckIndices (ec, initializers, 0, false))
6098 if (arguments.Count != dimensions) {
6099 Error_IncorrectArrayInitializer ();
6107 // Creates the type of the array
6109 bool LookupType (EmitContext ec)
6111 StringBuilder array_qualifier = new StringBuilder (rank);
6114 // `In the first form allocates an array instace of the type that results
6115 // from deleting each of the individual expression from the expression list'
6117 if (num_arguments > 0) {
6118 array_qualifier.Append ("[");
6119 for (int i = num_arguments-1; i > 0; i--)
6120 array_qualifier.Append (",");
6121 array_qualifier.Append ("]");
6127 TypeExpr array_type_expr;
6128 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
6129 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
6130 if (array_type_expr == null)
6133 type = array_type_expr.Type;
6134 underlying_type = TypeManager.GetElementType (type);
6135 dimensions = type.GetArrayRank ();
6140 public override Expression DoResolve (EmitContext ec)
6145 if (!LookupType (ec))
6148 array_element_type = TypeManager.GetElementType (type);
6149 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
6150 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
6155 // First step is to validate the initializers and fill
6156 // in any missing bits
6158 if (!ResolveInitializers (ec))
6162 if (arguments == null)
6165 arg_count = arguments.Count;
6166 foreach (Argument a in arguments){
6167 if (!a.Resolve (ec, loc))
6170 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
6171 if (real_arg == null)
6178 if (arg_count == 1) {
6179 is_one_dimensional = true;
6180 eclass = ExprClass.Value;
6184 is_builtin_type = TypeManager.IsBuiltinType (type);
6186 if (is_builtin_type) {
6189 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
6190 AllBindingFlags, loc);
6192 if (!(ml is MethodGroupExpr)) {
6193 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
6198 Error (-6, "New invocation: Can not find a constructor for " +
6199 "this argument list");
6203 new_method = Invocation.OverloadResolve (
6204 ec, (MethodGroupExpr) ml, arguments, false, loc);
6206 if (new_method == null) {
6207 Error (-6, "New invocation: Can not find a constructor for " +
6208 "this argument list");
6212 eclass = ExprClass.Value;
6215 ModuleBuilder mb = CodeGen.Module.Builder;
6216 ArrayList args = new ArrayList ();
6218 if (arguments != null) {
6219 for (int i = 0; i < arg_count; i++)
6220 args.Add (TypeManager.int32_type);
6223 Type [] arg_types = null;
6226 arg_types = new Type [args.Count];
6228 args.CopyTo (arg_types, 0);
6230 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6233 if (new_method == null) {
6234 Error (-6, "New invocation: Can not find a constructor for " +
6235 "this argument list");
6239 eclass = ExprClass.Value;
6244 byte [] MakeByteBlob ()
6249 int count = array_data.Count;
6251 if (underlying_type.IsEnum)
6252 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
6254 factor = GetTypeSize (underlying_type);
6256 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
6258 data = new byte [(count * factor + 4) & ~3];
6261 for (int i = 0; i < count; ++i) {
6262 object v = array_data [i];
6264 if (v is EnumConstant)
6265 v = ((EnumConstant) v).Child;
6267 if (v is Constant && !(v is StringConstant))
6268 v = ((Constant) v).GetValue ();
6274 if (underlying_type == TypeManager.int64_type){
6275 if (!(v is Expression)){
6276 long val = (long) v;
6278 for (int j = 0; j < factor; ++j) {
6279 data [idx + j] = (byte) (val & 0xFF);
6283 } else if (underlying_type == TypeManager.uint64_type){
6284 if (!(v is Expression)){
6285 ulong val = (ulong) v;
6287 for (int j = 0; j < factor; ++j) {
6288 data [idx + j] = (byte) (val & 0xFF);
6292 } else if (underlying_type == TypeManager.float_type) {
6293 if (!(v is Expression)){
6294 element = BitConverter.GetBytes ((float) v);
6296 for (int j = 0; j < factor; ++j)
6297 data [idx + j] = element [j];
6299 } else if (underlying_type == TypeManager.double_type) {
6300 if (!(v is Expression)){
6301 element = BitConverter.GetBytes ((double) v);
6303 for (int j = 0; j < factor; ++j)
6304 data [idx + j] = element [j];
6306 } else if (underlying_type == TypeManager.char_type){
6307 if (!(v is Expression)){
6308 int val = (int) ((char) v);
6310 data [idx] = (byte) (val & 0xff);
6311 data [idx+1] = (byte) (val >> 8);
6313 } else if (underlying_type == TypeManager.short_type){
6314 if (!(v is Expression)){
6315 int val = (int) ((short) v);
6317 data [idx] = (byte) (val & 0xff);
6318 data [idx+1] = (byte) (val >> 8);
6320 } else if (underlying_type == TypeManager.ushort_type){
6321 if (!(v is Expression)){
6322 int val = (int) ((ushort) v);
6324 data [idx] = (byte) (val & 0xff);
6325 data [idx+1] = (byte) (val >> 8);
6327 } else if (underlying_type == TypeManager.int32_type) {
6328 if (!(v is Expression)){
6331 data [idx] = (byte) (val & 0xff);
6332 data [idx+1] = (byte) ((val >> 8) & 0xff);
6333 data [idx+2] = (byte) ((val >> 16) & 0xff);
6334 data [idx+3] = (byte) (val >> 24);
6336 } else if (underlying_type == TypeManager.uint32_type) {
6337 if (!(v is Expression)){
6338 uint val = (uint) v;
6340 data [idx] = (byte) (val & 0xff);
6341 data [idx+1] = (byte) ((val >> 8) & 0xff);
6342 data [idx+2] = (byte) ((val >> 16) & 0xff);
6343 data [idx+3] = (byte) (val >> 24);
6345 } else if (underlying_type == TypeManager.sbyte_type) {
6346 if (!(v is Expression)){
6347 sbyte val = (sbyte) v;
6348 data [idx] = (byte) val;
6350 } else if (underlying_type == TypeManager.byte_type) {
6351 if (!(v is Expression)){
6352 byte val = (byte) v;
6353 data [idx] = (byte) val;
6355 } else if (underlying_type == TypeManager.bool_type) {
6356 if (!(v is Expression)){
6357 bool val = (bool) v;
6358 data [idx] = (byte) (val ? 1 : 0);
6360 } else if (underlying_type == TypeManager.decimal_type){
6361 if (!(v is Expression)){
6362 int [] bits = Decimal.GetBits ((decimal) v);
6365 // FIXME: For some reason, this doesn't work on the MS runtime.
6366 int [] nbits = new int [4];
6367 nbits [0] = bits [3];
6368 nbits [1] = bits [2];
6369 nbits [2] = bits [0];
6370 nbits [3] = bits [1];
6372 for (int j = 0; j < 4; j++){
6373 data [p++] = (byte) (nbits [j] & 0xff);
6374 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6375 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6376 data [p++] = (byte) (nbits [j] >> 24);
6380 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6389 // Emits the initializers for the array
6391 void EmitStaticInitializers (EmitContext ec)
6394 // First, the static data
6397 ILGenerator ig = ec.ig;
6399 byte [] data = MakeByteBlob ();
6401 fb = RootContext.MakeStaticData (data);
6403 ig.Emit (OpCodes.Dup);
6404 ig.Emit (OpCodes.Ldtoken, fb);
6405 ig.Emit (OpCodes.Call,
6406 TypeManager.void_initializearray_array_fieldhandle);
6410 // Emits pieces of the array that can not be computed at compile
6411 // time (variables and string locations).
6413 // This always expect the top value on the stack to be the array
6415 void EmitDynamicInitializers (EmitContext ec)
6417 ILGenerator ig = ec.ig;
6418 int dims = bounds.Count;
6419 int [] current_pos = new int [dims];
6421 MethodInfo set = null;
6424 Type [] args = new Type [dims + 1];
6426 for (int j = 0; j < dims; j++)
6427 args [j] = TypeManager.int32_type;
6428 args [dims] = array_element_type;
6430 set = CodeGen.Module.Builder.GetArrayMethod (
6432 CallingConventions.HasThis | CallingConventions.Standard,
6433 TypeManager.void_type, args);
6436 for (int i = 0; i < array_data.Count; i++){
6438 Expression e = (Expression)array_data [i];
6441 Type etype = e.Type;
6443 ig.Emit (OpCodes.Dup);
6445 for (int idx = 0; idx < dims; idx++)
6446 IntConstant.EmitInt (ig, current_pos [idx]);
6449 // If we are dealing with a struct, get the
6450 // address of it, so we can store it.
6452 if ((dims == 1) && etype.IsValueType &&
6453 (!TypeManager.IsBuiltinOrEnum (etype) ||
6454 etype == TypeManager.decimal_type)) {
6459 // Let new know that we are providing
6460 // the address where to store the results
6462 n.DisableTemporaryValueType ();
6465 ig.Emit (OpCodes.Ldelema, etype);
6471 bool is_stobj, has_type_arg;
6472 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
6474 ig.Emit (OpCodes.Stobj, etype);
6475 else if (has_type_arg)
6476 ig.Emit (op, etype);
6480 ig.Emit (OpCodes.Call, set);
6487 for (int j = dims - 1; j >= 0; j--){
6489 if (current_pos [j] < (int) bounds [j])
6491 current_pos [j] = 0;
6496 void EmitArrayArguments (EmitContext ec)
6498 ILGenerator ig = ec.ig;
6500 foreach (Argument a in arguments) {
6501 Type atype = a.Type;
6504 if (atype == TypeManager.uint64_type)
6505 ig.Emit (OpCodes.Conv_Ovf_U4);
6506 else if (atype == TypeManager.int64_type)
6507 ig.Emit (OpCodes.Conv_Ovf_I4);
6511 public override void Emit (EmitContext ec)
6513 ILGenerator ig = ec.ig;
6515 EmitArrayArguments (ec);
6516 if (is_one_dimensional)
6517 ig.Emit (OpCodes.Newarr, array_element_type);
6519 if (is_builtin_type)
6520 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6522 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6525 if (initializers == null)
6528 // This is a treshold for static initializers
6529 // I tried to make more accurate but it seems to me that Array.Initialize is
6530 // always slower (managed -> unmanaged switch?)
6531 const int max_automatic_initializers = 200;
6533 if (const_initializers_count > max_automatic_initializers && TypeManager.IsPrimitiveType (array_element_type)) {
6534 EmitStaticInitializers (ec);
6538 EmitDynamicInitializers (ec);
6541 public override bool GetAttributableValue (Type valueType, out object value)
6543 if (!is_one_dimensional){
6544 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6545 return base.GetAttributableValue (null, out value);
6548 if (array_data == null) {
6549 Constant c = (Constant)((Argument)arguments [0]).Expr;
6550 if (c.IsDefaultValue) {
6551 value = Array.CreateInstance (array_element_type, 0);
6554 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6555 return base.GetAttributableValue (null, out value);
6558 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6559 object element_value;
6560 for (int i = 0; i < ret.Length; ++i)
6562 Expression e = (Expression)array_data [i];
6563 if (e == null) // Is null when initializer is optimized away
6564 e = (Expression)initializers [i];
6566 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6570 ret.SetValue (element_value, i);
6577 public sealed class CompilerGeneratedThis : This
6579 public static This Instance = new CompilerGeneratedThis ();
6581 private CompilerGeneratedThis ()
6582 : base (Location.Null)
6586 public override Expression DoResolve (EmitContext ec)
6588 eclass = ExprClass.Variable;
6589 type = ec.ContainerType;
6590 variable = new SimpleThis (type);
6596 /// Represents the `this' construct
6599 public class This : VariableReference, IVariable
6602 VariableInfo variable_info;
6603 protected Variable variable;
6606 public This (Block block, Location loc)
6612 public This (Location loc)
6617 public VariableInfo VariableInfo {
6618 get { return variable_info; }
6621 public bool VerifyFixed ()
6623 return !TypeManager.IsValueType (Type);
6626 public override bool IsRef {
6627 get { return is_struct; }
6630 public override Variable Variable {
6631 get { return variable; }
6634 public bool ResolveBase (EmitContext ec)
6636 eclass = ExprClass.Variable;
6638 if (ec.TypeContainer.CurrentType != null)
6639 type = ec.TypeContainer.CurrentType;
6641 type = ec.ContainerType;
6643 is_struct = ec.TypeContainer is Struct;
6646 Error (26, "Keyword `this' is not valid in a static property, " +
6647 "static method, or static field initializer");
6651 if (block != null) {
6652 if (block.Toplevel.ThisVariable != null)
6653 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6655 AnonymousContainer am = ec.CurrentAnonymousMethod;
6656 if (is_struct && (am != null) && !am.IsIterator) {
6657 Report.Error (1673, loc, "Anonymous methods inside structs " +
6658 "cannot access instance members of `this'. " +
6659 "Consider copying `this' to a local variable " +
6660 "outside the anonymous method and using the " +
6665 AnonymousMethodHost host = block.Toplevel.AnonymousMethodHost;
6666 if ((host != null) && (!is_struct || host.IsIterator)) {
6667 variable = host.CaptureThis ();
6668 type = variable.Type;
6673 if (variable == null)
6674 variable = new SimpleThis (type);
6679 public override Expression DoResolve (EmitContext ec)
6681 if (!ResolveBase (ec))
6684 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) &&
6685 !variable_info.IsAssigned (ec)) {
6686 Error (188, "The `this' object cannot be used before all of its " +
6687 "fields are assigned to");
6688 variable_info.SetAssigned (ec);
6692 if (ec.IsFieldInitializer) {
6693 Error (27, "Keyword `this' is not available in the current context");
6700 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6702 if (!ResolveBase (ec))
6705 if (variable_info != null)
6706 variable_info.SetAssigned (ec);
6708 if (ec.TypeContainer is Class){
6709 Error (1604, "Cannot assign to 'this' because it is read-only");
6715 public override int GetHashCode()
6717 return block.GetHashCode ();
6720 public override bool Equals (object obj)
6722 This t = obj as This;
6726 return block == t.block;
6729 protected class SimpleThis : Variable
6733 public SimpleThis (Type type)
6738 public override Type Type {
6739 get { return type; }
6742 public override bool HasInstance {
6743 get { return false; }
6746 public override bool NeedsTemporary {
6747 get { return false; }
6750 public override void EmitInstance (EmitContext ec)
6755 public override void Emit (EmitContext ec)
6757 ec.ig.Emit (OpCodes.Ldarg_0);
6760 public override void EmitAssign (EmitContext ec)
6762 throw new InvalidOperationException ();
6765 public override void EmitAddressOf (EmitContext ec)
6767 ec.ig.Emit (OpCodes.Ldarg_0);
6773 /// Represents the `__arglist' construct
6775 public class ArglistAccess : Expression
6777 public ArglistAccess (Location loc)
6782 public override Expression DoResolve (EmitContext ec)
6784 eclass = ExprClass.Variable;
6785 type = TypeManager.runtime_argument_handle_type;
6787 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6789 Error (190, "The __arglist construct is valid only within " +
6790 "a variable argument method");
6797 public override void Emit (EmitContext ec)
6799 ec.ig.Emit (OpCodes.Arglist);
6804 /// Represents the `__arglist (....)' construct
6806 public class Arglist : Expression
6808 public readonly Argument[] Arguments;
6810 public Arglist (Argument[] args, Location l)
6816 public Type[] ArgumentTypes {
6818 Type[] retval = new Type [Arguments.Length];
6819 for (int i = 0; i < Arguments.Length; i++)
6820 retval [i] = Arguments [i].Type;
6825 public override Expression DoResolve (EmitContext ec)
6827 eclass = ExprClass.Variable;
6828 type = TypeManager.runtime_argument_handle_type;
6830 foreach (Argument arg in Arguments) {
6831 if (!arg.Resolve (ec, loc))
6838 public override void Emit (EmitContext ec)
6840 foreach (Argument arg in Arguments)
6846 // This produces the value that renders an instance, used by the iterators code
6848 public class ProxyInstance : Expression, IMemoryLocation {
6849 public override Expression DoResolve (EmitContext ec)
6851 eclass = ExprClass.Variable;
6852 type = ec.ContainerType;
6856 public override void Emit (EmitContext ec)
6858 ec.ig.Emit (OpCodes.Ldarg_0);
6862 public void AddressOf (EmitContext ec, AddressOp mode)
6864 ec.ig.Emit (OpCodes.Ldarg_0);
6869 /// Implements the typeof operator
6871 public class TypeOf : Expression {
6872 readonly Expression QueriedType;
6873 protected Type typearg;
6875 public TypeOf (Expression queried_type, Location l)
6877 QueriedType = queried_type;
6881 public override Expression DoResolve (EmitContext ec)
6883 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6887 typearg = texpr.Type;
6889 if (typearg == TypeManager.void_type) {
6890 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6894 if (typearg.IsPointer && !ec.InUnsafe){
6899 type = TypeManager.type_type;
6900 // Even though what is returned is a type object, it's treated as a value by the compiler.
6901 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6902 eclass = ExprClass.Value;
6906 public override void Emit (EmitContext ec)
6908 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6909 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6912 public override bool GetAttributableValue (Type valueType, out object value)
6914 if (TypeManager.ContainsGenericParameters (typearg)) {
6915 Report.SymbolRelatedToPreviousError(typearg);
6916 Report.Error(416, loc, "`{0}': an attribute argument cannot use type parameters",
6917 TypeManager.CSharpName(typearg));
6922 if (valueType == TypeManager.object_type) {
6923 value = (object)typearg;
6930 public Type TypeArgument
6940 /// Implements the `typeof (void)' operator
6942 public class TypeOfVoid : TypeOf {
6943 public TypeOfVoid (Location l) : base (null, l)
6948 public override Expression DoResolve (EmitContext ec)
6950 type = TypeManager.type_type;
6951 typearg = TypeManager.void_type;
6952 // See description in TypeOf.
6953 eclass = ExprClass.Value;
6959 /// Implements the sizeof expression
6961 public class SizeOf : Expression {
6962 public Expression QueriedType;
6965 public SizeOf (Expression queried_type, Location l)
6967 this.QueriedType = queried_type;
6971 public override Expression DoResolve (EmitContext ec)
6973 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6978 if (texpr is TypeParameterExpr){
6979 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
6984 type_queried = texpr.Type;
6985 if (type_queried.IsEnum)
6986 type_queried = TypeManager.EnumToUnderlying (type_queried);
6988 if (type_queried == TypeManager.void_type) {
6989 Expression.Error_VoidInvalidInTheContext (loc);
6993 int size_of = GetTypeSize (type_queried);
6995 return new IntConstant (size_of, loc);
6999 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)",
7000 TypeManager.CSharpName (type_queried));
7004 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
7008 type = TypeManager.int32_type;
7009 eclass = ExprClass.Value;
7013 public override void Emit (EmitContext ec)
7015 int size = GetTypeSize (type_queried);
7018 ec.ig.Emit (OpCodes.Sizeof, type_queried);
7020 IntConstant.EmitInt (ec.ig, size);
7025 /// Implements the qualified-alias-member (::) expression.
7027 public class QualifiedAliasMember : Expression
7029 string alias, identifier;
7031 public QualifiedAliasMember (string alias, string identifier, Location l)
7034 this.identifier = identifier;
7038 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7040 if (alias == "global")
7041 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
7043 int errors = Report.Errors;
7044 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7046 if (errors == Report.Errors)
7047 Report.Error (432, loc, "Alias `{0}' not found", alias);
7050 if (fne.eclass != ExprClass.Namespace) {
7052 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7055 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
7058 public override Expression DoResolve (EmitContext ec)
7060 FullNamedExpression fne;
7061 if (alias == "global") {
7062 fne = RootNamespace.Global;
7064 int errors = Report.Errors;
7065 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
7067 if (errors == Report.Errors)
7068 Report.Error (432, loc, "Alias `{0}' not found", alias);
7073 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
7077 if (!(retval is FullNamedExpression)) {
7078 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
7082 // We defer this check till the end to match the behaviour of CSC
7083 if (fne.eclass != ExprClass.Namespace) {
7084 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7090 public override void Emit (EmitContext ec)
7092 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
7096 public override string ToString ()
7098 return alias + "::" + identifier;
7101 public override string GetSignatureForError ()
7108 /// Implements the member access expression
7110 public class MemberAccess : Expression {
7111 public readonly string Identifier;
7114 public MemberAccess (Expression expr, string id)
7115 : this (expr, id, expr.Location)
7119 public MemberAccess (Expression expr, string identifier, Location loc)
7122 Identifier = identifier;
7126 public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
7127 : this (expr, identifier, loc)
7134 public Expression Expr {
7135 get { return expr; }
7138 protected string LookupIdentifier {
7139 get { return MemberName.MakeName (Identifier, args); }
7142 // TODO: this method has very poor performace for Enum fields and
7143 // probably for other constants as well
7144 Expression DoResolve (EmitContext ec, Expression right_side)
7147 throw new Exception ();
7150 // Resolve the expression with flow analysis turned off, we'll do the definite
7151 // assignment checks later. This is because we don't know yet what the expression
7152 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7153 // definite assignment check on the actual field and not on the whole struct.
7156 SimpleName original = expr as SimpleName;
7157 Expression new_expr = expr.Resolve (ec,
7158 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7159 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7161 if (new_expr == null)
7164 if (new_expr is Namespace) {
7165 Namespace ns = (Namespace) new_expr;
7166 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, LookupIdentifier, loc);
7168 if ((retval != null) && (args != null))
7169 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
7173 ns.Error_NamespaceDoesNotExist (ec.DeclContainer, loc, Identifier);
7177 Type expr_type = new_expr.Type;
7178 if (expr_type.IsPointer || expr_type == TypeManager.void_type || new_expr is NullLiteral){
7179 Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type);
7182 if (expr_type == TypeManager.anonymous_method_type){
7183 Unary.Error_OperatorCannotBeApplied (loc, ".", "anonymous method");
7187 Constant c = new_expr as Constant;
7188 if (c != null && c.GetValue () == null) {
7189 Report.Warning (1720, 1, loc, "Expression will always cause a `{0}'",
7190 "System.NullReferenceException");
7193 Expression member_lookup;
7194 member_lookup = MemberLookup (
7195 ec.ContainerType, expr_type, expr_type, Identifier, loc);
7197 if ((member_lookup == null) && (args != null)) {
7198 member_lookup = MemberLookup (
7199 ec.ContainerType, expr_type, expr_type, LookupIdentifier, loc);
7202 if (member_lookup == null) {
7203 MemberLookupFailed (
7204 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
7208 if (member_lookup is TypeExpr) {
7209 if (!(new_expr is TypeExpr) &&
7210 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
7211 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7212 Identifier, member_lookup.GetSignatureForError ());
7217 ConstructedType ct = new_expr as ConstructedType;
7220 // When looking up a nested type in a generic instance
7221 // via reflection, we always get a generic type definition
7222 // and not a generic instance - so we have to do this here.
7224 // See gtest-172-lib.cs and gtest-172.cs for an example.
7226 ct = new ConstructedType (
7227 member_lookup.Type, ct.TypeArguments, loc);
7229 return ct.ResolveAsTypeStep (ec, false);
7232 return member_lookup;
7235 MemberExpr me = (MemberExpr) member_lookup;
7236 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
7237 if (member_lookup == null)
7241 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
7243 throw new InternalErrorException ();
7245 return mg.ResolveGeneric (ec, args);
7248 if (original != null && !TypeManager.IsValueType (expr_type)) {
7249 me = member_lookup as MemberExpr;
7250 if (me != null && me.IsInstance) {
7251 LocalVariableReference var = new_expr as LocalVariableReference;
7252 if (var != null && !var.VerifyAssigned (ec))
7257 // The following DoResolve/DoResolveLValue will do the definite assignment
7260 if (right_side != null)
7261 return member_lookup.DoResolveLValue (ec, right_side);
7263 return member_lookup.DoResolve (ec);
7266 public override Expression DoResolve (EmitContext ec)
7268 return DoResolve (ec, null);
7271 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7273 return DoResolve (ec, right_side);
7276 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7278 return ResolveNamespaceOrType (ec, silent);
7281 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7283 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
7285 if (new_expr == null)
7288 if (new_expr is Namespace) {
7289 Namespace ns = (Namespace) new_expr;
7290 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, LookupIdentifier, loc);
7292 if ((retval != null) && (args != null))
7293 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
7295 if (!silent && retval == null)
7296 ns.Error_NamespaceDoesNotExist (rc.DeclContainer, loc, LookupIdentifier);
7300 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
7301 if (tnew_expr == null)
7304 Type expr_type = tnew_expr.Type;
7306 if (expr_type.IsPointer){
7307 Error (23, "The `.' operator can not be applied to pointer operands (" +
7308 TypeManager.CSharpName (expr_type) + ")");
7312 Expression member_lookup = MemberLookup (
7313 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
7314 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7315 if (member_lookup == null) {
7316 int errors = Report.Errors;
7317 MemberLookupFailed (
7318 rc.DeclContainer.TypeBuilder, expr_type, expr_type,
7319 LookupIdentifier, null, false, loc);
7321 if (!silent && errors == Report.Errors) {
7322 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7323 Identifier, new_expr.GetSignatureForError ());
7328 if (!(member_lookup is TypeExpr)) {
7329 new_expr.Error_UnexpectedKind (rc.DeclContainer, "type", loc);
7333 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
7338 TypeArguments the_args = args;
7339 if (TypeManager.HasGenericArguments (expr_type)) {
7340 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
7342 TypeArguments new_args = new TypeArguments (loc);
7343 foreach (Type decl in decl_args)
7344 new_args.Add (new TypeExpression (decl, loc));
7347 new_args.Add (args);
7349 the_args = new_args;
7352 if (the_args != null) {
7353 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
7354 return ctype.ResolveAsTypeStep (rc, false);
7361 public override void Emit (EmitContext ec)
7363 throw new Exception ("Should not happen");
7366 public override string ToString ()
7368 return expr + "." + MemberName.MakeName (Identifier, args);
7371 public override string GetSignatureForError ()
7373 return expr.GetSignatureForError () + "." + Identifier;
7378 /// Implements checked expressions
7380 public class CheckedExpr : Expression {
7382 public Expression Expr;
7384 public CheckedExpr (Expression e, Location l)
7390 public override Expression DoResolve (EmitContext ec)
7392 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7393 Expr = Expr.Resolve (ec);
7398 if (Expr is Constant)
7401 eclass = Expr.eclass;
7406 public override void Emit (EmitContext ec)
7408 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7412 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7414 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7415 Expr.EmitBranchable (ec, target, onTrue);
7420 /// Implements the unchecked expression
7422 public class UnCheckedExpr : Expression {
7424 public Expression Expr;
7426 public UnCheckedExpr (Expression e, Location l)
7432 public override Expression DoResolve (EmitContext ec)
7434 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7435 Expr = Expr.Resolve (ec);
7440 if (Expr is Constant)
7443 eclass = Expr.eclass;
7448 public override void Emit (EmitContext ec)
7450 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7454 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7456 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7457 Expr.EmitBranchable (ec, target, onTrue);
7462 /// An Element Access expression.
7464 /// During semantic analysis these are transformed into
7465 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7467 public class ElementAccess : Expression {
7468 public ArrayList Arguments;
7469 public Expression Expr;
7471 public ElementAccess (Expression e, ArrayList e_list)
7480 Arguments = new ArrayList ();
7481 foreach (Expression tmp in e_list)
7482 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7486 bool CommonResolve (EmitContext ec)
7488 Expr = Expr.Resolve (ec);
7493 if (Arguments == null)
7496 foreach (Argument a in Arguments){
7497 if (!a.Resolve (ec, loc))
7504 Expression MakePointerAccess (EmitContext ec, Type t)
7506 if (t == TypeManager.void_ptr_type){
7507 Error (242, "The array index operation is not valid on void pointers");
7510 if (Arguments.Count != 1){
7511 Error (196, "A pointer must be indexed by only one value");
7516 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7519 return new Indirection (p, loc).Resolve (ec);
7522 public override Expression DoResolve (EmitContext ec)
7524 if (!CommonResolve (ec))
7528 // We perform some simple tests, and then to "split" the emit and store
7529 // code we create an instance of a different class, and return that.
7531 // I am experimenting with this pattern.
7535 if (t == TypeManager.array_type){
7536 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7541 return (new ArrayAccess (this, loc)).Resolve (ec);
7543 return MakePointerAccess (ec, Expr.Type);
7545 FieldExpr fe = Expr as FieldExpr;
7547 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7549 return MakePointerAccess (ec, ff.ElementType);
7552 return (new IndexerAccess (this, loc)).Resolve (ec);
7555 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7557 if (!CommonResolve (ec))
7562 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7565 return MakePointerAccess (ec, Expr.Type);
7567 FieldExpr fe = Expr as FieldExpr;
7569 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7571 if (!(fe.InstanceExpression is LocalVariableReference) &&
7572 !(fe.InstanceExpression is This)) {
7573 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7576 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7577 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7580 return MakePointerAccess (ec, ff.ElementType);
7583 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7586 public override void Emit (EmitContext ec)
7588 throw new Exception ("Should never be reached");
7593 /// Implements array access
7595 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7597 // Points to our "data" repository
7601 LocalTemporary temp;
7604 public ArrayAccess (ElementAccess ea_data, Location l)
7607 eclass = ExprClass.Variable;
7611 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7613 return DoResolve (ec);
7616 public override Expression DoResolve (EmitContext ec)
7619 ExprClass eclass = ea.Expr.eclass;
7621 // As long as the type is valid
7622 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7623 eclass == ExprClass.Value)) {
7624 ea.Expr.Error_UnexpectedKind ("variable or value");
7629 Type t = ea.Expr.Type;
7630 if (t.GetArrayRank () != ea.Arguments.Count){
7631 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7632 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7636 type = TypeManager.GetElementType (t);
7637 if (type.IsPointer && !ec.InUnsafe){
7638 UnsafeError (ea.Location);
7642 foreach (Argument a in ea.Arguments){
7643 Type argtype = a.Type;
7645 if (argtype == TypeManager.int32_type ||
7646 argtype == TypeManager.uint32_type ||
7647 argtype == TypeManager.int64_type ||
7648 argtype == TypeManager.uint64_type) {
7649 Constant c = a.Expr as Constant;
7650 if (c != null && c.IsNegative) {
7651 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7657 // Mhm. This is strage, because the Argument.Type is not the same as
7658 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7660 // Wonder if I will run into trouble for this.
7662 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7667 eclass = ExprClass.Variable;
7673 /// Emits the right opcode to load an object of Type `t'
7674 /// from an array of T
7676 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7678 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7679 ig.Emit (OpCodes.Ldelem_U1);
7680 else if (type == TypeManager.sbyte_type)
7681 ig.Emit (OpCodes.Ldelem_I1);
7682 else if (type == TypeManager.short_type)
7683 ig.Emit (OpCodes.Ldelem_I2);
7684 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7685 ig.Emit (OpCodes.Ldelem_U2);
7686 else if (type == TypeManager.int32_type)
7687 ig.Emit (OpCodes.Ldelem_I4);
7688 else if (type == TypeManager.uint32_type)
7689 ig.Emit (OpCodes.Ldelem_U4);
7690 else if (type == TypeManager.uint64_type)
7691 ig.Emit (OpCodes.Ldelem_I8);
7692 else if (type == TypeManager.int64_type)
7693 ig.Emit (OpCodes.Ldelem_I8);
7694 else if (type == TypeManager.float_type)
7695 ig.Emit (OpCodes.Ldelem_R4);
7696 else if (type == TypeManager.double_type)
7697 ig.Emit (OpCodes.Ldelem_R8);
7698 else if (type == TypeManager.intptr_type)
7699 ig.Emit (OpCodes.Ldelem_I);
7700 else if (TypeManager.IsEnumType (type)){
7701 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7702 } else if (type.IsValueType){
7703 ig.Emit (OpCodes.Ldelema, type);
7704 ig.Emit (OpCodes.Ldobj, type);
7706 } else if (type.IsGenericParameter) {
7708 ig.Emit (OpCodes.Ldelem, type);
7710 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;
7753 } else if (t.IsGenericParameter) {
7754 has_type_arg = true;
7756 return OpCodes.Stelem;
7758 return OpCodes.Stelem_Any;
7762 } else if (t.IsPointer)
7763 return OpCodes.Stelem_I;
7765 return OpCodes.Stelem_Ref;
7768 MethodInfo FetchGetMethod ()
7770 ModuleBuilder mb = CodeGen.Module.Builder;
7771 int arg_count = ea.Arguments.Count;
7772 Type [] args = new Type [arg_count];
7775 for (int i = 0; i < arg_count; i++){
7776 //args [i++] = a.Type;
7777 args [i] = TypeManager.int32_type;
7780 get = mb.GetArrayMethod (
7781 ea.Expr.Type, "Get",
7782 CallingConventions.HasThis |
7783 CallingConventions.Standard,
7789 MethodInfo FetchAddressMethod ()
7791 ModuleBuilder mb = CodeGen.Module.Builder;
7792 int arg_count = ea.Arguments.Count;
7793 Type [] args = new Type [arg_count];
7797 ret_type = TypeManager.GetReferenceType (type);
7799 for (int i = 0; i < arg_count; i++){
7800 //args [i++] = a.Type;
7801 args [i] = TypeManager.int32_type;
7804 address = mb.GetArrayMethod (
7805 ea.Expr.Type, "Address",
7806 CallingConventions.HasThis |
7807 CallingConventions.Standard,
7814 // Load the array arguments into the stack.
7816 // If we have been requested to cache the values (cached_locations array
7817 // initialized), then load the arguments the first time and store them
7818 // in locals. otherwise load from local variables.
7820 void LoadArrayAndArguments (EmitContext ec)
7822 ILGenerator ig = ec.ig;
7825 foreach (Argument a in ea.Arguments){
7826 Type argtype = a.Expr.Type;
7830 if (argtype == TypeManager.int64_type)
7831 ig.Emit (OpCodes.Conv_Ovf_I);
7832 else if (argtype == TypeManager.uint64_type)
7833 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7837 public void Emit (EmitContext ec, bool leave_copy)
7839 int rank = ea.Expr.Type.GetArrayRank ();
7840 ILGenerator ig = ec.ig;
7843 LoadArrayAndArguments (ec);
7846 EmitLoadOpcode (ig, type);
7850 method = FetchGetMethod ();
7851 ig.Emit (OpCodes.Call, method);
7854 LoadFromPtr (ec.ig, this.type);
7857 ec.ig.Emit (OpCodes.Dup);
7858 temp = new LocalTemporary (this.type);
7863 public override void Emit (EmitContext ec)
7868 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7870 int rank = ea.Expr.Type.GetArrayRank ();
7871 ILGenerator ig = ec.ig;
7872 Type t = source.Type;
7873 prepared = prepare_for_load;
7875 if (prepare_for_load) {
7876 AddressOf (ec, AddressOp.LoadStore);
7877 ec.ig.Emit (OpCodes.Dup);
7880 ec.ig.Emit (OpCodes.Dup);
7881 temp = new LocalTemporary (this.type);
7884 StoreFromPtr (ec.ig, t);
7894 LoadArrayAndArguments (ec);
7897 bool is_stobj, has_type_arg;
7898 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7900 // The stobj opcode used by value types will need
7901 // an address on the stack, not really an array/array
7905 ig.Emit (OpCodes.Ldelema, t);
7909 ec.ig.Emit (OpCodes.Dup);
7910 temp = new LocalTemporary (this.type);
7915 ig.Emit (OpCodes.Stobj, t);
7916 else if (has_type_arg)
7921 ModuleBuilder mb = CodeGen.Module.Builder;
7922 int arg_count = ea.Arguments.Count;
7923 Type [] args = new Type [arg_count + 1];
7928 ec.ig.Emit (OpCodes.Dup);
7929 temp = new LocalTemporary (this.type);
7933 for (int i = 0; i < arg_count; i++){
7934 //args [i++] = a.Type;
7935 args [i] = TypeManager.int32_type;
7938 args [arg_count] = type;
7940 set = mb.GetArrayMethod (
7941 ea.Expr.Type, "Set",
7942 CallingConventions.HasThis |
7943 CallingConventions.Standard,
7944 TypeManager.void_type, args);
7946 ig.Emit (OpCodes.Call, set);
7955 public void AddressOf (EmitContext ec, AddressOp mode)
7957 int rank = ea.Expr.Type.GetArrayRank ();
7958 ILGenerator ig = ec.ig;
7960 LoadArrayAndArguments (ec);
7963 ig.Emit (OpCodes.Ldelema, type);
7965 MethodInfo address = FetchAddressMethod ();
7966 ig.Emit (OpCodes.Call, address);
7970 public void EmitGetLength (EmitContext ec, int dim)
7972 int rank = ea.Expr.Type.GetArrayRank ();
7973 ILGenerator ig = ec.ig;
7977 ig.Emit (OpCodes.Ldlen);
7978 ig.Emit (OpCodes.Conv_I4);
7980 IntLiteral.EmitInt (ig, dim);
7981 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7987 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7988 public readonly ArrayList Properties;
7989 static Indexers empty;
7991 public struct Indexer {
7992 public readonly PropertyInfo PropertyInfo;
7993 public readonly MethodInfo Getter, Setter;
7995 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7997 this.PropertyInfo = property_info;
8005 empty = new Indexers (null);
8008 Indexers (ArrayList array)
8013 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
8018 foreach (PropertyInfo property in mi){
8019 MethodInfo get, set;
8021 get = property.GetGetMethod (true);
8022 set = property.GetSetMethod (true);
8023 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
8025 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
8027 if (get != null || set != null) {
8029 ix = new Indexers (new ArrayList ());
8030 ix.Properties.Add (new Indexer (property, get, set));
8035 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
8037 string p_name = TypeManager.IndexerPropertyName (lookup_type);
8039 return TypeManager.MemberLookup (
8040 caller_type, caller_type, lookup_type, MemberTypes.Property,
8041 BindingFlags.Public | BindingFlags.Instance |
8042 BindingFlags.DeclaredOnly, p_name, null);
8045 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
8047 Indexers ix = empty;
8050 if (lookup_type.IsGenericParameter) {
8051 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
8055 if (gc.HasClassConstraint)
8056 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
8058 Type[] ifaces = gc.InterfaceConstraints;
8059 foreach (Type itype in ifaces)
8060 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8066 Type copy = lookup_type;
8067 while (copy != TypeManager.object_type && copy != null){
8068 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
8069 copy = copy.BaseType;
8072 if (lookup_type.IsInterface) {
8073 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
8074 if (ifaces != null) {
8075 foreach (Type itype in ifaces)
8076 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
8085 /// Expressions that represent an indexer call.
8087 public class IndexerAccess : Expression, IAssignMethod {
8089 // Points to our "data" repository
8091 MethodInfo get, set;
8092 ArrayList set_arguments;
8093 bool is_base_indexer;
8095 protected Type indexer_type;
8096 protected Type current_type;
8097 protected Expression instance_expr;
8098 protected ArrayList arguments;
8100 public IndexerAccess (ElementAccess ea, Location loc)
8101 : this (ea.Expr, false, loc)
8103 this.arguments = ea.Arguments;
8106 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
8109 this.instance_expr = instance_expr;
8110 this.is_base_indexer = is_base_indexer;
8111 this.eclass = ExprClass.Value;
8115 protected virtual bool CommonResolve (EmitContext ec)
8117 indexer_type = instance_expr.Type;
8118 current_type = ec.ContainerType;
8123 public override Expression DoResolve (EmitContext ec)
8125 if (!CommonResolve (ec))
8129 // Step 1: Query for all `Item' *properties*. Notice
8130 // that the actual methods are pointed from here.
8132 // This is a group of properties, piles of them.
8134 ArrayList AllGetters = null;
8136 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8137 if (ilist.Properties != null) {
8138 AllGetters = new ArrayList(ilist.Properties.Count);
8139 foreach (Indexers.Indexer ix in ilist.Properties) {
8140 if (ix.Getter != null)
8141 AllGetters.Add (ix.Getter);
8145 if (AllGetters == null) {
8146 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8147 TypeManager.CSharpName (indexer_type));
8151 if (AllGetters.Count == 0) {
8152 // FIXME: we cannot simply select first one as the error message is missleading when
8153 // multiple indexers exist
8154 Indexers.Indexer first_indexer = (Indexers.Indexer)ilist.Properties[ilist.Properties.Count - 1];
8155 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
8156 TypeManager.GetFullNameSignature (first_indexer.PropertyInfo));
8160 get = (MethodInfo)Invocation.OverloadResolve (ec, new MethodGroupExpr (AllGetters, loc),
8161 arguments, false, loc);
8164 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8169 // Only base will allow this invocation to happen.
8171 if (get.IsAbstract && this is BaseIndexerAccess){
8172 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
8176 type = get.ReturnType;
8177 if (type.IsPointer && !ec.InUnsafe){
8182 instance_expr.CheckMarshalByRefAccess ();
8184 eclass = ExprClass.IndexerAccess;
8188 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8190 if (right_side == EmptyExpression.OutAccess) {
8191 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
8192 GetSignatureForError ());
8196 // if the indexer returns a value type, and we try to set a field in it
8197 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
8198 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
8199 GetSignatureForError ());
8203 ArrayList AllSetters = new ArrayList();
8204 if (!CommonResolve (ec))
8207 bool found_any = false, found_any_setters = false;
8209 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8210 if (ilist.Properties != null) {
8212 foreach (Indexers.Indexer ix in ilist.Properties) {
8213 if (ix.Setter != null)
8214 AllSetters.Add (ix.Setter);
8217 if (AllSetters.Count > 0) {
8218 found_any_setters = true;
8219 set_arguments = (ArrayList) arguments.Clone ();
8220 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
8221 set = (MethodInfo) Invocation.OverloadResolve (
8222 ec, new MethodGroupExpr (AllSetters, loc),
8223 set_arguments, false, loc);
8227 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8228 TypeManager.CSharpName (indexer_type));
8232 if (!found_any_setters) {
8233 Error (154, "indexer can not be used in this context, because " +
8234 "it lacks a `set' accessor");
8239 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8244 // Only base will allow this invocation to happen.
8246 if (set.IsAbstract && this is BaseIndexerAccess){
8247 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
8252 // Now look for the actual match in the list of indexers to set our "return" type
8254 type = TypeManager.void_type; // default value
8255 foreach (Indexers.Indexer ix in ilist.Properties){
8256 if (ix.Setter == set){
8257 type = ix.PropertyInfo.PropertyType;
8262 instance_expr.CheckMarshalByRefAccess ();
8264 eclass = ExprClass.IndexerAccess;
8268 bool prepared = false;
8269 LocalTemporary temp;
8271 public void Emit (EmitContext ec, bool leave_copy)
8273 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
8275 ec.ig.Emit (OpCodes.Dup);
8276 temp = new LocalTemporary (Type);
8282 // source is ignored, because we already have a copy of it from the
8283 // LValue resolution and we have already constructed a pre-cached
8284 // version of the arguments (ea.set_arguments);
8286 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8288 prepared = prepare_for_load;
8289 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
8294 ec.ig.Emit (OpCodes.Dup);
8295 temp = new LocalTemporary (Type);
8298 } else if (leave_copy) {
8299 temp = new LocalTemporary (Type);
8305 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
8314 public override void Emit (EmitContext ec)
8319 public override string GetSignatureForError ()
8321 // FIXME: print the argument list of the indexer
8322 return instance_expr.GetSignatureForError () + ".this[...]";
8327 /// The base operator for method names
8329 public class BaseAccess : Expression {
8330 public readonly string Identifier;
8332 public BaseAccess (string member, Location l)
8334 this.Identifier = member;
8338 public BaseAccess (string member, TypeArguments args, Location l)
8346 public override Expression DoResolve (EmitContext ec)
8348 Expression c = CommonResolve (ec);
8354 // MethodGroups use this opportunity to flag an error on lacking ()
8356 if (!(c is MethodGroupExpr))
8357 return c.Resolve (ec);
8361 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8363 Expression c = CommonResolve (ec);
8369 // MethodGroups use this opportunity to flag an error on lacking ()
8371 if (! (c is MethodGroupExpr))
8372 return c.DoResolveLValue (ec, right_side);
8377 Expression CommonResolve (EmitContext ec)
8379 Expression member_lookup;
8380 Type current_type = ec.ContainerType;
8381 Type base_type = current_type.BaseType;
8384 Error (1511, "Keyword `base' is not available in a static method");
8388 if (ec.IsFieldInitializer){
8389 Error (1512, "Keyword `base' is not available in the current context");
8393 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8394 AllMemberTypes, AllBindingFlags, loc);
8395 if (member_lookup == null) {
8396 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8403 left = new TypeExpression (base_type, loc);
8405 left = ec.GetThis (loc);
8407 MemberExpr me = (MemberExpr) member_lookup;
8409 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8411 if (e is PropertyExpr) {
8412 PropertyExpr pe = (PropertyExpr) e;
8417 MethodGroupExpr mg = e as MethodGroupExpr;
8423 return mg.ResolveGeneric (ec, args);
8425 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8433 public override void Emit (EmitContext ec)
8435 throw new Exception ("Should never be called");
8440 /// The base indexer operator
8442 public class BaseIndexerAccess : IndexerAccess {
8443 public BaseIndexerAccess (ArrayList args, Location loc)
8444 : base (null, true, loc)
8446 arguments = new ArrayList ();
8447 foreach (Expression tmp in args)
8448 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8451 protected override bool CommonResolve (EmitContext ec)
8453 instance_expr = ec.GetThis (loc);
8455 current_type = ec.ContainerType.BaseType;
8456 indexer_type = current_type;
8458 foreach (Argument a in arguments){
8459 if (!a.Resolve (ec, loc))
8468 /// This class exists solely to pass the Type around and to be a dummy
8469 /// that can be passed to the conversion functions (this is used by
8470 /// foreach implementation to typecast the object return value from
8471 /// get_Current into the proper type. All code has been generated and
8472 /// we only care about the side effect conversions to be performed
8474 /// This is also now used as a placeholder where a no-action expression
8475 /// is needed (the `New' class).
8477 public class EmptyExpression : Expression {
8478 public static readonly EmptyExpression Null = new EmptyExpression ();
8480 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8481 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8482 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8484 static EmptyExpression temp = new EmptyExpression ();
8485 public static EmptyExpression Grab ()
8487 EmptyExpression retval = temp == null ? new EmptyExpression () : temp;
8492 public static void Release (EmptyExpression e)
8497 // TODO: should be protected
8498 public EmptyExpression ()
8500 type = TypeManager.object_type;
8501 eclass = ExprClass.Value;
8502 loc = Location.Null;
8505 public EmptyExpression (Type t)
8508 eclass = ExprClass.Value;
8509 loc = Location.Null;
8512 public override Expression DoResolve (EmitContext ec)
8517 public override void Emit (EmitContext ec)
8519 // nothing, as we only exist to not do anything.
8523 // This is just because we might want to reuse this bad boy
8524 // instead of creating gazillions of EmptyExpressions.
8525 // (CanImplicitConversion uses it)
8527 public void SetType (Type t)
8533 public class UserCast : Expression {
8537 public UserCast (MethodInfo method, Expression source, Location l)
8539 this.method = method;
8540 this.source = source;
8541 type = method.ReturnType;
8542 eclass = ExprClass.Value;
8546 public Expression Source {
8552 public override Expression DoResolve (EmitContext ec)
8555 // We are born fully resolved
8560 public override void Emit (EmitContext ec)
8562 ILGenerator ig = ec.ig;
8566 if (method is MethodInfo)
8567 ig.Emit (OpCodes.Call, (MethodInfo) method);
8569 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8575 // This class is used to "construct" the type during a typecast
8576 // operation. Since the Type.GetType class in .NET can parse
8577 // the type specification, we just use this to construct the type
8578 // one bit at a time.
8580 public class ComposedCast : TypeExpr {
8584 public ComposedCast (Expression left, string dim)
8585 : this (left, dim, left.Location)
8589 public ComposedCast (Expression left, string dim, Location l)
8597 public Expression RemoveNullable ()
8599 if (dim.EndsWith ("?")) {
8600 dim = dim.Substring (0, dim.Length - 1);
8609 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8611 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8615 Type ltype = lexpr.Type;
8616 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8617 Error_VoidInvalidInTheContext (loc);
8622 if ((dim.Length > 0) && (dim [0] == '?')) {
8623 TypeExpr nullable = new NullableType (left, loc);
8625 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8626 return nullable.ResolveAsTypeTerminal (ec, false);
8630 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc))
8633 if (dim != "" && dim [0] == '[' &&
8634 (ltype == TypeManager.arg_iterator_type || ltype == TypeManager.typed_reference_type)) {
8635 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (ltype));
8640 type = TypeManager.GetConstructedType (ltype, dim);
8645 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8647 if (type.IsPointer && !ec.IsInUnsafeScope){
8652 eclass = ExprClass.Type;
8656 public override string Name {
8657 get { return left + dim; }
8660 public override string FullName {
8661 get { return type.FullName; }
8664 public override string GetSignatureForError ()
8666 return left.GetSignatureForError () + dim;
8670 public class FixedBufferPtr : Expression {
8673 public FixedBufferPtr (Expression array, Type array_type, Location l)
8678 type = TypeManager.GetPointerType (array_type);
8679 eclass = ExprClass.Value;
8682 public override void Emit(EmitContext ec)
8687 public override Expression DoResolve (EmitContext ec)
8690 // We are born fully resolved
8698 // This class is used to represent the address of an array, used
8699 // only by the Fixed statement, this generates "&a [0]" construct
8700 // for fixed (char *pa = a)
8702 public class ArrayPtr : FixedBufferPtr {
8705 public ArrayPtr (Expression array, Type array_type, Location l):
8706 base (array, array_type, l)
8708 this.array_type = array_type;
8711 public override void Emit (EmitContext ec)
8715 ILGenerator ig = ec.ig;
8716 IntLiteral.EmitInt (ig, 0);
8717 ig.Emit (OpCodes.Ldelema, array_type);
8722 // Used by the fixed statement
8724 public class StringPtr : Expression {
8727 public StringPtr (LocalBuilder b, Location l)
8730 eclass = ExprClass.Value;
8731 type = TypeManager.char_ptr_type;
8735 public override Expression DoResolve (EmitContext ec)
8737 // This should never be invoked, we are born in fully
8738 // initialized state.
8743 public override void Emit (EmitContext ec)
8745 ILGenerator ig = ec.ig;
8747 ig.Emit (OpCodes.Ldloc, b);
8748 ig.Emit (OpCodes.Conv_I);
8749 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8750 ig.Emit (OpCodes.Add);
8755 // Implements the `stackalloc' keyword
8757 public class StackAlloc : Expression {
8762 public StackAlloc (Expression type, Expression count, Location l)
8769 public override Expression DoResolve (EmitContext ec)
8771 count = count.Resolve (ec);
8775 if (count.Type != TypeManager.int32_type){
8776 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8781 Constant c = count as Constant;
8782 if (c != null && c.IsNegative) {
8783 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8787 if (ec.InCatch || ec.InFinally) {
8788 Error (255, "Cannot use stackalloc in finally or catch");
8792 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8798 if (!TypeManager.VerifyUnManaged (otype, loc))
8801 type = TypeManager.GetPointerType (otype);
8802 eclass = ExprClass.Value;
8807 public override void Emit (EmitContext ec)
8809 int size = GetTypeSize (otype);
8810 ILGenerator ig = ec.ig;
8813 ig.Emit (OpCodes.Sizeof, otype);
8815 IntConstant.EmitInt (ig, size);
8817 ig.Emit (OpCodes.Mul);
8818 ig.Emit (OpCodes.Localloc);