2 // expression.cs: Expression representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@seznam.cz)
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2003, 2004 Novell, Inc.
13 namespace Mono.CSharp {
15 using System.Collections;
16 using System.Reflection;
17 using System.Reflection.Emit;
21 /// This is just a helper class, it is generated by Unary, UnaryMutator
22 /// when an overloaded method has been found. It just emits the code for a
25 public class StaticCallExpr : ExpressionStatement {
29 public StaticCallExpr (MethodInfo m, ArrayList a, Location l)
35 eclass = ExprClass.Value;
39 public override Expression DoResolve (EmitContext ec)
42 // We are born fully resolved
47 public override void Emit (EmitContext ec)
50 Invocation.EmitArguments (ec, mi, args, false, null);
52 ec.ig.Emit (OpCodes.Call, mi);
56 static public StaticCallExpr MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
57 Expression e, Location loc)
62 args = new ArrayList (1);
63 Argument a = new Argument (e, Argument.AType.Expression);
65 // We need to resolve the arguments before sending them in !
66 if (!a.Resolve (ec, loc))
70 method = Invocation.OverloadResolve (
71 ec, (MethodGroupExpr) mg, args, false, loc);
76 return new StaticCallExpr ((MethodInfo) method, args, loc);
79 public override void EmitStatement (EmitContext ec)
82 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
83 ec.ig.Emit (OpCodes.Pop);
86 public MethodInfo Method {
91 public class ParenthesizedExpression : Expression
93 public Expression Expr;
95 public ParenthesizedExpression (Expression expr)
100 public override Expression DoResolve (EmitContext ec)
102 Expr = Expr.Resolve (ec);
106 public override void Emit (EmitContext ec)
108 throw new Exception ("Should not happen");
111 public override Location Location
114 return Expr.Location;
120 /// Unary expressions.
124 /// Unary implements unary expressions. It derives from
125 /// ExpressionStatement becuase the pre/post increment/decrement
126 /// operators can be used in a statement context.
128 public class Unary : Expression {
129 public enum Operator : byte {
130 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
131 Indirection, AddressOf, TOP
134 public Operator Oper;
135 public Expression Expr;
137 public Unary (Operator op, Expression expr, Location loc)
145 /// Returns a stringified representation of the Operator
147 static public string OperName (Operator oper)
150 case Operator.UnaryPlus:
152 case Operator.UnaryNegation:
154 case Operator.LogicalNot:
156 case Operator.OnesComplement:
158 case Operator.AddressOf:
160 case Operator.Indirection:
164 return oper.ToString ();
167 public static readonly string [] oper_names;
171 oper_names = new string [(int)Operator.TOP];
173 oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus";
174 oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation";
175 oper_names [(int) Operator.LogicalNot] = "op_LogicalNot";
176 oper_names [(int) Operator.OnesComplement] = "op_OnesComplement";
177 oper_names [(int) Operator.Indirection] = "op_Indirection";
178 oper_names [(int) Operator.AddressOf] = "op_AddressOf";
181 void Error23 (Type t)
183 Report.Error (23, loc, "Operator `{0}' cannot be applied to operand of type `{1}'",
184 OperName (Oper), TypeManager.CSharpName (t));
188 /// The result has been already resolved:
190 /// FIXME: a minus constant -128 sbyte cant be turned into a
193 static Expression TryReduceNegative (Constant expr)
197 if (expr is IntConstant)
198 e = new IntConstant (-((IntConstant) expr).Value, expr.Location);
199 else if (expr is UIntConstant){
200 uint value = ((UIntConstant) expr).Value;
202 if (value < 2147483649)
203 return new IntConstant (-(int)value, expr.Location);
205 e = new LongConstant (-value, expr.Location);
207 else if (expr is LongConstant)
208 e = new LongConstant (-((LongConstant) expr).Value, expr.Location);
209 else if (expr is ULongConstant){
210 ulong value = ((ULongConstant) expr).Value;
212 if (value < 9223372036854775809)
213 return new LongConstant(-(long)value, expr.Location);
215 else if (expr is FloatConstant)
216 e = new FloatConstant (-((FloatConstant) expr).Value, expr.Location);
217 else if (expr is DoubleConstant)
218 e = new DoubleConstant (-((DoubleConstant) expr).Value, expr.Location);
219 else if (expr is DecimalConstant)
220 e = new DecimalConstant (-((DecimalConstant) expr).Value, expr.Location);
221 else if (expr is ShortConstant)
222 e = new IntConstant (-((ShortConstant) expr).Value, expr.Location);
223 else if (expr is UShortConstant)
224 e = new IntConstant (-((UShortConstant) expr).Value, expr.Location);
225 else if (expr is SByteConstant)
226 e = new IntConstant (-((SByteConstant) expr).Value, expr.Location);
227 else if (expr is ByteConstant)
228 e = new IntConstant (-((ByteConstant) expr).Value, expr.Location);
233 // This routine will attempt to simplify the unary expression when the
234 // argument is a constant. The result is returned in `result' and the
235 // function returns true or false depending on whether a reduction
236 // was performed or not
238 bool Reduce (EmitContext ec, Constant e, out Expression result)
240 Type expr_type = e.Type;
243 case Operator.UnaryPlus:
244 if (expr_type == TypeManager.bool_type){
253 case Operator.UnaryNegation:
254 result = TryReduceNegative (e);
255 return result != null;
257 case Operator.LogicalNot:
258 if (expr_type != TypeManager.bool_type) {
264 BoolConstant b = (BoolConstant) e;
265 result = new BoolConstant (!(b.Value), b.Location);
268 case Operator.OnesComplement:
269 if (!((expr_type == TypeManager.int32_type) ||
270 (expr_type == TypeManager.uint32_type) ||
271 (expr_type == TypeManager.int64_type) ||
272 (expr_type == TypeManager.uint64_type) ||
273 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
276 if (Convert.ImplicitConversionExists (ec, e, TypeManager.int32_type)){
277 result = new Cast (new TypeExpression (TypeManager.int32_type, loc), e, loc);
278 result = result.Resolve (ec);
279 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.uint32_type)){
280 result = new Cast (new TypeExpression (TypeManager.uint32_type, loc), e, loc);
281 result = result.Resolve (ec);
282 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.int64_type)){
283 result = new Cast (new TypeExpression (TypeManager.int64_type, loc), e, loc);
284 result = result.Resolve (ec);
285 } else if (Convert.ImplicitConversionExists (ec, e, TypeManager.uint64_type)){
286 result = new Cast (new TypeExpression (TypeManager.uint64_type, loc), e, loc);
287 result = result.Resolve (ec);
290 if (result == null || !(result is Constant)){
296 expr_type = result.Type;
297 e = (Constant) result;
300 if (e is EnumConstant){
301 EnumConstant enum_constant = (EnumConstant) e;
304 if (Reduce (ec, enum_constant.Child, out reduced)){
305 result = new EnumConstant ((Constant) reduced, enum_constant.Type);
313 if (expr_type == TypeManager.int32_type){
314 result = new IntConstant (~ ((IntConstant) e).Value, e.Location);
315 } else if (expr_type == TypeManager.uint32_type){
316 result = new UIntConstant (~ ((UIntConstant) e).Value, e.Location);
317 } else if (expr_type == TypeManager.int64_type){
318 result = new LongConstant (~ ((LongConstant) e).Value, e.Location);
319 } else if (expr_type == TypeManager.uint64_type){
320 result = new ULongConstant (~ ((ULongConstant) e).Value, e.Location);
328 case Operator.AddressOf:
332 case Operator.Indirection:
336 throw new Exception ("Can not constant fold: " + Oper.ToString());
339 Expression ResolveOperator (EmitContext ec)
342 // Step 1: Default operations on CLI native types.
345 // Attempt to use a constant folding operation.
346 if (Expr is Constant){
349 if (Reduce (ec, (Constant) Expr, out result))
354 // Step 2: Perform Operator Overload location
356 Type expr_type = Expr.Type;
360 op_name = oper_names [(int) Oper];
362 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
365 Expression e = StaticCallExpr.MakeSimpleCall (
366 ec, (MethodGroupExpr) mg, Expr, loc);
376 // Only perform numeric promotions on:
379 if (expr_type == null)
383 case Operator.LogicalNot:
384 if (expr_type != TypeManager.bool_type) {
385 Expr = ResolveBoolean (ec, Expr, loc);
392 type = TypeManager.bool_type;
395 case Operator.OnesComplement:
396 if (!((expr_type == TypeManager.int32_type) ||
397 (expr_type == TypeManager.uint32_type) ||
398 (expr_type == TypeManager.int64_type) ||
399 (expr_type == TypeManager.uint64_type) ||
400 (expr_type.IsSubclassOf (TypeManager.enum_type)))){
403 e = Convert.ImplicitConversion (ec, Expr, TypeManager.int32_type, loc);
406 e = Convert.ImplicitConversion (ec, Expr, TypeManager.uint32_type, loc);
409 e = Convert.ImplicitConversion (ec, Expr, TypeManager.int64_type, loc);
412 e = Convert.ImplicitConversion (ec, Expr, TypeManager.uint64_type, loc);
425 case Operator.AddressOf:
431 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){
435 IVariable variable = Expr as IVariable;
436 bool is_fixed = variable != null && variable.VerifyFixed ();
438 if (!ec.InFixedInitializer && !is_fixed) {
439 Error (212, "You can only take the address of unfixed expression inside " +
440 "of a fixed statement initializer");
444 if (ec.InFixedInitializer && is_fixed) {
445 Error (213, "You cannot use the fixed statement to take the address of an already fixed expression");
449 LocalVariableReference lr = Expr as LocalVariableReference;
451 if (lr.local_info.IsCaptured){
452 AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc);
455 lr.local_info.AddressTaken = true;
456 lr.local_info.Used = true;
459 ParameterReference pr = Expr as ParameterReference;
460 if ((pr != null) && pr.Parameter.IsCaptured) {
461 AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc);
465 // According to the specs, a variable is considered definitely assigned if you take
467 if ((variable != null) && (variable.VariableInfo != null)){
468 variable.VariableInfo.SetAssigned (ec);
471 type = TypeManager.GetPointerType (Expr.Type);
474 case Operator.Indirection:
480 if (!expr_type.IsPointer){
481 Error (193, "The * or -> operator must be applied to a pointer");
486 // We create an Indirection expression, because
487 // it can implement the IMemoryLocation.
489 return new Indirection (Expr, loc);
491 case Operator.UnaryPlus:
493 // A plus in front of something is just a no-op, so return the child.
497 case Operator.UnaryNegation:
499 // Deals with -literals
500 // int operator- (int x)
501 // long operator- (long x)
502 // float operator- (float f)
503 // double operator- (double d)
504 // decimal operator- (decimal d)
506 Expression expr = null;
509 // transform - - expr into expr
512 Unary unary = (Unary) Expr;
514 if (unary.Oper == Operator.UnaryNegation)
519 // perform numeric promotions to int,
523 // The following is inneficient, because we call
524 // ImplicitConversion too many times.
526 // It is also not clear if we should convert to Float
527 // or Double initially.
529 if (expr_type == TypeManager.uint32_type){
531 // FIXME: handle exception to this rule that
532 // permits the int value -2147483648 (-2^31) to
533 // bt wrote as a decimal interger literal
535 type = TypeManager.int64_type;
536 Expr = Convert.ImplicitConversion (ec, Expr, type, loc);
540 if (expr_type == TypeManager.uint64_type){
542 // FIXME: Handle exception of `long value'
543 // -92233720368547758087 (-2^63) to be wrote as
544 // decimal integer literal.
550 if (expr_type == TypeManager.float_type){
555 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int32_type, loc);
562 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.int64_type, loc);
569 expr = Convert.ImplicitConversion (ec, Expr, TypeManager.double_type, loc);
580 Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" +
581 TypeManager.CSharpName (expr_type) + "'");
585 public override Expression DoResolve (EmitContext ec)
587 if (Oper == Operator.AddressOf) {
588 Expr = Expr.DoResolveLValue (ec, new EmptyExpression ());
590 if (Expr == null || Expr.eclass != ExprClass.Variable){
591 Error (211, "Cannot take the address of the given expression");
596 Expr = Expr.Resolve (ec);
601 if (TypeManager.IsNullableValueType (Expr.Type))
602 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
604 eclass = ExprClass.Value;
605 return ResolveOperator (ec);
608 public override Expression DoResolveLValue (EmitContext ec, Expression right)
610 if (Oper == Operator.Indirection)
611 return DoResolve (ec);
616 public override void Emit (EmitContext ec)
618 ILGenerator ig = ec.ig;
621 case Operator.UnaryPlus:
622 throw new Exception ("This should be caught by Resolve");
624 case Operator.UnaryNegation:
625 if (ec.CheckState && type != TypeManager.float_type && type != TypeManager.double_type) {
626 ig.Emit (OpCodes.Ldc_I4_0);
627 if (type == TypeManager.int64_type)
628 ig.Emit (OpCodes.Conv_U8);
630 ig.Emit (OpCodes.Sub_Ovf);
633 ig.Emit (OpCodes.Neg);
638 case Operator.LogicalNot:
640 ig.Emit (OpCodes.Ldc_I4_0);
641 ig.Emit (OpCodes.Ceq);
644 case Operator.OnesComplement:
646 ig.Emit (OpCodes.Not);
649 case Operator.AddressOf:
650 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
654 throw new Exception ("This should not happen: Operator = "
659 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
661 if (Oper == Operator.LogicalNot)
662 Expr.EmitBranchable (ec, target, !onTrue);
664 base.EmitBranchable (ec, target, onTrue);
667 public override string ToString ()
669 return "Unary (" + Oper + ", " + Expr + ")";
675 // Unary operators are turned into Indirection expressions
676 // after semantic analysis (this is so we can take the address
677 // of an indirection).
679 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
681 LocalTemporary temporary;
684 public Indirection (Expression expr, Location l)
687 type = TypeManager.HasElementType (expr.Type) ? TypeManager.GetElementType (expr.Type) : expr.Type;
688 eclass = ExprClass.Variable;
692 public override void Emit (EmitContext ec)
697 LoadFromPtr (ec.ig, Type);
700 public void Emit (EmitContext ec, bool leave_copy)
704 ec.ig.Emit (OpCodes.Dup);
705 temporary = new LocalTemporary (expr.Type);
706 temporary.Store (ec);
710 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
712 prepared = prepare_for_load;
716 if (prepare_for_load)
717 ec.ig.Emit (OpCodes.Dup);
721 ec.ig.Emit (OpCodes.Dup);
722 temporary = new LocalTemporary (expr.Type);
723 temporary.Store (ec);
726 StoreFromPtr (ec.ig, type);
728 if (temporary != null)
732 public void AddressOf (EmitContext ec, AddressOp Mode)
737 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
739 return DoResolve (ec);
742 public override Expression DoResolve (EmitContext ec)
745 // Born fully resolved
750 public override string ToString ()
752 return "*(" + expr + ")";
755 #region IVariable Members
757 public VariableInfo VariableInfo {
763 public bool VerifyFixed ()
765 // A pointer-indirection is always fixed.
773 /// Unary Mutator expressions (pre and post ++ and --)
777 /// UnaryMutator implements ++ and -- expressions. It derives from
778 /// ExpressionStatement becuase the pre/post increment/decrement
779 /// operators can be used in a statement context.
781 /// FIXME: Idea, we could split this up in two classes, one simpler
782 /// for the common case, and one with the extra fields for more complex
783 /// classes (indexers require temporary access; overloaded require method)
786 public class UnaryMutator : ExpressionStatement {
788 public enum Mode : byte {
795 PreDecrement = IsDecrement,
796 PostIncrement = IsPost,
797 PostDecrement = IsPost | IsDecrement
801 bool is_expr = false;
802 bool recurse = false;
807 // This is expensive for the simplest case.
809 StaticCallExpr method;
811 public UnaryMutator (Mode m, Expression e, Location l)
818 static string OperName (Mode mode)
820 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
825 /// Returns whether an object of type `t' can be incremented
826 /// or decremented with add/sub (ie, basically whether we can
827 /// use pre-post incr-decr operations on it, but it is not a
828 /// System.Decimal, which we require operator overloading to catch)
830 static bool IsIncrementableNumber (Type t)
832 return (t == TypeManager.sbyte_type) ||
833 (t == TypeManager.byte_type) ||
834 (t == TypeManager.short_type) ||
835 (t == TypeManager.ushort_type) ||
836 (t == TypeManager.int32_type) ||
837 (t == TypeManager.uint32_type) ||
838 (t == TypeManager.int64_type) ||
839 (t == TypeManager.uint64_type) ||
840 (t == TypeManager.char_type) ||
841 (t.IsSubclassOf (TypeManager.enum_type)) ||
842 (t == TypeManager.float_type) ||
843 (t == TypeManager.double_type) ||
844 (t.IsPointer && t != TypeManager.void_ptr_type);
847 Expression ResolveOperator (EmitContext ec)
849 Type expr_type = expr.Type;
852 // Step 1: Perform Operator Overload location
857 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
858 op_name = "op_Increment";
860 op_name = "op_Decrement";
862 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
865 method = StaticCallExpr.MakeSimpleCall (
866 ec, (MethodGroupExpr) mg, expr, loc);
869 } else if (!IsIncrementableNumber (expr_type)) {
870 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
871 TypeManager.CSharpName (expr_type) + "'");
876 // The operand of the prefix/postfix increment decrement operators
877 // should be an expression that is classified as a variable,
878 // a property access or an indexer access
881 if (expr.eclass == ExprClass.Variable){
882 LocalVariableReference var = expr as LocalVariableReference;
883 if ((var != null) && var.IsReadOnly) {
884 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
887 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
888 expr = expr.ResolveLValue (ec, this, Location);
892 expr.Error_UnexpectedKind (ec.DeclContainer, "variable, indexer or property access", loc);
899 public override Expression DoResolve (EmitContext ec)
901 expr = expr.Resolve (ec);
906 eclass = ExprClass.Value;
908 if (TypeManager.IsNullableValueType (expr.Type))
909 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
911 return ResolveOperator (ec);
914 static int PtrTypeSize (Type t)
916 return GetTypeSize (TypeManager.GetElementType (t));
920 // Loads the proper "1" into the stack based on the type, then it emits the
921 // opcode for the operation requested
923 void LoadOneAndEmitOp (EmitContext ec, Type t)
926 // Measure if getting the typecode and using that is more/less efficient
927 // that comparing types. t.GetTypeCode() is an internal call.
929 ILGenerator ig = ec.ig;
931 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
932 LongConstant.EmitLong (ig, 1);
933 else if (t == TypeManager.double_type)
934 ig.Emit (OpCodes.Ldc_R8, 1.0);
935 else if (t == TypeManager.float_type)
936 ig.Emit (OpCodes.Ldc_R4, 1.0F);
937 else if (t.IsPointer){
938 int n = PtrTypeSize (t);
941 ig.Emit (OpCodes.Sizeof, t);
943 IntConstant.EmitInt (ig, n);
945 ig.Emit (OpCodes.Ldc_I4_1);
948 // Now emit the operation
951 if (t == TypeManager.int32_type ||
952 t == TypeManager.int64_type){
953 if ((mode & Mode.IsDecrement) != 0)
954 ig.Emit (OpCodes.Sub_Ovf);
956 ig.Emit (OpCodes.Add_Ovf);
957 } else if (t == TypeManager.uint32_type ||
958 t == TypeManager.uint64_type){
959 if ((mode & Mode.IsDecrement) != 0)
960 ig.Emit (OpCodes.Sub_Ovf_Un);
962 ig.Emit (OpCodes.Add_Ovf_Un);
964 if ((mode & Mode.IsDecrement) != 0)
965 ig.Emit (OpCodes.Sub_Ovf);
967 ig.Emit (OpCodes.Add_Ovf);
970 if ((mode & Mode.IsDecrement) != 0)
971 ig.Emit (OpCodes.Sub);
973 ig.Emit (OpCodes.Add);
976 if (t == TypeManager.sbyte_type){
978 ig.Emit (OpCodes.Conv_Ovf_I1);
980 ig.Emit (OpCodes.Conv_I1);
981 } else if (t == TypeManager.byte_type){
983 ig.Emit (OpCodes.Conv_Ovf_U1);
985 ig.Emit (OpCodes.Conv_U1);
986 } else if (t == TypeManager.short_type){
988 ig.Emit (OpCodes.Conv_Ovf_I2);
990 ig.Emit (OpCodes.Conv_I2);
991 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
993 ig.Emit (OpCodes.Conv_Ovf_U2);
995 ig.Emit (OpCodes.Conv_U2);
1000 void EmitCode (EmitContext ec, bool is_expr)
1003 this.is_expr = is_expr;
1004 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1007 public override void Emit (EmitContext ec)
1010 // We use recurse to allow ourselfs to be the source
1011 // of an assignment. This little hack prevents us from
1012 // having to allocate another expression
1015 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1017 LoadOneAndEmitOp (ec, expr.Type);
1019 ec.ig.Emit (OpCodes.Call, method.Method);
1024 EmitCode (ec, true);
1027 public override void EmitStatement (EmitContext ec)
1029 EmitCode (ec, false);
1034 /// Base class for the `Is' and `As' classes.
1038 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1041 public abstract class Probe : Expression {
1042 public Expression ProbeType;
1043 protected Expression expr;
1044 protected TypeExpr probe_type_expr;
1046 public Probe (Expression expr, Expression probe_type, Location l)
1048 ProbeType = probe_type;
1053 public Expression Expr {
1059 public override Expression DoResolve (EmitContext ec)
1061 probe_type_expr = ProbeType.ResolveAsTypeTerminal (ec, false);
1062 if (probe_type_expr == null)
1065 expr = expr.Resolve (ec);
1069 if (expr.Type.IsPointer) {
1070 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1078 /// Implementation of the `is' operator.
1080 public class Is : Probe {
1081 public Is (Expression expr, Expression probe_type, Location l)
1082 : base (expr, probe_type, l)
1087 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1092 public override void Emit (EmitContext ec)
1094 ILGenerator ig = ec.ig;
1099 case Action.AlwaysFalse:
1100 ig.Emit (OpCodes.Pop);
1101 IntConstant.EmitInt (ig, 0);
1103 case Action.AlwaysTrue:
1104 ig.Emit (OpCodes.Pop);
1105 IntConstant.EmitInt (ig, 1);
1107 case Action.LeaveOnStack:
1108 // the `e != null' rule.
1109 ig.Emit (OpCodes.Ldnull);
1110 ig.Emit (OpCodes.Ceq);
1111 ig.Emit (OpCodes.Ldc_I4_0);
1112 ig.Emit (OpCodes.Ceq);
1115 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1116 ig.Emit (OpCodes.Ldnull);
1117 ig.Emit (OpCodes.Cgt_Un);
1120 throw new Exception ("never reached");
1123 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1125 ILGenerator ig = ec.ig;
1128 case Action.AlwaysFalse:
1130 ig.Emit (OpCodes.Br, target);
1133 case Action.AlwaysTrue:
1135 ig.Emit (OpCodes.Br, target);
1138 case Action.LeaveOnStack:
1139 // the `e != null' rule.
1141 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1145 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1146 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1149 throw new Exception ("never reached");
1152 public override Expression DoResolve (EmitContext ec)
1154 Expression e = base.DoResolve (ec);
1156 if ((e == null) || (expr == null))
1159 Type etype = expr.Type;
1160 bool warning_always_matches = false;
1161 bool warning_never_matches = false;
1163 type = TypeManager.bool_type;
1164 eclass = ExprClass.Value;
1167 // First case, if at compile time, there is an implicit conversion
1168 // then e != null (objects) or true (value types)
1170 Type probe_type = probe_type_expr.Type;
1171 e = Convert.ImplicitConversionStandard (ec, expr, probe_type, loc);
1172 if (e != null && !(e is NullCast)){
1174 if (etype.IsValueType)
1175 action = Action.AlwaysTrue;
1177 action = Action.LeaveOnStack;
1179 warning_always_matches = true;
1180 } else if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1181 if (etype.IsGenericParameter)
1182 expr = new BoxedCast (expr, etype);
1185 // Second case: explicit reference convresion
1187 if (expr is NullLiteral)
1188 action = Action.AlwaysFalse;
1190 action = Action.Probe;
1191 } else if (etype.ContainsGenericParameters || probe_type.ContainsGenericParameters) {
1192 expr = new BoxedCast (expr, etype);
1193 action = Action.Probe;
1195 action = Action.AlwaysFalse;
1196 warning_never_matches = true;
1199 if (warning_always_matches)
1200 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1201 else if (warning_never_matches){
1202 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1203 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1211 /// Implementation of the `as' operator.
1213 public class As : Probe {
1214 public As (Expression expr, Expression probe_type, Location l)
1215 : base (expr, probe_type, l)
1219 bool do_isinst = false;
1220 Expression resolved_type;
1222 public override void Emit (EmitContext ec)
1224 ILGenerator ig = ec.ig;
1229 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1232 static void Error_CannotConvertType (Type source, Type target, Location loc)
1234 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1235 TypeManager.CSharpName (source),
1236 TypeManager.CSharpName (target));
1239 public override Expression DoResolve (EmitContext ec)
1241 if (resolved_type == null) {
1242 resolved_type = base.DoResolve (ec);
1244 if (resolved_type == null)
1248 type = probe_type_expr.Type;
1249 eclass = ExprClass.Value;
1250 Type etype = expr.Type;
1252 if (type.IsValueType) {
1253 Report.Error (77, loc, "The as operator must be used with a reference type (`" +
1254 TypeManager.CSharpName (type) + "' is a value type)");
1260 // If the type is a type parameter, ensure
1261 // that it is constrained by a class
1263 TypeParameterExpr tpe = probe_type_expr as TypeParameterExpr;
1265 Constraints constraints = tpe.TypeParameter.Constraints;
1268 if (constraints == null)
1271 if (!constraints.HasClassConstraint)
1272 if ((constraints.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == 0)
1276 Report.Error (413, loc,
1277 "The as operator requires that the `{0}' type parameter be constrained by a class",
1278 probe_type_expr.GetSignatureForError ());
1283 Expression e = Convert.ImplicitConversion (ec, expr, type, loc);
1290 if (Convert.ExplicitReferenceConversionExists (etype, type)){
1291 if (etype.IsGenericParameter)
1292 expr = new BoxedCast (expr, etype);
1298 if (etype.ContainsGenericParameters || type.ContainsGenericParameters) {
1299 expr = new BoxedCast (expr, etype);
1304 Error_CannotConvertType (etype, type, loc);
1308 public override bool GetAttributableValue (Type valueType, out object value)
1310 return expr.GetAttributableValue (valueType, out value);
1315 /// This represents a typecast in the source language.
1317 /// FIXME: Cast expressions have an unusual set of parsing
1318 /// rules, we need to figure those out.
1320 public class Cast : Expression {
1321 Expression target_type;
1324 public Cast (Expression cast_type, Expression expr)
1325 : this (cast_type, expr, cast_type.Location)
1329 public Cast (Expression cast_type, Expression expr, Location loc)
1331 this.target_type = cast_type;
1335 if (target_type == TypeManager.system_void_expr)
1336 Report.Error (1547, loc, "Keyword `void' cannot be used in this context");
1339 public Expression TargetType {
1340 get { return target_type; }
1343 public Expression Expr {
1344 get { return expr; }
1345 set { expr = value; }
1348 public override Expression DoResolve (EmitContext ec)
1350 expr = expr.Resolve (ec);
1354 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1360 if (type.IsAbstract && type.IsSealed) {
1361 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1365 eclass = ExprClass.Value;
1367 Constant c = expr as Constant;
1370 c = c.TryReduce (ec, type, loc);
1374 catch (OverflowException) {
1379 if (type.IsPointer && !ec.InUnsafe) {
1383 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1387 public override void Emit (EmitContext ec)
1389 throw new Exception ("Should not happen");
1394 /// Binary operators
1396 public class Binary : Expression {
1397 public enum Operator : byte {
1398 Multiply, Division, Modulus,
1399 Addition, Subtraction,
1400 LeftShift, RightShift,
1401 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1402 Equality, Inequality,
1412 Expression left, right;
1414 // This must be kept in sync with Operator!!!
1415 public static readonly string [] oper_names;
1419 oper_names = new string [(int) Operator.TOP];
1421 oper_names [(int) Operator.Multiply] = "op_Multiply";
1422 oper_names [(int) Operator.Division] = "op_Division";
1423 oper_names [(int) Operator.Modulus] = "op_Modulus";
1424 oper_names [(int) Operator.Addition] = "op_Addition";
1425 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1426 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1427 oper_names [(int) Operator.RightShift] = "op_RightShift";
1428 oper_names [(int) Operator.LessThan] = "op_LessThan";
1429 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1430 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1431 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1432 oper_names [(int) Operator.Equality] = "op_Equality";
1433 oper_names [(int) Operator.Inequality] = "op_Inequality";
1434 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1435 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1436 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1437 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1438 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1441 public Binary (Operator oper, Expression left, Expression right)
1446 this.loc = left.Location;
1449 public Operator Oper {
1458 public Expression Left {
1467 public Expression Right {
1478 /// Returns a stringified representation of the Operator
1480 public static string OperName (Operator oper)
1483 case Operator.Multiply:
1485 case Operator.Division:
1487 case Operator.Modulus:
1489 case Operator.Addition:
1491 case Operator.Subtraction:
1493 case Operator.LeftShift:
1495 case Operator.RightShift:
1497 case Operator.LessThan:
1499 case Operator.GreaterThan:
1501 case Operator.LessThanOrEqual:
1503 case Operator.GreaterThanOrEqual:
1505 case Operator.Equality:
1507 case Operator.Inequality:
1509 case Operator.BitwiseAnd:
1511 case Operator.BitwiseOr:
1513 case Operator.ExclusiveOr:
1515 case Operator.LogicalOr:
1517 case Operator.LogicalAnd:
1521 return oper.ToString ();
1524 public override string ToString ()
1526 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1527 right.ToString () + ")";
1530 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1532 if (expr.Type == target_type)
1535 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1538 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1541 34, loc, "Operator `" + OperName (oper)
1542 + "' is ambiguous on operands of type `"
1543 + TypeManager.CSharpName (l) + "' "
1544 + "and `" + TypeManager.CSharpName (r)
1548 bool IsConvertible (EmitContext ec, Expression le, Expression re, Type t)
1550 return Convert.ImplicitConversionExists (ec, le, t) && Convert.ImplicitConversionExists (ec, re, t);
1553 bool VerifyApplicable_Predefined (EmitContext ec, Type t)
1555 if (!IsConvertible (ec, left, right, t))
1557 left = ForceConversion (ec, left, t);
1558 right = ForceConversion (ec, right, t);
1563 bool IsApplicable_String (EmitContext ec, Expression le, Expression re, Operator oper)
1565 bool l = Convert.ImplicitConversionExists (ec, le, TypeManager.string_type);
1566 bool r = Convert.ImplicitConversionExists (ec, re, TypeManager.string_type);
1568 if (oper == Operator.Equality || oper == Operator.Inequality)
1570 if (oper == Operator.Addition)
1575 bool OverloadResolve_PredefinedString (EmitContext ec, Operator oper)
1577 if (!IsApplicable_String (ec, left, right, oper))
1579 Type t = TypeManager.string_type;
1580 if (Convert.ImplicitConversionExists (ec, left, t))
1581 left = ForceConversion (ec, left, t);
1582 if (Convert.ImplicitConversionExists (ec, right, t))
1583 right = ForceConversion (ec, right, t);
1588 bool OverloadResolve_PredefinedIntegral (EmitContext ec)
1590 return VerifyApplicable_Predefined (ec, TypeManager.int32_type) ||
1591 VerifyApplicable_Predefined (ec, TypeManager.uint32_type) ||
1592 VerifyApplicable_Predefined (ec, TypeManager.int64_type) ||
1593 VerifyApplicable_Predefined (ec, TypeManager.uint64_type) ||
1597 bool OverloadResolve_PredefinedFloating (EmitContext ec)
1599 return VerifyApplicable_Predefined (ec, TypeManager.float_type) ||
1600 VerifyApplicable_Predefined (ec, TypeManager.double_type) ||
1604 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1606 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1609 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1611 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1615 void Error_OperatorCannotBeApplied ()
1617 Error_OperatorCannotBeApplied (Location, OperName (oper), left.GetSignatureForError (), right.GetSignatureForError ());
1620 static bool is_unsigned (Type t)
1622 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1623 t == TypeManager.short_type || t == TypeManager.byte_type);
1626 Expression Make32or64 (EmitContext ec, Expression e)
1630 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1631 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1633 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1636 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1639 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1642 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1648 Expression CheckShiftArguments (EmitContext ec)
1650 Expression new_left = Make32or64 (ec, left);
1651 Expression new_right = ForceConversion (ec, right, TypeManager.int32_type);
1652 if (new_left == null || new_right == null) {
1653 Error_OperatorCannotBeApplied ();
1656 type = new_left.Type;
1657 int shiftmask = (type == TypeManager.int32_type || type == TypeManager.uint32_type) ? 31 : 63;
1659 right = new Binary (Binary.Operator.BitwiseAnd, new_right, new IntConstant (shiftmask, loc)).DoResolve (ec);
1664 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1665 // i.e., not invoke op_Equality.
1667 static bool EqualsNullIsReferenceEquals (Type t)
1669 return t == TypeManager.object_type || t == TypeManager.string_type ||
1670 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1673 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1675 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1676 "Possible unintended reference comparison; to get a value comparison, " +
1677 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1680 Expression ResolveOperator (EmitContext ec)
1683 Type r = right.Type;
1685 if (oper == Operator.Equality || oper == Operator.Inequality){
1686 if (l.IsGenericParameter && (right is NullLiteral)) {
1687 if (l.BaseType == TypeManager.value_type) {
1688 Error_OperatorCannotBeApplied ();
1692 left = new BoxedCast (left, TypeManager.object_type);
1693 Type = TypeManager.bool_type;
1697 if (r.IsGenericParameter && (left is NullLiteral)) {
1698 if (r.BaseType == TypeManager.value_type) {
1699 Error_OperatorCannotBeApplied ();
1703 right = new BoxedCast (right, TypeManager.object_type);
1704 Type = TypeManager.bool_type;
1709 // Optimize out call to op_Equality in a few cases.
1711 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1712 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1713 Type = TypeManager.bool_type;
1718 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1719 Type = TypeManager.bool_type;
1726 // Do not perform operator overload resolution when both sides are
1729 Expression left_operators = null, right_operators = null;
1730 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))) {
1732 // Step 1: Perform Operator Overload location
1734 string op = oper_names [(int) oper];
1736 MethodGroupExpr union;
1737 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc);
1739 right_operators = MemberLookup (
1740 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc);
1741 union = Invocation.MakeUnionSet (left_operators, right_operators, loc);
1743 union = (MethodGroupExpr) left_operators;
1745 if (union != null) {
1746 ArrayList args = new ArrayList (2);
1747 args.Add (new Argument (left, Argument.AType.Expression));
1748 args.Add (new Argument (right, Argument.AType.Expression));
1750 MethodBase method = Invocation.OverloadResolve (ec, union, args, true, Location.Null);
1752 if (method != null) {
1753 MethodInfo mi = (MethodInfo) method;
1754 return new BinaryMethod (mi.ReturnType, method, args);
1760 // Step 0: String concatenation (because overloading will get this wrong)
1762 if (oper == Operator.Addition){
1764 // If any of the arguments is a string, cast to string
1767 // Simple constant folding
1768 if (left is StringConstant && right is StringConstant)
1769 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1771 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1773 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1774 Error_OperatorCannotBeApplied ();
1778 // try to fold it in on the left
1779 if (left is StringConcat) {
1782 // We have to test here for not-null, since we can be doubly-resolved
1783 // take care of not appending twice
1786 type = TypeManager.string_type;
1787 ((StringConcat) left).Append (ec, right);
1788 return left.Resolve (ec);
1794 // Otherwise, start a new concat expression
1795 return new StringConcat (ec, loc, left, right).Resolve (ec);
1799 // Transform a + ( - b) into a - b
1801 if (right is Unary){
1802 Unary right_unary = (Unary) right;
1804 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1805 oper = Operator.Subtraction;
1806 right = right_unary.Expr;
1812 if (oper == Operator.Equality || oper == Operator.Inequality){
1813 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1814 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1815 Error_OperatorCannotBeApplied ();
1819 type = TypeManager.bool_type;
1823 if (l.IsPointer || r.IsPointer) {
1824 if (l.IsPointer && r.IsPointer) {
1825 type = TypeManager.bool_type;
1829 if (l.IsPointer && r == TypeManager.null_type) {
1830 right = new EmptyCast (NullPointer.Null, l);
1831 type = TypeManager.bool_type;
1835 if (r.IsPointer && l == TypeManager.null_type) {
1836 left = new EmptyCast (NullPointer.Null, r);
1837 type = TypeManager.bool_type;
1842 if (l.IsGenericParameter && r.IsGenericParameter) {
1843 GenericConstraints l_gc, r_gc;
1845 l_gc = TypeManager.GetTypeParameterConstraints (l);
1846 r_gc = TypeManager.GetTypeParameterConstraints (r);
1848 if ((l_gc == null) || (r_gc == null) ||
1849 !(l_gc.HasReferenceTypeConstraint || l_gc.HasClassConstraint) ||
1850 !(r_gc.HasReferenceTypeConstraint || r_gc.HasClassConstraint)) {
1851 Error_OperatorCannotBeApplied ();
1858 // operator != (object a, object b)
1859 // operator == (object a, object b)
1861 // For this to be used, both arguments have to be reference-types.
1862 // Read the rationale on the spec (14.9.6)
1864 if (!(l.IsValueType || r.IsValueType)){
1865 type = TypeManager.bool_type;
1871 // Also, a standard conversion must exist from either one
1873 bool left_to_right =
1874 Convert.ImplicitStandardConversionExists (left, r);
1875 bool right_to_left = !left_to_right &&
1876 Convert.ImplicitStandardConversionExists (right, l);
1878 if (!left_to_right && !right_to_left) {
1879 Error_OperatorCannotBeApplied ();
1883 if (left_to_right && left_operators != null &&
1884 RootContext.WarningLevel >= 2) {
1885 ArrayList args = new ArrayList (2);
1886 args.Add (new Argument (left, Argument.AType.Expression));
1887 args.Add (new Argument (left, Argument.AType.Expression));
1888 MethodBase method = Invocation.OverloadResolve (
1889 ec, (MethodGroupExpr) left_operators, args, true, Location.Null);
1891 Warning_UnintendedReferenceComparison (loc, "right", l);
1894 if (right_to_left && right_operators != null &&
1895 RootContext.WarningLevel >= 2) {
1896 ArrayList args = new ArrayList (2);
1897 args.Add (new Argument (right, Argument.AType.Expression));
1898 args.Add (new Argument (right, Argument.AType.Expression));
1899 MethodBase method = Invocation.OverloadResolve (
1900 ec, (MethodGroupExpr) right_operators, args, true, Location.Null);
1902 Warning_UnintendedReferenceComparison (loc, "left", r);
1906 // We are going to have to convert to an object to compare
1908 if (l != TypeManager.object_type)
1909 left = new EmptyCast (left, TypeManager.object_type);
1910 if (r != TypeManager.object_type)
1911 right = new EmptyCast (right, TypeManager.object_type);
1917 // Only perform numeric promotions on:
1918 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
1920 if (oper == Operator.Addition || oper == Operator.Subtraction) {
1921 if (TypeManager.IsDelegateType (l)){
1922 if (((right.eclass == ExprClass.MethodGroup) ||
1923 (r == TypeManager.anonymous_method_type))){
1924 if ((RootContext.Version != LanguageVersion.ISO_1)){
1925 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
1933 if (TypeManager.IsDelegateType (r) || right is NullLiteral){
1935 ArrayList args = new ArrayList (2);
1937 args = new ArrayList (2);
1938 args.Add (new Argument (left, Argument.AType.Expression));
1939 args.Add (new Argument (right, Argument.AType.Expression));
1941 if (oper == Operator.Addition)
1942 method = TypeManager.delegate_combine_delegate_delegate;
1944 method = TypeManager.delegate_remove_delegate_delegate;
1946 if (!TypeManager.IsEqual (l, r) && !(right is NullLiteral)) {
1947 Error_OperatorCannotBeApplied ();
1951 return new BinaryDelegate (l, method, args);
1956 // Pointer arithmetic:
1958 // T* operator + (T* x, int y);
1959 // T* operator + (T* x, uint y);
1960 // T* operator + (T* x, long y);
1961 // T* operator + (T* x, ulong y);
1963 // T* operator + (int y, T* x);
1964 // T* operator + (uint y, T *x);
1965 // T* operator + (long y, T *x);
1966 // T* operator + (ulong y, T *x);
1968 // T* operator - (T* x, int y);
1969 // T* operator - (T* x, uint y);
1970 // T* operator - (T* x, long y);
1971 // T* operator - (T* x, ulong y);
1973 // long operator - (T* x, T *y)
1976 if (r.IsPointer && oper == Operator.Subtraction){
1978 return new PointerArithmetic (
1979 false, left, right, TypeManager.int64_type,
1982 Expression t = Make32or64 (ec, right);
1984 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
1986 } else if (r.IsPointer && oper == Operator.Addition){
1987 Expression t = Make32or64 (ec, left);
1989 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
1994 // Enumeration operators
1996 bool lie = TypeManager.IsEnumType (l);
1997 bool rie = TypeManager.IsEnumType (r);
2001 // U operator - (E e, E f)
2003 if (oper == Operator.Subtraction){
2005 type = TypeManager.EnumToUnderlying (l);
2008 Error_OperatorCannotBeApplied ();
2014 // operator + (E e, U x)
2015 // operator - (E e, U x)
2017 if (oper == Operator.Addition || oper == Operator.Subtraction){
2018 Type enum_type = lie ? l : r;
2019 Type other_type = lie ? r : l;
2020 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2022 if (underlying_type != other_type){
2023 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2033 Error_OperatorCannotBeApplied ();
2042 temp = Convert.ImplicitConversion (ec, right, l, loc);
2046 Error_OperatorCannotBeApplied ();
2050 temp = Convert.ImplicitConversion (ec, left, r, loc);
2055 Error_OperatorCannotBeApplied ();
2060 if (oper == Operator.Equality || oper == Operator.Inequality ||
2061 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2062 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2063 if (left.Type != right.Type){
2064 Error_OperatorCannotBeApplied ();
2067 type = TypeManager.bool_type;
2071 if (oper == Operator.BitwiseAnd ||
2072 oper == Operator.BitwiseOr ||
2073 oper == Operator.ExclusiveOr){
2074 if (left.Type != right.Type){
2075 Error_OperatorCannotBeApplied ();
2081 Error_OperatorCannotBeApplied ();
2085 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2086 return CheckShiftArguments (ec);
2088 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2089 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2090 type = TypeManager.bool_type;
2095 Error_OperatorCannotBeApplied ();
2099 Expression e = new ConditionalLogicalOperator (
2100 oper == Operator.LogicalAnd, left, right, l, loc);
2101 return e.Resolve (ec);
2104 Expression orig_left = left;
2105 Expression orig_right = right;
2108 // operator & (bool x, bool y)
2109 // operator | (bool x, bool y)
2110 // operator ^ (bool x, bool y)
2112 if (oper == Operator.BitwiseAnd ||
2113 oper == Operator.BitwiseOr ||
2114 oper == Operator.ExclusiveOr) {
2115 if (OverloadResolve_PredefinedIntegral (ec)) {
2116 if (IsConvertible (ec, orig_left, orig_right, TypeManager.bool_type)) {
2117 Error_OperatorAmbiguous (loc, oper, l, r);
2120 } else if (!VerifyApplicable_Predefined (ec, TypeManager.bool_type)) {
2121 Error_OperatorCannotBeApplied ();
2128 // Pointer comparison
2130 if (l.IsPointer && r.IsPointer){
2131 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2132 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2133 type = TypeManager.bool_type;
2138 if (OverloadResolve_PredefinedIntegral (ec)) {
2139 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2140 Error_OperatorAmbiguous (loc, oper, l, r);
2143 } else if (OverloadResolve_PredefinedFloating (ec)) {
2144 if (IsConvertible (ec, orig_left, orig_right, TypeManager.decimal_type) ||
2145 IsApplicable_String (ec, orig_left, orig_right, oper)) {
2146 Error_OperatorAmbiguous (loc, oper, l, r);
2149 } else if (VerifyApplicable_Predefined (ec, TypeManager.decimal_type)) {
2150 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2151 Error_OperatorAmbiguous (loc, oper, l, r);
2154 } else if (!OverloadResolve_PredefinedString (ec, oper)) {
2155 Error_OperatorCannotBeApplied ();
2159 if (oper == Operator.Equality ||
2160 oper == Operator.Inequality ||
2161 oper == Operator.LessThanOrEqual ||
2162 oper == Operator.LessThan ||
2163 oper == Operator.GreaterThanOrEqual ||
2164 oper == Operator.GreaterThan)
2165 type = TypeManager.bool_type;
2170 if (l == TypeManager.decimal_type || l == TypeManager.string_type || r == TypeManager.string_type) {
2172 if (r == TypeManager.string_type)
2174 MethodGroupExpr ops = (MethodGroupExpr) MemberLookup (
2175 ec.ContainerType, lookup, oper_names [(int) oper],
2176 MemberTypes.Method, AllBindingFlags, loc);
2177 ArrayList args = new ArrayList (2);
2178 args.Add (new Argument (left, Argument.AType.Expression));
2179 args.Add (new Argument (right, Argument.AType.Expression));
2180 MethodBase method = Invocation.OverloadResolve (ec, ops, args, true, Location.Null);
2181 return new BinaryMethod (type, method, args);
2187 Constant EnumLiftUp (Constant left, Constant right)
2190 case Operator.BitwiseOr:
2191 case Operator.BitwiseAnd:
2192 case Operator.ExclusiveOr:
2193 case Operator.Equality:
2194 case Operator.Inequality:
2195 case Operator.LessThan:
2196 case Operator.LessThanOrEqual:
2197 case Operator.GreaterThan:
2198 case Operator.GreaterThanOrEqual:
2199 if (left is EnumConstant)
2202 if (left.IsZeroInteger)
2203 return new EnumConstant (left, right.Type);
2207 case Operator.Addition:
2208 case Operator.Subtraction:
2211 case Operator.Multiply:
2212 case Operator.Division:
2213 case Operator.Modulus:
2214 case Operator.LeftShift:
2215 case Operator.RightShift:
2216 if (right is EnumConstant || left is EnumConstant)
2220 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2224 public override Expression DoResolve (EmitContext ec)
2229 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2230 left = ((ParenthesizedExpression) left).Expr;
2231 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2235 if (left.eclass == ExprClass.Type) {
2236 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2240 left = left.Resolve (ec);
2245 Constant lc = left as Constant;
2246 if (lc != null && lc.Type == TypeManager.bool_type &&
2247 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2248 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2250 // TODO: make a sense to resolve unreachable expression as we do for statement
2251 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2255 right = right.Resolve (ec);
2259 eclass = ExprClass.Value;
2260 Constant rc = right as Constant;
2262 // The conversion rules are ignored in enum context but why
2263 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2264 left = lc = EnumLiftUp (lc, rc);
2268 right = rc = EnumLiftUp (rc, lc);
2273 if (oper == Operator.BitwiseAnd) {
2274 if (rc != null && rc.IsZeroInteger) {
2275 return lc is EnumConstant ?
2276 new EnumConstant (rc, lc.Type):
2280 if (lc != null && lc.IsZeroInteger) {
2281 return rc is EnumConstant ?
2282 new EnumConstant (lc, rc.Type):
2286 else if (oper == Operator.BitwiseOr) {
2287 if (lc is EnumConstant &&
2288 rc != null && rc.IsZeroInteger)
2290 if (rc is EnumConstant &&
2291 lc != null && lc.IsZeroInteger)
2293 } else if (oper == Operator.LogicalAnd) {
2294 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2296 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2300 if (rc != null && lc != null){
2301 int prev_e = Report.Errors;
2302 Expression e = ConstantFold.BinaryFold (
2303 ec, oper, lc, rc, loc);
2304 if (e != null || Report.Errors != prev_e)
2308 Type ltype = left.Type, rtype = right.Type;
2309 if ((left is NullLiteral || ltype.IsValueType) &&
2310 (right is NullLiteral || rtype.IsValueType) &&
2311 !(left is NullLiteral && right is NullLiteral) &&
2312 (TypeManager.IsNullableType (ltype) || TypeManager.IsNullableType (rtype)))
2313 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2315 // Comparison warnings
2316 if (oper == Operator.Equality || oper == Operator.Inequality ||
2317 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2318 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2319 if (left.Equals (right)) {
2320 Report.Warning (1718, 3, loc, "Comparison made to same variable; did you mean to compare something else?");
2322 CheckUselessComparison (lc, right.Type);
2323 CheckUselessComparison (rc, left.Type);
2326 return ResolveOperator (ec);
2329 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2334 private void CheckUselessComparison (Constant c, Type type)
2336 if (c == null || !IsTypeIntegral (type)
2337 || c is StringConstant
2338 || c is BoolConstant
2339 || c is CharConstant
2340 || c is FloatConstant
2341 || c is DoubleConstant
2342 || c is DecimalConstant
2348 if (c is ULongConstant) {
2349 ulong uvalue = ((ULongConstant) c).Value;
2350 if (uvalue > long.MaxValue) {
2351 if (type == TypeManager.byte_type ||
2352 type == TypeManager.sbyte_type ||
2353 type == TypeManager.short_type ||
2354 type == TypeManager.ushort_type ||
2355 type == TypeManager.int32_type ||
2356 type == TypeManager.uint32_type ||
2357 type == TypeManager.int64_type)
2358 WarnUselessComparison (type);
2361 value = (long) uvalue;
2363 else if (c is ByteConstant)
2364 value = ((ByteConstant) c).Value;
2365 else if (c is SByteConstant)
2366 value = ((SByteConstant) c).Value;
2367 else if (c is ShortConstant)
2368 value = ((ShortConstant) c).Value;
2369 else if (c is UShortConstant)
2370 value = ((UShortConstant) c).Value;
2371 else if (c is IntConstant)
2372 value = ((IntConstant) c).Value;
2373 else if (c is UIntConstant)
2374 value = ((UIntConstant) c).Value;
2375 else if (c is LongConstant)
2376 value = ((LongConstant) c).Value;
2379 if (IsValueOutOfRange (value, type))
2380 WarnUselessComparison (type);
2385 private bool IsValueOutOfRange (long value, Type type)
2387 if (IsTypeUnsigned (type) && value < 0)
2389 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2390 type == TypeManager.byte_type && value >= 0x100 ||
2391 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2392 type == TypeManager.ushort_type && value >= 0x10000 ||
2393 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2394 type == TypeManager.uint32_type && value >= 0x100000000;
2397 private static bool IsTypeIntegral (Type type)
2399 return type == TypeManager.uint64_type ||
2400 type == TypeManager.int64_type ||
2401 type == TypeManager.uint32_type ||
2402 type == TypeManager.int32_type ||
2403 type == TypeManager.ushort_type ||
2404 type == TypeManager.short_type ||
2405 type == TypeManager.sbyte_type ||
2406 type == TypeManager.byte_type;
2409 private static bool IsTypeUnsigned (Type type)
2411 return type == TypeManager.uint64_type ||
2412 type == TypeManager.uint32_type ||
2413 type == TypeManager.ushort_type ||
2414 type == TypeManager.byte_type;
2417 private void WarnUselessComparison (Type type)
2419 Report.Warning (652, 2, loc, "Comparison to integral constant is useless; the constant is outside the range of type `{0}'",
2420 TypeManager.CSharpName (type));
2424 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2425 /// context of a conditional bool expression. This function will return
2426 /// false if it is was possible to use EmitBranchable, or true if it was.
2428 /// The expression's code is generated, and we will generate a branch to `target'
2429 /// if the resulting expression value is equal to isTrue
2431 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2433 ILGenerator ig = ec.ig;
2436 // This is more complicated than it looks, but its just to avoid
2437 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2438 // but on top of that we want for == and != to use a special path
2439 // if we are comparing against null
2441 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2442 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2445 // put the constant on the rhs, for simplicity
2447 if (left is Constant) {
2448 Expression swap = right;
2453 if (((Constant) right).IsZeroInteger) {
2456 ig.Emit (OpCodes.Brtrue, target);
2458 ig.Emit (OpCodes.Brfalse, target);
2461 } else if (right is BoolConstant) {
2463 if (my_on_true != ((BoolConstant) right).Value)
2464 ig.Emit (OpCodes.Brtrue, target);
2466 ig.Emit (OpCodes.Brfalse, target);
2471 } else if (oper == Operator.LogicalAnd) {
2474 Label tests_end = ig.DefineLabel ();
2476 left.EmitBranchable (ec, tests_end, false);
2477 right.EmitBranchable (ec, target, true);
2478 ig.MarkLabel (tests_end);
2480 left.EmitBranchable (ec, target, false);
2481 right.EmitBranchable (ec, target, false);
2486 } else if (oper == Operator.LogicalOr){
2488 left.EmitBranchable (ec, target, true);
2489 right.EmitBranchable (ec, target, true);
2492 Label tests_end = ig.DefineLabel ();
2493 left.EmitBranchable (ec, tests_end, true);
2494 right.EmitBranchable (ec, target, false);
2495 ig.MarkLabel (tests_end);
2500 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2501 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2502 oper == Operator.Equality || oper == Operator.Inequality)) {
2503 base.EmitBranchable (ec, target, onTrue);
2511 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2514 case Operator.Equality:
2516 ig.Emit (OpCodes.Beq, target);
2518 ig.Emit (OpCodes.Bne_Un, target);
2521 case Operator.Inequality:
2523 ig.Emit (OpCodes.Bne_Un, target);
2525 ig.Emit (OpCodes.Beq, target);
2528 case Operator.LessThan:
2531 ig.Emit (OpCodes.Blt_Un, target);
2533 ig.Emit (OpCodes.Blt, target);
2536 ig.Emit (OpCodes.Bge_Un, target);
2538 ig.Emit (OpCodes.Bge, target);
2541 case Operator.GreaterThan:
2544 ig.Emit (OpCodes.Bgt_Un, target);
2546 ig.Emit (OpCodes.Bgt, target);
2549 ig.Emit (OpCodes.Ble_Un, target);
2551 ig.Emit (OpCodes.Ble, target);
2554 case Operator.LessThanOrEqual:
2557 ig.Emit (OpCodes.Ble_Un, target);
2559 ig.Emit (OpCodes.Ble, target);
2562 ig.Emit (OpCodes.Bgt_Un, target);
2564 ig.Emit (OpCodes.Bgt, target);
2568 case Operator.GreaterThanOrEqual:
2571 ig.Emit (OpCodes.Bge_Un, target);
2573 ig.Emit (OpCodes.Bge, target);
2576 ig.Emit (OpCodes.Blt_Un, target);
2578 ig.Emit (OpCodes.Blt, target);
2581 Console.WriteLine (oper);
2582 throw new Exception ("what is THAT");
2586 public override void Emit (EmitContext ec)
2588 ILGenerator ig = ec.ig;
2593 // Handle short-circuit operators differently
2596 if (oper == Operator.LogicalAnd) {
2597 Label load_zero = ig.DefineLabel ();
2598 Label end = ig.DefineLabel ();
2600 left.EmitBranchable (ec, load_zero, false);
2602 ig.Emit (OpCodes.Br, end);
2604 ig.MarkLabel (load_zero);
2605 ig.Emit (OpCodes.Ldc_I4_0);
2608 } else if (oper == Operator.LogicalOr) {
2609 Label load_one = ig.DefineLabel ();
2610 Label end = ig.DefineLabel ();
2612 left.EmitBranchable (ec, load_one, true);
2614 ig.Emit (OpCodes.Br, end);
2616 ig.MarkLabel (load_one);
2617 ig.Emit (OpCodes.Ldc_I4_1);
2625 bool isUnsigned = is_unsigned (left.Type);
2628 case Operator.Multiply:
2630 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2631 opcode = OpCodes.Mul_Ovf;
2632 else if (isUnsigned)
2633 opcode = OpCodes.Mul_Ovf_Un;
2635 opcode = OpCodes.Mul;
2637 opcode = OpCodes.Mul;
2641 case Operator.Division:
2643 opcode = OpCodes.Div_Un;
2645 opcode = OpCodes.Div;
2648 case Operator.Modulus:
2650 opcode = OpCodes.Rem_Un;
2652 opcode = OpCodes.Rem;
2655 case Operator.Addition:
2657 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2658 opcode = OpCodes.Add_Ovf;
2659 else if (isUnsigned)
2660 opcode = OpCodes.Add_Ovf_Un;
2662 opcode = OpCodes.Add;
2664 opcode = OpCodes.Add;
2667 case Operator.Subtraction:
2669 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2670 opcode = OpCodes.Sub_Ovf;
2671 else if (isUnsigned)
2672 opcode = OpCodes.Sub_Ovf_Un;
2674 opcode = OpCodes.Sub;
2676 opcode = OpCodes.Sub;
2679 case Operator.RightShift:
2681 opcode = OpCodes.Shr_Un;
2683 opcode = OpCodes.Shr;
2686 case Operator.LeftShift:
2687 opcode = OpCodes.Shl;
2690 case Operator.Equality:
2691 opcode = OpCodes.Ceq;
2694 case Operator.Inequality:
2695 ig.Emit (OpCodes.Ceq);
2696 ig.Emit (OpCodes.Ldc_I4_0);
2698 opcode = OpCodes.Ceq;
2701 case Operator.LessThan:
2703 opcode = OpCodes.Clt_Un;
2705 opcode = OpCodes.Clt;
2708 case Operator.GreaterThan:
2710 opcode = OpCodes.Cgt_Un;
2712 opcode = OpCodes.Cgt;
2715 case Operator.LessThanOrEqual:
2716 Type lt = left.Type;
2718 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2719 ig.Emit (OpCodes.Cgt_Un);
2721 ig.Emit (OpCodes.Cgt);
2722 ig.Emit (OpCodes.Ldc_I4_0);
2724 opcode = OpCodes.Ceq;
2727 case Operator.GreaterThanOrEqual:
2728 Type le = left.Type;
2730 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2731 ig.Emit (OpCodes.Clt_Un);
2733 ig.Emit (OpCodes.Clt);
2735 ig.Emit (OpCodes.Ldc_I4_0);
2737 opcode = OpCodes.Ceq;
2740 case Operator.BitwiseOr:
2741 opcode = OpCodes.Or;
2744 case Operator.BitwiseAnd:
2745 opcode = OpCodes.And;
2748 case Operator.ExclusiveOr:
2749 opcode = OpCodes.Xor;
2753 throw new Exception ("This should not happen: Operator = "
2754 + oper.ToString ());
2762 // Object created by Binary when the binary operator uses an method instead of being
2763 // a binary operation that maps to a CIL binary operation.
2765 public class BinaryMethod : Expression {
2766 public MethodBase method;
2767 public ArrayList Arguments;
2769 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2774 eclass = ExprClass.Value;
2777 public override Expression DoResolve (EmitContext ec)
2782 public override void Emit (EmitContext ec)
2784 ILGenerator ig = ec.ig;
2786 if (Arguments != null)
2787 Invocation.EmitArguments (ec, method, Arguments, false, null);
2789 if (method is MethodInfo)
2790 ig.Emit (OpCodes.Call, (MethodInfo) method);
2792 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2797 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2798 // b, c, d... may be strings or objects.
2800 public class StringConcat : Expression {
2802 bool invalid = false;
2803 bool emit_conv_done = false;
2805 // Are we also concating objects?
2807 bool is_strings_only = true;
2809 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2812 type = TypeManager.string_type;
2813 eclass = ExprClass.Value;
2815 operands = new ArrayList (2);
2820 public override Expression DoResolve (EmitContext ec)
2828 public void Append (EmitContext ec, Expression operand)
2833 StringConstant sc = operand as StringConstant;
2835 // TODO: it will be better to do this silently as an optimalization
2837 // string s = "" + i;
2838 // because this code has poor performace
2839 // if (sc.Value.Length == 0)
2840 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2842 if (operands.Count != 0) {
2843 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2844 if (last_operand != null) {
2845 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2852 // Conversion to object
2854 if (operand.Type != TypeManager.string_type) {
2855 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2858 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2864 operands.Add (operand);
2867 public override void Emit (EmitContext ec)
2869 MethodInfo concat_method = null;
2872 // Do conversion to arguments; check for strings only
2875 // This can get called multiple times, so we have to deal with that.
2876 if (!emit_conv_done) {
2877 emit_conv_done = true;
2878 for (int i = 0; i < operands.Count; i ++) {
2879 Expression e = (Expression) operands [i];
2880 is_strings_only &= e.Type == TypeManager.string_type;
2883 for (int i = 0; i < operands.Count; i ++) {
2884 Expression e = (Expression) operands [i];
2886 if (! is_strings_only && e.Type == TypeManager.string_type) {
2887 // need to make sure this is an object, because the EmitParams
2888 // method might look at the type of this expression, see it is a
2889 // string and emit a string [] when we want an object [];
2891 e = new EmptyCast (e, TypeManager.object_type);
2893 operands [i] = new Argument (e, Argument.AType.Expression);
2898 // Find the right method
2900 switch (operands.Count) {
2903 // This should not be possible, because simple constant folding
2904 // is taken care of in the Binary code.
2906 throw new Exception ("how did you get here?");
2909 concat_method = is_strings_only ?
2910 TypeManager.string_concat_string_string :
2911 TypeManager.string_concat_object_object ;
2914 concat_method = is_strings_only ?
2915 TypeManager.string_concat_string_string_string :
2916 TypeManager.string_concat_object_object_object ;
2920 // There is not a 4 param overlaod for object (the one that there is
2921 // is actually a varargs methods, and is only in corlib because it was
2922 // introduced there before.).
2924 if (!is_strings_only)
2927 concat_method = TypeManager.string_concat_string_string_string_string;
2930 concat_method = is_strings_only ?
2931 TypeManager.string_concat_string_dot_dot_dot :
2932 TypeManager.string_concat_object_dot_dot_dot ;
2936 Invocation.EmitArguments (ec, concat_method, operands, false, null);
2937 ec.ig.Emit (OpCodes.Call, concat_method);
2942 // Object created with +/= on delegates
2944 public class BinaryDelegate : Expression {
2948 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
2953 eclass = ExprClass.Value;
2956 public override Expression DoResolve (EmitContext ec)
2961 public override void Emit (EmitContext ec)
2963 ILGenerator ig = ec.ig;
2965 Invocation.EmitArguments (ec, method, args, false, null);
2967 ig.Emit (OpCodes.Call, (MethodInfo) method);
2968 ig.Emit (OpCodes.Castclass, type);
2971 public Expression Right {
2973 Argument arg = (Argument) args [1];
2978 public bool IsAddition {
2980 return method == TypeManager.delegate_combine_delegate_delegate;
2986 // User-defined conditional logical operator
2987 public class ConditionalLogicalOperator : Expression {
2988 Expression left, right;
2991 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
2994 eclass = ExprClass.Value;
2998 this.is_and = is_and;
3001 protected void Error19 ()
3003 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3006 protected void Error218 ()
3008 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3009 "declarations of operator true and operator false");
3012 Expression op_true, op_false, op;
3013 LocalTemporary left_temp;
3015 public override Expression DoResolve (EmitContext ec)
3018 Expression operator_group;
3020 operator_group = MethodLookup (ec, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc);
3021 if (operator_group == null) {
3026 left_temp = new LocalTemporary (type);
3028 ArrayList arguments = new ArrayList ();
3029 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3030 arguments.Add (new Argument (right, Argument.AType.Expression));
3031 method = Invocation.OverloadResolve (
3032 ec, (MethodGroupExpr) operator_group, arguments, false, loc)
3034 if (method == null) {
3039 if (method.ReturnType != type) {
3040 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3041 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3045 op = new StaticCallExpr (method, arguments, loc);
3047 op_true = GetOperatorTrue (ec, left_temp, loc);
3048 op_false = GetOperatorFalse (ec, left_temp, loc);
3049 if ((op_true == null) || (op_false == null)) {
3057 public override void Emit (EmitContext ec)
3059 ILGenerator ig = ec.ig;
3060 Label false_target = ig.DefineLabel ();
3061 Label end_target = ig.DefineLabel ();
3064 left_temp.Store (ec);
3066 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3067 left_temp.Emit (ec);
3068 ig.Emit (OpCodes.Br, end_target);
3069 ig.MarkLabel (false_target);
3071 ig.MarkLabel (end_target);
3075 public class PointerArithmetic : Expression {
3076 Expression left, right;
3080 // We assume that `l' is always a pointer
3082 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3088 is_add = is_addition;
3091 public override Expression DoResolve (EmitContext ec)
3093 eclass = ExprClass.Variable;
3095 if (left.Type == TypeManager.void_ptr_type) {
3096 Error (242, "The operation in question is undefined on void pointers");
3103 public override void Emit (EmitContext ec)
3105 Type op_type = left.Type;
3106 ILGenerator ig = ec.ig;
3108 // It must be either array or fixed buffer
3109 Type element = TypeManager.HasElementType (op_type) ?
3110 element = TypeManager.GetElementType (op_type) :
3111 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3113 int size = GetTypeSize (element);
3114 Type rtype = right.Type;
3116 if (rtype.IsPointer){
3118 // handle (pointer - pointer)
3122 ig.Emit (OpCodes.Sub);
3126 ig.Emit (OpCodes.Sizeof, element);
3128 IntLiteral.EmitInt (ig, size);
3129 ig.Emit (OpCodes.Div);
3131 ig.Emit (OpCodes.Conv_I8);
3134 // handle + and - on (pointer op int)
3137 ig.Emit (OpCodes.Conv_I);
3139 Constant right_const = right as Constant;
3140 if (right_const != null && size != 0) {
3141 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3149 ig.Emit (OpCodes.Sizeof, element);
3151 IntLiteral.EmitInt (ig, size);
3152 if (rtype == TypeManager.int64_type)
3153 ig.Emit (OpCodes.Conv_I8);
3154 else if (rtype == TypeManager.uint64_type)
3155 ig.Emit (OpCodes.Conv_U8);
3156 ig.Emit (OpCodes.Mul);
3160 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3161 ig.Emit (OpCodes.Conv_I);
3164 ig.Emit (OpCodes.Add);
3166 ig.Emit (OpCodes.Sub);
3172 /// Implements the ternary conditional operator (?:)
3174 public class Conditional : Expression {
3175 Expression expr, trueExpr, falseExpr;
3177 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3180 this.trueExpr = trueExpr;
3181 this.falseExpr = falseExpr;
3182 this.loc = expr.Location;
3185 public Expression Expr {
3191 public Expression TrueExpr {
3197 public Expression FalseExpr {
3203 public override Expression DoResolve (EmitContext ec)
3205 expr = expr.Resolve (ec);
3210 if (TypeManager.IsNullableValueType (expr.Type))
3211 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
3213 if (expr.Type != TypeManager.bool_type){
3214 expr = Expression.ResolveBoolean (
3221 Assign ass = expr as Assign;
3222 if (ass != null && ass.Source is Constant) {
3223 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3226 trueExpr = trueExpr.Resolve (ec);
3227 falseExpr = falseExpr.Resolve (ec);
3229 if (trueExpr == null || falseExpr == null)
3232 eclass = ExprClass.Value;
3233 if (trueExpr.Type == falseExpr.Type)
3234 type = trueExpr.Type;
3237 Type true_type = trueExpr.Type;
3238 Type false_type = falseExpr.Type;
3241 // First, if an implicit conversion exists from trueExpr
3242 // to falseExpr, then the result type is of type falseExpr.Type
3244 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3247 // Check if both can convert implicitl to each other's type
3249 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3251 "Can not compute type of conditional expression " +
3252 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3253 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3254 "' convert implicitly to each other");
3259 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3263 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3264 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3269 // Dead code optimalization
3270 if (expr is BoolConstant){
3271 BoolConstant bc = (BoolConstant) expr;
3273 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3274 return bc.Value ? trueExpr : falseExpr;
3280 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3285 public override void Emit (EmitContext ec)
3287 ILGenerator ig = ec.ig;
3288 Label false_target = ig.DefineLabel ();
3289 Label end_target = ig.DefineLabel ();
3291 expr.EmitBranchable (ec, false_target, false);
3293 ig.Emit (OpCodes.Br, end_target);
3294 ig.MarkLabel (false_target);
3295 falseExpr.Emit (ec);
3296 ig.MarkLabel (end_target);
3304 public class LocalVariableReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3305 public readonly string Name;
3306 public readonly Block Block;
3307 public LocalInfo local_info;
3310 LocalTemporary temp;
3313 public LocalVariableReference (Block block, string name, Location l)
3318 eclass = ExprClass.Variable;
3322 // Setting `is_readonly' to false will allow you to create a writable
3323 // reference to a read-only variable. This is used by foreach and using.
3325 public LocalVariableReference (Block block, string name, Location l,
3326 LocalInfo local_info, bool is_readonly)
3327 : this (block, name, l)
3329 this.local_info = local_info;
3330 this.is_readonly = is_readonly;
3333 public VariableInfo VariableInfo {
3334 get { return local_info.VariableInfo; }
3337 public bool IsReadOnly {
3338 get { return is_readonly; }
3341 public bool VerifyAssigned (EmitContext ec)
3343 VariableInfo variable_info = local_info.VariableInfo;
3344 return variable_info == null || variable_info.IsAssigned (ec, loc);
3347 void ResolveLocalInfo ()
3349 if (local_info == null) {
3350 local_info = Block.GetLocalInfo (Name);
3351 is_readonly = local_info.ReadOnly;
3355 protected Expression DoResolveBase (EmitContext ec)
3357 type = local_info.VariableType;
3359 Expression e = Block.GetConstantExpression (Name);
3361 return e.Resolve (ec);
3363 if (!VerifyAssigned (ec))
3367 // If we are referencing a variable from the external block
3368 // flag it for capturing
3370 if (ec.MustCaptureVariable (local_info)) {
3371 if (local_info.AddressTaken){
3372 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3376 ScopeInfo scope = local_info.Block.CreateScopeInfo ();
3377 variable = scope.AddLocal (local_info);
3378 type = variable.Type;
3384 public override Expression DoResolve (EmitContext ec)
3386 ResolveLocalInfo ();
3387 local_info.Used = true;
3388 return DoResolveBase (ec);
3391 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3393 ResolveLocalInfo ();
3398 if (right_side == EmptyExpression.OutAccess) {
3399 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3400 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3401 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3402 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3403 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3405 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
3407 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3412 if (right_side == EmptyExpression.OutAccess)
3413 local_info.Used = true;
3415 if (VariableInfo != null)
3416 VariableInfo.SetAssigned (ec);
3418 return DoResolveBase (ec);
3421 public bool VerifyFixed ()
3423 // A local Variable is always fixed.
3427 public override int GetHashCode ()
3429 return Name.GetHashCode ();
3432 public override bool Equals (object obj)
3434 LocalVariableReference lvr = obj as LocalVariableReference;
3438 return Name == lvr.Name && Block == lvr.Block;
3441 public Variable Variable {
3442 get { return variable != null ? variable : local_info.Variable; }
3445 public override void Emit (EmitContext ec)
3447 Report.Debug (64, "EMIT LOCAL VARIABLE REFERENCE", this, ec, Variable, loc);
3449 Variable.EmitInstance (ec);
3453 public void Emit (EmitContext ec, bool leave_copy)
3457 ec.ig.Emit (OpCodes.Dup);
3458 if (Variable.NeedsTemporary) {
3459 temp = new LocalTemporary (Type);
3465 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
3466 bool prepare_for_load)
3468 ILGenerator ig = ec.ig;
3469 prepared = prepare_for_load;
3471 Report.Debug (64, "LOCAL VAR REF EMIT ASSIGN", this, source, loc);
3472 Variable.EmitInstance (ec);
3473 if (prepare_for_load && Variable.HasInstance)
3474 ig.Emit (OpCodes.Dup);
3477 ig.Emit (OpCodes.Dup);
3478 if (Variable.NeedsTemporary) {
3479 temp = new LocalTemporary (Type);
3483 Variable.EmitAssign (ec);
3488 public void AddressOf (EmitContext ec, AddressOp mode)
3490 Variable.EmitInstance (ec);
3491 Variable.EmitAddressOf (ec);
3494 public override string ToString ()
3496 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3501 /// This represents a reference to a parameter in the intermediate
3504 public class ParameterReference : Expression, IAssignMethod, IMemoryLocation, IVariable {
3510 public bool is_ref, is_out, prepared;
3524 public string Name {
3530 public Parameter Parameter {
3536 LocalTemporary temp;
3539 public ParameterReference (Parameter par, Block block, int idx, Location loc)
3542 this.name = par.Name;
3546 eclass = ExprClass.Variable;
3549 public VariableInfo VariableInfo {
3553 public Variable Variable {
3554 get { return variable != null ? variable : par.Variable; }
3557 public bool VerifyFixed ()
3559 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3560 return par.ModFlags == Parameter.Modifier.NONE;
3563 public bool IsAssigned (EmitContext ec, Location loc)
3565 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (vi))
3568 Report.Error (269, loc,
3569 "Use of unassigned out parameter `{0}'", par.Name);
3573 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3575 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (vi, field_name))
3578 Report.Error (170, loc,
3579 "Use of possibly unassigned field `" + field_name + "'");
3583 public void SetAssigned (EmitContext ec)
3585 if (is_out && ec.DoFlowAnalysis)
3586 ec.CurrentBranching.SetAssigned (vi);
3589 public void SetFieldAssigned (EmitContext ec, string field_name)
3591 if (is_out && ec.DoFlowAnalysis)
3592 ec.CurrentBranching.SetFieldAssigned (vi, field_name);
3595 protected bool DoResolveBase (EmitContext ec)
3597 if (!par.Resolve (ec)) {
3601 type = par.ParameterType;
3602 Parameter.Modifier mod = par.ModFlags;
3603 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3604 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3605 eclass = ExprClass.Variable;
3608 vi = block.ParameterMap [idx];
3610 AnonymousContainer am = ec.CurrentAnonymousMethod;
3614 if (is_ref && !block.Toplevel.IsLocalParameter (name)){
3615 Report.Error (1628, Location,
3616 "Cannot use ref or out parameter `{0}' inside an " +
3617 "anonymous method block", par.Name);
3621 if (!am.IsIterator && block.Toplevel.IsLocalParameter (name))
3624 AnonymousMethodHost host = null;
3625 ToplevelBlock toplevel = block.Toplevel;
3626 while (toplevel != null) {
3627 if (toplevel.IsLocalParameter (name)) {
3628 host = toplevel.AnonymousMethodHost;
3632 toplevel = toplevel.Container;
3635 variable = host.AddParameter (par, idx);
3636 type = variable.Type;
3640 public override int GetHashCode()
3642 return name.GetHashCode ();
3645 public override bool Equals (object obj)
3647 ParameterReference pr = obj as ParameterReference;
3651 return name == pr.name && block == pr.block;
3655 // Notice that for ref/out parameters, the type exposed is not the
3656 // same type exposed externally.
3659 // externally we expose "int&"
3660 // here we expose "int".
3662 // We record this in "is_ref". This means that the type system can treat
3663 // the type as it is expected, but when we generate the code, we generate
3664 // the alternate kind of code.
3666 public override Expression DoResolve (EmitContext ec)
3668 if (!DoResolveBase (ec))
3671 if (is_out && ec.DoFlowAnalysis &&
3672 (!ec.OmitStructFlowAnalysis || !vi.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3678 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3680 if (!DoResolveBase (ec))
3688 static public void EmitLdArg (ILGenerator ig, int x)
3692 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3693 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3694 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3695 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3696 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3699 ig.Emit (OpCodes.Ldarg, x);
3703 // This method is used by parameters that are references, that are
3704 // being passed as references: we only want to pass the pointer (that
3705 // is already stored in the parameter, not the address of the pointer,
3706 // and not the value of the variable).
3708 public void EmitLoad (EmitContext ec)
3710 ILGenerator ig = ec.ig;
3713 if (!ec.MethodIsStatic)
3716 EmitLdArg (ig, arg_idx);
3719 // FIXME: Review for anonymous methods
3723 public override void Emit (EmitContext ec)
3728 public void Emit (EmitContext ec, bool leave_copy)
3731 Variable.EmitInstance (ec);
3736 ec.ig.Emit (OpCodes.Dup);
3739 // If we are a reference, we loaded on the stack a pointer
3740 // Now lets load the real value
3742 LoadFromPtr (ec.ig, type);
3746 ec.ig.Emit (OpCodes.Dup);
3748 if (is_ref || Variable.NeedsTemporary) {
3749 temp = new LocalTemporary (Type);
3755 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
3756 bool prepare_for_load)
3758 ILGenerator ig = ec.ig;
3759 prepared = prepare_for_load;
3761 Variable.EmitInstance (ec);
3762 if (prepare_for_load && Variable.HasInstance)
3763 ig.Emit (OpCodes.Dup);
3769 ig.Emit (OpCodes.Dup);
3770 if (Variable.NeedsTemporary) {
3771 temp = new LocalTemporary (Type);
3776 StoreFromPtr (ig, type);
3778 Variable.EmitAssign (ec);
3783 public void AddressOf (EmitContext ec, AddressOp mode)
3785 Variable.EmitInstance (ec);
3786 Variable.EmitAddressOf (ec);
3789 public override string ToString ()
3791 return "ParameterReference[" + name + "]";
3796 /// Used for arguments to New(), Invocation()
3798 public class Argument {
3799 public enum AType : byte {
3806 public readonly AType ArgType;
3807 public Expression Expr;
3809 public Argument (Expression expr, AType type)
3812 this.ArgType = type;
3815 public Argument (Expression expr)
3818 this.ArgType = AType.Expression;
3823 if (ArgType == AType.Ref || ArgType == AType.Out)
3824 return TypeManager.GetReferenceType (Expr.Type);
3830 public Parameter.Modifier Modifier
3835 return Parameter.Modifier.OUT;
3838 return Parameter.Modifier.REF;
3841 return Parameter.Modifier.NONE;
3846 public static string FullDesc (Argument a)
3848 if (a.ArgType == AType.ArgList)
3851 return (a.ArgType == AType.Ref ? "ref " :
3852 (a.ArgType == AType.Out ? "out " : "")) +
3853 TypeManager.CSharpName (a.Expr.Type);
3856 public bool ResolveMethodGroup (EmitContext ec)
3858 SimpleName sn = Expr as SimpleName;
3860 Expr = sn.GetMethodGroup ();
3862 // FIXME: csc doesn't report any error if you try to use `ref' or
3863 // `out' in a delegate creation expression.
3864 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3871 public bool Resolve (EmitContext ec, Location loc)
3873 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3874 // Verify that the argument is readable
3875 if (ArgType != AType.Out)
3876 Expr = Expr.Resolve (ec);
3878 // Verify that the argument is writeable
3879 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3880 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3882 return Expr != null;
3886 public void Emit (EmitContext ec)
3888 if (ArgType != AType.Ref && ArgType != AType.Out) {
3893 AddressOp mode = AddressOp.Store;
3894 if (ArgType == AType.Ref)
3895 mode |= AddressOp.Load;
3897 IMemoryLocation ml = (IMemoryLocation) Expr;
3898 ParameterReference pr = ml as ParameterReference;
3901 // ParameterReferences might already be references, so we want
3902 // to pass just the value
3904 if (pr != null && pr.IsRef)
3907 ml.AddressOf (ec, mode);
3912 /// Invocation of methods or delegates.
3914 public class Invocation : ExpressionStatement {
3915 public readonly ArrayList Arguments;
3918 MethodBase method = null;
3921 // arguments is an ArrayList, but we do not want to typecast,
3922 // as it might be null.
3924 // FIXME: only allow expr to be a method invocation or a
3925 // delegate invocation (7.5.5)
3927 public Invocation (Expression expr, ArrayList arguments)
3930 Arguments = arguments;
3931 loc = expr.Location;
3934 public Expression Expr {
3941 /// Determines "better conversion" as specified in 14.4.2.3
3943 /// Returns : p if a->p is better,
3944 /// q if a->q is better,
3945 /// null if neither is better
3947 static Type BetterConversion (EmitContext ec, Argument a, Type p, Type q)
3949 Type argument_type = TypeManager.TypeToCoreType (a.Type);
3950 Expression argument_expr = a.Expr;
3952 // p = TypeManager.TypeToCoreType (p);
3953 // q = TypeManager.TypeToCoreType (q);
3955 if (argument_type == null)
3956 throw new Exception ("Expression of type " + a.Expr +
3957 " does not resolve its type");
3959 if (p == null || q == null)
3960 throw new InternalErrorException ("BetterConversion Got a null conversion");
3965 if (argument_expr is NullLiteral) {
3967 // If the argument is null and one of the types to compare is 'object' and
3968 // the other is a reference type, we prefer the other.
3970 // This follows from the usual rules:
3971 // * There is an implicit conversion from 'null' to type 'object'
3972 // * There is an implicit conversion from 'null' to any reference type
3973 // * There is an implicit conversion from any reference type to type 'object'
3974 // * There is no implicit conversion from type 'object' to other reference types
3975 // => Conversion of 'null' to a reference type is better than conversion to 'object'
3977 // FIXME: This probably isn't necessary, since the type of a NullLiteral is the
3978 // null type. I think it used to be 'object' and thus needed a special
3979 // case to avoid the immediately following two checks.
3981 if (!p.IsValueType && q == TypeManager.object_type)
3983 if (!q.IsValueType && p == TypeManager.object_type)
3987 if (argument_type == p)
3990 if (argument_type == q)
3993 Expression p_tmp = new EmptyExpression (p);
3994 Expression q_tmp = new EmptyExpression (q);
3996 bool p_to_q = Convert.ImplicitConversionExists (ec, p_tmp, q);
3997 bool q_to_p = Convert.ImplicitConversionExists (ec, q_tmp, p);
3999 if (p_to_q && !q_to_p)
4002 if (q_to_p && !p_to_q)
4005 if (p == TypeManager.sbyte_type)
4006 if (q == TypeManager.byte_type || q == TypeManager.ushort_type ||
4007 q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4009 if (q == TypeManager.sbyte_type)
4010 if (p == TypeManager.byte_type || p == TypeManager.ushort_type ||
4011 p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4014 if (p == TypeManager.short_type)
4015 if (q == TypeManager.ushort_type || q == TypeManager.uint32_type ||
4016 q == TypeManager.uint64_type)
4018 if (q == TypeManager.short_type)
4019 if (p == TypeManager.ushort_type || p == TypeManager.uint32_type ||
4020 p == TypeManager.uint64_type)
4023 if (p == TypeManager.int32_type)
4024 if (q == TypeManager.uint32_type || q == TypeManager.uint64_type)
4026 if (q == TypeManager.int32_type)
4027 if (p == TypeManager.uint32_type || p == TypeManager.uint64_type)
4030 if (p == TypeManager.int64_type)
4031 if (q == TypeManager.uint64_type)
4033 if (q == TypeManager.int64_type)
4034 if (p == TypeManager.uint64_type)
4040 static Type MoreSpecific (Type p, Type q)
4042 if (p.IsGenericParameter && !q.IsGenericParameter)
4044 if (!p.IsGenericParameter && q.IsGenericParameter)
4047 if (p.IsGenericType) {
4048 Type[] pargs = TypeManager.GetTypeArguments (p);
4049 Type[] qargs = TypeManager.GetTypeArguments (q);
4051 bool p_specific_at_least_once = false;
4052 bool q_specific_at_least_once = false;
4054 for (int i = 0; i < pargs.Length; i++) {
4055 Type specific = MoreSpecific (pargs [i], qargs [i]);
4056 if (specific == pargs [i])
4057 p_specific_at_least_once = true;
4058 if (specific == qargs [i])
4059 q_specific_at_least_once = true;
4062 if (p_specific_at_least_once && !q_specific_at_least_once)
4064 if (!p_specific_at_least_once && q_specific_at_least_once)
4066 } else if (TypeManager.HasElementType (p)) {
4067 Type pe = TypeManager.GetElementType (p);
4068 Type qe = TypeManager.GetElementType (q);
4069 Type specific = MoreSpecific (pe, qe);
4080 /// Determines "Better function" between candidate
4081 /// and the current best match
4084 /// Returns a boolean indicating :
4085 /// false if candidate ain't better
4086 /// true if candidate is better than the current best match
4088 static bool BetterFunction (EmitContext ec, ArrayList args, int argument_count,
4089 MethodBase candidate, bool candidate_params,
4090 MethodBase best, bool best_params)
4092 ParameterData candidate_pd = TypeManager.GetParameterData (candidate);
4093 ParameterData best_pd = TypeManager.GetParameterData (best);
4095 bool better_at_least_one = false;
4097 for (int j = 0; j < argument_count; ++j) {
4098 Argument a = (Argument) args [j];
4100 Type ct = TypeManager.TypeToCoreType (candidate_pd.ParameterType (j));
4101 Type bt = TypeManager.TypeToCoreType (best_pd.ParameterType (j));
4103 if (candidate_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4104 if (candidate_params)
4105 ct = TypeManager.GetElementType (ct);
4107 if (best_pd.ParameterModifier (j) == Parameter.Modifier.PARAMS)
4109 bt = TypeManager.GetElementType (bt);
4115 Type better = BetterConversion (ec, a, ct, bt);
4117 // for each argument, the conversion to 'ct' should be no worse than
4118 // the conversion to 'bt'.
4122 // for at least one argument, the conversion to 'ct' should be better than
4123 // the conversion to 'bt'.
4125 better_at_least_one = true;
4128 if (better_at_least_one)
4132 // This handles the case
4134 // Add (float f1, float f2, float f3);
4135 // Add (params decimal [] foo);
4137 // The call Add (3, 4, 5) should be ambiguous. Without this check, the
4138 // first candidate would've chosen as better.
4144 // The two methods have equal parameter types. Now apply tie-breaking rules
4146 if (TypeManager.IsGenericMethod (best) && !TypeManager.IsGenericMethod (candidate))
4148 if (!TypeManager.IsGenericMethod (best) && TypeManager.IsGenericMethod (candidate))
4152 // This handles the following cases:
4154 // Trim () is better than Trim (params char[] chars)
4155 // Concat (string s1, string s2, string s3) is better than
4156 // Concat (string s1, params string [] srest)
4157 // Foo (int, params int [] rest) is better than Foo (params int [] rest)
4159 if (!candidate_params && best_params)
4161 if (candidate_params && !best_params)
4164 int candidate_param_count = candidate_pd.Count;
4165 int best_param_count = best_pd.Count;
4167 if (candidate_param_count != best_param_count)
4168 // can only happen if (candidate_params && best_params)
4169 return candidate_param_count > best_param_count;
4172 // now, both methods have the same number of parameters, and the parameters have the same types
4173 // Pick the "more specific" signature
4176 MethodBase orig_candidate = TypeManager.DropGenericMethodArguments (candidate);
4177 MethodBase orig_best = TypeManager.DropGenericMethodArguments (best);
4179 ParameterData orig_candidate_pd = TypeManager.GetParameterData (orig_candidate);
4180 ParameterData orig_best_pd = TypeManager.GetParameterData (orig_best);
4182 bool specific_at_least_once = false;
4183 for (int j = 0; j < candidate_param_count; ++j) {
4184 Type ct = TypeManager.TypeToCoreType (orig_candidate_pd.ParameterType (j));
4185 Type bt = TypeManager.TypeToCoreType (orig_best_pd.ParameterType (j));
4188 Type specific = MoreSpecific (ct, bt);
4192 specific_at_least_once = true;
4195 if (specific_at_least_once)
4198 // FIXME: handle lifted operators
4204 internal static bool IsOverride (MethodBase cand_method, MethodBase base_method)
4206 if (!IsAncestralType (base_method.DeclaringType, cand_method.DeclaringType))
4209 ParameterData cand_pd = TypeManager.GetParameterData (cand_method);
4210 ParameterData base_pd = TypeManager.GetParameterData (base_method);
4212 if (cand_pd.Count != base_pd.Count)
4215 for (int j = 0; j < cand_pd.Count; ++j) {
4216 Parameter.Modifier cm = cand_pd.ParameterModifier (j);
4217 Parameter.Modifier bm = base_pd.ParameterModifier (j);
4218 Type ct = TypeManager.TypeToCoreType (cand_pd.ParameterType (j));
4219 Type bt = TypeManager.TypeToCoreType (base_pd.ParameterType (j));
4221 if (cm != bm || ct != bt)
4228 public static string FullMethodDesc (MethodBase mb)
4234 if (mb is MethodInfo) {
4235 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4239 sb = new StringBuilder ();
4241 sb.Append (TypeManager.CSharpSignature (mb));
4242 return sb.ToString ();
4245 public static MethodGroupExpr MakeUnionSet (Expression mg1, Expression mg2, Location loc)
4247 MemberInfo [] miset;
4248 MethodGroupExpr union;
4253 return (MethodGroupExpr) mg2;
4256 return (MethodGroupExpr) mg1;
4259 MethodGroupExpr left_set = null, right_set = null;
4260 int length1 = 0, length2 = 0;
4262 left_set = (MethodGroupExpr) mg1;
4263 length1 = left_set.Methods.Length;
4265 right_set = (MethodGroupExpr) mg2;
4266 length2 = right_set.Methods.Length;
4268 ArrayList common = new ArrayList ();
4270 foreach (MethodBase r in right_set.Methods){
4271 if (TypeManager.ArrayContainsMethod (left_set.Methods, r))
4275 miset = new MemberInfo [length1 + length2 - common.Count];
4276 left_set.Methods.CopyTo (miset, 0);
4280 foreach (MethodBase r in right_set.Methods) {
4281 if (!common.Contains (r))
4285 union = new MethodGroupExpr (miset, loc);
4290 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4291 ArrayList arguments, int arg_count,
4292 ref MethodBase candidate)
4294 return IsParamsMethodApplicable (
4295 ec, me, arguments, arg_count, false, ref candidate) ||
4296 IsParamsMethodApplicable (
4297 ec, me, arguments, arg_count, true, ref candidate);
4302 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4303 ArrayList arguments, int arg_count,
4304 bool do_varargs, ref MethodBase candidate)
4306 if (!me.HasTypeArguments &&
4307 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4310 if (TypeManager.IsGenericMethodDefinition (candidate))
4311 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4313 return IsParamsMethodApplicable (
4314 ec, arguments, arg_count, candidate, do_varargs);
4318 /// Determines if the candidate method, if a params method, is applicable
4319 /// in its expanded form to the given set of arguments
4321 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4322 int arg_count, MethodBase candidate,
4325 ParameterData pd = TypeManager.GetParameterData (candidate);
4327 int pd_count = pd.Count;
4331 int count = pd_count - 1;
4333 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4335 if (pd_count != arg_count)
4342 if (count > arg_count)
4345 if (pd_count == 1 && arg_count == 0)
4349 // If we have come this far, the case which
4350 // remains is when the number of parameters is
4351 // less than or equal to the argument count.
4353 for (int i = 0; i < count; ++i) {
4355 Argument a = (Argument) arguments [i];
4357 Parameter.Modifier a_mod = a.Modifier &
4358 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4359 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4360 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4362 if (a_mod == p_mod) {
4364 if (a_mod == Parameter.Modifier.NONE)
4365 if (!Convert.ImplicitConversionExists (ec,
4367 pd.ParameterType (i)))
4370 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4371 Type pt = pd.ParameterType (i);
4374 pt = TypeManager.GetReferenceType (pt);
4385 Argument a = (Argument) arguments [count];
4386 if (!(a.Expr is Arglist))
4392 Type element_type = TypeManager.GetElementType (pd.ParameterType (pd_count - 1));
4394 for (int i = pd_count - 1; i < arg_count; i++) {
4395 Argument a = (Argument) arguments [i];
4397 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4404 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4405 ArrayList arguments, int arg_count,
4406 ref MethodBase candidate)
4408 if (!me.HasTypeArguments &&
4409 !TypeManager.InferTypeArguments (arguments, ref candidate))
4412 if (TypeManager.IsGenericMethodDefinition (candidate))
4413 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4415 return IsApplicable (ec, arguments, arg_count, candidate);
4419 /// Determines if the candidate method is applicable (section 14.4.2.1)
4420 /// to the given set of arguments
4422 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4423 MethodBase candidate)
4425 ParameterData pd = TypeManager.GetParameterData (candidate);
4427 if (arg_count != pd.Count)
4430 for (int i = arg_count; i > 0; ) {
4433 Argument a = (Argument) arguments [i];
4435 Parameter.Modifier a_mod = a.Modifier &
4436 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4438 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4439 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4441 if (a_mod == p_mod) {
4442 Type pt = pd.ParameterType (i);
4444 if (a_mod == Parameter.Modifier.NONE) {
4445 if (!TypeManager.IsEqual (a.Type, pt) &&
4446 !Convert.ImplicitConversionExists (ec, a.Expr, pt))
4460 static internal bool IsAncestralType (Type first_type, Type second_type)
4462 return first_type != second_type &&
4463 (TypeManager.IsSubclassOf (second_type, first_type) ||
4464 TypeManager.ImplementsInterface (second_type, first_type));
4468 /// Find the Applicable Function Members (7.4.2.1)
4470 /// me: Method Group expression with the members to select.
4471 /// it might contain constructors or methods (or anything
4472 /// that maps to a method).
4474 /// Arguments: ArrayList containing resolved Argument objects.
4476 /// loc: The location if we want an error to be reported, or a Null
4477 /// location for "probing" purposes.
4479 /// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
4480 /// that is the best match of me on Arguments.
4483 public static MethodBase OverloadResolve (EmitContext ec, MethodGroupExpr me,
4484 ArrayList Arguments, bool may_fail,
4487 MethodBase method = null;
4488 bool method_params = false;
4489 Type applicable_type = null;
4491 ArrayList candidates = new ArrayList (2);
4492 ArrayList candidate_overrides = null;
4495 // Used to keep a map between the candidate
4496 // and whether it is being considered in its
4497 // normal or expanded form
4499 // false is normal form, true is expanded form
4501 Hashtable candidate_to_form = null;
4503 if (Arguments != null)
4504 arg_count = Arguments.Count;
4506 if ((me.Name == "Invoke") &&
4507 TypeManager.IsDelegateType (me.DeclaringType)) {
4508 Error_InvokeOnDelegate (loc);
4512 MethodBase[] methods = me.Methods;
4514 int nmethods = methods.Length;
4518 // Methods marked 'override' don't take part in 'applicable_type'
4519 // computation, nor in the actual overload resolution.
4520 // However, they still need to be emitted instead of a base virtual method.
4521 // So, we salt them away into the 'candidate_overrides' array.
4523 // In case of reflected methods, we replace each overriding method with
4524 // its corresponding base virtual method. This is to improve compatibility
4525 // with non-C# libraries which change the visibility of overrides (#75636)
4528 for (int i = 0; i < methods.Length; ++i) {
4529 MethodBase m = methods [i];
4530 Type [] gen_args = m.IsGenericMethod && !m.IsGenericMethodDefinition ? m.GetGenericArguments () : null;
4531 if (TypeManager.IsOverride (m)) {
4532 if (candidate_overrides == null)
4533 candidate_overrides = new ArrayList ();
4534 candidate_overrides.Add (m);
4535 m = TypeManager.TryGetBaseDefinition (m);
4536 if (m != null && gen_args != null) {
4537 if (!m.IsGenericMethodDefinition)
4538 throw new InternalErrorException ("GetBaseDefinition didn't return a GenericMethodDefinition");
4539 m = ((MethodInfo) m).MakeGenericMethod (gen_args);
4548 int applicable_errors = Report.Errors;
4551 // First we construct the set of applicable methods
4553 bool is_sorted = true;
4554 for (int i = 0; i < nmethods; i++){
4555 Type decl_type = methods [i].DeclaringType;
4558 // If we have already found an applicable method
4559 // we eliminate all base types (Section 14.5.5.1)
4561 if (applicable_type != null && IsAncestralType (decl_type, applicable_type))
4565 // Check if candidate is applicable (section 14.4.2.1)
4566 // Is candidate applicable in normal form?
4568 bool is_applicable = IsApplicable (ec, me, Arguments, arg_count, ref methods [i]);
4570 if (!is_applicable && IsParamsMethodApplicable (ec, me, Arguments, arg_count, ref methods [i])) {
4571 MethodBase candidate = methods [i];
4572 if (candidate_to_form == null)
4573 candidate_to_form = new PtrHashtable ();
4574 candidate_to_form [candidate] = candidate;
4575 // Candidate is applicable in expanded form
4576 is_applicable = true;
4582 candidates.Add (methods [i]);
4584 if (applicable_type == null)
4585 applicable_type = decl_type;
4586 else if (applicable_type != decl_type) {
4588 if (IsAncestralType (applicable_type, decl_type))
4589 applicable_type = decl_type;
4593 if (applicable_errors != Report.Errors)
4596 int candidate_top = candidates.Count;
4598 if (applicable_type == null) {
4600 // Okay so we have failed to find anything so we
4601 // return by providing info about the closest match
4603 int errors = Report.Errors;
4604 for (int i = 0; i < nmethods; ++i) {
4605 MethodBase c = (MethodBase) methods [i];
4606 ParameterData pd = TypeManager.GetParameterData (c);
4608 if (pd.Count != arg_count)
4611 if (!TypeManager.InferTypeArguments (Arguments, ref c))
4614 if (TypeManager.IsGenericMethodDefinition (c))
4617 VerifyArgumentsCompat (ec, Arguments, arg_count,
4618 c, false, null, may_fail, loc);
4620 if (!may_fail && errors == Report.Errors)
4621 throw new InternalErrorException (
4622 "VerifyArgumentsCompat and IsApplicable do not agree; " +
4623 "likely reason: ImplicitConversion and ImplicitConversionExists have gone out of sync");
4628 if (!may_fail && errors == Report.Errors) {
4629 string report_name = me.Name;
4630 if (report_name == ".ctor")
4631 report_name = me.DeclaringType.ToString ();
4633 for (int i = 0; i < methods.Length; ++i) {
4634 MethodBase c = methods [i];
4635 ParameterData pd = TypeManager.GetParameterData (c);
4637 if (pd.Count != arg_count)
4640 if (TypeManager.InferTypeArguments (Arguments, ref c))
4644 411, loc, "The type arguments for " +
4645 "method `{0}' cannot be infered from " +
4646 "the usage. Try specifying the type " +
4647 "arguments explicitly.", report_name);
4651 Error_WrongNumArguments (loc, report_name, arg_count);
4659 // At this point, applicable_type is _one_ of the most derived types
4660 // in the set of types containing the methods in this MethodGroup.
4661 // Filter the candidates so that they only contain methods from the
4662 // most derived types.
4665 int finalized = 0; // Number of finalized candidates
4668 // Invariant: applicable_type is a most derived type
4670 // We'll try to complete Section 14.5.5.1 for 'applicable_type' by
4671 // eliminating all it's base types. At the same time, we'll also move
4672 // every unrelated type to the end of the array, and pick the next
4673 // 'applicable_type'.
4675 Type next_applicable_type = null;
4676 int j = finalized; // where to put the next finalized candidate
4677 int k = finalized; // where to put the next undiscarded candidate
4678 for (int i = finalized; i < candidate_top; ++i) {
4679 MethodBase candidate = (MethodBase) candidates [i];
4680 Type decl_type = candidate.DeclaringType;
4682 if (decl_type == applicable_type) {
4683 candidates [k++] = candidates [j];
4684 candidates [j++] = candidates [i];
4688 if (IsAncestralType (decl_type, applicable_type))
4691 if (next_applicable_type != null &&
4692 IsAncestralType (decl_type, next_applicable_type))
4695 candidates [k++] = candidates [i];
4697 if (next_applicable_type == null ||
4698 IsAncestralType (next_applicable_type, decl_type))
4699 next_applicable_type = decl_type;
4702 applicable_type = next_applicable_type;
4705 } while (applicable_type != null);
4709 // Now we actually find the best method
4712 method = (MethodBase) candidates [0];
4713 method_params = candidate_to_form != null && candidate_to_form.Contains (method);
4714 for (int ix = 1; ix < candidate_top; ix++){
4715 MethodBase candidate = (MethodBase) candidates [ix];
4717 if (candidate == method)
4720 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4722 if (BetterFunction (ec, Arguments, arg_count,
4723 candidate, cand_params,
4724 method, method_params)) {
4726 method_params = cand_params;
4730 // Now check that there are no ambiguities i.e the selected method
4731 // should be better than all the others
4733 MethodBase ambiguous = null;
4734 for (int ix = 0; ix < candidate_top; ix++){
4735 MethodBase candidate = (MethodBase) candidates [ix];
4737 if (candidate == method)
4740 bool cand_params = candidate_to_form != null && candidate_to_form.Contains (candidate);
4741 if (!BetterFunction (ec, Arguments, arg_count,
4742 method, method_params,
4743 candidate, cand_params)) {
4744 Report.SymbolRelatedToPreviousError (candidate);
4745 ambiguous = candidate;
4749 if (ambiguous != null) {
4750 Report.SymbolRelatedToPreviousError (method);
4751 Report.Error (121, loc, "The call is ambiguous between the following methods or properties: `{0}' and `{1}'",
4752 TypeManager.CSharpSignature (ambiguous), TypeManager.CSharpSignature (method));
4757 // If the method is a virtual function, pick an override closer to the LHS type.
4759 if (!me.IsBase && method.IsVirtual) {
4760 if (TypeManager.IsOverride (method))
4761 throw new InternalErrorException (
4762 "Should not happen. An 'override' method took part in overload resolution: " + method);
4764 if (candidate_overrides != null)
4765 foreach (MethodBase candidate in candidate_overrides) {
4766 if (IsOverride (candidate, method))
4772 // And now check if the arguments are all
4773 // compatible, perform conversions if
4774 // necessary etc. and return if everything is
4777 if (!VerifyArgumentsCompat (ec, Arguments, arg_count, method,
4778 method_params, null, may_fail, loc))
4784 MethodBase the_method = TypeManager.DropGenericMethodArguments (method);
4785 if (the_method.IsGenericMethodDefinition &&
4786 !ConstraintChecker.CheckConstraints (ec, the_method, method, loc))
4789 IMethodData data = TypeManager.GetMethod (the_method);
4791 data.SetMemberIsUsed ();
4796 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4798 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4799 name, arg_count.ToString ());
4802 static void Error_InvokeOnDelegate (Location loc)
4804 Report.Error (1533, loc,
4805 "Invoke cannot be called directly on a delegate");
4808 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4809 Type delegate_type, Argument a, ParameterData expected_par)
4811 if (delegate_type == null)
4812 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4813 TypeManager.CSharpSignature (method));
4815 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4816 TypeManager.CSharpName (delegate_type));
4818 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4820 string index = (idx + 1).ToString ();
4821 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4822 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4823 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4824 index, Parameter.GetModifierSignature (a.Modifier));
4826 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4827 index, Parameter.GetModifierSignature (mod));
4829 Report.Error (1503, loc, "Argument {0}: Cannot convert from `{1}' to `{2}'",
4830 index, Argument.FullDesc (a), expected_par.ParameterDesc (idx));
4834 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4835 int arg_count, MethodBase method,
4836 bool chose_params_expanded,
4837 Type delegate_type, bool may_fail,
4840 ParameterData pd = TypeManager.GetParameterData (method);
4842 for (j = 0; j < arg_count; j++) {
4843 Argument a = (Argument) Arguments [j];
4844 Expression a_expr = a.Expr;
4845 Type parameter_type = pd.ParameterType (j);
4846 Parameter.Modifier pm = pd.ParameterModifier (j);
4847 Parameter.Modifier am = a.Modifier;
4849 if (pm == Parameter.Modifier.ARGLIST) {
4850 if (!(a.Expr is Arglist))
4855 if (pm == Parameter.Modifier.PARAMS) {
4856 pm = Parameter.Modifier.NONE;
4857 if (chose_params_expanded)
4858 parameter_type = TypeManager.GetElementType (parameter_type);
4864 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4865 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4868 Expression conv = Convert.ImplicitConversion (ec, a_expr, parameter_type, loc);
4872 // Update the argument with the implicit conversion
4877 if (parameter_type.IsPointer && !ec.InUnsafe) {
4887 Error_InvalidArguments (loc, j, method, delegate_type, (Argument) Arguments [j], pd);
4891 private bool resolved = false;
4892 public override Expression DoResolve (EmitContext ec)
4895 return this.method == null ? null : this;
4899 // First, resolve the expression that is used to
4900 // trigger the invocation
4902 SimpleName sn = expr as SimpleName;
4904 expr = sn.GetMethodGroup ();
4906 expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4910 if (!(expr is MethodGroupExpr)) {
4911 Type expr_type = expr.Type;
4913 if (expr_type != null){
4914 bool IsDelegate = TypeManager.IsDelegateType (expr_type);
4916 return (new DelegateInvocation (
4917 this.expr, Arguments, loc)).Resolve (ec);
4921 if (!(expr is MethodGroupExpr)){
4922 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4927 // Next, evaluate all the expressions in the argument list
4929 if (Arguments != null){
4930 foreach (Argument a in Arguments){
4931 if (!a.Resolve (ec, loc))
4936 MethodGroupExpr mg = (MethodGroupExpr) expr;
4937 MethodBase method = OverloadResolve (ec, mg, Arguments, false, loc);
4942 MethodInfo mi = method as MethodInfo;
4944 type = TypeManager.TypeToCoreType (mi.ReturnType);
4945 Expression iexpr = mg.InstanceExpression;
4947 if (iexpr == null ||
4948 iexpr is This || iexpr is EmptyExpression ||
4949 mg.IdenticalTypeName) {
4950 mg.InstanceExpression = null;
4952 MemberExpr.error176 (loc, TypeManager.CSharpSignature (mi));
4956 if (iexpr == null || iexpr is EmptyExpression) {
4957 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (mi));
4963 if (type.IsPointer){
4971 // Only base will allow this invocation to happen.
4973 if (mg.IsBase && method.IsAbstract){
4974 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
4978 if (Arguments == null && method.Name == "Finalize") {
4980 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
4982 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
4986 if ((method.Attributes & MethodAttributes.SpecialName) != 0 && IsSpecialMethodInvocation (method)) {
4990 if (mg.InstanceExpression != null)
4991 mg.InstanceExpression.CheckMarshalByRefAccess ();
4993 eclass = ExprClass.Value;
4994 this.method = method;
4998 bool IsSpecialMethodInvocation (MethodBase method)
5000 IMethodData md = TypeManager.GetMethod (method);
5002 if (!(md is AbstractPropertyEventMethod) && !(md is Operator))
5005 if (!TypeManager.IsSpecialMethod (method))
5008 int args = TypeManager.GetParameterData (method).Count;
5009 if (method.Name.StartsWith ("get_") && args > 0)
5011 else if (method.Name.StartsWith ("set_") && args > 2)
5014 // TODO: check operators and events as well ?
5017 Report.SymbolRelatedToPreviousError (method);
5018 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
5019 TypeManager.CSharpSignature (method, true));
5025 // Emits the list of arguments as an array
5027 static void EmitParams (EmitContext ec, int idx, ArrayList arguments)
5029 ILGenerator ig = ec.ig;
5030 int count = arguments.Count - idx;
5031 Argument a = (Argument) arguments [idx];
5032 Type t = a.Expr.Type;
5034 IntConstant.EmitInt (ig, count);
5035 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
5037 int top = arguments.Count;
5038 for (int j = idx; j < top; j++){
5039 a = (Argument) arguments [j];
5041 ig.Emit (OpCodes.Dup);
5042 IntConstant.EmitInt (ig, j - idx);
5044 bool is_stobj, has_type_arg;
5045 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
5047 ig.Emit (OpCodes.Ldelema, t);
5059 /// Emits a list of resolved Arguments that are in the arguments
5062 /// The MethodBase argument might be null if the
5063 /// emission of the arguments is known not to contain
5064 /// a `params' field (for example in constructors or other routines
5065 /// that keep their arguments in this structure)
5067 /// if `dup_args' is true, a copy of the arguments will be left
5068 /// on the stack. If `dup_args' is true, you can specify `this_arg'
5069 /// which will be duplicated before any other args. Only EmitCall
5070 /// should be using this interface.
5072 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
5074 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
5075 int top = arguments == null ? 0 : arguments.Count;
5076 LocalTemporary [] temps = null;
5078 if (dup_args && top != 0)
5079 temps = new LocalTemporary [top];
5081 for (int i = 0; i < top; i++){
5082 Argument a = (Argument) arguments [i];
5085 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
5087 // Special case if we are passing the same data as the
5088 // params argument, do not put it in an array.
5090 if (pd.ParameterType (i) == a.Type)
5093 EmitParams (ec, i, arguments);
5100 ec.ig.Emit (OpCodes.Dup);
5101 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
5106 if (this_arg != null)
5109 for (int i = 0; i < top; i ++)
5110 temps [i].Emit (ec);
5113 if (pd != null && pd.Count > top &&
5114 pd.ParameterModifier (top) == Parameter.Modifier.PARAMS){
5115 ILGenerator ig = ec.ig;
5117 IntConstant.EmitInt (ig, 0);
5118 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (top)));
5122 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
5124 ParameterData pd = TypeManager.GetParameterData (mb);
5126 if (arguments == null)
5127 return new Type [0];
5129 Argument a = (Argument) arguments [pd.Count - 1];
5130 Arglist list = (Arglist) a.Expr;
5132 return list.ArgumentTypes;
5136 /// This checks the ConditionalAttribute on the method
5138 static bool IsMethodExcluded (MethodBase method)
5140 if (method.IsConstructor)
5143 IMethodData md = TypeManager.GetMethod (method);
5145 return md.IsExcluded ();
5147 // For some methods (generated by delegate class) GetMethod returns null
5148 // because they are not included in builder_to_method table
5149 if (method.DeclaringType is TypeBuilder)
5152 return AttributeTester.IsConditionalMethodExcluded (method);
5156 /// is_base tells whether we want to force the use of the `call'
5157 /// opcode instead of using callvirt. Call is required to call
5158 /// a specific method, while callvirt will always use the most
5159 /// recent method in the vtable.
5161 /// is_static tells whether this is an invocation on a static method
5163 /// instance_expr is an expression that represents the instance
5164 /// it must be non-null if is_static is false.
5166 /// method is the method to invoke.
5168 /// Arguments is the list of arguments to pass to the method or constructor.
5170 public static void EmitCall (EmitContext ec, bool is_base,
5171 bool is_static, Expression instance_expr,
5172 MethodBase method, ArrayList Arguments, Location loc)
5174 EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
5177 // `dup_args' leaves an extra copy of the arguments on the stack
5178 // `omit_args' does not leave any arguments at all.
5179 // So, basically, you could make one call with `dup_args' set to true,
5180 // and then another with `omit_args' set to true, and the two calls
5181 // would have the same set of arguments. However, each argument would
5182 // only have been evaluated once.
5183 public static void EmitCall (EmitContext ec, bool is_base,
5184 bool is_static, Expression instance_expr,
5185 MethodBase method, ArrayList Arguments, Location loc,
5186 bool dup_args, bool omit_args)
5188 ILGenerator ig = ec.ig;
5189 bool struct_call = false;
5190 bool this_call = false;
5191 LocalTemporary this_arg = null;
5193 Type decl_type = method.DeclaringType;
5195 if (!RootContext.StdLib) {
5196 // Replace any calls to the system's System.Array type with calls to
5197 // the newly created one.
5198 if (method == TypeManager.system_int_array_get_length)
5199 method = TypeManager.int_array_get_length;
5200 else if (method == TypeManager.system_int_array_get_rank)
5201 method = TypeManager.int_array_get_rank;
5202 else if (method == TypeManager.system_object_array_clone)
5203 method = TypeManager.object_array_clone;
5204 else if (method == TypeManager.system_int_array_get_length_int)
5205 method = TypeManager.int_array_get_length_int;
5206 else if (method == TypeManager.system_int_array_get_lower_bound_int)
5207 method = TypeManager.int_array_get_lower_bound_int;
5208 else if (method == TypeManager.system_int_array_get_upper_bound_int)
5209 method = TypeManager.int_array_get_upper_bound_int;
5210 else if (method == TypeManager.system_void_array_copyto_array_int)
5211 method = TypeManager.void_array_copyto_array_int;
5214 if (!ec.IsInObsoleteScope) {
5216 // This checks ObsoleteAttribute on the method and on the declaring type
5218 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
5220 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
5222 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
5224 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
5228 if (IsMethodExcluded (method))
5232 if (instance_expr == EmptyExpression.Null) {
5233 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
5237 this_call = instance_expr is This;
5238 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
5242 // If this is ourselves, push "this"
5246 Type iexpr_type = instance_expr.Type;
5249 // Push the instance expression
5251 if (TypeManager.IsValueType (iexpr_type)) {
5253 // Special case: calls to a function declared in a
5254 // reference-type with a value-type argument need
5255 // to have their value boxed.
5256 if (decl_type.IsValueType ||
5257 iexpr_type.IsGenericParameter) {
5259 // If the expression implements IMemoryLocation, then
5260 // we can optimize and use AddressOf on the
5263 // If not we have to use some temporary storage for
5265 if (instance_expr is IMemoryLocation) {
5266 ((IMemoryLocation)instance_expr).
5267 AddressOf (ec, AddressOp.LoadStore);
5269 LocalTemporary temp = new LocalTemporary (iexpr_type);
5270 instance_expr.Emit (ec);
5272 temp.AddressOf (ec, AddressOp.Load);
5275 // avoid the overhead of doing this all the time.
5277 t = TypeManager.GetReferenceType (iexpr_type);
5279 instance_expr.Emit (ec);
5280 ig.Emit (OpCodes.Box, instance_expr.Type);
5281 t = TypeManager.object_type;
5284 instance_expr.Emit (ec);
5285 t = instance_expr.Type;
5289 ig.Emit (OpCodes.Dup);
5290 if (Arguments != null && Arguments.Count != 0) {
5291 this_arg = new LocalTemporary (t);
5292 this_arg.Store (ec);
5299 EmitArguments (ec, method, Arguments, dup_args, this_arg);
5301 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
5302 ig.Emit (OpCodes.Constrained, instance_expr.Type);
5305 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
5306 call_op = OpCodes.Call;
5308 call_op = OpCodes.Callvirt;
5310 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
5311 Type[] varargs_types = GetVarargsTypes (method, Arguments);
5312 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
5319 // and DoFoo is not virtual, you can omit the callvirt,
5320 // because you don't need the null checking behavior.
5322 if (method is MethodInfo)
5323 ig.Emit (call_op, (MethodInfo) method);
5325 ig.Emit (call_op, (ConstructorInfo) method);
5328 public override void Emit (EmitContext ec)
5330 MethodGroupExpr mg = (MethodGroupExpr) this.expr;
5332 EmitCall (ec, mg.IsBase, method.IsStatic, mg.InstanceExpression, method, Arguments, loc);
5335 public override void EmitStatement (EmitContext ec)
5340 // Pop the return value if there is one
5342 if (method is MethodInfo){
5343 Type ret = ((MethodInfo)method).ReturnType;
5344 if (TypeManager.TypeToCoreType (ret) != TypeManager.void_type)
5345 ec.ig.Emit (OpCodes.Pop);
5350 public class InvocationOrCast : ExpressionStatement
5353 Expression argument;
5355 public InvocationOrCast (Expression expr, Expression argument)
5358 this.argument = argument;
5359 this.loc = expr.Location;
5362 public override Expression DoResolve (EmitContext ec)
5365 // First try to resolve it as a cast.
5367 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5368 if ((te != null) && (te.eclass == ExprClass.Type)) {
5369 Cast cast = new Cast (te, argument, loc);
5370 return cast.Resolve (ec);
5374 // This can either be a type or a delegate invocation.
5375 // Let's just resolve it and see what we'll get.
5377 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5382 // Ok, so it's a Cast.
5384 if (expr.eclass == ExprClass.Type) {
5385 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
5386 return cast.Resolve (ec);
5390 // It's a delegate invocation.
5392 if (!TypeManager.IsDelegateType (expr.Type)) {
5393 Error (149, "Method name expected");
5397 ArrayList args = new ArrayList ();
5398 args.Add (new Argument (argument, Argument.AType.Expression));
5399 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5400 return invocation.Resolve (ec);
5405 Error (201, "Only assignment, call, increment, decrement and new object " +
5406 "expressions can be used as a statement");
5409 public override ExpressionStatement ResolveStatement (EmitContext ec)
5412 // First try to resolve it as a cast.
5414 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
5415 if ((te != null) && (te.eclass == ExprClass.Type)) {
5421 // This can either be a type or a delegate invocation.
5422 // Let's just resolve it and see what we'll get.
5424 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
5425 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
5431 // It's a delegate invocation.
5433 if (!TypeManager.IsDelegateType (expr.Type)) {
5434 Error (149, "Method name expected");
5438 ArrayList args = new ArrayList ();
5439 args.Add (new Argument (argument, Argument.AType.Expression));
5440 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
5441 return invocation.ResolveStatement (ec);
5444 public override void Emit (EmitContext ec)
5446 throw new Exception ("Cannot happen");
5449 public override void EmitStatement (EmitContext ec)
5451 throw new Exception ("Cannot happen");
5456 // This class is used to "disable" the code generation for the
5457 // temporary variable when initializing value types.
5459 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
5460 public void AddressOf (EmitContext ec, AddressOp Mode)
5467 /// Implements the new expression
5469 public class New : ExpressionStatement, IMemoryLocation {
5470 public readonly ArrayList Arguments;
5473 // During bootstrap, it contains the RequestedType,
5474 // but if `type' is not null, it *might* contain a NewDelegate
5475 // (because of field multi-initialization)
5477 public Expression RequestedType;
5479 MethodBase method = null;
5482 // If set, the new expression is for a value_target, and
5483 // we will not leave anything on the stack.
5485 Expression value_target;
5486 bool value_target_set = false;
5487 bool is_type_parameter = false;
5489 public New (Expression requested_type, ArrayList arguments, Location l)
5491 RequestedType = requested_type;
5492 Arguments = arguments;
5496 public bool SetValueTypeVariable (Expression value)
5498 value_target = value;
5499 value_target_set = true;
5500 if (!(value_target is IMemoryLocation)){
5501 Error_UnexpectedKind (null, "variable", loc);
5508 // This function is used to disable the following code sequence for
5509 // value type initialization:
5511 // AddressOf (temporary)
5515 // Instead the provide will have provided us with the address on the
5516 // stack to store the results.
5518 static Expression MyEmptyExpression;
5520 public void DisableTemporaryValueType ()
5522 if (MyEmptyExpression == null)
5523 MyEmptyExpression = new EmptyAddressOf ();
5526 // To enable this, look into:
5527 // test-34 and test-89 and self bootstrapping.
5529 // For instance, we can avoid a copy by using `newobj'
5530 // instead of Call + Push-temp on value types.
5531 // value_target = MyEmptyExpression;
5536 /// Converts complex core type syntax like 'new int ()' to simple constant
5538 public static Constant Constantify (Type t)
5540 if (t == TypeManager.int32_type)
5541 return new IntConstant (0, Location.Null);
5542 if (t == TypeManager.uint32_type)
5543 return new UIntConstant (0, Location.Null);
5544 if (t == TypeManager.int64_type)
5545 return new LongConstant (0, Location.Null);
5546 if (t == TypeManager.uint64_type)
5547 return new ULongConstant (0, Location.Null);
5548 if (t == TypeManager.float_type)
5549 return new FloatConstant (0, Location.Null);
5550 if (t == TypeManager.double_type)
5551 return new DoubleConstant (0, Location.Null);
5552 if (t == TypeManager.short_type)
5553 return new ShortConstant (0, Location.Null);
5554 if (t == TypeManager.ushort_type)
5555 return new UShortConstant (0, Location.Null);
5556 if (t == TypeManager.sbyte_type)
5557 return new SByteConstant (0, Location.Null);
5558 if (t == TypeManager.byte_type)
5559 return new ByteConstant (0, Location.Null);
5560 if (t == TypeManager.char_type)
5561 return new CharConstant ('\0', Location.Null);
5562 if (t == TypeManager.bool_type)
5563 return new BoolConstant (false, Location.Null);
5564 if (t == TypeManager.decimal_type)
5565 return new DecimalConstant (0, Location.Null);
5571 // Checks whether the type is an interface that has the
5572 // [ComImport, CoClass] attributes and must be treated
5575 public Expression CheckComImport (EmitContext ec)
5577 if (!type.IsInterface)
5581 // Turn the call into:
5582 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5584 Type real_class = AttributeTester.GetCoClassAttribute (type);
5585 if (real_class == null)
5588 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5589 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5590 return cast.Resolve (ec);
5593 public override Expression DoResolve (EmitContext ec)
5596 // The New DoResolve might be called twice when initializing field
5597 // expressions (see EmitFieldInitializers, the call to
5598 // GetInitializerExpression will perform a resolve on the expression,
5599 // and later the assign will trigger another resolution
5601 // This leads to bugs (#37014)
5604 if (RequestedType is NewDelegate)
5605 return RequestedType;
5609 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5615 if (Arguments == null) {
5616 Expression c = Constantify (type);
5621 if (TypeManager.IsDelegateType (type)) {
5622 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5623 if (RequestedType != null)
5624 if (!(RequestedType is DelegateCreation))
5625 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5626 return RequestedType;
5629 if (type.IsGenericParameter) {
5630 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5632 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5633 Error (304, String.Format (
5634 "Cannot create an instance of the " +
5635 "variable type '{0}' because it " +
5636 "doesn't have the new() constraint",
5641 if ((Arguments != null) && (Arguments.Count != 0)) {
5642 Error (417, String.Format (
5643 "`{0}': cannot provide arguments " +
5644 "when creating an instance of a " +
5645 "variable type.", type));
5649 is_type_parameter = true;
5650 eclass = ExprClass.Value;
5654 if (type.IsAbstract && type.IsSealed) {
5655 Report.SymbolRelatedToPreviousError (type);
5656 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5660 if (type.IsInterface || type.IsAbstract){
5661 RequestedType = CheckComImport (ec);
5662 if (RequestedType != null)
5663 return RequestedType;
5665 Report.SymbolRelatedToPreviousError (type);
5666 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5670 bool is_struct = type.IsValueType;
5671 eclass = ExprClass.Value;
5674 // SRE returns a match for .ctor () on structs (the object constructor),
5675 // so we have to manually ignore it.
5677 if (is_struct && Arguments == null)
5680 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5681 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5682 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5687 MethodGroupExpr mg = ml as MethodGroupExpr;
5690 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5694 if (Arguments != null){
5695 foreach (Argument a in Arguments){
5696 if (!a.Resolve (ec, loc))
5701 method = Invocation.OverloadResolve (ec, mg, Arguments, false, loc);
5702 if (method == null) {
5703 if (almostMatchedMembers.Count != 0)
5704 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5711 bool DoEmitTypeParameter (EmitContext ec)
5713 ILGenerator ig = ec.ig;
5715 ig.Emit (OpCodes.Ldtoken, type);
5716 ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
5717 ig.Emit (OpCodes.Call, TypeManager.activator_create_instance);
5718 ig.Emit (OpCodes.Unbox_Any, type);
5724 // This DoEmit can be invoked in two contexts:
5725 // * As a mechanism that will leave a value on the stack (new object)
5726 // * As one that wont (init struct)
5728 // You can control whether a value is required on the stack by passing
5729 // need_value_on_stack. The code *might* leave a value on the stack
5730 // so it must be popped manually
5732 // If we are dealing with a ValueType, we have a few
5733 // situations to deal with:
5735 // * The target is a ValueType, and we have been provided
5736 // the instance (this is easy, we are being assigned).
5738 // * The target of New is being passed as an argument,
5739 // to a boxing operation or a function that takes a
5742 // In this case, we need to create a temporary variable
5743 // that is the argument of New.
5745 // Returns whether a value is left on the stack
5747 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5749 bool is_value_type = TypeManager.IsValueType (type);
5750 ILGenerator ig = ec.ig;
5755 // Allow DoEmit() to be called multiple times.
5756 // We need to create a new LocalTemporary each time since
5757 // you can't share LocalBuilders among ILGeneators.
5758 if (!value_target_set)
5759 value_target = new LocalTemporary (type);
5761 ml = (IMemoryLocation) value_target;
5762 ml.AddressOf (ec, AddressOp.Store);
5766 Invocation.EmitArguments (ec, method, Arguments, false, null);
5770 ig.Emit (OpCodes.Initobj, type);
5772 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5773 if (need_value_on_stack){
5774 value_target.Emit (ec);
5779 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5784 public override void Emit (EmitContext ec)
5786 if (is_type_parameter)
5787 DoEmitTypeParameter (ec);
5792 public override void EmitStatement (EmitContext ec)
5794 if (is_type_parameter)
5795 throw new InvalidOperationException ();
5797 if (DoEmit (ec, false))
5798 ec.ig.Emit (OpCodes.Pop);
5801 public void AddressOf (EmitContext ec, AddressOp Mode)
5803 if (is_type_parameter)
5804 throw new InvalidOperationException ();
5806 if (!type.IsValueType){
5808 // We throw an exception. So far, I believe we only need to support
5810 // foreach (int j in new StructType ())
5813 throw new Exception ("AddressOf should not be used for classes");
5816 if (!value_target_set)
5817 value_target = new LocalTemporary (type);
5819 IMemoryLocation ml = (IMemoryLocation) value_target;
5820 ml.AddressOf (ec, AddressOp.Store);
5822 Invocation.EmitArguments (ec, method, Arguments, false, null);
5825 ec.ig.Emit (OpCodes.Initobj, type);
5827 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5829 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5834 /// 14.5.10.2: Represents an array creation expression.
5838 /// There are two possible scenarios here: one is an array creation
5839 /// expression that specifies the dimensions and optionally the
5840 /// initialization data and the other which does not need dimensions
5841 /// specified but where initialization data is mandatory.
5843 public class ArrayCreation : Expression {
5844 Expression requested_base_type;
5845 ArrayList initializers;
5848 // The list of Argument types.
5849 // This is used to construct the `newarray' or constructor signature
5851 ArrayList arguments;
5854 // Method used to create the array object.
5856 MethodBase new_method = null;
5858 Type array_element_type;
5859 Type underlying_type;
5860 bool is_one_dimensional = false;
5861 bool is_builtin_type = false;
5862 bool expect_initializers = false;
5863 int num_arguments = 0;
5867 ArrayList array_data;
5871 // The number of constants in array initializers
5872 int const_initializers_count;
5874 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5876 this.requested_base_type = requested_base_type;
5877 this.initializers = initializers;
5881 arguments = new ArrayList ();
5883 foreach (Expression e in exprs) {
5884 arguments.Add (new Argument (e, Argument.AType.Expression));
5889 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5891 this.requested_base_type = requested_base_type;
5892 this.initializers = initializers;
5896 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5898 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5900 //dimensions = tmp.Length - 1;
5901 expect_initializers = true;
5904 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5906 StringBuilder sb = new StringBuilder (rank);
5909 for (int i = 1; i < idx_count; i++)
5914 return new ComposedCast (base_type, sb.ToString (), loc);
5917 void Error_IncorrectArrayInitializer ()
5919 Error (178, "Invalid rank specifier: expected `,' or `]'");
5922 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5924 if (specified_dims) {
5925 Argument a = (Argument) arguments [idx];
5927 if (!a.Resolve (ec, loc))
5930 Constant c = a.Expr as Constant;
5932 c = c.ToType (TypeManager.int32_type, a.Expr.Location);
5936 Report.Error (150, a.Expr.Location, "A constant value is expected");
5940 int value = (int) c.GetValue ();
5942 if (value != probe.Count) {
5943 Error_IncorrectArrayInitializer ();
5947 bounds [idx] = value;
5950 int child_bounds = -1;
5951 for (int i = 0; i < probe.Count; ++i) {
5952 object o = probe [i];
5953 if (o is ArrayList) {
5954 ArrayList sub_probe = o as ArrayList;
5955 int current_bounds = sub_probe.Count;
5957 if (child_bounds == -1)
5958 child_bounds = current_bounds;
5960 else if (child_bounds != current_bounds){
5961 Error_IncorrectArrayInitializer ();
5964 if (idx + 1 >= dimensions){
5965 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
5969 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
5973 if (child_bounds != -1){
5974 Error_IncorrectArrayInitializer ();
5978 Expression tmp = (Expression) o;
5979 tmp = tmp.Resolve (ec);
5983 Expression conv = Convert.ImplicitConversionRequired (
5984 ec, tmp, underlying_type, loc);
5989 // Initializers with the default values can be ignored
5990 Constant c = tmp as Constant;
5992 if (c.IsDefaultInitializer (array_element_type)) {
5996 ++const_initializers_count;
5999 // Used to invalidate static initializer
6000 const_initializers_count = int.MinValue;
6003 array_data.Add (conv);
6010 public void UpdateIndices ()
6013 for (ArrayList probe = initializers; probe != null;) {
6014 if (probe.Count > 0 && probe [0] is ArrayList) {
6015 Expression e = new IntConstant (probe.Count, Location.Null);
6016 arguments.Add (new Argument (e, Argument.AType.Expression));
6018 bounds [i++] = probe.Count;
6020 probe = (ArrayList) probe [0];
6023 Expression e = new IntConstant (probe.Count, Location.Null);
6024 arguments.Add (new Argument (e, Argument.AType.Expression));
6026 bounds [i++] = probe.Count;
6033 bool ResolveInitializers (EmitContext ec)
6035 if (initializers == null) {
6036 return !expect_initializers;
6039 if (underlying_type == null)
6043 // We use this to store all the date values in the order in which we
6044 // will need to store them in the byte blob later
6046 array_data = new ArrayList ();
6047 bounds = new System.Collections.Specialized.HybridDictionary ();
6049 if (arguments != null)
6050 return CheckIndices (ec, initializers, 0, true);
6052 arguments = new ArrayList ();
6054 if (!CheckIndices (ec, initializers, 0, false))
6059 if (arguments.Count != dimensions) {
6060 Error_IncorrectArrayInitializer ();
6068 // Creates the type of the array
6070 bool LookupType (EmitContext ec)
6072 StringBuilder array_qualifier = new StringBuilder (rank);
6075 // `In the first form allocates an array instace of the type that results
6076 // from deleting each of the individual expression from the expression list'
6078 if (num_arguments > 0) {
6079 array_qualifier.Append ("[");
6080 for (int i = num_arguments-1; i > 0; i--)
6081 array_qualifier.Append (",");
6082 array_qualifier.Append ("]");
6088 TypeExpr array_type_expr;
6089 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
6090 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
6091 if (array_type_expr == null)
6094 type = array_type_expr.Type;
6095 underlying_type = TypeManager.GetElementType (type);
6096 dimensions = type.GetArrayRank ();
6101 public override Expression DoResolve (EmitContext ec)
6106 if (!LookupType (ec))
6109 array_element_type = TypeManager.GetElementType (type);
6110 if (array_element_type.IsAbstract && array_element_type.IsSealed) {
6111 Report.Error (719, loc, "`{0}': array elements cannot be of static type", TypeManager.CSharpName (array_element_type));
6116 // First step is to validate the initializers and fill
6117 // in any missing bits
6119 if (!ResolveInitializers (ec))
6123 if (arguments == null)
6126 arg_count = arguments.Count;
6127 foreach (Argument a in arguments){
6128 if (!a.Resolve (ec, loc))
6131 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
6132 if (real_arg == null)
6139 if (arg_count == 1) {
6140 is_one_dimensional = true;
6141 eclass = ExprClass.Value;
6145 is_builtin_type = TypeManager.IsBuiltinType (type);
6147 if (is_builtin_type) {
6150 ml = MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
6151 AllBindingFlags, loc);
6153 if (!(ml is MethodGroupExpr)) {
6154 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
6159 Error (-6, "New invocation: Can not find a constructor for " +
6160 "this argument list");
6164 new_method = Invocation.OverloadResolve (
6165 ec, (MethodGroupExpr) ml, arguments, false, loc);
6167 if (new_method == null) {
6168 Error (-6, "New invocation: Can not find a constructor for " +
6169 "this argument list");
6173 eclass = ExprClass.Value;
6176 ModuleBuilder mb = CodeGen.Module.Builder;
6177 ArrayList args = new ArrayList ();
6179 if (arguments != null) {
6180 for (int i = 0; i < arg_count; i++)
6181 args.Add (TypeManager.int32_type);
6184 Type [] arg_types = null;
6187 arg_types = new Type [args.Count];
6189 args.CopyTo (arg_types, 0);
6191 new_method = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
6194 if (new_method == null) {
6195 Error (-6, "New invocation: Can not find a constructor for " +
6196 "this argument list");
6200 eclass = ExprClass.Value;
6205 byte [] MakeByteBlob ()
6210 int count = array_data.Count;
6212 if (underlying_type.IsEnum)
6213 underlying_type = TypeManager.EnumToUnderlying (underlying_type);
6215 factor = GetTypeSize (underlying_type);
6217 throw new Exception ("unrecognized type in MakeByteBlob: " + underlying_type);
6219 data = new byte [(count * factor + 4) & ~3];
6222 for (int i = 0; i < count; ++i) {
6223 object v = array_data [i];
6225 if (v is EnumConstant)
6226 v = ((EnumConstant) v).Child;
6228 if (v is Constant && !(v is StringConstant))
6229 v = ((Constant) v).GetValue ();
6235 if (underlying_type == TypeManager.int64_type){
6236 if (!(v is Expression)){
6237 long val = (long) v;
6239 for (int j = 0; j < factor; ++j) {
6240 data [idx + j] = (byte) (val & 0xFF);
6244 } else if (underlying_type == TypeManager.uint64_type){
6245 if (!(v is Expression)){
6246 ulong val = (ulong) v;
6248 for (int j = 0; j < factor; ++j) {
6249 data [idx + j] = (byte) (val & 0xFF);
6253 } else if (underlying_type == TypeManager.float_type) {
6254 if (!(v is Expression)){
6255 element = BitConverter.GetBytes ((float) v);
6257 for (int j = 0; j < factor; ++j)
6258 data [idx + j] = element [j];
6260 } else if (underlying_type == TypeManager.double_type) {
6261 if (!(v is Expression)){
6262 element = BitConverter.GetBytes ((double) v);
6264 for (int j = 0; j < factor; ++j)
6265 data [idx + j] = element [j];
6267 } else if (underlying_type == TypeManager.char_type){
6268 if (!(v is Expression)){
6269 int val = (int) ((char) v);
6271 data [idx] = (byte) (val & 0xff);
6272 data [idx+1] = (byte) (val >> 8);
6274 } else if (underlying_type == TypeManager.short_type){
6275 if (!(v is Expression)){
6276 int val = (int) ((short) v);
6278 data [idx] = (byte) (val & 0xff);
6279 data [idx+1] = (byte) (val >> 8);
6281 } else if (underlying_type == TypeManager.ushort_type){
6282 if (!(v is Expression)){
6283 int val = (int) ((ushort) v);
6285 data [idx] = (byte) (val & 0xff);
6286 data [idx+1] = (byte) (val >> 8);
6288 } else if (underlying_type == TypeManager.int32_type) {
6289 if (!(v is Expression)){
6292 data [idx] = (byte) (val & 0xff);
6293 data [idx+1] = (byte) ((val >> 8) & 0xff);
6294 data [idx+2] = (byte) ((val >> 16) & 0xff);
6295 data [idx+3] = (byte) (val >> 24);
6297 } else if (underlying_type == TypeManager.uint32_type) {
6298 if (!(v is Expression)){
6299 uint val = (uint) v;
6301 data [idx] = (byte) (val & 0xff);
6302 data [idx+1] = (byte) ((val >> 8) & 0xff);
6303 data [idx+2] = (byte) ((val >> 16) & 0xff);
6304 data [idx+3] = (byte) (val >> 24);
6306 } else if (underlying_type == TypeManager.sbyte_type) {
6307 if (!(v is Expression)){
6308 sbyte val = (sbyte) v;
6309 data [idx] = (byte) val;
6311 } else if (underlying_type == TypeManager.byte_type) {
6312 if (!(v is Expression)){
6313 byte val = (byte) v;
6314 data [idx] = (byte) val;
6316 } else if (underlying_type == TypeManager.bool_type) {
6317 if (!(v is Expression)){
6318 bool val = (bool) v;
6319 data [idx] = (byte) (val ? 1 : 0);
6321 } else if (underlying_type == TypeManager.decimal_type){
6322 if (!(v is Expression)){
6323 int [] bits = Decimal.GetBits ((decimal) v);
6326 // FIXME: For some reason, this doesn't work on the MS runtime.
6327 int [] nbits = new int [4];
6328 nbits [0] = bits [3];
6329 nbits [1] = bits [2];
6330 nbits [2] = bits [0];
6331 nbits [3] = bits [1];
6333 for (int j = 0; j < 4; j++){
6334 data [p++] = (byte) (nbits [j] & 0xff);
6335 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
6336 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
6337 data [p++] = (byte) (nbits [j] >> 24);
6341 throw new Exception ("Unrecognized type in MakeByteBlob: " + underlying_type);
6350 // Emits the initializers for the array
6352 void EmitStaticInitializers (EmitContext ec)
6355 // First, the static data
6358 ILGenerator ig = ec.ig;
6360 byte [] data = MakeByteBlob ();
6362 fb = RootContext.MakeStaticData (data);
6364 ig.Emit (OpCodes.Dup);
6365 ig.Emit (OpCodes.Ldtoken, fb);
6366 ig.Emit (OpCodes.Call,
6367 TypeManager.void_initializearray_array_fieldhandle);
6371 // Emits pieces of the array that can not be computed at compile
6372 // time (variables and string locations).
6374 // This always expect the top value on the stack to be the array
6376 void EmitDynamicInitializers (EmitContext ec)
6378 ILGenerator ig = ec.ig;
6379 int dims = bounds.Count;
6380 int [] current_pos = new int [dims];
6382 MethodInfo set = null;
6385 Type [] args = new Type [dims + 1];
6387 for (int j = 0; j < dims; j++)
6388 args [j] = TypeManager.int32_type;
6389 args [dims] = array_element_type;
6391 set = CodeGen.Module.Builder.GetArrayMethod (
6393 CallingConventions.HasThis | CallingConventions.Standard,
6394 TypeManager.void_type, args);
6397 for (int i = 0; i < array_data.Count; i++){
6399 Expression e = (Expression)array_data [i];
6402 Type etype = e.Type;
6404 ig.Emit (OpCodes.Dup);
6406 for (int idx = 0; idx < dims; idx++)
6407 IntConstant.EmitInt (ig, current_pos [idx]);
6410 // If we are dealing with a struct, get the
6411 // address of it, so we can store it.
6414 TypeManager.IsValueType (etype) &&
6415 (!TypeManager.IsBuiltinOrEnum (etype) ||
6416 etype == TypeManager.decimal_type)) {
6421 // Let new know that we are providing
6422 // the address where to store the results
6424 n.DisableTemporaryValueType ();
6427 ig.Emit (OpCodes.Ldelema, etype);
6433 bool is_stobj, has_type_arg;
6434 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
6436 ig.Emit (OpCodes.Stobj, etype);
6437 else if (has_type_arg)
6438 ig.Emit (op, etype);
6442 ig.Emit (OpCodes.Call, set);
6449 for (int j = dims - 1; j >= 0; j--){
6451 if (current_pos [j] < (int) bounds [j])
6453 current_pos [j] = 0;
6458 void EmitArrayArguments (EmitContext ec)
6460 ILGenerator ig = ec.ig;
6462 foreach (Argument a in arguments) {
6463 Type atype = a.Type;
6466 if (atype == TypeManager.uint64_type)
6467 ig.Emit (OpCodes.Conv_Ovf_U4);
6468 else if (atype == TypeManager.int64_type)
6469 ig.Emit (OpCodes.Conv_Ovf_I4);
6473 public override void Emit (EmitContext ec)
6475 ILGenerator ig = ec.ig;
6477 EmitArrayArguments (ec);
6478 if (is_one_dimensional)
6479 ig.Emit (OpCodes.Newarr, array_element_type);
6481 if (is_builtin_type)
6482 ig.Emit (OpCodes.Newobj, (ConstructorInfo) new_method);
6484 ig.Emit (OpCodes.Newobj, (MethodInfo) new_method);
6487 if (initializers == null)
6490 // This is a treshold for static initializers
6491 // I tried to make more accurate but it seems to me that Array.Initialize is
6492 // always slower (managed -> unmanaged switch?)
6493 const int max_automatic_initializers = 200;
6495 if (const_initializers_count > max_automatic_initializers && TypeManager.IsPrimitiveType (array_element_type)) {
6496 EmitStaticInitializers (ec);
6500 EmitDynamicInitializers (ec);
6503 public override bool GetAttributableValue (Type valueType, out object value)
6505 if (!is_one_dimensional){
6506 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6507 return base.GetAttributableValue (null, out value);
6510 if (array_data == null) {
6511 Constant c = (Constant)((Argument)arguments [0]).Expr;
6512 if (c.IsDefaultValue) {
6513 value = Array.CreateInstance (array_element_type, 0);
6516 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6517 return base.GetAttributableValue (null, out value);
6520 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6521 object element_value;
6522 for (int i = 0; i < ret.Length; ++i)
6524 Expression e = (Expression)array_data [i];
6525 if (e == null) // Is null when initializer is optimized away
6526 e = (Expression)initializers [i];
6528 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6532 ret.SetValue (element_value, i);
6539 public sealed class CompilerGeneratedThis : This
6541 public static This Instance = new CompilerGeneratedThis ();
6543 private CompilerGeneratedThis ()
6544 : base (Location.Null)
6548 public override Expression DoResolve (EmitContext ec)
6550 eclass = ExprClass.Variable;
6551 type = ec.ContainerType;
6557 /// Represents the `this' construct
6559 public class This : Expression, IAssignMethod, IMemoryLocation, IVariable {
6562 VariableInfo variable_info;
6564 public This (Block block, Location loc)
6570 public This (Location loc)
6575 public VariableInfo VariableInfo {
6576 get { return variable_info; }
6579 public bool VerifyFixed ()
6581 return !TypeManager.IsValueType (Type);
6584 public bool ResolveBase (EmitContext ec)
6586 eclass = ExprClass.Variable;
6588 if (ec.TypeContainer.CurrentType != null)
6589 type = ec.TypeContainer.CurrentType;
6591 type = ec.ContainerType;
6594 Error (26, "Keyword `this' is not valid in a static property, static method, or static field initializer");
6598 if (block != null) {
6599 if (block.Toplevel.ThisVariable != null)
6600 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6602 AnonymousMethodHost host = block.Toplevel.AnonymousMethodHost;
6604 host.CaptureThis ();
6610 public override Expression DoResolve (EmitContext ec)
6612 if (!ResolveBase (ec))
6615 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) && !variable_info.IsAssigned (ec)) {
6616 Error (188, "The `this' object cannot be used before all of its fields are assigned to");
6617 variable_info.SetAssigned (ec);
6621 if (ec.IsFieldInitializer) {
6622 Error (27, "Keyword `this' is not available in the current context");
6629 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6631 if (!ResolveBase (ec))
6634 if (variable_info != null)
6635 variable_info.SetAssigned (ec);
6637 if (ec.TypeContainer is Class){
6638 Error (1604, "Cannot assign to 'this' because it is read-only");
6645 public void Emit (EmitContext ec, bool leave_copy)
6649 ec.ig.Emit (OpCodes.Dup);
6652 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
6654 ILGenerator ig = ec.ig;
6656 if (ec.TypeContainer is Struct){
6657 ec.EmitThis (false);
6660 LocalTemporary t = null;
6662 t = new LocalTemporary (type);
6663 ec.ig.Emit (OpCodes.Dup);
6667 ig.Emit (OpCodes.Stobj, type);
6672 throw new Exception ("how did you get here");
6676 public override void Emit (EmitContext ec)
6678 ILGenerator ig = ec.ig;
6680 ec.EmitThis (false);
6681 if (ec.TypeContainer is Struct)
6682 ig.Emit (OpCodes.Ldobj, type);
6685 public override int GetHashCode()
6687 return block.GetHashCode ();
6690 public override bool Equals (object obj)
6692 This t = obj as This;
6696 return block == t.block;
6699 public void AddressOf (EmitContext ec, AddressOp mode)
6704 // FIGURE OUT WHY LDARG_S does not work
6706 // consider: struct X { int val; int P { set { val = value; }}}
6708 // Yes, this looks very bad. Look at `NOTAS' for
6710 // ec.ig.Emit (OpCodes.Ldarga_S, (byte) 0);
6715 /// Represents the `__arglist' construct
6717 public class ArglistAccess : Expression
6719 public ArglistAccess (Location loc)
6724 public override Expression DoResolve (EmitContext ec)
6726 eclass = ExprClass.Variable;
6727 type = TypeManager.runtime_argument_handle_type;
6729 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6731 Error (190, "The __arglist construct is valid only within " +
6732 "a variable argument method");
6739 public override void Emit (EmitContext ec)
6741 ec.ig.Emit (OpCodes.Arglist);
6746 /// Represents the `__arglist (....)' construct
6748 public class Arglist : Expression
6750 public readonly Argument[] Arguments;
6752 public Arglist (Argument[] args, Location l)
6758 public Type[] ArgumentTypes {
6760 Type[] retval = new Type [Arguments.Length];
6761 for (int i = 0; i < Arguments.Length; i++)
6762 retval [i] = Arguments [i].Type;
6767 public override Expression DoResolve (EmitContext ec)
6769 eclass = ExprClass.Variable;
6770 type = TypeManager.runtime_argument_handle_type;
6772 foreach (Argument arg in Arguments) {
6773 if (!arg.Resolve (ec, loc))
6780 public override void Emit (EmitContext ec)
6782 foreach (Argument arg in Arguments)
6788 // This produces the value that renders an instance, used by the iterators code
6790 public class ProxyInstance : Expression, IMemoryLocation {
6791 public override Expression DoResolve (EmitContext ec)
6793 eclass = ExprClass.Variable;
6794 type = ec.ContainerType;
6798 public override void Emit (EmitContext ec)
6800 ec.ig.Emit (OpCodes.Ldarg_0);
6804 public void AddressOf (EmitContext ec, AddressOp mode)
6806 ec.ig.Emit (OpCodes.Ldarg_0);
6811 /// Implements the typeof operator
6813 public class TypeOf : Expression {
6814 readonly Expression QueriedType;
6815 protected Type typearg;
6817 public TypeOf (Expression queried_type, Location l)
6819 QueriedType = queried_type;
6823 public override Expression DoResolve (EmitContext ec)
6825 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6829 typearg = texpr.Type;
6831 if (typearg == TypeManager.void_type) {
6832 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6836 if (typearg.IsPointer && !ec.InUnsafe){
6841 type = TypeManager.type_type;
6842 // Even though what is returned is a type object, it's treated as a value by the compiler.
6843 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6844 eclass = ExprClass.Value;
6848 public override void Emit (EmitContext ec)
6850 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6851 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6854 public override bool GetAttributableValue (Type valueType, out object value)
6856 if (valueType == TypeManager.object_type) {
6857 value = (object)typearg;
6864 public Type TypeArgument
6874 /// Implements the `typeof (void)' operator
6876 public class TypeOfVoid : TypeOf {
6877 public TypeOfVoid (Location l) : base (null, l)
6882 public override Expression DoResolve (EmitContext ec)
6884 type = TypeManager.type_type;
6885 typearg = TypeManager.void_type;
6886 // See description in TypeOf.
6887 eclass = ExprClass.Value;
6893 /// Implements the sizeof expression
6895 public class SizeOf : Expression {
6896 public Expression QueriedType;
6899 public SizeOf (Expression queried_type, Location l)
6901 this.QueriedType = queried_type;
6905 public override Expression DoResolve (EmitContext ec)
6907 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6911 if (texpr is TypeParameterExpr){
6912 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
6916 type_queried = texpr.Type;
6918 int size_of = GetTypeSize (type_queried);
6920 return new IntConstant (size_of, loc);
6924 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)",
6925 TypeManager.CSharpName (type_queried));
6929 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
6933 type = TypeManager.int32_type;
6934 eclass = ExprClass.Value;
6938 public override void Emit (EmitContext ec)
6940 int size = GetTypeSize (type_queried);
6943 ec.ig.Emit (OpCodes.Sizeof, type_queried);
6945 IntConstant.EmitInt (ec.ig, size);
6950 /// Implements the qualified-alias-member (::) expression.
6952 public class QualifiedAliasMember : Expression
6954 string alias, identifier;
6956 public QualifiedAliasMember (string alias, string identifier, Location l)
6959 this.identifier = identifier;
6963 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
6965 if (alias == "global")
6966 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
6968 int errors = Report.Errors;
6969 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6971 if (errors == Report.Errors)
6972 Report.Error (432, loc, "Alias `{0}' not found", alias);
6975 if (fne.eclass != ExprClass.Namespace) {
6977 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6980 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
6983 public override Expression DoResolve (EmitContext ec)
6985 FullNamedExpression fne;
6986 if (alias == "global") {
6987 fne = RootNamespace.Global;
6989 int errors = Report.Errors;
6990 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6992 if (errors == Report.Errors)
6993 Report.Error (432, loc, "Alias `{0}' not found", alias);
6998 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
7002 if (!(retval is FullNamedExpression)) {
7003 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
7007 // We defer this check till the end to match the behaviour of CSC
7008 if (fne.eclass != ExprClass.Namespace) {
7009 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
7015 public override void Emit (EmitContext ec)
7017 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
7021 public override string ToString ()
7023 return alias + "::" + identifier;
7026 public override string GetSignatureForError ()
7033 /// Implements the member access expression
7035 public class MemberAccess : Expression {
7036 public readonly string Identifier;
7040 public MemberAccess (Expression expr, string id)
7041 : this (expr, id, expr.Location)
7045 public MemberAccess (Expression expr, string identifier, Location loc)
7048 Identifier = identifier;
7052 public MemberAccess (Expression expr, string id, TypeArguments args)
7058 public Expression Expr {
7059 get { return expr; }
7062 // TODO: this method has very poor performace for Enum fields and
7063 // probably for other constants as well
7064 Expression DoResolve (EmitContext ec, Expression right_side)
7067 throw new Exception ();
7070 // Resolve the expression with flow analysis turned off, we'll do the definite
7071 // assignment checks later. This is because we don't know yet what the expression
7072 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
7073 // definite assignment check on the actual field and not on the whole struct.
7076 SimpleName original = expr as SimpleName;
7077 Expression new_expr = expr.Resolve (ec,
7078 ResolveFlags.VariableOrValue | ResolveFlags.Type |
7079 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
7081 if (new_expr == null)
7084 if (new_expr is Namespace) {
7085 Namespace ns = (Namespace) new_expr;
7086 string lookup_id = MemberName.MakeName (Identifier, args);
7087 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, lookup_id, loc);
7088 if ((retval != null) && (args != null))
7089 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
7091 ns.Error_NamespaceDoesNotExist (loc, Identifier);
7095 Type expr_type = new_expr.Type;
7096 if (expr_type.IsPointer){
7097 Error (23, "The `.' operator can not be applied to pointer operands (" +
7098 TypeManager.CSharpName (expr_type) + ")");
7100 } else if (expr_type == TypeManager.void_type) {
7101 Error (23, "The `.' operator can not be applied to operands of type 'void'");
7103 } else if (expr_type == TypeManager.anonymous_method_type){
7104 Error (23, "The `.' operator can not be applied to anonymous methods");
7108 Expression member_lookup;
7109 member_lookup = MemberLookup (
7110 ec.ContainerType, expr_type, expr_type, Identifier, loc);
7111 if ((member_lookup == null) && (args != null)) {
7112 string lookup_id = MemberName.MakeName (Identifier, args);
7113 member_lookup = MemberLookup (
7114 ec.ContainerType, expr_type, expr_type, lookup_id, loc);
7116 if (member_lookup == null) {
7117 MemberLookupFailed (
7118 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
7122 if (member_lookup is TypeExpr) {
7123 if (!(new_expr is TypeExpr) &&
7124 (original == null || !original.IdenticalNameAndTypeName (ec, new_expr, loc))) {
7125 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
7126 Identifier, member_lookup.GetSignatureForError ());
7130 ConstructedType ct = new_expr as ConstructedType;
7133 // When looking up a nested type in a generic instance
7134 // via reflection, we always get a generic type definition
7135 // and not a generic instance - so we have to do this here.
7137 // See gtest-172-lib.cs and gtest-172.cs for an example.
7139 ct = new ConstructedType (
7140 member_lookup.Type, ct.TypeArguments, loc);
7142 return ct.ResolveAsTypeStep (ec, false);
7145 return member_lookup;
7148 MemberExpr me = (MemberExpr) member_lookup;
7149 member_lookup = me.ResolveMemberAccess (ec, new_expr, loc, original);
7150 if (member_lookup == null)
7154 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
7156 throw new InternalErrorException ();
7158 return mg.ResolveGeneric (ec, args);
7161 if (original != null && !TypeManager.IsValueType (expr_type)) {
7162 me = member_lookup as MemberExpr;
7163 if (me != null && me.IsInstance) {
7164 LocalVariableReference var = new_expr as LocalVariableReference;
7165 if (var != null && !var.VerifyAssigned (ec))
7170 // The following DoResolve/DoResolveLValue will do the definite assignment
7173 if (right_side != null)
7174 return member_lookup.DoResolveLValue (ec, right_side);
7176 return member_lookup.DoResolve (ec);
7179 public override Expression DoResolve (EmitContext ec)
7181 return DoResolve (ec, null);
7184 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7186 return DoResolve (ec, right_side);
7189 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
7191 return ResolveNamespaceOrType (ec, silent);
7194 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
7196 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
7198 if (new_expr == null)
7201 string lookup_id = MemberName.MakeName (Identifier, args);
7203 if (new_expr is Namespace) {
7204 Namespace ns = (Namespace) new_expr;
7205 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, lookup_id, loc);
7206 if ((retval != null) && (args != null))
7207 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
7208 if (!silent && retval == null)
7209 ns.Error_NamespaceDoesNotExist (loc, Identifier);
7213 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
7214 if (tnew_expr == null)
7217 Type expr_type = tnew_expr.Type;
7219 if (expr_type.IsPointer){
7220 Error (23, "The `.' operator can not be applied to pointer operands (" +
7221 TypeManager.CSharpName (expr_type) + ")");
7225 Expression member_lookup = MemberLookup (
7226 rc.DeclContainer.TypeBuilder, expr_type, expr_type, lookup_id,
7227 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
7228 if (member_lookup == null) {
7229 int errors = Report.Errors;
7230 MemberLookupFailed (rc.DeclContainer.TypeBuilder, expr_type, expr_type, lookup_id, null, false, loc);
7232 if (!silent && errors == Report.Errors) {
7233 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
7234 Identifier, new_expr.GetSignatureForError ());
7239 if (!(member_lookup is TypeExpr)) {
7240 new_expr.Error_UnexpectedKind (rc.DeclContainer, "type", loc);
7244 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
7248 TypeArguments the_args = args;
7249 if (TypeManager.HasGenericArguments (expr_type)) {
7250 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
7252 TypeArguments new_args = new TypeArguments (loc);
7253 foreach (Type decl in decl_args)
7254 new_args.Add (new TypeExpression (decl, loc));
7257 new_args.Add (args);
7259 the_args = new_args;
7262 if (the_args != null) {
7263 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
7264 return ctype.ResolveAsTypeStep (rc, false);
7270 public override void Emit (EmitContext ec)
7272 throw new Exception ("Should not happen");
7275 public override string ToString ()
7277 return expr + "." + MemberName.MakeName (Identifier, args);
7280 public override string GetSignatureForError ()
7282 return expr.GetSignatureForError () + "." + Identifier;
7287 /// Implements checked expressions
7289 public class CheckedExpr : Expression {
7291 public Expression Expr;
7293 public CheckedExpr (Expression e, Location l)
7299 public override Expression DoResolve (EmitContext ec)
7301 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7302 Expr = Expr.Resolve (ec);
7307 if (Expr is Constant)
7310 eclass = Expr.eclass;
7315 public override void Emit (EmitContext ec)
7317 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7321 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7323 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7324 Expr.EmitBranchable (ec, target, onTrue);
7329 /// Implements the unchecked expression
7331 public class UnCheckedExpr : Expression {
7333 public Expression Expr;
7335 public UnCheckedExpr (Expression e, Location l)
7341 public override Expression DoResolve (EmitContext ec)
7343 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7344 Expr = Expr.Resolve (ec);
7349 if (Expr is Constant)
7352 eclass = Expr.eclass;
7357 public override void Emit (EmitContext ec)
7359 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7363 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7365 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7366 Expr.EmitBranchable (ec, target, onTrue);
7371 /// An Element Access expression.
7373 /// During semantic analysis these are transformed into
7374 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7376 public class ElementAccess : Expression {
7377 public ArrayList Arguments;
7378 public Expression Expr;
7380 public ElementAccess (Expression e, ArrayList e_list)
7389 Arguments = new ArrayList ();
7390 foreach (Expression tmp in e_list)
7391 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7395 bool CommonResolve (EmitContext ec)
7397 Expr = Expr.Resolve (ec);
7402 if (Arguments == null)
7405 foreach (Argument a in Arguments){
7406 if (!a.Resolve (ec, loc))
7413 Expression MakePointerAccess (EmitContext ec, Type t)
7415 if (t == TypeManager.void_ptr_type){
7416 Error (242, "The array index operation is not valid on void pointers");
7419 if (Arguments.Count != 1){
7420 Error (196, "A pointer must be indexed by only one value");
7425 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7428 return new Indirection (p, loc).Resolve (ec);
7431 public override Expression DoResolve (EmitContext ec)
7433 if (!CommonResolve (ec))
7437 // We perform some simple tests, and then to "split" the emit and store
7438 // code we create an instance of a different class, and return that.
7440 // I am experimenting with this pattern.
7444 if (t == TypeManager.array_type){
7445 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7450 return (new ArrayAccess (this, loc)).Resolve (ec);
7452 return MakePointerAccess (ec, Expr.Type);
7454 FieldExpr fe = Expr as FieldExpr;
7456 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7458 return MakePointerAccess (ec, ff.ElementType);
7461 return (new IndexerAccess (this, loc)).Resolve (ec);
7464 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7466 if (!CommonResolve (ec))
7471 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7474 return MakePointerAccess (ec, Expr.Type);
7476 FieldExpr fe = Expr as FieldExpr;
7478 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7480 if (!(fe.InstanceExpression is LocalVariableReference) &&
7481 !(fe.InstanceExpression is This)) {
7482 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7485 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7486 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7489 return MakePointerAccess (ec, ff.ElementType);
7492 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7495 public override void Emit (EmitContext ec)
7497 throw new Exception ("Should never be reached");
7502 /// Implements array access
7504 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7506 // Points to our "data" repository
7510 LocalTemporary temp;
7513 public ArrayAccess (ElementAccess ea_data, Location l)
7516 eclass = ExprClass.Variable;
7520 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7522 return DoResolve (ec);
7525 public override Expression DoResolve (EmitContext ec)
7528 ExprClass eclass = ea.Expr.eclass;
7530 // As long as the type is valid
7531 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7532 eclass == ExprClass.Value)) {
7533 ea.Expr.Error_UnexpectedKind ("variable or value");
7538 Type t = ea.Expr.Type;
7539 if (t.GetArrayRank () != ea.Arguments.Count){
7540 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7541 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7545 type = TypeManager.GetElementType (t);
7546 if (type.IsPointer && !ec.InUnsafe){
7547 UnsafeError (ea.Location);
7551 foreach (Argument a in ea.Arguments){
7552 Type argtype = a.Type;
7554 if (argtype == TypeManager.int32_type ||
7555 argtype == TypeManager.uint32_type ||
7556 argtype == TypeManager.int64_type ||
7557 argtype == TypeManager.uint64_type) {
7558 Constant c = a.Expr as Constant;
7559 if (c != null && c.IsNegative) {
7560 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7566 // Mhm. This is strage, because the Argument.Type is not the same as
7567 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7569 // Wonder if I will run into trouble for this.
7571 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7576 eclass = ExprClass.Variable;
7582 /// Emits the right opcode to load an object of Type `t'
7583 /// from an array of T
7585 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7587 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7588 ig.Emit (OpCodes.Ldelem_U1);
7589 else if (type == TypeManager.sbyte_type)
7590 ig.Emit (OpCodes.Ldelem_I1);
7591 else if (type == TypeManager.short_type)
7592 ig.Emit (OpCodes.Ldelem_I2);
7593 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7594 ig.Emit (OpCodes.Ldelem_U2);
7595 else if (type == TypeManager.int32_type)
7596 ig.Emit (OpCodes.Ldelem_I4);
7597 else if (type == TypeManager.uint32_type)
7598 ig.Emit (OpCodes.Ldelem_U4);
7599 else if (type == TypeManager.uint64_type)
7600 ig.Emit (OpCodes.Ldelem_I8);
7601 else if (type == TypeManager.int64_type)
7602 ig.Emit (OpCodes.Ldelem_I8);
7603 else if (type == TypeManager.float_type)
7604 ig.Emit (OpCodes.Ldelem_R4);
7605 else if (type == TypeManager.double_type)
7606 ig.Emit (OpCodes.Ldelem_R8);
7607 else if (type == TypeManager.intptr_type)
7608 ig.Emit (OpCodes.Ldelem_I);
7609 else if (TypeManager.IsEnumType (type)){
7610 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7611 } else if (type.IsValueType){
7612 ig.Emit (OpCodes.Ldelema, type);
7613 ig.Emit (OpCodes.Ldobj, type);
7614 } else if (type.IsGenericParameter)
7616 ig.Emit (OpCodes.Ldelem, type);
7618 ig.Emit (OpCodes.Ldelem_Any, type);
7620 else if (type.IsPointer)
7621 ig.Emit (OpCodes.Ldelem_I);
7623 ig.Emit (OpCodes.Ldelem_Ref);
7627 /// Returns the right opcode to store an object of Type `t'
7628 /// from an array of T.
7630 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7632 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7633 has_type_arg = false; is_stobj = false;
7634 t = TypeManager.TypeToCoreType (t);
7635 if (TypeManager.IsEnumType (t))
7636 t = TypeManager.EnumToUnderlying (t);
7637 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7638 t == TypeManager.bool_type)
7639 return OpCodes.Stelem_I1;
7640 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7641 t == TypeManager.char_type)
7642 return OpCodes.Stelem_I2;
7643 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7644 return OpCodes.Stelem_I4;
7645 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7646 return OpCodes.Stelem_I8;
7647 else if (t == TypeManager.float_type)
7648 return OpCodes.Stelem_R4;
7649 else if (t == TypeManager.double_type)
7650 return OpCodes.Stelem_R8;
7651 else if (t == TypeManager.intptr_type) {
7652 has_type_arg = true;
7654 return OpCodes.Stobj;
7655 } else if (t.IsValueType) {
7656 has_type_arg = true;
7658 return OpCodes.Stobj;
7659 } else if (t.IsGenericParameter) {
7660 has_type_arg = true;
7662 return OpCodes.Stelem;
7664 return OpCodes.Stelem_Any;
7667 } else if (t.IsPointer)
7668 return OpCodes.Stelem_I;
7670 return OpCodes.Stelem_Ref;
7673 MethodInfo FetchGetMethod ()
7675 ModuleBuilder mb = CodeGen.Module.Builder;
7676 int arg_count = ea.Arguments.Count;
7677 Type [] args = new Type [arg_count];
7680 for (int i = 0; i < arg_count; i++){
7681 //args [i++] = a.Type;
7682 args [i] = TypeManager.int32_type;
7685 get = mb.GetArrayMethod (
7686 ea.Expr.Type, "Get",
7687 CallingConventions.HasThis |
7688 CallingConventions.Standard,
7694 MethodInfo FetchAddressMethod ()
7696 ModuleBuilder mb = CodeGen.Module.Builder;
7697 int arg_count = ea.Arguments.Count;
7698 Type [] args = new Type [arg_count];
7702 ret_type = TypeManager.GetReferenceType (type);
7704 for (int i = 0; i < arg_count; i++){
7705 //args [i++] = a.Type;
7706 args [i] = TypeManager.int32_type;
7709 address = mb.GetArrayMethod (
7710 ea.Expr.Type, "Address",
7711 CallingConventions.HasThis |
7712 CallingConventions.Standard,
7719 // Load the array arguments into the stack.
7721 // If we have been requested to cache the values (cached_locations array
7722 // initialized), then load the arguments the first time and store them
7723 // in locals. otherwise load from local variables.
7725 void LoadArrayAndArguments (EmitContext ec)
7727 ILGenerator ig = ec.ig;
7730 foreach (Argument a in ea.Arguments){
7731 Type argtype = a.Expr.Type;
7735 if (argtype == TypeManager.int64_type)
7736 ig.Emit (OpCodes.Conv_Ovf_I);
7737 else if (argtype == TypeManager.uint64_type)
7738 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7742 public void Emit (EmitContext ec, bool leave_copy)
7744 int rank = ea.Expr.Type.GetArrayRank ();
7745 ILGenerator ig = ec.ig;
7748 LoadArrayAndArguments (ec);
7751 EmitLoadOpcode (ig, type);
7755 method = FetchGetMethod ();
7756 ig.Emit (OpCodes.Call, method);
7759 LoadFromPtr (ec.ig, this.type);
7762 ec.ig.Emit (OpCodes.Dup);
7763 temp = new LocalTemporary (this.type);
7768 public override void Emit (EmitContext ec)
7773 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7775 int rank = ea.Expr.Type.GetArrayRank ();
7776 ILGenerator ig = ec.ig;
7777 Type t = source.Type;
7778 prepared = prepare_for_load;
7780 if (prepare_for_load) {
7781 AddressOf (ec, AddressOp.LoadStore);
7782 ec.ig.Emit (OpCodes.Dup);
7785 ec.ig.Emit (OpCodes.Dup);
7786 temp = new LocalTemporary (this.type);
7789 StoreFromPtr (ec.ig, t);
7797 LoadArrayAndArguments (ec);
7800 bool is_stobj, has_type_arg;
7801 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7804 // The stobj opcode used by value types will need
7805 // an address on the stack, not really an array/array
7809 ig.Emit (OpCodes.Ldelema, t);
7813 ec.ig.Emit (OpCodes.Dup);
7814 temp = new LocalTemporary (this.type);
7819 ig.Emit (OpCodes.Stobj, t);
7820 else if (has_type_arg)
7825 ModuleBuilder mb = CodeGen.Module.Builder;
7826 int arg_count = ea.Arguments.Count;
7827 Type [] args = new Type [arg_count + 1];
7832 ec.ig.Emit (OpCodes.Dup);
7833 temp = new LocalTemporary (this.type);
7837 for (int i = 0; i < arg_count; i++){
7838 //args [i++] = a.Type;
7839 args [i] = TypeManager.int32_type;
7842 args [arg_count] = type;
7844 set = mb.GetArrayMethod (
7845 ea.Expr.Type, "Set",
7846 CallingConventions.HasThis |
7847 CallingConventions.Standard,
7848 TypeManager.void_type, args);
7850 ig.Emit (OpCodes.Call, set);
7857 public void AddressOf (EmitContext ec, AddressOp mode)
7859 int rank = ea.Expr.Type.GetArrayRank ();
7860 ILGenerator ig = ec.ig;
7862 LoadArrayAndArguments (ec);
7865 ig.Emit (OpCodes.Ldelema, type);
7867 MethodInfo address = FetchAddressMethod ();
7868 ig.Emit (OpCodes.Call, address);
7872 public void EmitGetLength (EmitContext ec, int dim)
7874 int rank = ea.Expr.Type.GetArrayRank ();
7875 ILGenerator ig = ec.ig;
7879 ig.Emit (OpCodes.Ldlen);
7880 ig.Emit (OpCodes.Conv_I4);
7882 IntLiteral.EmitInt (ig, dim);
7883 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7889 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7890 public readonly ArrayList Properties;
7891 static Indexers empty;
7893 public struct Indexer {
7894 public readonly PropertyInfo PropertyInfo;
7895 public readonly MethodInfo Getter, Setter;
7897 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7899 this.PropertyInfo = property_info;
7907 empty = new Indexers (null);
7910 Indexers (ArrayList array)
7915 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
7920 foreach (PropertyInfo property in mi){
7921 MethodInfo get, set;
7923 get = property.GetGetMethod (true);
7924 set = property.GetSetMethod (true);
7925 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
7927 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
7929 if (get != null || set != null) {
7931 ix = new Indexers (new ArrayList ());
7932 ix.Properties.Add (new Indexer (property, get, set));
7937 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
7939 string p_name = TypeManager.IndexerPropertyName (lookup_type);
7941 return TypeManager.MemberLookup (
7942 caller_type, caller_type, lookup_type, MemberTypes.Property,
7943 BindingFlags.Public | BindingFlags.Instance |
7944 BindingFlags.DeclaredOnly, p_name, null);
7947 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
7949 Indexers ix = empty;
7951 if (lookup_type.IsGenericParameter) {
7952 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
7956 if (gc.HasClassConstraint)
7957 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
7959 Type[] ifaces = gc.InterfaceConstraints;
7960 foreach (Type itype in ifaces)
7961 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
7966 Type copy = lookup_type;
7967 while (copy != TypeManager.object_type && copy != null){
7968 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
7969 copy = copy.BaseType;
7972 if (lookup_type.IsInterface) {
7973 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
7974 if (ifaces != null) {
7975 foreach (Type itype in ifaces)
7976 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
7985 /// Expressions that represent an indexer call.
7987 public class IndexerAccess : Expression, IAssignMethod {
7989 // Points to our "data" repository
7991 MethodInfo get, set;
7992 ArrayList set_arguments;
7993 bool is_base_indexer;
7995 protected Type indexer_type;
7996 protected Type current_type;
7997 protected Expression instance_expr;
7998 protected ArrayList arguments;
8000 public IndexerAccess (ElementAccess ea, Location loc)
8001 : this (ea.Expr, false, loc)
8003 this.arguments = ea.Arguments;
8006 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
8009 this.instance_expr = instance_expr;
8010 this.is_base_indexer = is_base_indexer;
8011 this.eclass = ExprClass.Value;
8015 protected virtual bool CommonResolve (EmitContext ec)
8017 indexer_type = instance_expr.Type;
8018 current_type = ec.ContainerType;
8023 public override Expression DoResolve (EmitContext ec)
8025 ArrayList AllGetters = new ArrayList();
8026 if (!CommonResolve (ec))
8030 // Step 1: Query for all `Item' *properties*. Notice
8031 // that the actual methods are pointed from here.
8033 // This is a group of properties, piles of them.
8035 bool found_any = false, found_any_getters = false;
8036 Type lookup_type = indexer_type;
8038 Indexers ilist = Indexers.GetIndexersForType (current_type, lookup_type);
8039 if (ilist.Properties != null) {
8041 foreach (Indexers.Indexer ix in ilist.Properties) {
8042 if (ix.Getter != null)
8043 AllGetters.Add (ix.Getter);
8047 if (AllGetters.Count > 0) {
8048 found_any_getters = true;
8049 get = (MethodInfo) Invocation.OverloadResolve (
8050 ec, new MethodGroupExpr (AllGetters, loc),
8051 arguments, false, loc);
8055 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8056 TypeManager.CSharpName (indexer_type));
8060 if (!found_any_getters) {
8061 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
8067 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8072 // Only base will allow this invocation to happen.
8074 if (get.IsAbstract && this is BaseIndexerAccess){
8075 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
8079 type = get.ReturnType;
8080 if (type.IsPointer && !ec.InUnsafe){
8085 instance_expr.CheckMarshalByRefAccess ();
8087 eclass = ExprClass.IndexerAccess;
8091 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8093 if (right_side == EmptyExpression.OutAccess) {
8094 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
8095 GetSignatureForError ());
8099 // if the indexer returns a value type, and we try to set a field in it
8100 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
8101 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
8102 GetSignatureForError ());
8106 ArrayList AllSetters = new ArrayList();
8107 if (!CommonResolve (ec))
8110 bool found_any = false, found_any_setters = false;
8112 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
8113 if (ilist.Properties != null) {
8115 foreach (Indexers.Indexer ix in ilist.Properties) {
8116 if (ix.Setter != null)
8117 AllSetters.Add (ix.Setter);
8120 if (AllSetters.Count > 0) {
8121 found_any_setters = true;
8122 set_arguments = (ArrayList) arguments.Clone ();
8123 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
8124 set = (MethodInfo) Invocation.OverloadResolve (
8125 ec, new MethodGroupExpr (AllSetters, loc),
8126 set_arguments, false, loc);
8130 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
8131 TypeManager.CSharpName (indexer_type));
8135 if (!found_any_setters) {
8136 Error (154, "indexer can not be used in this context, because " +
8137 "it lacks a `set' accessor");
8142 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
8147 // Only base will allow this invocation to happen.
8149 if (set.IsAbstract && this is BaseIndexerAccess){
8150 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
8155 // Now look for the actual match in the list of indexers to set our "return" type
8157 type = TypeManager.void_type; // default value
8158 foreach (Indexers.Indexer ix in ilist.Properties){
8159 if (ix.Setter == set){
8160 type = ix.PropertyInfo.PropertyType;
8165 instance_expr.CheckMarshalByRefAccess ();
8167 eclass = ExprClass.IndexerAccess;
8171 bool prepared = false;
8172 LocalTemporary temp;
8174 public void Emit (EmitContext ec, bool leave_copy)
8176 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
8178 ec.ig.Emit (OpCodes.Dup);
8179 temp = new LocalTemporary (Type);
8185 // source is ignored, because we already have a copy of it from the
8186 // LValue resolution and we have already constructed a pre-cached
8187 // version of the arguments (ea.set_arguments);
8189 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
8191 prepared = prepare_for_load;
8192 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
8197 ec.ig.Emit (OpCodes.Dup);
8198 temp = new LocalTemporary (Type);
8201 } else if (leave_copy) {
8202 temp = new LocalTemporary (Type);
8208 Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
8215 public override void Emit (EmitContext ec)
8220 public override string GetSignatureForError ()
8222 // FIXME: print the argument list of the indexer
8223 return instance_expr.GetSignatureForError () + ".this[...]";
8228 /// The base operator for method names
8230 public class BaseAccess : Expression {
8231 public readonly string Identifier;
8234 public BaseAccess (string member, TypeArguments args, Location l)
8236 this.Identifier = member;
8241 public override Expression DoResolve (EmitContext ec)
8243 Expression c = CommonResolve (ec);
8249 // MethodGroups use this opportunity to flag an error on lacking ()
8251 if (!(c is MethodGroupExpr))
8252 return c.Resolve (ec);
8256 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8258 Expression c = CommonResolve (ec);
8264 // MethodGroups use this opportunity to flag an error on lacking ()
8266 if (! (c is MethodGroupExpr))
8267 return c.DoResolveLValue (ec, right_side);
8272 Expression CommonResolve (EmitContext ec)
8274 Expression member_lookup;
8275 Type current_type = ec.ContainerType;
8276 Type base_type = current_type.BaseType;
8279 Error (1511, "Keyword `base' is not available in a static method");
8283 if (ec.IsFieldInitializer){
8284 Error (1512, "Keyword `base' is not available in the current context");
8288 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8289 AllMemberTypes, AllBindingFlags, loc);
8290 if (member_lookup == null) {
8291 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8298 left = new TypeExpression (base_type, loc);
8300 left = ec.GetThis (loc);
8302 MemberExpr me = (MemberExpr) member_lookup;
8304 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8306 if (e is PropertyExpr) {
8307 PropertyExpr pe = (PropertyExpr) e;
8312 MethodGroupExpr mg = e as MethodGroupExpr;
8318 return mg.ResolveGeneric (ec, args);
8320 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8328 public override void Emit (EmitContext ec)
8330 throw new Exception ("Should never be called");
8335 /// The base indexer operator
8337 public class BaseIndexerAccess : IndexerAccess {
8338 public BaseIndexerAccess (ArrayList args, Location loc)
8339 : base (null, true, loc)
8341 arguments = new ArrayList ();
8342 foreach (Expression tmp in args)
8343 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8346 protected override bool CommonResolve (EmitContext ec)
8348 instance_expr = ec.GetThis (loc);
8350 current_type = ec.ContainerType.BaseType;
8351 indexer_type = current_type;
8353 foreach (Argument a in arguments){
8354 if (!a.Resolve (ec, loc))
8363 /// This class exists solely to pass the Type around and to be a dummy
8364 /// that can be passed to the conversion functions (this is used by
8365 /// foreach implementation to typecast the object return value from
8366 /// get_Current into the proper type. All code has been generated and
8367 /// we only care about the side effect conversions to be performed
8369 /// This is also now used as a placeholder where a no-action expression
8370 /// is needed (the `New' class).
8372 public class EmptyExpression : Expression {
8373 public static readonly EmptyExpression Null = new EmptyExpression ();
8375 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8376 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8377 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8379 static EmptyExpression temp = new EmptyExpression ();
8380 public static EmptyExpression Grab ()
8383 throw new InternalErrorException ("Nested Grab");
8384 EmptyExpression retval = temp;
8389 public static void Release (EmptyExpression e)
8392 throw new InternalErrorException ("Already released");
8396 // TODO: should be protected
8397 public EmptyExpression ()
8399 type = TypeManager.object_type;
8400 eclass = ExprClass.Value;
8401 loc = Location.Null;
8404 public EmptyExpression (Type t)
8407 eclass = ExprClass.Value;
8408 loc = Location.Null;
8411 public override Expression DoResolve (EmitContext ec)
8416 public override void Emit (EmitContext ec)
8418 // nothing, as we only exist to not do anything.
8422 // This is just because we might want to reuse this bad boy
8423 // instead of creating gazillions of EmptyExpressions.
8424 // (CanImplicitConversion uses it)
8426 public void SetType (Type t)
8432 public class UserCast : Expression {
8436 public UserCast (MethodInfo method, Expression source, Location l)
8438 this.method = method;
8439 this.source = source;
8440 type = method.ReturnType;
8441 eclass = ExprClass.Value;
8445 public Expression Source {
8451 public override Expression DoResolve (EmitContext ec)
8454 // We are born fully resolved
8459 public override void Emit (EmitContext ec)
8461 ILGenerator ig = ec.ig;
8465 if (method is MethodInfo)
8466 ig.Emit (OpCodes.Call, (MethodInfo) method);
8468 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8474 // This class is used to "construct" the type during a typecast
8475 // operation. Since the Type.GetType class in .NET can parse
8476 // the type specification, we just use this to construct the type
8477 // one bit at a time.
8479 public class ComposedCast : TypeExpr {
8483 public ComposedCast (Expression left, string dim)
8484 : this (left, dim, left.Location)
8488 public ComposedCast (Expression left, string dim, Location l)
8495 public Expression RemoveNullable ()
8497 if (dim.EndsWith ("?")) {
8498 dim = dim.Substring (0, dim.Length - 1);
8506 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8508 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8512 Type ltype = lexpr.Type;
8513 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8514 Report.Error (1547, Location,
8515 "Keyword 'void' cannot be used in this context");
8519 if ((dim.Length > 0) && (dim [0] == '?')) {
8520 TypeExpr nullable = new NullableType (left, loc);
8522 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8523 return nullable.ResolveAsTypeTerminal (ec, false);
8526 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc)) {
8531 type = TypeManager.GetConstructedType (ltype, dim);
8536 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8539 if (type.IsPointer && !ec.IsInUnsafeScope){
8544 if (type.IsArray && (type.GetElementType () == TypeManager.arg_iterator_type ||
8545 type.GetElementType () == TypeManager.typed_reference_type)) {
8546 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (type.GetElementType ()));
8550 eclass = ExprClass.Type;
8554 public override string Name {
8555 get { return left + dim; }
8558 public override string FullName {
8559 get { return type.FullName; }
8562 public override string GetSignatureForError ()
8564 return left.GetSignatureForError () + dim;
8568 public class FixedBufferPtr : Expression {
8571 public FixedBufferPtr (Expression array, Type array_type, Location l)
8576 type = TypeManager.GetPointerType (array_type);
8577 eclass = ExprClass.Value;
8580 public override void Emit(EmitContext ec)
8585 public override Expression DoResolve (EmitContext ec)
8588 // We are born fully resolved
8596 // This class is used to represent the address of an array, used
8597 // only by the Fixed statement, this generates "&a [0]" construct
8598 // for fixed (char *pa = a)
8600 public class ArrayPtr : FixedBufferPtr {
8603 public ArrayPtr (Expression array, Type array_type, Location l):
8604 base (array, array_type, l)
8606 this.array_type = array_type;
8609 public override void Emit (EmitContext ec)
8613 ILGenerator ig = ec.ig;
8614 IntLiteral.EmitInt (ig, 0);
8615 ig.Emit (OpCodes.Ldelema, array_type);
8620 // Used by the fixed statement
8622 public class StringPtr : Expression {
8625 public StringPtr (LocalBuilder b, Location l)
8628 eclass = ExprClass.Value;
8629 type = TypeManager.char_ptr_type;
8633 public override Expression DoResolve (EmitContext ec)
8635 // This should never be invoked, we are born in fully
8636 // initialized state.
8641 public override void Emit (EmitContext ec)
8643 ILGenerator ig = ec.ig;
8645 ig.Emit (OpCodes.Ldloc, b);
8646 ig.Emit (OpCodes.Conv_I);
8647 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8648 ig.Emit (OpCodes.Add);
8653 // Implements the `stackalloc' keyword
8655 public class StackAlloc : Expression {
8660 public StackAlloc (Expression type, Expression count, Location l)
8667 public override Expression DoResolve (EmitContext ec)
8669 count = count.Resolve (ec);
8673 if (count.Type != TypeManager.int32_type){
8674 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8679 Constant c = count as Constant;
8680 if (c != null && c.IsNegative) {
8681 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8685 if (ec.InCatch || ec.InFinally) {
8686 Error (255, "Cannot use stackalloc in finally or catch");
8690 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8696 if (!TypeManager.VerifyUnManaged (otype, loc))
8699 type = TypeManager.GetPointerType (otype);
8700 eclass = ExprClass.Value;
8705 public override void Emit (EmitContext ec)
8707 int size = GetTypeSize (otype);
8708 ILGenerator ig = ec.ig;
8711 ig.Emit (OpCodes.Sizeof, otype);
8713 IntConstant.EmitInt (ig, size);
8715 ig.Emit (OpCodes.Mul);
8716 ig.Emit (OpCodes.Localloc);