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)
61 args = new ArrayList (1);
62 Argument a = new Argument (e, Argument.AType.Expression);
64 // We need to resolve the arguments before sending them in !
65 if (!a.Resolve (ec, loc))
69 mg = mg.OverloadResolve (ec, args, false, loc);
74 return new StaticCallExpr ((MethodInfo) mg, args, loc);
77 public override void EmitStatement (EmitContext ec)
80 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
81 ec.ig.Emit (OpCodes.Pop);
84 public MethodInfo Method {
89 public class ParenthesizedExpression : Expression
91 public Expression Expr;
93 public ParenthesizedExpression (Expression expr)
98 public override Expression DoResolve (EmitContext ec)
100 Expr = Expr.Resolve (ec);
104 public override void Emit (EmitContext ec)
106 throw new Exception ("Should not happen");
109 public override Location Location
112 return Expr.Location;
116 protected override void CloneTo (CloneContext clonectx, Expression t)
118 ParenthesizedExpression target = (ParenthesizedExpression) t;
120 target.Expr = Expr.Clone (clonectx);
125 /// Unary expressions.
129 /// Unary implements unary expressions. It derives from
130 /// ExpressionStatement becuase the pre/post increment/decrement
131 /// operators can be used in a statement context.
133 public class Unary : Expression {
134 public enum Operator : byte {
135 UnaryPlus, UnaryNegation, LogicalNot, OnesComplement,
136 Indirection, AddressOf, TOP
139 public Operator Oper;
140 public Expression Expr;
142 public Unary (Operator op, Expression expr, Location loc)
150 /// Returns a stringified representation of the Operator
152 static public string OperName (Operator oper)
155 case Operator.UnaryPlus:
157 case Operator.UnaryNegation:
159 case Operator.LogicalNot:
161 case Operator.OnesComplement:
163 case Operator.AddressOf:
165 case Operator.Indirection:
169 return oper.ToString ();
172 public static readonly string [] oper_names;
176 oper_names = new string [(int)Operator.TOP];
178 oper_names [(int) Operator.UnaryPlus] = "op_UnaryPlus";
179 oper_names [(int) Operator.UnaryNegation] = "op_UnaryNegation";
180 oper_names [(int) Operator.LogicalNot] = "op_LogicalNot";
181 oper_names [(int) Operator.OnesComplement] = "op_OnesComplement";
182 oper_names [(int) Operator.Indirection] = "op_Indirection";
183 oper_names [(int) Operator.AddressOf] = "op_AddressOf";
186 public static void Error_OperatorCannotBeApplied (Location loc, string oper, Type t)
188 Error_OperatorCannotBeApplied (loc, oper, TypeManager.CSharpName (t));
191 public static void Error_OperatorCannotBeApplied (Location loc, string oper, string type)
193 Report.Error (23, loc, "The `{0}' operator cannot be applied to operand of type `{1}'",
197 void Error23 (Type t)
199 Error_OperatorCannotBeApplied (loc, OperName (Oper), t);
203 // This routine will attempt to simplify the unary expression when the
204 // argument is a constant.
206 Constant TryReduceConstant (EmitContext ec, Constant e)
208 Type expr_type = e.Type;
211 case Operator.UnaryPlus:
212 // Unary numeric promotions
213 if (expr_type == TypeManager.byte_type)
214 return new IntConstant (((ByteConstant)e).Value, e.Location);
215 if (expr_type == TypeManager.sbyte_type)
216 return new IntConstant (((SByteConstant)e).Value, e.Location);
217 if (expr_type == TypeManager.short_type)
218 return new IntConstant (((ShortConstant)e).Value, e.Location);
219 if (expr_type == TypeManager.ushort_type)
220 return new IntConstant (((UShortConstant)e).Value, e.Location);
221 if (expr_type == TypeManager.char_type)
222 return new IntConstant (((CharConstant)e).Value, e.Location);
224 // Predefined operators
225 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
226 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
227 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
228 expr_type == TypeManager.decimal_type)
235 case Operator.UnaryNegation:
236 // Unary numeric promotions
237 if (expr_type == TypeManager.byte_type)
238 return new IntConstant (-((ByteConstant)e).Value, e.Location);
239 if (expr_type == TypeManager.sbyte_type)
240 return new IntConstant (-((SByteConstant)e).Value, e.Location);
241 if (expr_type == TypeManager.short_type)
242 return new IntConstant (-((ShortConstant)e).Value, e.Location);
243 if (expr_type == TypeManager.ushort_type)
244 return new IntConstant (-((UShortConstant)e).Value, e.Location);
245 if (expr_type == TypeManager.char_type)
246 return new IntConstant (-((CharConstant)e).Value, e.Location);
248 // Predefined operators
249 if (expr_type == TypeManager.int32_type) {
250 int value = ((IntConstant)e).Value;
251 if (value == int.MinValue) {
252 if (ec.ConstantCheckState) {
253 ConstantFold.Error_CompileTimeOverflow (loc);
258 return new IntConstant (-value, e.Location);
260 if (expr_type == TypeManager.int64_type) {
261 long value = ((LongConstant)e).Value;
262 if (value == long.MinValue) {
263 if (ec.ConstantCheckState) {
264 ConstantFold.Error_CompileTimeOverflow (loc);
269 return new LongConstant (-value, e.Location);
272 if (expr_type == TypeManager.uint32_type) {
273 UIntLiteral uil = e as UIntLiteral;
275 if (uil.Value == 2147483648)
276 return new IntLiteral (int.MinValue, e.Location);
277 return new LongLiteral (-uil.Value, e.Location);
279 return new LongConstant (-((UIntConstant)e).Value, e.Location);
282 if (expr_type == TypeManager.uint64_type) {
283 ULongLiteral ull = e as ULongLiteral;
284 if (ull != null && ull.Value == 9223372036854775808)
285 return new LongLiteral (long.MinValue, e.Location);
289 if (expr_type == TypeManager.float_type) {
290 FloatLiteral fl = e as FloatLiteral;
291 // For better error reporting
293 fl.Value = -fl.Value;
296 return new FloatConstant (-((FloatConstant)e).Value, e.Location);
298 if (expr_type == TypeManager.double_type) {
299 DoubleLiteral dl = e as DoubleLiteral;
300 // For better error reporting
302 dl.Value = -dl.Value;
306 return new DoubleConstant (-((DoubleConstant)e).Value, e.Location);
308 if (expr_type == TypeManager.decimal_type)
309 return new DecimalConstant (-((DecimalConstant)e).Value, e.Location);
313 case Operator.LogicalNot:
314 if (expr_type != TypeManager.bool_type)
317 BoolConstant b = (BoolConstant) e;
318 return new BoolConstant (!(b.Value), b.Location);
320 case Operator.OnesComplement:
321 // Unary numeric promotions
322 if (expr_type == TypeManager.byte_type)
323 return new IntConstant (~((ByteConstant)e).Value, e.Location);
324 if (expr_type == TypeManager.sbyte_type)
325 return new IntConstant (~((SByteConstant)e).Value, e.Location);
326 if (expr_type == TypeManager.short_type)
327 return new IntConstant (~((ShortConstant)e).Value, e.Location);
328 if (expr_type == TypeManager.ushort_type)
329 return new IntConstant (~((UShortConstant)e).Value, e.Location);
330 if (expr_type == TypeManager.char_type)
331 return new IntConstant (~((CharConstant)e).Value, e.Location);
333 // Predefined operators
334 if (expr_type == TypeManager.int32_type)
335 return new IntConstant (~((IntConstant)e).Value, e.Location);
336 if (expr_type == TypeManager.uint32_type)
337 return new UIntConstant (~((UIntConstant)e).Value, e.Location);
338 if (expr_type == TypeManager.int64_type)
339 return new LongConstant (~((LongConstant)e).Value, e.Location);
340 if (expr_type == TypeManager.uint64_type){
341 return new ULongConstant (~((ULongConstant)e).Value, e.Location);
343 if (e is EnumConstant) {
344 e = TryReduceConstant (ec, ((EnumConstant)e).Child);
346 e = new EnumConstant (e, expr_type);
351 case Operator.AddressOf:
354 case Operator.Indirection:
357 throw new Exception ("Can not constant fold: " + Oper.ToString());
360 Expression ResolveOperator (EmitContext ec)
363 // Step 1: Default operations on CLI native types.
366 // Attempt to use a constant folding operation.
367 Constant cexpr = Expr as Constant;
369 cexpr = TryReduceConstant (ec, cexpr);
376 // Step 2: Perform Operator Overload location
378 Type expr_type = Expr.Type;
379 string op_name = oper_names [(int) Oper];
381 Expression mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
383 Expression e = StaticCallExpr.MakeSimpleCall (
384 ec, (MethodGroupExpr) mg, Expr, loc);
395 case Operator.LogicalNot:
396 if (expr_type != TypeManager.bool_type) {
397 Expr = ResolveBoolean (ec, Expr, loc);
404 type = TypeManager.bool_type;
407 case Operator.OnesComplement:
408 // Unary numeric promotions
409 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
410 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
411 expr_type == TypeManager.char_type)
413 type = TypeManager.int32_type;
414 return new EmptyCast (this, type);
417 // Predefined operators
418 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
419 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
420 TypeManager.IsEnumType (expr_type))
426 type = TypeManager.int32_type;
427 Expr = Convert.ImplicitUserConversion(ec, Expr, type, loc);
434 case Operator.AddressOf:
440 if (!TypeManager.VerifyUnManaged (Expr.Type, loc)){
444 IVariable variable = Expr as IVariable;
445 bool is_fixed = variable != null && variable.VerifyFixed ();
447 if (!ec.InFixedInitializer && !is_fixed) {
448 Error (212, "You can only take the address of unfixed expression inside " +
449 "of a fixed statement initializer");
453 if (ec.InFixedInitializer && is_fixed) {
454 Error (213, "You cannot use the fixed statement to take the address of an already fixed expression");
458 LocalVariableReference lr = Expr as LocalVariableReference;
460 if (lr.local_info.IsCaptured){
461 AnonymousMethod.Error_AddressOfCapturedVar (lr.Name, loc);
464 lr.local_info.AddressTaken = true;
465 lr.local_info.Used = true;
468 ParameterReference pr = Expr as ParameterReference;
469 if ((pr != null) && pr.Parameter.IsCaptured) {
470 AnonymousMethod.Error_AddressOfCapturedVar (pr.Name, loc);
474 // According to the specs, a variable is considered definitely assigned if you take
476 if ((variable != null) && (variable.VariableInfo != null)){
477 variable.VariableInfo.SetAssigned (ec);
480 type = TypeManager.GetPointerType (Expr.Type);
483 case Operator.Indirection:
489 if (!expr_type.IsPointer){
490 Error (193, "The * or -> operator must be applied to a pointer");
495 // We create an Indirection expression, because
496 // it can implement the IMemoryLocation.
498 return new Indirection (Expr, loc);
500 case Operator.UnaryPlus:
501 // Unary numeric promotions
502 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
503 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
504 expr_type == TypeManager.char_type)
506 return new EmptyCast (Expr, TypeManager.int32_type);
509 // Predefined operators
510 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.uint32_type ||
511 expr_type == TypeManager.int64_type || expr_type == TypeManager.uint64_type ||
512 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
513 expr_type == TypeManager.decimal_type)
518 Expr = Convert.ImplicitUserConversion(ec, Expr, TypeManager.int32_type, loc);
520 // Because we can completely ignore unary +
527 case Operator.UnaryNegation:
529 // transform - - expr into expr
531 Unary u = Expr as Unary;
532 if (u != null && u.Oper == Operator.UnaryNegation) {
536 // Unary numeric promotions
537 if (expr_type == TypeManager.byte_type || expr_type == TypeManager.sbyte_type ||
538 expr_type == TypeManager.short_type || expr_type == TypeManager.ushort_type ||
539 expr_type == TypeManager.char_type)
541 type = TypeManager.int32_type;
542 return new EmptyCast (this, type);
546 // Predefined operators
548 if (expr_type == TypeManager.uint32_type) {
549 type = TypeManager.int64_type;
550 Expr = Convert.ImplicitNumericConversion (Expr, type);
554 if (expr_type == TypeManager.int32_type || expr_type == TypeManager.int64_type ||
555 expr_type == TypeManager.float_type || expr_type == TypeManager.double_type ||
556 expr_type == TypeManager.decimal_type)
565 type = TypeManager.int32_type;
566 Expr = Convert.ImplicitUserConversion(ec, Expr, type, loc);
574 Error (187, "No such operator '" + OperName (Oper) + "' defined for type '" +
575 TypeManager.CSharpName (expr_type) + "'");
579 public override Expression DoResolve (EmitContext ec)
581 if (Oper == Operator.AddressOf) {
582 Expr = Expr.DoResolveLValue (ec, new EmptyExpression ());
584 if (Expr == null || Expr.eclass != ExprClass.Variable){
585 Error (211, "Cannot take the address of the given expression");
590 Expr = Expr.Resolve (ec);
596 if (TypeManager.IsNullableValueType (Expr.Type))
597 return new Nullable.LiftedUnaryOperator (Oper, Expr, loc).Resolve (ec);
600 eclass = ExprClass.Value;
601 return ResolveOperator (ec);
604 public override Expression DoResolveLValue (EmitContext ec, Expression right)
606 if (Oper == Operator.Indirection)
607 return DoResolve (ec);
612 public override void Emit (EmitContext ec)
614 ILGenerator ig = ec.ig;
617 case Operator.UnaryPlus:
618 throw new Exception ("This should be caught by Resolve");
620 case Operator.UnaryNegation:
621 if (ec.CheckState && type != TypeManager.float_type && type != TypeManager.double_type) {
622 ig.Emit (OpCodes.Ldc_I4_0);
623 if (type == TypeManager.int64_type)
624 ig.Emit (OpCodes.Conv_U8);
626 ig.Emit (OpCodes.Sub_Ovf);
629 ig.Emit (OpCodes.Neg);
634 case Operator.LogicalNot:
636 ig.Emit (OpCodes.Ldc_I4_0);
637 ig.Emit (OpCodes.Ceq);
640 case Operator.OnesComplement:
642 ig.Emit (OpCodes.Not);
645 case Operator.AddressOf:
646 ((IMemoryLocation)Expr).AddressOf (ec, AddressOp.LoadStore);
650 throw new Exception ("This should not happen: Operator = "
655 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
657 if (Oper == Operator.LogicalNot)
658 Expr.EmitBranchable (ec, target, !onTrue);
660 base.EmitBranchable (ec, target, onTrue);
663 public override string ToString ()
665 return "Unary (" + Oper + ", " + Expr + ")";
668 protected override void CloneTo (CloneContext clonectx, Expression t)
670 Unary target = (Unary) t;
672 target.Expr = Expr.Clone (clonectx);
677 // Unary operators are turned into Indirection expressions
678 // after semantic analysis (this is so we can take the address
679 // of an indirection).
681 public class Indirection : Expression, IMemoryLocation, IAssignMethod, IVariable {
683 LocalTemporary temporary;
686 public Indirection (Expression expr, Location l)
689 type = TypeManager.HasElementType (expr.Type) ? TypeManager.GetElementType (expr.Type) : expr.Type;
690 eclass = ExprClass.Variable;
694 public override void Emit (EmitContext ec)
699 LoadFromPtr (ec.ig, Type);
702 public void Emit (EmitContext ec, bool leave_copy)
706 ec.ig.Emit (OpCodes.Dup);
707 temporary = new LocalTemporary (expr.Type);
708 temporary.Store (ec);
712 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
714 prepared = prepare_for_load;
718 if (prepare_for_load)
719 ec.ig.Emit (OpCodes.Dup);
723 ec.ig.Emit (OpCodes.Dup);
724 temporary = new LocalTemporary (expr.Type);
725 temporary.Store (ec);
728 StoreFromPtr (ec.ig, type);
730 if (temporary != null) {
732 temporary.Release (ec);
736 public void AddressOf (EmitContext ec, AddressOp Mode)
741 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
743 return DoResolve (ec);
746 public override Expression DoResolve (EmitContext ec)
749 // Born fully resolved
754 public override string ToString ()
756 return "*(" + expr + ")";
759 #region IVariable Members
761 public VariableInfo VariableInfo {
765 public bool VerifyFixed ()
767 // A pointer-indirection is always fixed.
775 /// Unary Mutator expressions (pre and post ++ and --)
779 /// UnaryMutator implements ++ and -- expressions. It derives from
780 /// ExpressionStatement becuase the pre/post increment/decrement
781 /// operators can be used in a statement context.
783 /// FIXME: Idea, we could split this up in two classes, one simpler
784 /// for the common case, and one with the extra fields for more complex
785 /// classes (indexers require temporary access; overloaded require method)
788 public class UnaryMutator : ExpressionStatement {
790 public enum Mode : byte {
797 PreDecrement = IsDecrement,
798 PostIncrement = IsPost,
799 PostDecrement = IsPost | IsDecrement
803 bool is_expr = false;
804 bool recurse = false;
809 // This is expensive for the simplest case.
811 StaticCallExpr method;
813 public UnaryMutator (Mode m, Expression e, Location l)
820 static string OperName (Mode mode)
822 return (mode == Mode.PreIncrement || mode == Mode.PostIncrement) ?
827 /// Returns whether an object of type `t' can be incremented
828 /// or decremented with add/sub (ie, basically whether we can
829 /// use pre-post incr-decr operations on it, but it is not a
830 /// System.Decimal, which we require operator overloading to catch)
832 static bool IsIncrementableNumber (Type t)
834 return (t == TypeManager.sbyte_type) ||
835 (t == TypeManager.byte_type) ||
836 (t == TypeManager.short_type) ||
837 (t == TypeManager.ushort_type) ||
838 (t == TypeManager.int32_type) ||
839 (t == TypeManager.uint32_type) ||
840 (t == TypeManager.int64_type) ||
841 (t == TypeManager.uint64_type) ||
842 (t == TypeManager.char_type) ||
843 (t.IsSubclassOf (TypeManager.enum_type)) ||
844 (t == TypeManager.float_type) ||
845 (t == TypeManager.double_type) ||
846 (t.IsPointer && t != TypeManager.void_ptr_type);
849 Expression ResolveOperator (EmitContext ec)
851 Type expr_type = expr.Type;
854 // Step 1: Perform Operator Overload location
859 if (mode == Mode.PreIncrement || mode == Mode.PostIncrement)
860 op_name = "op_Increment";
862 op_name = "op_Decrement";
864 mg = MemberLookup (ec.ContainerType, expr_type, op_name, MemberTypes.Method, AllBindingFlags, loc);
867 method = StaticCallExpr.MakeSimpleCall (
868 ec, (MethodGroupExpr) mg, expr, loc);
871 } else if (!IsIncrementableNumber (expr_type)) {
872 Error (187, "No such operator '" + OperName (mode) + "' defined for type '" +
873 TypeManager.CSharpName (expr_type) + "'");
878 // The operand of the prefix/postfix increment decrement operators
879 // should be an expression that is classified as a variable,
880 // a property access or an indexer access
883 if (expr.eclass == ExprClass.Variable){
884 LocalVariableReference var = expr as LocalVariableReference;
885 if ((var != null) && var.IsReadOnly) {
886 Error (1604, "cannot assign to `" + var.Name + "' because it is readonly");
889 } else if (expr.eclass == ExprClass.IndexerAccess || expr.eclass == ExprClass.PropertyAccess){
890 expr = expr.ResolveLValue (ec, this, Location);
894 if (expr.eclass == ExprClass.Value) {
895 Error_ValueAssignment (loc);
897 expr.Error_UnexpectedKind (ec.DeclContainer, "variable, indexer or property access", loc);
905 public override Expression DoResolve (EmitContext ec)
907 expr = expr.Resolve (ec);
912 eclass = ExprClass.Value;
915 if (TypeManager.IsNullableValueType (expr.Type))
916 return new Nullable.LiftedUnaryMutator (mode, expr, loc).Resolve (ec);
919 return ResolveOperator (ec);
922 static int PtrTypeSize (Type t)
924 return GetTypeSize (TypeManager.GetElementType (t));
928 // Loads the proper "1" into the stack based on the type, then it emits the
929 // opcode for the operation requested
931 void LoadOneAndEmitOp (EmitContext ec, Type t)
934 // Measure if getting the typecode and using that is more/less efficient
935 // that comparing types. t.GetTypeCode() is an internal call.
937 ILGenerator ig = ec.ig;
939 if (t == TypeManager.uint64_type || t == TypeManager.int64_type)
940 LongConstant.EmitLong (ig, 1);
941 else if (t == TypeManager.double_type)
942 ig.Emit (OpCodes.Ldc_R8, 1.0);
943 else if (t == TypeManager.float_type)
944 ig.Emit (OpCodes.Ldc_R4, 1.0F);
945 else if (t.IsPointer){
946 int n = PtrTypeSize (t);
949 ig.Emit (OpCodes.Sizeof, t);
951 IntConstant.EmitInt (ig, n);
953 ig.Emit (OpCodes.Ldc_I4_1);
956 // Now emit the operation
959 if (t == TypeManager.int32_type ||
960 t == TypeManager.int64_type){
961 if ((mode & Mode.IsDecrement) != 0)
962 ig.Emit (OpCodes.Sub_Ovf);
964 ig.Emit (OpCodes.Add_Ovf);
965 } else if (t == TypeManager.uint32_type ||
966 t == TypeManager.uint64_type){
967 if ((mode & Mode.IsDecrement) != 0)
968 ig.Emit (OpCodes.Sub_Ovf_Un);
970 ig.Emit (OpCodes.Add_Ovf_Un);
972 if ((mode & Mode.IsDecrement) != 0)
973 ig.Emit (OpCodes.Sub_Ovf);
975 ig.Emit (OpCodes.Add_Ovf);
978 if ((mode & Mode.IsDecrement) != 0)
979 ig.Emit (OpCodes.Sub);
981 ig.Emit (OpCodes.Add);
984 if (t == TypeManager.sbyte_type){
986 ig.Emit (OpCodes.Conv_Ovf_I1);
988 ig.Emit (OpCodes.Conv_I1);
989 } else if (t == TypeManager.byte_type){
991 ig.Emit (OpCodes.Conv_Ovf_U1);
993 ig.Emit (OpCodes.Conv_U1);
994 } else if (t == TypeManager.short_type){
996 ig.Emit (OpCodes.Conv_Ovf_I2);
998 ig.Emit (OpCodes.Conv_I2);
999 } else if (t == TypeManager.ushort_type || t == TypeManager.char_type){
1001 ig.Emit (OpCodes.Conv_Ovf_U2);
1003 ig.Emit (OpCodes.Conv_U2);
1008 void EmitCode (EmitContext ec, bool is_expr)
1011 this.is_expr = is_expr;
1012 ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
1015 public override void Emit (EmitContext ec)
1018 // We use recurse to allow ourselfs to be the source
1019 // of an assignment. This little hack prevents us from
1020 // having to allocate another expression
1023 ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
1025 LoadOneAndEmitOp (ec, expr.Type);
1027 ec.ig.Emit (OpCodes.Call, method.Method);
1032 EmitCode (ec, true);
1035 public override void EmitStatement (EmitContext ec)
1037 EmitCode (ec, false);
1040 protected override void CloneTo (CloneContext clonectx, Expression t)
1042 UnaryMutator target = (UnaryMutator) t;
1044 target.expr = expr.Clone (clonectx);
1049 /// Base class for the `Is' and `As' classes.
1053 /// FIXME: Split this in two, and we get to save the `Operator' Oper
1056 public abstract class Probe : Expression {
1057 public Expression ProbeType;
1058 protected Expression expr;
1059 protected TypeExpr probe_type_expr;
1061 public Probe (Expression expr, Expression probe_type, Location l)
1063 ProbeType = probe_type;
1068 public Expression Expr {
1074 public override Expression DoResolve (EmitContext ec)
1076 probe_type_expr = ProbeType.ResolveAsTypeTerminal (ec, false);
1077 if (probe_type_expr == null)
1080 expr = expr.Resolve (ec);
1084 if (expr.Type.IsPointer) {
1085 Report.Error (244, loc, "\"is\" or \"as\" are not valid on pointer types");
1091 protected override void CloneTo (CloneContext clonectx, Expression t)
1093 Probe target = (Probe) t;
1095 target.expr = expr.Clone (clonectx);
1096 target.ProbeType = ProbeType.Clone (clonectx);
1102 /// Implementation of the `is' operator.
1104 public class Is : Probe {
1105 public Is (Expression expr, Expression probe_type, Location l)
1106 : base (expr, probe_type, l)
1111 AlwaysTrue, AlwaysNull, AlwaysFalse, LeaveOnStack, Probe
1116 public override void Emit (EmitContext ec)
1118 ILGenerator ig = ec.ig;
1123 case Action.AlwaysFalse:
1124 ig.Emit (OpCodes.Pop);
1125 IntConstant.EmitInt (ig, 0);
1127 case Action.AlwaysTrue:
1128 ig.Emit (OpCodes.Pop);
1129 IntConstant.EmitInt (ig, 1);
1131 case Action.LeaveOnStack:
1132 // the `e != null' rule.
1133 ig.Emit (OpCodes.Ldnull);
1134 ig.Emit (OpCodes.Ceq);
1135 ig.Emit (OpCodes.Ldc_I4_0);
1136 ig.Emit (OpCodes.Ceq);
1139 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1140 ig.Emit (OpCodes.Ldnull);
1141 ig.Emit (OpCodes.Cgt_Un);
1144 throw new Exception ("never reached");
1147 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
1149 ILGenerator ig = ec.ig;
1152 case Action.AlwaysFalse:
1154 ig.Emit (OpCodes.Br, target);
1157 case Action.AlwaysTrue:
1159 ig.Emit (OpCodes.Br, target);
1162 case Action.LeaveOnStack:
1163 // the `e != null' rule.
1165 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1169 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1170 ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
1173 throw new Exception ("never reached");
1176 public override Expression DoResolve (EmitContext ec)
1178 Expression e = base.DoResolve (ec);
1180 if ((e == null) || (expr == null))
1183 Type etype = expr.Type;
1184 type = TypeManager.bool_type;
1185 eclass = ExprClass.Value;
1188 // First case, if at compile time, there is an implicit conversion
1189 // then e != null (objects) or true (value types)
1191 Type probe_type = probe_type_expr.Type;
1192 e = Convert.ImplicitConversionStandard (ec, expr, probe_type, loc);
1195 if (etype.IsValueType)
1196 action = Action.AlwaysTrue;
1198 action = Action.LeaveOnStack;
1200 Constant c = e as Constant;
1201 if (c != null && c.GetValue () == null) {
1202 action = Action.AlwaysFalse;
1203 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
1204 TypeManager.CSharpName (probe_type));
1205 } else if (etype.IsValueType) {
1206 Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
1207 TypeManager.CSharpName (probe_type));
1212 if (Convert.ExplicitReferenceConversionExists (etype, probe_type)){
1213 if (TypeManager.IsGenericParameter (etype))
1214 expr = new BoxedCast (expr, etype);
1217 // Second case: explicit reference convresion
1219 if (expr is NullLiteral)
1220 action = Action.AlwaysFalse;
1222 action = Action.Probe;
1223 } else if (TypeManager.ContainsGenericParameters (etype) ||
1224 TypeManager.ContainsGenericParameters (probe_type)) {
1225 expr = new BoxedCast (expr, etype);
1226 action = Action.Probe;
1228 action = Action.AlwaysFalse;
1229 if (!(probe_type.IsInterface || expr.Type.IsInterface))
1230 Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type", TypeManager.CSharpName (probe_type));
1238 /// Implementation of the `as' operator.
1240 public class As : Probe {
1241 public As (Expression expr, Expression probe_type, Location l)
1242 : base (expr, probe_type, l)
1246 bool do_isinst = false;
1247 Expression resolved_type;
1249 public override void Emit (EmitContext ec)
1251 ILGenerator ig = ec.ig;
1256 ig.Emit (OpCodes.Isinst, probe_type_expr.Type);
1259 if (TypeManager.IsNullableType (type))
1260 ig.Emit (OpCodes.Unbox_Any, type);
1264 static void Error_CannotConvertType (Type source, Type target, Location loc)
1266 Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
1267 TypeManager.CSharpName (source),
1268 TypeManager.CSharpName (target));
1271 public override Expression DoResolve (EmitContext ec)
1273 if (resolved_type == null) {
1274 resolved_type = base.DoResolve (ec);
1276 if (resolved_type == null)
1280 type = probe_type_expr.Type;
1281 eclass = ExprClass.Value;
1282 Type etype = expr.Type;
1284 if (type.IsValueType && !TypeManager.IsNullableType (type)) {
1285 Report.Error (77, loc, "The as operator must be used with a reference type (`" +
1286 TypeManager.CSharpName (type) + "' is a value type)");
1293 // If the type is a type parameter, ensure
1294 // that it is constrained by a class
1296 TypeParameterExpr tpe = probe_type_expr as TypeParameterExpr;
1298 GenericConstraints constraints = tpe.TypeParameter.GenericConstraints;
1301 if (constraints == null)
1304 if (!constraints.HasClassConstraint)
1305 if ((constraints.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) == 0)
1309 Report.Error (413, loc,
1310 "The as operator requires that the `{0}' type parameter be constrained by a class",
1311 probe_type_expr.GetSignatureForError ());
1317 Expression e = Convert.ImplicitConversion (ec, expr, type, loc);
1324 if (Convert.ExplicitReferenceConversionExists (etype, type)){
1325 if (TypeManager.IsGenericParameter (etype))
1326 expr = new BoxedCast (expr, etype);
1332 if (TypeManager.ContainsGenericParameters (etype) ||
1333 TypeManager.ContainsGenericParameters (type)) {
1334 expr = new BoxedCast (expr, etype);
1339 Error_CannotConvertType (etype, type, loc);
1343 public override bool GetAttributableValue (Type valueType, out object value)
1345 return expr.GetAttributableValue (valueType, out value);
1350 /// This represents a typecast in the source language.
1352 /// FIXME: Cast expressions have an unusual set of parsing
1353 /// rules, we need to figure those out.
1355 public class Cast : Expression {
1356 Expression target_type;
1359 public Cast (Expression cast_type, Expression expr)
1360 : this (cast_type, expr, cast_type.Location)
1364 public Cast (Expression cast_type, Expression expr, Location loc)
1366 this.target_type = cast_type;
1370 if (target_type == TypeManager.system_void_expr)
1371 Error_VoidInvalidInTheContext (loc);
1374 public Expression TargetType {
1375 get { return target_type; }
1378 public Expression Expr {
1379 get { return expr; }
1380 set { expr = value; }
1383 public override Expression DoResolve (EmitContext ec)
1385 expr = expr.Resolve (ec);
1389 TypeExpr target = target_type.ResolveAsTypeTerminal (ec, false);
1395 if (type.IsAbstract && type.IsSealed) {
1396 Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
1400 eclass = ExprClass.Value;
1402 Constant c = expr as Constant;
1404 c = c.TryReduce (ec, type, loc);
1409 if (type.IsPointer && !ec.InUnsafe) {
1413 expr = Convert.ExplicitConversion (ec, expr, type, loc);
1417 public override void Emit (EmitContext ec)
1419 throw new Exception ("Should not happen");
1422 protected override void CloneTo (CloneContext clonectx, Expression t)
1424 Cast target = (Cast) t;
1426 target.target_type = target_type.Clone (clonectx);
1427 target.expr = expr.Clone (clonectx);
1432 /// Binary operators
1434 public class Binary : Expression {
1435 public enum Operator : byte {
1436 Multiply, Division, Modulus,
1437 Addition, Subtraction,
1438 LeftShift, RightShift,
1439 LessThan, GreaterThan, LessThanOrEqual, GreaterThanOrEqual,
1440 Equality, Inequality,
1450 Expression left, right;
1452 // This must be kept in sync with Operator!!!
1453 public static readonly string [] oper_names;
1457 oper_names = new string [(int) Operator.TOP];
1459 oper_names [(int) Operator.Multiply] = "op_Multiply";
1460 oper_names [(int) Operator.Division] = "op_Division";
1461 oper_names [(int) Operator.Modulus] = "op_Modulus";
1462 oper_names [(int) Operator.Addition] = "op_Addition";
1463 oper_names [(int) Operator.Subtraction] = "op_Subtraction";
1464 oper_names [(int) Operator.LeftShift] = "op_LeftShift";
1465 oper_names [(int) Operator.RightShift] = "op_RightShift";
1466 oper_names [(int) Operator.LessThan] = "op_LessThan";
1467 oper_names [(int) Operator.GreaterThan] = "op_GreaterThan";
1468 oper_names [(int) Operator.LessThanOrEqual] = "op_LessThanOrEqual";
1469 oper_names [(int) Operator.GreaterThanOrEqual] = "op_GreaterThanOrEqual";
1470 oper_names [(int) Operator.Equality] = "op_Equality";
1471 oper_names [(int) Operator.Inequality] = "op_Inequality";
1472 oper_names [(int) Operator.BitwiseAnd] = "op_BitwiseAnd";
1473 oper_names [(int) Operator.BitwiseOr] = "op_BitwiseOr";
1474 oper_names [(int) Operator.ExclusiveOr] = "op_ExclusiveOr";
1475 oper_names [(int) Operator.LogicalOr] = "op_LogicalOr";
1476 oper_names [(int) Operator.LogicalAnd] = "op_LogicalAnd";
1479 public Binary (Operator oper, Expression left, Expression right)
1484 this.loc = left.Location;
1487 public Operator Oper {
1496 public Expression Left {
1505 public Expression Right {
1516 /// Returns a stringified representation of the Operator
1518 public static string OperName (Operator oper)
1521 case Operator.Multiply:
1523 case Operator.Division:
1525 case Operator.Modulus:
1527 case Operator.Addition:
1529 case Operator.Subtraction:
1531 case Operator.LeftShift:
1533 case Operator.RightShift:
1535 case Operator.LessThan:
1537 case Operator.GreaterThan:
1539 case Operator.LessThanOrEqual:
1541 case Operator.GreaterThanOrEqual:
1543 case Operator.Equality:
1545 case Operator.Inequality:
1547 case Operator.BitwiseAnd:
1549 case Operator.BitwiseOr:
1551 case Operator.ExclusiveOr:
1553 case Operator.LogicalOr:
1555 case Operator.LogicalAnd:
1559 return oper.ToString ();
1562 public override string ToString ()
1564 return "operator " + OperName (oper) + "(" + left.ToString () + ", " +
1565 right.ToString () + ")";
1568 Expression ForceConversion (EmitContext ec, Expression expr, Type target_type)
1570 if (expr.Type == target_type)
1573 return Convert.ImplicitConversion (ec, expr, target_type, loc);
1576 public static void Error_OperatorAmbiguous (Location loc, Operator oper, Type l, Type r)
1579 34, loc, "Operator `" + OperName (oper)
1580 + "' is ambiguous on operands of type `"
1581 + TypeManager.CSharpName (l) + "' "
1582 + "and `" + TypeManager.CSharpName (r)
1586 bool IsConvertible (EmitContext ec, Expression le, Expression re, Type t)
1588 return Convert.ImplicitConversionExists (ec, le, t) && Convert.ImplicitConversionExists (ec, re, t);
1591 bool VerifyApplicable_Predefined (EmitContext ec, Type t)
1593 if (!IsConvertible (ec, left, right, t))
1595 left = ForceConversion (ec, left, t);
1596 right = ForceConversion (ec, right, t);
1601 bool IsApplicable_String (EmitContext ec, Expression le, Expression re, Operator oper)
1603 bool l = Convert.ImplicitConversionExists (ec, le, TypeManager.string_type);
1604 bool r = Convert.ImplicitConversionExists (ec, re, TypeManager.string_type);
1606 if (oper == Operator.Equality || oper == Operator.Inequality)
1608 if (oper == Operator.Addition)
1613 bool OverloadResolve_PredefinedString (EmitContext ec, Operator oper)
1615 if (!IsApplicable_String (ec, left, right, oper))
1617 Type t = TypeManager.string_type;
1618 if (Convert.ImplicitConversionExists (ec, left, t))
1619 left = ForceConversion (ec, left, t);
1620 if (Convert.ImplicitConversionExists (ec, right, t))
1621 right = ForceConversion (ec, right, t);
1626 bool OverloadResolve_PredefinedIntegral (EmitContext ec)
1628 return VerifyApplicable_Predefined (ec, TypeManager.int32_type) ||
1629 VerifyApplicable_Predefined (ec, TypeManager.uint32_type) ||
1630 VerifyApplicable_Predefined (ec, TypeManager.int64_type) ||
1631 VerifyApplicable_Predefined (ec, TypeManager.uint64_type) ||
1635 bool OverloadResolve_PredefinedFloating (EmitContext ec)
1637 return VerifyApplicable_Predefined (ec, TypeManager.float_type) ||
1638 VerifyApplicable_Predefined (ec, TypeManager.double_type) ||
1642 static public void Error_OperatorCannotBeApplied (Location loc, string name, Type l, Type r)
1644 Error_OperatorCannotBeApplied (loc, name, TypeManager.CSharpName (l), TypeManager.CSharpName (r));
1647 public static void Error_OperatorCannotBeApplied (Location loc, string name, string left, string right)
1649 Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
1653 void Error_OperatorCannotBeApplied ()
1655 Error_OperatorCannotBeApplied (Location, OperName (oper), TypeManager.CSharpName (left.Type),
1656 TypeManager.CSharpName(right.Type));
1659 static bool is_unsigned (Type t)
1661 return (t == TypeManager.uint32_type || t == TypeManager.uint64_type ||
1662 t == TypeManager.short_type || t == TypeManager.byte_type);
1665 Expression Make32or64 (EmitContext ec, Expression e)
1669 if (t == TypeManager.int32_type || t == TypeManager.uint32_type ||
1670 t == TypeManager.int64_type || t == TypeManager.uint64_type)
1672 Expression ee = Convert.ImplicitConversion (ec, e, TypeManager.int32_type, loc);
1675 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint32_type, loc);
1678 ee = Convert.ImplicitConversion (ec, e, TypeManager.int64_type, loc);
1681 ee = Convert.ImplicitConversion (ec, e, TypeManager.uint64_type, loc);
1687 Expression CheckShiftArguments (EmitContext ec)
1689 Expression new_left = Make32or64 (ec, left);
1690 Expression new_right = ForceConversion (ec, right, TypeManager.int32_type);
1691 if (new_left == null || new_right == null) {
1692 Error_OperatorCannotBeApplied ();
1695 type = new_left.Type;
1696 int shiftmask = (type == TypeManager.int32_type || type == TypeManager.uint32_type) ? 31 : 63;
1698 right = new Binary (Binary.Operator.BitwiseAnd, new_right, new IntConstant (shiftmask, loc)).DoResolve (ec);
1703 // This is used to check if a test 'x == null' can be optimized to a reference equals,
1704 // i.e., not invoke op_Equality.
1706 static bool EqualsNullIsReferenceEquals (Type t)
1708 return t == TypeManager.object_type || t == TypeManager.string_type ||
1709 t == TypeManager.delegate_type || t.IsSubclassOf (TypeManager.delegate_type);
1712 static void Warning_UnintendedReferenceComparison (Location loc, string side, Type type)
1714 Report.Warning ((side == "left" ? 252 : 253), 2, loc,
1715 "Possible unintended reference comparison; to get a value comparison, " +
1716 "cast the {0} hand side to type `{1}'.", side, TypeManager.CSharpName (type));
1719 Expression ResolveOperator (EmitContext ec)
1722 Type r = right.Type;
1724 if (oper == Operator.Equality || oper == Operator.Inequality){
1725 if (TypeManager.IsGenericParameter (l) && (right is NullLiteral)) {
1726 if (l.BaseType == TypeManager.value_type) {
1727 Error_OperatorCannotBeApplied ();
1731 left = new BoxedCast (left, TypeManager.object_type);
1732 Type = TypeManager.bool_type;
1736 if (TypeManager.IsGenericParameter (r) && (left is NullLiteral)) {
1737 if (r.BaseType == TypeManager.value_type) {
1738 Error_OperatorCannotBeApplied ();
1742 right = new BoxedCast (right, TypeManager.object_type);
1743 Type = TypeManager.bool_type;
1748 // Optimize out call to op_Equality in a few cases.
1750 if ((l == TypeManager.null_type && EqualsNullIsReferenceEquals (r)) ||
1751 (r == TypeManager.null_type && EqualsNullIsReferenceEquals (l))) {
1752 Type = TypeManager.bool_type;
1757 if (l == TypeManager.intptr_type && r == TypeManager.intptr_type) {
1758 Type = TypeManager.bool_type;
1764 // Delegate equality
1766 MethodGroupExpr mg = null;
1767 Type delegate_type = null;
1768 if (left.eclass == ExprClass.MethodGroup) {
1769 if (!TypeManager.IsDelegateType(r)) {
1770 Error_OperatorCannotBeApplied(Location, OperName(oper),
1771 left.ExprClassName, right.ExprClassName);
1774 mg = (MethodGroupExpr)left;
1776 } else if (right.eclass == ExprClass.MethodGroup) {
1777 if (!TypeManager.IsDelegateType(l)) {
1778 Error_OperatorCannotBeApplied(Location, OperName(oper),
1779 left.ExprClassName, right.ExprClassName);
1782 mg = (MethodGroupExpr)right;
1787 Expression e = ImplicitDelegateCreation.Create (ec, mg, delegate_type, loc);
1791 // Find operator method
1792 string op = oper_names[(int)oper];
1793 MemberInfo[] mi = TypeManager.MemberLookup(ec.ContainerType, null,
1794 TypeManager.delegate_type, MemberTypes.Method, AllBindingFlags, op, null);
1796 ArrayList args = new ArrayList(2);
1797 args.Add(new Argument(e, Argument.AType.Expression));
1798 if (delegate_type == l)
1799 args.Insert(0, new Argument(left, Argument.AType.Expression));
1801 args.Add(new Argument(right, Argument.AType.Expression));
1803 return new BinaryMethod (TypeManager.bool_type, (MethodInfo)mi [0], args);
1806 if (l == TypeManager.anonymous_method_type || r == TypeManager.anonymous_method_type) {
1807 Error_OperatorCannotBeApplied(Location, OperName(oper),
1808 left.ExprClassName, right.ExprClassName);
1815 // Do not perform operator overload resolution when both sides are
1818 MethodGroupExpr left_operators = null, right_operators = null;
1819 if (!(TypeManager.IsPrimitiveType (l) && TypeManager.IsPrimitiveType (r))) {
1821 // Step 1: Perform Operator Overload location
1823 string op = oper_names [(int) oper];
1825 MethodGroupExpr union;
1826 left_operators = MemberLookup (ec.ContainerType, l, op, MemberTypes.Method, AllBindingFlags, loc) as MethodGroupExpr;
1828 right_operators = MemberLookup (
1829 ec.ContainerType, r, op, MemberTypes.Method, AllBindingFlags, loc) as MethodGroupExpr;
1830 union = MethodGroupExpr.MakeUnionSet (left_operators, right_operators, loc);
1832 union = left_operators;
1834 if (union != null) {
1835 ArrayList args = new ArrayList (2);
1836 args.Add (new Argument (left, Argument.AType.Expression));
1837 args.Add (new Argument (right, Argument.AType.Expression));
1839 union = union.OverloadResolve (ec, args, true, Location.Null);
1841 if (union != null) {
1842 MethodInfo mi = (MethodInfo) union;
1843 return new BinaryMethod (mi.ReturnType, mi, args);
1849 // Step 0: String concatenation (because overloading will get this wrong)
1851 if (oper == Operator.Addition){
1853 // If any of the arguments is a string, cast to string
1856 // Simple constant folding
1857 if (left is StringConstant && right is StringConstant)
1858 return new StringConstant (((StringConstant) left).Value + ((StringConstant) right).Value, left.Location);
1860 if (l == TypeManager.string_type || r == TypeManager.string_type) {
1862 if (r == TypeManager.void_type || l == TypeManager.void_type) {
1863 Error_OperatorCannotBeApplied ();
1867 // try to fold it in on the left
1868 if (left is StringConcat) {
1871 // We have to test here for not-null, since we can be doubly-resolved
1872 // take care of not appending twice
1875 type = TypeManager.string_type;
1876 ((StringConcat) left).Append (ec, right);
1877 return left.Resolve (ec);
1883 // Otherwise, start a new concat expression
1884 return new StringConcat (ec, loc, left, right).Resolve (ec);
1888 // Transform a + ( - b) into a - b
1890 if (right is Unary){
1891 Unary right_unary = (Unary) right;
1893 if (right_unary.Oper == Unary.Operator.UnaryNegation){
1894 oper = Operator.Subtraction;
1895 right = right_unary.Expr;
1901 if (oper == Operator.Equality || oper == Operator.Inequality){
1902 if (l == TypeManager.bool_type || r == TypeManager.bool_type){
1903 if (r != TypeManager.bool_type || l != TypeManager.bool_type){
1904 Error_OperatorCannotBeApplied ();
1908 type = TypeManager.bool_type;
1912 if (l.IsPointer || r.IsPointer) {
1913 if (l.IsPointer && r.IsPointer) {
1914 type = TypeManager.bool_type;
1918 if (l.IsPointer && r == TypeManager.null_type) {
1919 right = new EmptyCast (NullPointer.Null, l);
1920 type = TypeManager.bool_type;
1924 if (r.IsPointer && l == TypeManager.null_type) {
1925 left = new EmptyCast (NullPointer.Null, r);
1926 type = TypeManager.bool_type;
1932 if (l.IsGenericParameter && r.IsGenericParameter) {
1933 GenericConstraints l_gc, r_gc;
1935 l_gc = TypeManager.GetTypeParameterConstraints (l);
1936 r_gc = TypeManager.GetTypeParameterConstraints (r);
1938 if ((l_gc == null) || (r_gc == null) ||
1939 !(l_gc.HasReferenceTypeConstraint || l_gc.HasClassConstraint) ||
1940 !(r_gc.HasReferenceTypeConstraint || r_gc.HasClassConstraint)) {
1941 Error_OperatorCannotBeApplied ();
1949 // operator != (object a, object b)
1950 // operator == (object a, object b)
1952 // For this to be used, both arguments have to be reference-types.
1953 // Read the rationale on the spec (14.9.6)
1955 if (!(l.IsValueType || r.IsValueType)){
1956 type = TypeManager.bool_type;
1962 // Also, a standard conversion must exist from either one
1964 bool left_to_right =
1965 Convert.ImplicitStandardConversionExists (left, r);
1966 bool right_to_left = !left_to_right &&
1967 Convert.ImplicitStandardConversionExists (right, l);
1969 if (!left_to_right && !right_to_left) {
1970 Error_OperatorCannotBeApplied ();
1974 if (left_to_right && left_operators != null &&
1975 RootContext.WarningLevel >= 2) {
1976 ArrayList args = new ArrayList (2);
1977 args.Add (new Argument (left, Argument.AType.Expression));
1978 args.Add (new Argument (left, Argument.AType.Expression));
1979 if (left_operators.OverloadResolve (ec, args, true, Location.Null) != null)
1980 Warning_UnintendedReferenceComparison (loc, "right", l);
1983 if (right_to_left && right_operators != null &&
1984 RootContext.WarningLevel >= 2) {
1985 ArrayList args = new ArrayList (2);
1986 args.Add (new Argument (right, Argument.AType.Expression));
1987 args.Add (new Argument (right, Argument.AType.Expression));
1988 if (right_operators.OverloadResolve (ec, args, true, Location.Null) != null)
1989 Warning_UnintendedReferenceComparison (loc, "left", r);
1993 // We are going to have to convert to an object to compare
1995 if (l != TypeManager.object_type)
1996 left = new EmptyCast (left, TypeManager.object_type);
1997 if (r != TypeManager.object_type)
1998 right = new EmptyCast (right, TypeManager.object_type);
2004 // Only perform numeric promotions on:
2005 // +, -, *, /, %, &, |, ^, ==, !=, <, >, <=, >=
2007 if (oper == Operator.Addition || oper == Operator.Subtraction) {
2008 if (TypeManager.IsDelegateType (l)){
2009 if (((right.eclass == ExprClass.MethodGroup) ||
2010 (r == TypeManager.anonymous_method_type))){
2011 if ((RootContext.Version != LanguageVersion.ISO_1)){
2012 Expression tmp = Convert.ImplicitConversionRequired (ec, right, l, loc);
2020 if (TypeManager.IsDelegateType (r) || right is NullLiteral){
2022 ArrayList args = new ArrayList (2);
2024 args = new ArrayList (2);
2025 args.Add (new Argument (left, Argument.AType.Expression));
2026 args.Add (new Argument (right, Argument.AType.Expression));
2028 if (oper == Operator.Addition)
2029 method = TypeManager.delegate_combine_delegate_delegate;
2031 method = TypeManager.delegate_remove_delegate_delegate;
2033 if (!TypeManager.IsEqual (l, r) && !(right is NullLiteral)) {
2034 Error_OperatorCannotBeApplied ();
2038 return new BinaryDelegate (l, method, args);
2043 // Pointer arithmetic:
2045 // T* operator + (T* x, int y);
2046 // T* operator + (T* x, uint y);
2047 // T* operator + (T* x, long y);
2048 // T* operator + (T* x, ulong y);
2050 // T* operator + (int y, T* x);
2051 // T* operator + (uint y, T *x);
2052 // T* operator + (long y, T *x);
2053 // T* operator + (ulong y, T *x);
2055 // T* operator - (T* x, int y);
2056 // T* operator - (T* x, uint y);
2057 // T* operator - (T* x, long y);
2058 // T* operator - (T* x, ulong y);
2060 // long operator - (T* x, T *y)
2063 if (r.IsPointer && oper == Operator.Subtraction){
2065 return new PointerArithmetic (
2066 false, left, right, TypeManager.int64_type,
2069 Expression t = Make32or64 (ec, right);
2071 return new PointerArithmetic (oper == Operator.Addition, left, t, l, loc).Resolve (ec);
2073 } else if (r.IsPointer && oper == Operator.Addition){
2074 Expression t = Make32or64 (ec, left);
2076 return new PointerArithmetic (true, right, t, r, loc).Resolve (ec);
2081 // Enumeration operators
2083 bool lie = TypeManager.IsEnumType (l);
2084 bool rie = TypeManager.IsEnumType (r);
2088 // U operator - (E e, E f)
2090 if (oper == Operator.Subtraction){
2092 type = TypeManager.EnumToUnderlying (l);
2095 Error_OperatorCannotBeApplied ();
2101 // operator + (E e, U x)
2102 // operator - (E e, U x)
2104 if (oper == Operator.Addition || oper == Operator.Subtraction){
2105 Type enum_type = lie ? l : r;
2106 Type other_type = lie ? r : l;
2107 Type underlying_type = TypeManager.EnumToUnderlying (enum_type);
2109 if (underlying_type != other_type){
2110 temp = Convert.ImplicitConversion (ec, lie ? right : left, underlying_type, loc);
2120 Error_OperatorCannotBeApplied ();
2129 temp = Convert.ImplicitConversion (ec, right, l, loc);
2133 Error_OperatorCannotBeApplied ();
2137 temp = Convert.ImplicitConversion (ec, left, r, loc);
2142 Error_OperatorCannotBeApplied ();
2147 if (oper == Operator.Equality || oper == Operator.Inequality ||
2148 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2149 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2150 if (left.Type != right.Type){
2151 Error_OperatorCannotBeApplied ();
2154 type = TypeManager.bool_type;
2158 if (oper == Operator.BitwiseAnd ||
2159 oper == Operator.BitwiseOr ||
2160 oper == Operator.ExclusiveOr){
2161 if (left.Type != right.Type){
2162 Error_OperatorCannotBeApplied ();
2168 Error_OperatorCannotBeApplied ();
2172 if (oper == Operator.LeftShift || oper == Operator.RightShift)
2173 return CheckShiftArguments (ec);
2175 if (oper == Operator.LogicalOr || oper == Operator.LogicalAnd){
2176 if (l == TypeManager.bool_type && r == TypeManager.bool_type) {
2177 type = TypeManager.bool_type;
2181 Expression left_operators_e = l == TypeManager.bool_type ?
2182 left : Convert.ImplicitUserConversion (ec, left, TypeManager.bool_type, loc);
2183 Expression right_operators_e = r == TypeManager.bool_type ?
2184 right : Convert.ImplicitUserConversion (ec, right, TypeManager.bool_type, loc);
2186 if (left_operators_e != null && right_operators_e != null) {
2187 left = left_operators_e;
2188 right = right_operators_e;
2189 type = TypeManager.bool_type;
2193 Expression e = new ConditionalLogicalOperator (
2194 oper == Operator.LogicalAnd, left, right, l, loc);
2195 return e.Resolve (ec);
2198 Expression orig_left = left;
2199 Expression orig_right = right;
2202 // operator & (bool x, bool y)
2203 // operator | (bool x, bool y)
2204 // operator ^ (bool x, bool y)
2206 if (oper == Operator.BitwiseAnd ||
2207 oper == Operator.BitwiseOr ||
2208 oper == Operator.ExclusiveOr) {
2209 if (OverloadResolve_PredefinedIntegral (ec)) {
2210 if (IsConvertible (ec, orig_left, orig_right, TypeManager.bool_type)) {
2211 Error_OperatorAmbiguous (loc, oper, l, r);
2215 if (oper == Operator.BitwiseOr && l != r && !(orig_right is Constant) && right is OpcodeCast &&
2216 (r == TypeManager.sbyte_type || r == TypeManager.short_type ||
2217 r == TypeManager.int32_type || r == TypeManager.int64_type)) {
2218 Report.Warning (675, 3, loc, "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first",
2219 TypeManager.CSharpName (r));
2222 } else if (!VerifyApplicable_Predefined (ec, TypeManager.bool_type)) {
2223 Error_OperatorCannotBeApplied ();
2230 // Pointer comparison
2232 if (l.IsPointer && r.IsPointer){
2233 if (oper == Operator.LessThan || oper == Operator.LessThanOrEqual ||
2234 oper == Operator.GreaterThan || oper == Operator.GreaterThanOrEqual){
2235 type = TypeManager.bool_type;
2240 if (OverloadResolve_PredefinedIntegral (ec)) {
2241 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2242 Error_OperatorAmbiguous (loc, oper, l, r);
2245 } else if (OverloadResolve_PredefinedFloating (ec)) {
2246 if (IsConvertible (ec, orig_left, orig_right, TypeManager.decimal_type) ||
2247 IsApplicable_String (ec, orig_left, orig_right, oper)) {
2248 Error_OperatorAmbiguous (loc, oper, l, r);
2251 } else if (VerifyApplicable_Predefined (ec, TypeManager.decimal_type)) {
2252 if (IsApplicable_String (ec, orig_left, orig_right, oper)) {
2253 Error_OperatorAmbiguous (loc, oper, l, r);
2256 } else if (!OverloadResolve_PredefinedString (ec, oper)) {
2257 Error_OperatorCannotBeApplied ();
2261 if (oper == Operator.Equality ||
2262 oper == Operator.Inequality ||
2263 oper == Operator.LessThanOrEqual ||
2264 oper == Operator.LessThan ||
2265 oper == Operator.GreaterThanOrEqual ||
2266 oper == Operator.GreaterThan)
2267 type = TypeManager.bool_type;
2272 if (l == TypeManager.decimal_type || l == TypeManager.string_type || r == TypeManager.string_type) {
2274 if (r == TypeManager.string_type)
2276 MethodGroupExpr ops = (MethodGroupExpr) MemberLookup (
2277 ec.ContainerType, lookup, oper_names [(int) oper],
2278 MemberTypes.Method, AllBindingFlags, loc);
2279 ArrayList args = new ArrayList (2);
2280 args.Add (new Argument (left, Argument.AType.Expression));
2281 args.Add (new Argument (right, Argument.AType.Expression));
2282 ops = ops.OverloadResolve (ec, args, true, Location.Null);
2283 return new BinaryMethod (type, (MethodInfo)ops, args);
2289 Constant EnumLiftUp (Constant left, Constant right)
2292 case Operator.BitwiseOr:
2293 case Operator.BitwiseAnd:
2294 case Operator.ExclusiveOr:
2295 case Operator.Equality:
2296 case Operator.Inequality:
2297 case Operator.LessThan:
2298 case Operator.LessThanOrEqual:
2299 case Operator.GreaterThan:
2300 case Operator.GreaterThanOrEqual:
2301 if (left is EnumConstant)
2304 if (left.IsZeroInteger)
2305 return new EnumConstant (left, right.Type);
2309 case Operator.Addition:
2310 case Operator.Subtraction:
2313 case Operator.Multiply:
2314 case Operator.Division:
2315 case Operator.Modulus:
2316 case Operator.LeftShift:
2317 case Operator.RightShift:
2318 if (right is EnumConstant || left is EnumConstant)
2322 Error_OperatorCannotBeApplied (loc, Binary.OperName (oper), left.Type, right.Type);
2326 public override Expression DoResolve (EmitContext ec)
2331 if ((oper == Operator.Subtraction) && (left is ParenthesizedExpression)) {
2332 left = ((ParenthesizedExpression) left).Expr;
2333 left = left.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.Type);
2337 if (left.eclass == ExprClass.Type) {
2338 Report.Error (75, loc, "To cast a negative value, you must enclose the value in parentheses");
2342 left = left.Resolve (ec);
2347 Constant lc = left as Constant;
2348 if (lc != null && lc.Type == TypeManager.bool_type &&
2349 ((oper == Operator.LogicalAnd && (bool)lc.GetValue () == false) ||
2350 (oper == Operator.LogicalOr && (bool)lc.GetValue () == true))) {
2352 // TODO: make a sense to resolve unreachable expression as we do for statement
2353 Report.Warning (429, 4, loc, "Unreachable expression code detected");
2357 right = right.Resolve (ec);
2361 eclass = ExprClass.Value;
2362 Constant rc = right as Constant;
2364 // The conversion rules are ignored in enum context but why
2365 if (!ec.InEnumContext && lc != null && rc != null && (TypeManager.IsEnumType (left.Type) || TypeManager.IsEnumType (right.Type))) {
2366 left = lc = EnumLiftUp (lc, rc);
2370 right = rc = EnumLiftUp (rc, lc);
2375 if (oper == Operator.BitwiseAnd) {
2376 if (rc != null && rc.IsZeroInteger) {
2377 return lc is EnumConstant ?
2378 new EnumConstant (rc, lc.Type):
2382 if (lc != null && lc.IsZeroInteger) {
2383 return rc is EnumConstant ?
2384 new EnumConstant (lc, rc.Type):
2388 else if (oper == Operator.BitwiseOr) {
2389 if (lc is EnumConstant &&
2390 rc != null && rc.IsZeroInteger)
2392 if (rc is EnumConstant &&
2393 lc != null && lc.IsZeroInteger)
2395 } else if (oper == Operator.LogicalAnd) {
2396 if (rc != null && rc.IsDefaultValue && rc.Type == TypeManager.bool_type)
2398 if (lc != null && lc.IsDefaultValue && lc.Type == TypeManager.bool_type)
2402 if (rc != null && lc != null){
2403 int prev_e = Report.Errors;
2404 Expression e = ConstantFold.BinaryFold (
2405 ec, oper, lc, rc, loc);
2406 if (e != null || Report.Errors != prev_e)
2411 if ((left is NullLiteral || left.Type.IsValueType) &&
2412 (right is NullLiteral || right.Type.IsValueType) &&
2413 !(left is NullLiteral && right is NullLiteral) &&
2414 (TypeManager.IsNullableType (left.Type) || TypeManager.IsNullableType (right.Type)))
2415 return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
2418 // Comparison warnings
2419 if (oper == Operator.Equality || oper == Operator.Inequality ||
2420 oper == Operator.LessThanOrEqual || oper == Operator.LessThan ||
2421 oper == Operator.GreaterThanOrEqual || oper == Operator.GreaterThan){
2422 if (left.Equals (right)) {
2423 Report.Warning (1718, 3, loc, "A comparison made to same variable. Did you mean to compare something else?");
2425 CheckUselessComparison (lc, right.Type);
2426 CheckUselessComparison (rc, left.Type);
2429 return ResolveOperator (ec);
2432 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
2437 private void CheckUselessComparison (Constant c, Type type)
2439 if (c == null || !IsTypeIntegral (type)
2440 || c is StringConstant
2441 || c is BoolConstant
2442 || c is FloatConstant
2443 || c is DoubleConstant
2444 || c is DecimalConstant
2450 if (c is ULongConstant) {
2451 ulong uvalue = ((ULongConstant) c).Value;
2452 if (uvalue > long.MaxValue) {
2453 if (type == TypeManager.byte_type ||
2454 type == TypeManager.sbyte_type ||
2455 type == TypeManager.short_type ||
2456 type == TypeManager.ushort_type ||
2457 type == TypeManager.int32_type ||
2458 type == TypeManager.uint32_type ||
2459 type == TypeManager.int64_type ||
2460 type == TypeManager.char_type)
2461 WarnUselessComparison (type);
2464 value = (long) uvalue;
2466 else if (c is ByteConstant)
2467 value = ((ByteConstant) c).Value;
2468 else if (c is SByteConstant)
2469 value = ((SByteConstant) c).Value;
2470 else if (c is ShortConstant)
2471 value = ((ShortConstant) c).Value;
2472 else if (c is UShortConstant)
2473 value = ((UShortConstant) c).Value;
2474 else if (c is IntConstant)
2475 value = ((IntConstant) c).Value;
2476 else if (c is UIntConstant)
2477 value = ((UIntConstant) c).Value;
2478 else if (c is LongConstant)
2479 value = ((LongConstant) c).Value;
2480 else if (c is CharConstant)
2481 value = ((CharConstant)c).Value;
2486 if (IsValueOutOfRange (value, type))
2487 WarnUselessComparison (type);
2490 private bool IsValueOutOfRange (long value, Type type)
2492 if (IsTypeUnsigned (type) && value < 0)
2494 return type == TypeManager.sbyte_type && (value >= 0x80 || value < -0x80) ||
2495 type == TypeManager.byte_type && value >= 0x100 ||
2496 type == TypeManager.short_type && (value >= 0x8000 || value < -0x8000) ||
2497 type == TypeManager.ushort_type && value >= 0x10000 ||
2498 type == TypeManager.int32_type && (value >= 0x80000000 || value < -0x80000000) ||
2499 type == TypeManager.uint32_type && value >= 0x100000000;
2502 private static bool IsTypeIntegral (Type type)
2504 return type == TypeManager.uint64_type ||
2505 type == TypeManager.int64_type ||
2506 type == TypeManager.uint32_type ||
2507 type == TypeManager.int32_type ||
2508 type == TypeManager.ushort_type ||
2509 type == TypeManager.short_type ||
2510 type == TypeManager.sbyte_type ||
2511 type == TypeManager.byte_type ||
2512 type == TypeManager.char_type;
2515 private static bool IsTypeUnsigned (Type type)
2517 return type == TypeManager.uint64_type ||
2518 type == TypeManager.uint32_type ||
2519 type == TypeManager.ushort_type ||
2520 type == TypeManager.byte_type ||
2521 type == TypeManager.char_type;
2524 private void WarnUselessComparison (Type type)
2526 Report.Warning (652, 2, loc, "A comparison between a constant and a variable is useless. The constant is out of the range of the variable type `{0}'",
2527 TypeManager.CSharpName (type));
2531 /// EmitBranchable is called from Statement.EmitBoolExpression in the
2532 /// context of a conditional bool expression. This function will return
2533 /// false if it is was possible to use EmitBranchable, or true if it was.
2535 /// The expression's code is generated, and we will generate a branch to `target'
2536 /// if the resulting expression value is equal to isTrue
2538 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
2540 ILGenerator ig = ec.ig;
2543 // This is more complicated than it looks, but its just to avoid
2544 // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
2545 // but on top of that we want for == and != to use a special path
2546 // if we are comparing against null
2548 if ((oper == Operator.Equality || oper == Operator.Inequality) && (left is Constant || right is Constant)) {
2549 bool my_on_true = oper == Operator.Inequality ? onTrue : !onTrue;
2552 // put the constant on the rhs, for simplicity
2554 if (left is Constant) {
2555 Expression swap = right;
2560 if (((Constant) right).IsZeroInteger) {
2563 ig.Emit (OpCodes.Brtrue, target);
2565 ig.Emit (OpCodes.Brfalse, target);
2568 } else if (right is BoolConstant) {
2570 if (my_on_true != ((BoolConstant) right).Value)
2571 ig.Emit (OpCodes.Brtrue, target);
2573 ig.Emit (OpCodes.Brfalse, target);
2578 } else if (oper == Operator.LogicalAnd) {
2581 Label tests_end = ig.DefineLabel ();
2583 left.EmitBranchable (ec, tests_end, false);
2584 right.EmitBranchable (ec, target, true);
2585 ig.MarkLabel (tests_end);
2588 // This optimizes code like this
2589 // if (true && i > 4)
2591 if (!(left is Constant))
2592 left.EmitBranchable (ec, target, false);
2594 if (!(right is Constant))
2595 right.EmitBranchable (ec, target, false);
2600 } else if (oper == Operator.LogicalOr){
2602 left.EmitBranchable (ec, target, true);
2603 right.EmitBranchable (ec, target, true);
2606 Label tests_end = ig.DefineLabel ();
2607 left.EmitBranchable (ec, tests_end, true);
2608 right.EmitBranchable (ec, target, false);
2609 ig.MarkLabel (tests_end);
2614 } else if (!(oper == Operator.LessThan || oper == Operator.GreaterThan ||
2615 oper == Operator.LessThanOrEqual || oper == Operator.GreaterThanOrEqual ||
2616 oper == Operator.Equality || oper == Operator.Inequality)) {
2617 base.EmitBranchable (ec, target, onTrue);
2625 bool isUnsigned = is_unsigned (t) || t == TypeManager.double_type || t == TypeManager.float_type;
2628 case Operator.Equality:
2630 ig.Emit (OpCodes.Beq, target);
2632 ig.Emit (OpCodes.Bne_Un, target);
2635 case Operator.Inequality:
2637 ig.Emit (OpCodes.Bne_Un, target);
2639 ig.Emit (OpCodes.Beq, target);
2642 case Operator.LessThan:
2645 ig.Emit (OpCodes.Blt_Un, target);
2647 ig.Emit (OpCodes.Blt, target);
2650 ig.Emit (OpCodes.Bge_Un, target);
2652 ig.Emit (OpCodes.Bge, target);
2655 case Operator.GreaterThan:
2658 ig.Emit (OpCodes.Bgt_Un, target);
2660 ig.Emit (OpCodes.Bgt, target);
2663 ig.Emit (OpCodes.Ble_Un, target);
2665 ig.Emit (OpCodes.Ble, target);
2668 case Operator.LessThanOrEqual:
2671 ig.Emit (OpCodes.Ble_Un, target);
2673 ig.Emit (OpCodes.Ble, target);
2676 ig.Emit (OpCodes.Bgt_Un, target);
2678 ig.Emit (OpCodes.Bgt, target);
2682 case Operator.GreaterThanOrEqual:
2685 ig.Emit (OpCodes.Bge_Un, target);
2687 ig.Emit (OpCodes.Bge, target);
2690 ig.Emit (OpCodes.Blt_Un, target);
2692 ig.Emit (OpCodes.Blt, target);
2695 Console.WriteLine (oper);
2696 throw new Exception ("what is THAT");
2700 public override void Emit (EmitContext ec)
2702 ILGenerator ig = ec.ig;
2707 // Handle short-circuit operators differently
2710 if (oper == Operator.LogicalAnd) {
2711 Label load_zero = ig.DefineLabel ();
2712 Label end = ig.DefineLabel ();
2714 left.EmitBranchable (ec, load_zero, false);
2716 ig.Emit (OpCodes.Br, end);
2718 ig.MarkLabel (load_zero);
2719 ig.Emit (OpCodes.Ldc_I4_0);
2722 } else if (oper == Operator.LogicalOr) {
2723 Label load_one = ig.DefineLabel ();
2724 Label end = ig.DefineLabel ();
2726 left.EmitBranchable (ec, load_one, true);
2728 ig.Emit (OpCodes.Br, end);
2730 ig.MarkLabel (load_one);
2731 ig.Emit (OpCodes.Ldc_I4_1);
2739 bool isUnsigned = is_unsigned (left.Type);
2742 case Operator.Multiply:
2744 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2745 opcode = OpCodes.Mul_Ovf;
2746 else if (isUnsigned)
2747 opcode = OpCodes.Mul_Ovf_Un;
2749 opcode = OpCodes.Mul;
2751 opcode = OpCodes.Mul;
2755 case Operator.Division:
2757 opcode = OpCodes.Div_Un;
2759 opcode = OpCodes.Div;
2762 case Operator.Modulus:
2764 opcode = OpCodes.Rem_Un;
2766 opcode = OpCodes.Rem;
2769 case Operator.Addition:
2771 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2772 opcode = OpCodes.Add_Ovf;
2773 else if (isUnsigned)
2774 opcode = OpCodes.Add_Ovf_Un;
2776 opcode = OpCodes.Add;
2778 opcode = OpCodes.Add;
2781 case Operator.Subtraction:
2783 if (l == TypeManager.int32_type || l == TypeManager.int64_type)
2784 opcode = OpCodes.Sub_Ovf;
2785 else if (isUnsigned)
2786 opcode = OpCodes.Sub_Ovf_Un;
2788 opcode = OpCodes.Sub;
2790 opcode = OpCodes.Sub;
2793 case Operator.RightShift:
2795 opcode = OpCodes.Shr_Un;
2797 opcode = OpCodes.Shr;
2800 case Operator.LeftShift:
2801 opcode = OpCodes.Shl;
2804 case Operator.Equality:
2805 opcode = OpCodes.Ceq;
2808 case Operator.Inequality:
2809 ig.Emit (OpCodes.Ceq);
2810 ig.Emit (OpCodes.Ldc_I4_0);
2812 opcode = OpCodes.Ceq;
2815 case Operator.LessThan:
2817 opcode = OpCodes.Clt_Un;
2819 opcode = OpCodes.Clt;
2822 case Operator.GreaterThan:
2824 opcode = OpCodes.Cgt_Un;
2826 opcode = OpCodes.Cgt;
2829 case Operator.LessThanOrEqual:
2830 Type lt = left.Type;
2832 if (isUnsigned || (lt == TypeManager.double_type || lt == TypeManager.float_type))
2833 ig.Emit (OpCodes.Cgt_Un);
2835 ig.Emit (OpCodes.Cgt);
2836 ig.Emit (OpCodes.Ldc_I4_0);
2838 opcode = OpCodes.Ceq;
2841 case Operator.GreaterThanOrEqual:
2842 Type le = left.Type;
2844 if (isUnsigned || (le == TypeManager.double_type || le == TypeManager.float_type))
2845 ig.Emit (OpCodes.Clt_Un);
2847 ig.Emit (OpCodes.Clt);
2849 ig.Emit (OpCodes.Ldc_I4_0);
2851 opcode = OpCodes.Ceq;
2854 case Operator.BitwiseOr:
2855 opcode = OpCodes.Or;
2858 case Operator.BitwiseAnd:
2859 opcode = OpCodes.And;
2862 case Operator.ExclusiveOr:
2863 opcode = OpCodes.Xor;
2867 throw new Exception ("This should not happen: Operator = "
2868 + oper.ToString ());
2874 protected override void CloneTo (CloneContext clonectx, Expression t)
2876 Binary target = (Binary) t;
2878 target.left = left.Clone (clonectx);
2879 target.right = right.Clone (clonectx);
2884 // Object created by Binary when the binary operator uses an method instead of being
2885 // a binary operation that maps to a CIL binary operation.
2887 public class BinaryMethod : Expression {
2888 public MethodBase method;
2889 public ArrayList Arguments;
2891 public BinaryMethod (Type t, MethodBase m, ArrayList args)
2896 eclass = ExprClass.Value;
2899 public override Expression DoResolve (EmitContext ec)
2904 public override void Emit (EmitContext ec)
2906 ILGenerator ig = ec.ig;
2908 if (Arguments != null)
2909 Invocation.EmitArguments (ec, method, Arguments, false, null);
2911 if (method is MethodInfo)
2912 ig.Emit (OpCodes.Call, (MethodInfo) method);
2914 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
2919 // Represents the operation a + b [+ c [+ d [+ ...]]], where a is a string
2920 // b, c, d... may be strings or objects.
2922 public class StringConcat : Expression {
2924 bool invalid = false;
2925 bool emit_conv_done = false;
2927 // Are we also concating objects?
2929 bool is_strings_only = true;
2931 public StringConcat (EmitContext ec, Location loc, Expression left, Expression right)
2934 type = TypeManager.string_type;
2935 eclass = ExprClass.Value;
2937 operands = new ArrayList (2);
2942 public override Expression DoResolve (EmitContext ec)
2950 public void Append (EmitContext ec, Expression operand)
2955 StringConstant sc = operand as StringConstant;
2957 // TODO: it will be better to do this silently as an optimalization
2959 // string s = "" + i;
2960 // because this code has poor performace
2961 // if (sc.Value.Length == 0)
2962 // Report.Warning (-300, 3, Location, "Appending an empty string has no effect. Did you intend to append a space string?");
2964 if (operands.Count != 0) {
2965 StringConstant last_operand = operands [operands.Count - 1] as StringConstant;
2966 if (last_operand != null) {
2967 operands [operands.Count - 1] = new StringConstant (last_operand.Value + ((StringConstant) operand).Value, last_operand.Location);
2974 // Conversion to object
2976 if (operand.Type != TypeManager.string_type) {
2977 Expression no = Convert.ImplicitConversion (ec, operand, TypeManager.object_type, loc);
2980 Binary.Error_OperatorCannotBeApplied (loc, "+", TypeManager.string_type, operand.Type);
2986 operands.Add (operand);
2989 public override void Emit (EmitContext ec)
2991 MethodInfo concat_method = null;
2994 // Do conversion to arguments; check for strings only
2997 // This can get called multiple times, so we have to deal with that.
2998 if (!emit_conv_done) {
2999 emit_conv_done = true;
3000 for (int i = 0; i < operands.Count; i ++) {
3001 Expression e = (Expression) operands [i];
3002 is_strings_only &= e.Type == TypeManager.string_type;
3005 for (int i = 0; i < operands.Count; i ++) {
3006 Expression e = (Expression) operands [i];
3008 if (! is_strings_only && e.Type == TypeManager.string_type) {
3009 // need to make sure this is an object, because the EmitParams
3010 // method might look at the type of this expression, see it is a
3011 // string and emit a string [] when we want an object [];
3013 e = new EmptyCast (e, TypeManager.object_type);
3015 operands [i] = new Argument (e, Argument.AType.Expression);
3020 // Find the right method
3022 switch (operands.Count) {
3025 // This should not be possible, because simple constant folding
3026 // is taken care of in the Binary code.
3028 throw new Exception ("how did you get here?");
3031 concat_method = is_strings_only ?
3032 TypeManager.string_concat_string_string :
3033 TypeManager.string_concat_object_object ;
3036 concat_method = is_strings_only ?
3037 TypeManager.string_concat_string_string_string :
3038 TypeManager.string_concat_object_object_object ;
3042 // There is not a 4 param overlaod for object (the one that there is
3043 // is actually a varargs methods, and is only in corlib because it was
3044 // introduced there before.).
3046 if (!is_strings_only)
3049 concat_method = TypeManager.string_concat_string_string_string_string;
3052 concat_method = is_strings_only ?
3053 TypeManager.string_concat_string_dot_dot_dot :
3054 TypeManager.string_concat_object_dot_dot_dot ;
3058 Invocation.EmitArguments (ec, concat_method, operands, false, null);
3059 ec.ig.Emit (OpCodes.Call, concat_method);
3064 // Object created with +/= on delegates
3066 public class BinaryDelegate : Expression {
3070 public BinaryDelegate (Type t, MethodInfo mi, ArrayList args)
3075 eclass = ExprClass.Value;
3078 public override Expression DoResolve (EmitContext ec)
3083 public override void Emit (EmitContext ec)
3085 ILGenerator ig = ec.ig;
3087 Invocation.EmitArguments (ec, method, args, false, null);
3089 ig.Emit (OpCodes.Call, (MethodInfo) method);
3090 ig.Emit (OpCodes.Castclass, type);
3093 public Expression Right {
3095 Argument arg = (Argument) args [1];
3100 public bool IsAddition {
3102 return method == TypeManager.delegate_combine_delegate_delegate;
3108 // User-defined conditional logical operator
3109 public class ConditionalLogicalOperator : Expression {
3110 Expression left, right;
3113 public ConditionalLogicalOperator (bool is_and, Expression left, Expression right, Type t, Location loc)
3116 eclass = ExprClass.Value;
3120 this.is_and = is_and;
3123 protected void Error19 ()
3125 Binary.Error_OperatorCannotBeApplied (loc, is_and ? "&&" : "||", left.GetSignatureForError (), right.GetSignatureForError ());
3128 protected void Error218 ()
3130 Error (218, "The type ('" + TypeManager.CSharpName (type) + "') must contain " +
3131 "declarations of operator true and operator false");
3134 Expression op_true, op_false, op;
3135 LocalTemporary left_temp;
3137 public override Expression DoResolve (EmitContext ec)
3139 MethodGroupExpr operator_group;
3141 operator_group = MethodLookup (ec.ContainerType, type, is_and ? "op_BitwiseAnd" : "op_BitwiseOr", loc) as MethodGroupExpr;
3142 if (operator_group == null) {
3147 left_temp = new LocalTemporary (type);
3149 ArrayList arguments = new ArrayList (2);
3150 arguments.Add (new Argument (left_temp, Argument.AType.Expression));
3151 arguments.Add (new Argument (right, Argument.AType.Expression));
3152 operator_group = operator_group.OverloadResolve (ec, arguments, false, loc);
3153 if (operator_group == null) {
3158 MethodInfo method = (MethodInfo)operator_group;
3159 if (method.ReturnType != type) {
3160 Report.Error (217, loc, "In order to be applicable as a short circuit operator a user-defined logical operator `{0}' " +
3161 "must have the same return type as the type of its 2 parameters", TypeManager.CSharpSignature (method));
3165 op = new StaticCallExpr (method, arguments, loc);
3167 op_true = GetOperatorTrue (ec, left_temp, loc);
3168 op_false = GetOperatorFalse (ec, left_temp, loc);
3169 if ((op_true == null) || (op_false == null)) {
3177 public override void Emit (EmitContext ec)
3179 ILGenerator ig = ec.ig;
3180 Label false_target = ig.DefineLabel ();
3181 Label end_target = ig.DefineLabel ();
3184 left_temp.Store (ec);
3186 (is_and ? op_false : op_true).EmitBranchable (ec, false_target, false);
3187 left_temp.Emit (ec);
3188 ig.Emit (OpCodes.Br, end_target);
3189 ig.MarkLabel (false_target);
3191 ig.MarkLabel (end_target);
3193 // We release 'left_temp' here since 'op' may refer to it too
3194 left_temp.Release (ec);
3198 public class PointerArithmetic : Expression {
3199 Expression left, right;
3203 // We assume that `l' is always a pointer
3205 public PointerArithmetic (bool is_addition, Expression l, Expression r, Type t, Location loc)
3211 is_add = is_addition;
3214 public override Expression DoResolve (EmitContext ec)
3216 eclass = ExprClass.Variable;
3218 if (left.Type == TypeManager.void_ptr_type) {
3219 Error (242, "The operation in question is undefined on void pointers");
3226 public override void Emit (EmitContext ec)
3228 Type op_type = left.Type;
3229 ILGenerator ig = ec.ig;
3231 // It must be either array or fixed buffer
3232 Type element = TypeManager.HasElementType (op_type) ?
3233 element = TypeManager.GetElementType (op_type) :
3234 element = AttributeTester.GetFixedBuffer (((FieldExpr)left).FieldInfo).ElementType;
3236 int size = GetTypeSize (element);
3237 Type rtype = right.Type;
3239 if (rtype.IsPointer){
3241 // handle (pointer - pointer)
3245 ig.Emit (OpCodes.Sub);
3249 ig.Emit (OpCodes.Sizeof, element);
3251 IntLiteral.EmitInt (ig, size);
3252 ig.Emit (OpCodes.Div);
3254 ig.Emit (OpCodes.Conv_I8);
3257 // handle + and - on (pointer op int)
3260 ig.Emit (OpCodes.Conv_I);
3262 Constant right_const = right as Constant;
3263 if (right_const != null && size != 0) {
3264 Expression ex = ConstantFold.BinaryFold (ec, Binary.Operator.Multiply, new IntConstant (size, right.Location), right_const, loc);
3272 ig.Emit (OpCodes.Sizeof, element);
3274 IntLiteral.EmitInt (ig, size);
3275 if (rtype == TypeManager.int64_type)
3276 ig.Emit (OpCodes.Conv_I8);
3277 else if (rtype == TypeManager.uint64_type)
3278 ig.Emit (OpCodes.Conv_U8);
3279 ig.Emit (OpCodes.Mul);
3283 if (rtype == TypeManager.int64_type || rtype == TypeManager.uint64_type)
3284 ig.Emit (OpCodes.Conv_I);
3287 ig.Emit (OpCodes.Add);
3289 ig.Emit (OpCodes.Sub);
3295 /// Implements the ternary conditional operator (?:)
3297 public class Conditional : Expression {
3298 Expression expr, trueExpr, falseExpr;
3300 public Conditional (Expression expr, Expression trueExpr, Expression falseExpr)
3303 this.trueExpr = trueExpr;
3304 this.falseExpr = falseExpr;
3305 this.loc = expr.Location;
3308 public Expression Expr {
3314 public Expression TrueExpr {
3320 public Expression FalseExpr {
3326 public override Expression DoResolve (EmitContext ec)
3328 expr = expr.Resolve (ec);
3334 if (TypeManager.IsNullableValueType (expr.Type))
3335 return new Nullable.LiftedConditional (expr, trueExpr, falseExpr, loc).Resolve (ec);
3338 if (expr.Type != TypeManager.bool_type){
3339 expr = Expression.ResolveBoolean (
3346 Assign ass = expr as Assign;
3347 if (ass != null && ass.Source is Constant) {
3348 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
3351 trueExpr = trueExpr.Resolve (ec);
3352 falseExpr = falseExpr.Resolve (ec);
3354 if (trueExpr == null || falseExpr == null)
3357 eclass = ExprClass.Value;
3358 if (trueExpr.Type == falseExpr.Type) {
3359 type = trueExpr.Type;
3360 if (type == TypeManager.null_type) {
3361 // TODO: probably will have to implement ConditionalConstant
3362 // to call method without return constant as well
3363 Report.Warning (-101, 1, loc, "Conditional expression will always return same value");
3368 Type true_type = trueExpr.Type;
3369 Type false_type = falseExpr.Type;
3372 // First, if an implicit conversion exists from trueExpr
3373 // to falseExpr, then the result type is of type falseExpr.Type
3375 conv = Convert.ImplicitConversion (ec, trueExpr, false_type, loc);
3378 // Check if both can convert implicitl to each other's type
3380 if (Convert.ImplicitConversion (ec, falseExpr, true_type, loc) != null){
3382 "Can not compute type of conditional expression " +
3383 "as `" + TypeManager.CSharpName (trueExpr.Type) +
3384 "' and `" + TypeManager.CSharpName (falseExpr.Type) +
3385 "' convert implicitly to each other");
3390 } else if ((conv = Convert.ImplicitConversion(ec, falseExpr, true_type,loc))!= null){
3394 Report.Error (173, loc, "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
3395 trueExpr.GetSignatureForError (), falseExpr.GetSignatureForError ());
3400 // Dead code optimalization
3401 if (expr is BoolConstant){
3402 BoolConstant bc = (BoolConstant) expr;
3404 Report.Warning (429, 4, bc.Value ? falseExpr.Location : trueExpr.Location, "Unreachable expression code detected");
3405 return bc.Value ? trueExpr : falseExpr;
3411 public override TypeExpr ResolveAsTypeTerminal (IResolveContext ec, bool silent)
3416 public override void Emit (EmitContext ec)
3418 ILGenerator ig = ec.ig;
3419 Label false_target = ig.DefineLabel ();
3420 Label end_target = ig.DefineLabel ();
3422 expr.EmitBranchable (ec, false_target, false);
3424 ig.Emit (OpCodes.Br, end_target);
3425 ig.MarkLabel (false_target);
3426 falseExpr.Emit (ec);
3427 ig.MarkLabel (end_target);
3430 protected override void CloneTo (CloneContext clonectx, Expression t)
3432 Conditional target = (Conditional) t;
3434 target.expr = expr.Clone (clonectx);
3435 target.trueExpr = trueExpr.Clone (clonectx);
3436 target.falseExpr = falseExpr.Clone (clonectx);
3440 public abstract class VariableReference : Expression, IAssignMethod, IMemoryLocation {
3442 LocalTemporary temp;
3444 public abstract Variable Variable {
3448 public abstract bool IsRef {
3452 public override void Emit (EmitContext ec)
3458 // This method is used by parameters that are references, that are
3459 // being passed as references: we only want to pass the pointer (that
3460 // is already stored in the parameter, not the address of the pointer,
3461 // and not the value of the variable).
3463 public void EmitLoad (EmitContext ec)
3465 Report.Debug (64, "VARIABLE EMIT LOAD", this, Variable, type, loc);
3467 Variable.EmitInstance (ec);
3471 public void Emit (EmitContext ec, bool leave_copy)
3473 Report.Debug (64, "VARIABLE EMIT", this, Variable, type, IsRef, loc);
3479 ec.ig.Emit (OpCodes.Dup);
3482 // If we are a reference, we loaded on the stack a pointer
3483 // Now lets load the real value
3485 LoadFromPtr (ec.ig, type);
3489 ec.ig.Emit (OpCodes.Dup);
3491 if (IsRef || Variable.NeedsTemporary) {
3492 temp = new LocalTemporary (Type);
3498 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy,
3499 bool prepare_for_load)
3501 Report.Debug (64, "VARIABLE EMIT ASSIGN", this, Variable, type, IsRef,
3504 ILGenerator ig = ec.ig;
3505 prepared = prepare_for_load;
3507 Variable.EmitInstance (ec);
3508 if (prepare_for_load && Variable.HasInstance)
3509 ig.Emit (OpCodes.Dup);
3510 else if (IsRef && !prepared)
3516 ig.Emit (OpCodes.Dup);
3517 if (IsRef || Variable.NeedsTemporary) {
3518 temp = new LocalTemporary (Type);
3524 StoreFromPtr (ig, type);
3526 Variable.EmitAssign (ec);
3534 public void AddressOf (EmitContext ec, AddressOp mode)
3536 Variable.EmitInstance (ec);
3537 Variable.EmitAddressOf (ec);
3544 public class LocalVariableReference : VariableReference, IVariable {
3545 public readonly string Name;
3547 public LocalInfo local_info;
3551 public LocalVariableReference (Block block, string name, Location l)
3556 eclass = ExprClass.Variable;
3560 // Setting `is_readonly' to false will allow you to create a writable
3561 // reference to a read-only variable. This is used by foreach and using.
3563 public LocalVariableReference (Block block, string name, Location l,
3564 LocalInfo local_info, bool is_readonly)
3565 : this (block, name, l)
3567 this.local_info = local_info;
3568 this.is_readonly = is_readonly;
3571 public VariableInfo VariableInfo {
3572 get { return local_info.VariableInfo; }
3575 public override bool IsRef {
3576 get { return false; }
3579 public bool IsReadOnly {
3580 get { return is_readonly; }
3583 public bool VerifyAssigned (EmitContext ec)
3585 VariableInfo variable_info = local_info.VariableInfo;
3586 return variable_info == null || variable_info.IsAssigned (ec, loc);
3589 void ResolveLocalInfo ()
3591 if (local_info == null) {
3592 local_info = Block.GetLocalInfo (Name);
3593 type = local_info.VariableType;
3594 is_readonly = local_info.ReadOnly;
3598 protected Expression DoResolveBase (EmitContext ec)
3600 type = local_info.VariableType;
3602 Expression e = Block.GetConstantExpression (Name);
3604 return e.Resolve (ec);
3606 if (!VerifyAssigned (ec))
3610 // If we are referencing a variable from the external block
3611 // flag it for capturing
3613 if (ec.MustCaptureVariable (local_info)) {
3614 if (local_info.AddressTaken){
3615 AnonymousMethod.Error_AddressOfCapturedVar (local_info.Name, loc);
3619 ScopeInfo scope = local_info.Block.CreateScopeInfo ();
3620 variable = scope.AddLocal (local_info);
3621 type = variable.Type;
3627 public override Expression DoResolve (EmitContext ec)
3629 ResolveLocalInfo ();
3630 local_info.Used = true;
3632 if (type == null && local_info.Type is VarExpr) {
3633 local_info.VariableType = TypeManager.object_type;
3634 Error_VariableIsUsedBeforeItIsDeclared (Name);
3638 return DoResolveBase (ec);
3641 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3643 ResolveLocalInfo ();
3646 if (right_side == EmptyExpression.OutAccess)
3647 local_info.Used = true;
3649 // Infer implicitly typed local variable
3651 VarExpr ve = local_info.Type as VarExpr;
3653 ve.DoResolveLValue (ec, right_side);
3654 type = local_info.VariableType = ve.Type;
3661 if (right_side == EmptyExpression.OutAccess) {
3662 code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
3663 } else if (right_side == EmptyExpression.LValueMemberAccess) {
3664 code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
3665 } else if (right_side == EmptyExpression.LValueMemberOutAccess) {
3666 code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
3668 code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
3670 Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
3674 if (VariableInfo != null)
3675 VariableInfo.SetAssigned (ec);
3677 return DoResolveBase (ec);
3680 public bool VerifyFixed ()
3682 // A local Variable is always fixed.
3686 public override int GetHashCode ()
3688 return Name.GetHashCode ();
3691 public override bool Equals (object obj)
3693 LocalVariableReference lvr = obj as LocalVariableReference;
3697 return Name == lvr.Name && Block == lvr.Block;
3700 public override Variable Variable {
3701 get { return variable != null ? variable : local_info.Variable; }
3704 public override string ToString ()
3706 return String.Format ("{0} ({1}:{2})", GetType (), Name, loc);
3709 protected override void CloneTo (CloneContext clonectx, Expression t)
3711 LocalVariableReference target = (LocalVariableReference) t;
3713 target.Block = clonectx.LookupBlock (Block);
3718 /// This represents a reference to a parameter in the intermediate
3721 public class ParameterReference : VariableReference, IVariable {
3722 ToplevelParameterInfo pi;
3723 ToplevelBlock referenced;
3725 public bool is_ref, is_out;
3728 get { return is_out; }
3731 public override bool IsRef {
3732 get { return is_ref; }
3735 public string Name {
3736 get { return Parameter.Name; }
3739 public Parameter Parameter {
3740 get { return pi.Parameter; }
3745 public ParameterReference (ToplevelBlock referenced, ToplevelParameterInfo pi, Location loc)
3748 this.referenced = referenced;
3750 eclass = ExprClass.Variable;
3753 public VariableInfo VariableInfo {
3754 get { return pi.VariableInfo; }
3757 public override Variable Variable {
3758 get { return variable != null ? variable : Parameter.Variable; }
3761 public bool VerifyFixed ()
3763 // A parameter is fixed if it's a value parameter (i.e., no modifier like out, ref, param).
3764 return Parameter.ModFlags == Parameter.Modifier.NONE;
3767 public bool IsAssigned (EmitContext ec, Location loc)
3769 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsAssigned (VariableInfo))
3772 Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
3776 public bool IsFieldAssigned (EmitContext ec, string field_name, Location loc)
3778 if (!ec.DoFlowAnalysis || !is_out || ec.CurrentBranching.IsFieldAssigned (VariableInfo, field_name))
3781 Report.Error (170, loc, "Use of possibly unassigned field `{0}'", field_name);
3785 public void SetAssigned (EmitContext ec)
3787 if (is_out && ec.DoFlowAnalysis)
3788 ec.CurrentBranching.SetAssigned (VariableInfo);
3791 public void SetFieldAssigned (EmitContext ec, string field_name)
3793 if (is_out && ec.DoFlowAnalysis)
3794 ec.CurrentBranching.SetFieldAssigned (VariableInfo, field_name);
3797 protected bool DoResolveBase (EmitContext ec)
3799 Parameter par = Parameter;
3800 if (!par.Resolve (ec)) {
3804 type = par.ParameterType;
3805 Parameter.Modifier mod = par.ModFlags;
3806 is_ref = (mod & Parameter.Modifier.ISBYREF) != 0;
3807 is_out = (mod & Parameter.Modifier.OUT) == Parameter.Modifier.OUT;
3808 eclass = ExprClass.Variable;
3810 ToplevelBlock declared = pi.Block;
3812 AnonymousContainer am = ec.CurrentAnonymousMethod;
3816 if (is_ref && declared != referenced) {
3817 Report.Error (1628, Location,
3818 "Cannot use ref or out parameter `{0}' inside an " +
3819 "anonymous method block", par.Name);
3823 if (!am.IsIterator && declared == referenced)
3826 ScopeInfo scope = declared.CreateScopeInfo ();
3827 variable = scope.AddParameter (par, pi.Index);
3828 type = variable.Type;
3832 public override int GetHashCode ()
3834 return Name.GetHashCode ();
3837 public override bool Equals (object obj)
3839 ParameterReference pr = obj as ParameterReference;
3843 return Name == pr.Name && referenced == pr.referenced;
3847 // Notice that for ref/out parameters, the type exposed is not the
3848 // same type exposed externally.
3851 // externally we expose "int&"
3852 // here we expose "int".
3854 // We record this in "is_ref". This means that the type system can treat
3855 // the type as it is expected, but when we generate the code, we generate
3856 // the alternate kind of code.
3858 public override Expression DoResolve (EmitContext ec)
3860 if (!DoResolveBase (ec))
3863 if (is_out && ec.DoFlowAnalysis &&
3864 (!ec.OmitStructFlowAnalysis || !VariableInfo.TypeInfo.IsStruct) && !IsAssigned (ec, loc))
3870 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
3872 if (!DoResolveBase (ec))
3880 static public void EmitLdArg (ILGenerator ig, int x)
3884 case 0: ig.Emit (OpCodes.Ldarg_0); break;
3885 case 1: ig.Emit (OpCodes.Ldarg_1); break;
3886 case 2: ig.Emit (OpCodes.Ldarg_2); break;
3887 case 3: ig.Emit (OpCodes.Ldarg_3); break;
3888 default: ig.Emit (OpCodes.Ldarg_S, (byte) x); break;
3891 ig.Emit (OpCodes.Ldarg, x);
3894 public override string ToString ()
3896 return "ParameterReference[" + Name + "]";
3901 /// Used for arguments to New(), Invocation()
3903 public class Argument {
3904 public enum AType : byte {
3911 public static readonly Argument[] Empty = new Argument [0];
3913 public readonly AType ArgType;
3914 public Expression Expr;
3916 public Argument (Expression expr, AType type)
3919 this.ArgType = type;
3922 public Argument (Expression expr)
3925 this.ArgType = AType.Expression;
3930 if (ArgType == AType.Ref || ArgType == AType.Out)
3931 return TypeManager.GetReferenceType (Expr.Type);
3937 public Parameter.Modifier Modifier
3942 return Parameter.Modifier.OUT;
3945 return Parameter.Modifier.REF;
3948 return Parameter.Modifier.NONE;
3953 public static string FullDesc (Argument a)
3955 if (a.ArgType == AType.ArgList)
3958 return (a.ArgType == AType.Ref ? "ref " :
3959 (a.ArgType == AType.Out ? "out " : "")) +
3960 TypeManager.CSharpName (a.Expr.Type);
3963 public bool ResolveMethodGroup (EmitContext ec)
3965 SimpleName sn = Expr as SimpleName;
3967 Expr = sn.GetMethodGroup ();
3969 // FIXME: csc doesn't report any error if you try to use `ref' or
3970 // `out' in a delegate creation expression.
3971 Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
3978 public bool Resolve (EmitContext ec, Location loc)
3980 using (ec.With (EmitContext.Flags.DoFlowAnalysis, true)) {
3981 // Verify that the argument is readable
3982 if (ArgType != AType.Out)
3983 Expr = Expr.Resolve (ec);
3985 // Verify that the argument is writeable
3986 if (Expr != null && (ArgType == AType.Out || ArgType == AType.Ref))
3987 Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess, loc);
3989 return Expr != null;
3993 public void Emit (EmitContext ec)
3995 if (ArgType != AType.Ref && ArgType != AType.Out) {
4000 AddressOp mode = AddressOp.Store;
4001 if (ArgType == AType.Ref)
4002 mode |= AddressOp.Load;
4004 IMemoryLocation ml = (IMemoryLocation) Expr;
4005 ParameterReference pr = ml as ParameterReference;
4008 // ParameterReferences might already be references, so we want
4009 // to pass just the value
4011 if (pr != null && pr.IsRef)
4014 ml.AddressOf (ec, mode);
4017 public Argument Clone (CloneContext clonectx)
4019 return new Argument (Expr.Clone (clonectx), ArgType);
4024 /// Invocation of methods or delegates.
4026 public class Invocation : ExpressionStatement {
4027 ArrayList Arguments;
4032 // arguments is an ArrayList, but we do not want to typecast,
4033 // as it might be null.
4035 public Invocation (Expression expr, ArrayList arguments)
4037 SimpleName sn = expr as SimpleName;
4039 this.expr = sn.GetMethodGroup ();
4043 Arguments = arguments;
4044 loc = expr.Location;
4047 public static string FullMethodDesc (MethodBase mb)
4053 if (mb is MethodInfo) {
4054 sb = new StringBuilder (TypeManager.CSharpName (((MethodInfo) mb).ReturnType));
4058 sb = new StringBuilder ();
4060 sb.Append (TypeManager.CSharpSignature (mb));
4061 return sb.ToString ();
4064 public static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4065 ArrayList arguments, int arg_count,
4066 ref MethodBase candidate)
4068 return IsParamsMethodApplicable (
4069 ec, me, arguments, arg_count, false, ref candidate) ||
4070 IsParamsMethodApplicable (
4071 ec, me, arguments, arg_count, true, ref candidate);
4076 static bool IsParamsMethodApplicable (EmitContext ec, MethodGroupExpr me,
4077 ArrayList arguments, int arg_count,
4078 bool do_varargs, ref MethodBase candidate)
4081 if (!me.HasTypeArguments &&
4082 !TypeManager.InferParamsTypeArguments (ec, arguments, ref candidate))
4085 if (TypeManager.IsGenericMethodDefinition (candidate))
4086 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4089 return IsParamsMethodApplicable (
4090 ec, arguments, arg_count, candidate, do_varargs);
4094 /// Determines if the candidate method, if a params method, is applicable
4095 /// in its expanded form to the given set of arguments
4097 static bool IsParamsMethodApplicable (EmitContext ec, ArrayList arguments,
4098 int arg_count, MethodBase candidate,
4101 ParameterData pd = TypeManager.GetParameterData (candidate);
4103 int pd_count = pd.Count;
4107 int count = pd_count - 1;
4109 if (pd.ParameterModifier (count) != Parameter.Modifier.ARGLIST)
4111 if (pd_count != arg_count)
4114 if (!(((Argument) arguments [count]).Expr is Arglist))
4122 if (count > arg_count)
4125 if (pd_count == 1 && arg_count == 0)
4129 // If we have come this far, the case which
4130 // remains is when the number of parameters is
4131 // less than or equal to the argument count.
4133 int argument_index = 0;
4135 for (int i = 0; i < pd_count; ++i) {
4137 if ((pd.ParameterModifier (i) & Parameter.Modifier.PARAMS) != 0) {
4138 Type element_type = TypeManager.GetElementType (pd.ParameterType (i));
4139 int params_args_count = arg_count - pd_count;
4140 if (params_args_count < 0)
4144 a = (Argument) arguments [argument_index++];
4146 if (!Convert.ImplicitConversionExists (ec, a.Expr, element_type))
4148 } while (params_args_count-- > 0);
4152 a = (Argument) arguments [argument_index++];
4154 Parameter.Modifier a_mod = a.Modifier &
4155 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4156 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4157 (unchecked (~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK)));
4159 if (a_mod == p_mod) {
4161 if (a_mod == Parameter.Modifier.NONE)
4162 if (!Convert.ImplicitConversionExists (ec,
4164 pd.ParameterType (i)))
4167 if ((a_mod & Parameter.Modifier.ISBYREF) != 0) {
4168 Type pt = pd.ParameterType (i);
4171 pt = TypeManager.GetReferenceType (pt);
4184 public static bool IsApplicable (EmitContext ec, MethodGroupExpr me,
4185 ArrayList arguments, int arg_count,
4186 ref MethodBase method)
4188 MethodBase candidate = method;
4191 if (!me.HasTypeArguments &&
4192 !TypeManager.InferTypeArguments (ec, arguments, ref candidate))
4195 if (TypeManager.IsGenericMethodDefinition (candidate))
4196 throw new InternalErrorException ("a generic method definition took part in overload resolution");
4199 if (IsApplicable (ec, arguments, arg_count, candidate)) {
4208 /// Determines if the candidate method is applicable (section 14.4.2.1)
4209 /// to the given set of arguments
4211 public static bool IsApplicable (EmitContext ec, ArrayList arguments, int arg_count,
4212 MethodBase candidate)
4214 ParameterData pd = TypeManager.GetParameterData (candidate);
4216 if (arg_count != pd.Count)
4219 for (int i = arg_count; i > 0; ) {
4222 Argument a = (Argument) arguments [i];
4224 Parameter.Modifier a_mod = a.Modifier &
4225 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK);
4227 Parameter.Modifier p_mod = pd.ParameterModifier (i) &
4228 ~(Parameter.Modifier.OUTMASK | Parameter.Modifier.REFMASK | Parameter.Modifier.PARAMS);
4233 Type pt = pd.ParameterType (i);
4235 if (TypeManager.IsEqual (pt, a.Type))
4238 if (a_mod != Parameter.Modifier.NONE)
4241 // FIXME: Kill this abomination (EmitContext.TempEc)
4242 EmitContext prevec = EmitContext.TempEc;
4243 EmitContext.TempEc = ec;
4245 if (!Convert.ImplicitConversionExists (ec, a.Expr, pt))
4248 EmitContext.TempEc = prevec;
4255 public static void Error_WrongNumArguments (Location loc, String name, int arg_count)
4257 Report.Error (1501, loc, "No overload for method `{0}' takes `{1}' arguments",
4258 name, arg_count.ToString ());
4261 static void Error_InvalidArguments (Location loc, int idx, MethodBase method,
4262 Type delegate_type, Argument a, ParameterData expected_par)
4264 if (delegate_type == null)
4265 Report.Error (1502, loc, "The best overloaded method match for `{0}' has some invalid arguments",
4266 TypeManager.CSharpSignature (method));
4268 Report.Error (1594, loc, "Delegate `{0}' has some invalid arguments",
4269 TypeManager.CSharpName (delegate_type));
4271 Parameter.Modifier mod = expected_par.ParameterModifier (idx);
4273 string index = (idx + 1).ToString ();
4274 if (mod != Parameter.Modifier.ARGLIST && mod != a.Modifier) {
4275 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) == 0)
4276 Report.Error (1615, loc, "Argument `{0}' should not be passed with the `{1}' keyword",
4277 index, Parameter.GetModifierSignature (a.Modifier));
4279 Report.Error (1620, loc, "Argument `{0}' must be passed with the `{1}' keyword",
4280 index, Parameter.GetModifierSignature (mod));
4282 string p1 = Argument.FullDesc (a);
4283 string p2 = TypeManager.CSharpName (expected_par.ParameterType (idx));
4286 Report.ExtraInformation (loc, "(equally named types possibly from different assemblies in previous ");
4287 Report.SymbolRelatedToPreviousError (a.Expr.Type);
4288 Report.SymbolRelatedToPreviousError (expected_par.ParameterType (idx));
4290 Report.Error (1503, loc, "Argument {0}: Cannot convert type `{1}' to `{2}'", index, p1, p2);
4294 public static bool VerifyArgumentsCompat (EmitContext ec, ArrayList Arguments,
4295 int arg_count, MethodBase method,
4296 bool chose_params_expanded,
4297 Type delegate_type, bool may_fail,
4300 ParameterData pd = TypeManager.GetParameterData (method);
4304 for (j = 0; j < pd.Count; j++) {
4305 Type parameter_type = pd.ParameterType (j);
4306 Parameter.Modifier pm = pd.ParameterModifier (j);
4308 if (pm == Parameter.Modifier.ARGLIST) {
4309 a = (Argument) Arguments [a_idx];
4310 if (!(a.Expr is Arglist))
4316 int params_arg_count = 1;
4317 if (pm == Parameter.Modifier.PARAMS) {
4318 pm = Parameter.Modifier.NONE;
4319 params_arg_count = arg_count - pd.Count + 1;
4320 if (chose_params_expanded)
4321 parameter_type = TypeManager.GetElementType (parameter_type);
4324 while (params_arg_count > 0) {
4325 a = (Argument) Arguments [a_idx];
4326 if (pm != a.Modifier)
4329 if (!TypeManager.IsEqual (a.Type, parameter_type)) {
4330 if (pm == Parameter.Modifier.OUT || pm == Parameter.Modifier.REF)
4333 Expression conv = Convert.ImplicitConversion (ec, a.Expr, parameter_type, loc);
4337 // Update the argument with the implicit conversion
4345 if (params_arg_count > 0)
4348 if (parameter_type.IsPointer && !ec.InUnsafe) {
4355 if (a_idx == arg_count)
4359 Error_InvalidArguments (loc, a_idx, method, delegate_type, a, pd);
4363 public override Expression DoResolve (EmitContext ec)
4365 Expression expr_resolved = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
4366 if (expr_resolved == null)
4369 mg = expr_resolved as MethodGroupExpr;
4371 Type expr_type = expr_resolved.Type;
4373 if (expr_type != null && TypeManager.IsDelegateType (expr_type)){
4374 return (new DelegateInvocation (
4375 expr_resolved, Arguments, loc)).Resolve (ec);
4377 expr.Error_UnexpectedKind (ResolveFlags.MethodGroup, loc);
4382 // Next, evaluate all the expressions in the argument list
4384 if (Arguments != null){
4385 foreach (Argument a in Arguments){
4386 if (!a.Resolve (ec, loc))
4391 mg = mg.OverloadResolve (ec, Arguments, false, loc);
4395 MethodInfo method = (MethodInfo)mg;
4396 if (method != null) {
4397 type = TypeManager.TypeToCoreType (method.ReturnType);
4398 Expression iexpr = mg.InstanceExpression;
4399 if (method.IsStatic) {
4400 if (iexpr == null ||
4401 iexpr is This || iexpr is EmptyExpression ||
4402 mg.IdenticalTypeName) {
4403 mg.InstanceExpression = null;
4405 MemberExpr.error176 (loc, mg.GetSignatureForError ());
4409 if (iexpr == null || iexpr is EmptyExpression) {
4410 SimpleName.Error_ObjectRefRequired (ec, loc, mg.GetSignatureForError ());
4416 if (type.IsPointer){
4424 // Only base will allow this invocation to happen.
4426 if (mg.IsBase && method.IsAbstract){
4427 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (method));
4431 if (Arguments == null && method.Name == "Finalize") {
4433 Report.Error (250, loc, "Do not directly call your base class Finalize method. It is called automatically from your destructor");
4435 Report.Error (245, loc, "Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose if available");
4439 if (IsSpecialMethodInvocation (method)) {
4443 if (mg.InstanceExpression != null){
4444 mg.InstanceExpression.CheckMarshalByRefAccess ();
4447 // This is used to check that no methods are called in struct
4448 // constructors before all the fields on the struct have been
4451 if (!method.IsStatic){
4452 This mgthis = mg.InstanceExpression as This;
4453 if (mgthis != null){
4454 if (!mgthis.CheckThisUsage (ec))
4460 eclass = ExprClass.Value;
4464 bool IsSpecialMethodInvocation (MethodBase method)
4466 if (!TypeManager.IsSpecialMethod (method))
4469 Report.SymbolRelatedToPreviousError (method);
4470 Report.Error (571, loc, "`{0}': cannot explicitly call operator or accessor",
4471 TypeManager.CSharpSignature (method, true));
4477 // Emits the list of arguments as an array
4479 static void EmitParams (EmitContext ec, ArrayList arguments, int idx, int count)
4481 ILGenerator ig = ec.ig;
4483 for (int j = 0; j < count; j++){
4484 Argument a = (Argument) arguments [j + idx];
4487 IntConstant.EmitInt (ig, count);
4488 ig.Emit (OpCodes.Newarr, TypeManager.TypeToCoreType (t));
4491 ig.Emit (OpCodes.Dup);
4492 IntConstant.EmitInt (ig, j);
4494 bool is_stobj, has_type_arg;
4495 OpCode op = ArrayAccess.GetStoreOpcode (t, out is_stobj, out has_type_arg);
4497 ig.Emit (OpCodes.Ldelema, t);
4509 /// Emits a list of resolved Arguments that are in the arguments
4512 /// The MethodBase argument might be null if the
4513 /// emission of the arguments is known not to contain
4514 /// a `params' field (for example in constructors or other routines
4515 /// that keep their arguments in this structure)
4517 /// if `dup_args' is true, a copy of the arguments will be left
4518 /// on the stack. If `dup_args' is true, you can specify `this_arg'
4519 /// which will be duplicated before any other args. Only EmitCall
4520 /// should be using this interface.
4522 public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
4524 ParameterData pd = mb == null ? null : TypeManager.GetParameterData (mb);
4526 LocalTemporary [] temps = null;
4528 if (dup_args && top != 0)
4529 temps = new LocalTemporary [top];
4531 int argument_index = 0;
4533 for (int i = 0; i < top; i++){
4535 if (pd.ParameterModifier (i) == Parameter.Modifier.PARAMS){
4536 //Type element_type = TypeManager.GetElementType (pd.ParameterType (i));
4537 int params_args_count = arguments == null ?
4538 0 : arguments.Count - top + 1;
4540 // Fill not provided argument
4541 if (params_args_count <= 0) {
4542 ILGenerator ig = ec.ig;
4543 IntConstant.EmitInt (ig, 0);
4544 ig.Emit (OpCodes.Newarr, TypeManager.GetElementType (pd.ParameterType (i)));
4549 // Special case if we are passing the same data as the
4550 // params argument, we do not need to recreate an array.
4552 a = (Argument) arguments [argument_index];
4553 if (params_args_count == 1 && pd.ParameterType (i) == a.Type) {
4559 EmitParams (ec, arguments, i, params_args_count);
4560 argument_index += params_args_count;
4565 a = (Argument) arguments [argument_index++];
4568 ec.ig.Emit (OpCodes.Dup);
4569 (temps [i] = new LocalTemporary (a.Type)).Store (ec);
4574 if (this_arg != null)
4577 for (int i = 0; i < top; i ++) {
4578 temps [i].Emit (ec);
4579 temps [i].Release (ec);
4584 static Type[] GetVarargsTypes (MethodBase mb, ArrayList arguments)
4586 ParameterData pd = TypeManager.GetParameterData (mb);
4588 if (arguments == null)
4589 return new Type [0];
4591 Argument a = (Argument) arguments [pd.Count - 1];
4592 Arglist list = (Arglist) a.Expr;
4594 return list.ArgumentTypes;
4598 /// This checks the ConditionalAttribute on the method
4600 static bool IsMethodExcluded (MethodBase method)
4602 if (method.IsConstructor)
4605 IMethodData md = TypeManager.GetMethod (method);
4607 return md.IsExcluded ();
4609 // For some methods (generated by delegate class) GetMethod returns null
4610 // because they are not included in builder_to_method table
4611 if (method.DeclaringType is TypeBuilder)
4614 return AttributeTester.IsConditionalMethodExcluded (method);
4618 /// is_base tells whether we want to force the use of the `call'
4619 /// opcode instead of using callvirt. Call is required to call
4620 /// a specific method, while callvirt will always use the most
4621 /// recent method in the vtable.
4623 /// is_static tells whether this is an invocation on a static method
4625 /// instance_expr is an expression that represents the instance
4626 /// it must be non-null if is_static is false.
4628 /// method is the method to invoke.
4630 /// Arguments is the list of arguments to pass to the method or constructor.
4632 public static void EmitCall (EmitContext ec, bool is_base,
4633 Expression instance_expr,
4634 MethodBase method, ArrayList Arguments, Location loc)
4636 EmitCall (ec, is_base, instance_expr, method, Arguments, loc, false, false);
4639 // `dup_args' leaves an extra copy of the arguments on the stack
4640 // `omit_args' does not leave any arguments at all.
4641 // So, basically, you could make one call with `dup_args' set to true,
4642 // and then another with `omit_args' set to true, and the two calls
4643 // would have the same set of arguments. However, each argument would
4644 // only have been evaluated once.
4645 public static void EmitCall (EmitContext ec, bool is_base,
4646 Expression instance_expr,
4647 MethodBase method, ArrayList Arguments, Location loc,
4648 bool dup_args, bool omit_args)
4650 ILGenerator ig = ec.ig;
4651 bool struct_call = false;
4652 bool this_call = false;
4653 LocalTemporary this_arg = null;
4655 Type decl_type = method.DeclaringType;
4657 if (!RootContext.StdLib) {
4658 // Replace any calls to the system's System.Array type with calls to
4659 // the newly created one.
4660 if (method == TypeManager.system_int_array_get_length)
4661 method = TypeManager.int_array_get_length;
4662 else if (method == TypeManager.system_int_array_get_rank)
4663 method = TypeManager.int_array_get_rank;
4664 else if (method == TypeManager.system_object_array_clone)
4665 method = TypeManager.object_array_clone;
4666 else if (method == TypeManager.system_int_array_get_length_int)
4667 method = TypeManager.int_array_get_length_int;
4668 else if (method == TypeManager.system_int_array_get_lower_bound_int)
4669 method = TypeManager.int_array_get_lower_bound_int;
4670 else if (method == TypeManager.system_int_array_get_upper_bound_int)
4671 method = TypeManager.int_array_get_upper_bound_int;
4672 else if (method == TypeManager.system_void_array_copyto_array_int)
4673 method = TypeManager.void_array_copyto_array_int;
4676 if (!ec.IsInObsoleteScope) {
4678 // This checks ObsoleteAttribute on the method and on the declaring type
4680 ObsoleteAttribute oa = AttributeTester.GetMethodObsoleteAttribute (method);
4682 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.CSharpSignature (method), loc);
4684 oa = AttributeTester.GetObsoleteAttribute (method.DeclaringType);
4686 AttributeTester.Report_ObsoleteMessage (oa, method.DeclaringType.FullName, loc);
4690 if (IsMethodExcluded (method))
4693 bool is_static = method.IsStatic;
4695 if (instance_expr == EmptyExpression.Null) {
4696 SimpleName.Error_ObjectRefRequired (ec, loc, TypeManager.CSharpSignature (method));
4700 this_call = instance_expr is This;
4701 if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
4705 // If this is ourselves, push "this"
4709 Type iexpr_type = instance_expr.Type;
4712 // Push the instance expression
4714 if (TypeManager.IsValueType (iexpr_type)) {
4716 // Special case: calls to a function declared in a
4717 // reference-type with a value-type argument need
4718 // to have their value boxed.
4719 if (decl_type.IsValueType ||
4720 TypeManager.IsGenericParameter (iexpr_type)) {
4722 // If the expression implements IMemoryLocation, then
4723 // we can optimize and use AddressOf on the
4726 // If not we have to use some temporary storage for
4728 if (instance_expr is IMemoryLocation) {
4729 ((IMemoryLocation)instance_expr).
4730 AddressOf (ec, AddressOp.LoadStore);
4732 LocalTemporary temp = new LocalTemporary (iexpr_type);
4733 instance_expr.Emit (ec);
4735 temp.AddressOf (ec, AddressOp.Load);
4738 // avoid the overhead of doing this all the time.
4740 t = TypeManager.GetReferenceType (iexpr_type);
4742 instance_expr.Emit (ec);
4743 ig.Emit (OpCodes.Box, instance_expr.Type);
4744 t = TypeManager.object_type;
4747 instance_expr.Emit (ec);
4748 t = instance_expr.Type;
4752 ig.Emit (OpCodes.Dup);
4753 if (Arguments != null && Arguments.Count != 0) {
4754 this_arg = new LocalTemporary (t);
4755 this_arg.Store (ec);
4762 EmitArguments (ec, method, Arguments, dup_args, this_arg);
4765 if ((instance_expr != null) && (instance_expr.Type.IsGenericParameter))
4766 ig.Emit (OpCodes.Constrained, instance_expr.Type);
4770 if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
4771 call_op = OpCodes.Call;
4773 call_op = OpCodes.Callvirt;
4775 if ((method.CallingConvention & CallingConventions.VarArgs) != 0) {
4776 Type[] varargs_types = GetVarargsTypes (method, Arguments);
4777 ig.EmitCall (call_op, (MethodInfo) method, varargs_types);
4784 // and DoFoo is not virtual, you can omit the callvirt,
4785 // because you don't need the null checking behavior.
4787 if (method is MethodInfo)
4788 ig.Emit (call_op, (MethodInfo) method);
4790 ig.Emit (call_op, (ConstructorInfo) method);
4793 public override void Emit (EmitContext ec)
4795 mg.EmitCall (ec, Arguments);
4798 public override void EmitStatement (EmitContext ec)
4803 // Pop the return value if there is one
4805 if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
4806 ec.ig.Emit (OpCodes.Pop);
4809 protected override void CloneTo (CloneContext clonectx, Expression t)
4811 Invocation target = (Invocation) t;
4813 if (Arguments != null){
4814 target.Arguments = new ArrayList ();
4815 foreach (Argument a in Arguments)
4816 target.Arguments.Add (a.Clone (clonectx));
4819 expr = expr.Clone (clonectx);
4823 public class InvocationOrCast : ExpressionStatement
4826 Expression argument;
4828 public InvocationOrCast (Expression expr, Expression argument)
4831 this.argument = argument;
4832 this.loc = expr.Location;
4835 public override Expression DoResolve (EmitContext ec)
4838 // First try to resolve it as a cast.
4840 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
4841 if ((te != null) && (te.eclass == ExprClass.Type)) {
4842 Cast cast = new Cast (te, argument, loc);
4843 return cast.Resolve (ec);
4847 // This can either be a type or a delegate invocation.
4848 // Let's just resolve it and see what we'll get.
4850 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
4855 // Ok, so it's a Cast.
4857 if (expr.eclass == ExprClass.Type) {
4858 Cast cast = new Cast (new TypeExpression (expr.Type, loc), argument, loc);
4859 return cast.Resolve (ec);
4863 // It's a delegate invocation.
4865 if (!TypeManager.IsDelegateType (expr.Type)) {
4866 Error (149, "Method name expected");
4870 ArrayList args = new ArrayList ();
4871 args.Add (new Argument (argument, Argument.AType.Expression));
4872 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
4873 return invocation.Resolve (ec);
4876 public override ExpressionStatement ResolveStatement (EmitContext ec)
4879 // First try to resolve it as a cast.
4881 TypeExpr te = expr.ResolveAsTypeTerminal (ec, true);
4882 if ((te != null) && (te.eclass == ExprClass.Type)) {
4883 Error_InvalidExpressionStatement ();
4888 // This can either be a type or a delegate invocation.
4889 // Let's just resolve it and see what we'll get.
4891 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
4892 if ((expr == null) || (expr.eclass == ExprClass.Type)) {
4893 Error_InvalidExpressionStatement ();
4898 // It's a delegate invocation.
4900 if (!TypeManager.IsDelegateType (expr.Type)) {
4901 Error (149, "Method name expected");
4905 ArrayList args = new ArrayList ();
4906 args.Add (new Argument (argument, Argument.AType.Expression));
4907 DelegateInvocation invocation = new DelegateInvocation (expr, args, loc);
4908 return invocation.ResolveStatement (ec);
4911 public override void Emit (EmitContext ec)
4913 throw new Exception ("Cannot happen");
4916 public override void EmitStatement (EmitContext ec)
4918 throw new Exception ("Cannot happen");
4921 protected override void CloneTo (CloneContext clonectx, Expression t)
4923 InvocationOrCast target = (InvocationOrCast) t;
4925 target.expr = expr.Clone (clonectx);
4926 target.argument = argument.Clone (clonectx);
4931 // This class is used to "disable" the code generation for the
4932 // temporary variable when initializing value types.
4934 class EmptyAddressOf : EmptyExpression, IMemoryLocation {
4935 public void AddressOf (EmitContext ec, AddressOp Mode)
4942 /// Implements the new expression
4944 public class New : ExpressionStatement, IMemoryLocation {
4945 ArrayList Arguments;
4948 // During bootstrap, it contains the RequestedType,
4949 // but if `type' is not null, it *might* contain a NewDelegate
4950 // (because of field multi-initialization)
4952 public Expression RequestedType;
4954 MethodGroupExpr method;
4957 // If set, the new expression is for a value_target, and
4958 // we will not leave anything on the stack.
4960 Expression value_target;
4961 bool value_target_set = false;
4962 bool is_type_parameter = false;
4964 public New (Expression requested_type, ArrayList arguments, Location l)
4966 RequestedType = requested_type;
4967 Arguments = arguments;
4971 public bool SetValueTypeVariable (Expression value)
4973 value_target = value;
4974 value_target_set = true;
4975 if (!(value_target is IMemoryLocation)){
4976 Error_UnexpectedKind (null, "variable", loc);
4983 // This function is used to disable the following code sequence for
4984 // value type initialization:
4986 // AddressOf (temporary)
4990 // Instead the provide will have provided us with the address on the
4991 // stack to store the results.
4993 static Expression MyEmptyExpression;
4995 public void DisableTemporaryValueType ()
4997 if (MyEmptyExpression == null)
4998 MyEmptyExpression = new EmptyAddressOf ();
5001 // To enable this, look into:
5002 // test-34 and test-89 and self bootstrapping.
5004 // For instance, we can avoid a copy by using `newobj'
5005 // instead of Call + Push-temp on value types.
5006 // value_target = MyEmptyExpression;
5011 /// Converts complex core type syntax like 'new int ()' to simple constant
5013 public static Constant Constantify (Type t)
5015 if (t == TypeManager.int32_type)
5016 return new IntConstant (0, Location.Null);
5017 if (t == TypeManager.uint32_type)
5018 return new UIntConstant (0, Location.Null);
5019 if (t == TypeManager.int64_type)
5020 return new LongConstant (0, Location.Null);
5021 if (t == TypeManager.uint64_type)
5022 return new ULongConstant (0, Location.Null);
5023 if (t == TypeManager.float_type)
5024 return new FloatConstant (0, Location.Null);
5025 if (t == TypeManager.double_type)
5026 return new DoubleConstant (0, Location.Null);
5027 if (t == TypeManager.short_type)
5028 return new ShortConstant (0, Location.Null);
5029 if (t == TypeManager.ushort_type)
5030 return new UShortConstant (0, Location.Null);
5031 if (t == TypeManager.sbyte_type)
5032 return new SByteConstant (0, Location.Null);
5033 if (t == TypeManager.byte_type)
5034 return new ByteConstant (0, Location.Null);
5035 if (t == TypeManager.char_type)
5036 return new CharConstant ('\0', Location.Null);
5037 if (t == TypeManager.bool_type)
5038 return new BoolConstant (false, Location.Null);
5039 if (t == TypeManager.decimal_type)
5040 return new DecimalConstant (0, Location.Null);
5041 if (TypeManager.IsEnumType (t))
5042 return new EnumConstant (Constantify (TypeManager.EnumToUnderlying (t)), t);
5048 // Checks whether the type is an interface that has the
5049 // [ComImport, CoClass] attributes and must be treated
5052 public Expression CheckComImport (EmitContext ec)
5054 if (!type.IsInterface)
5058 // Turn the call into:
5059 // (the-interface-stated) (new class-referenced-in-coclassattribute ())
5061 Type real_class = AttributeTester.GetCoClassAttribute (type);
5062 if (real_class == null)
5065 New proxy = new New (new TypeExpression (real_class, loc), Arguments, loc);
5066 Cast cast = new Cast (new TypeExpression (type, loc), proxy, loc);
5067 return cast.Resolve (ec);
5070 public override Expression DoResolve (EmitContext ec)
5073 // The New DoResolve might be called twice when initializing field
5074 // expressions (see EmitFieldInitializers, the call to
5075 // GetInitializerExpression will perform a resolve on the expression,
5076 // and later the assign will trigger another resolution
5078 // This leads to bugs (#37014)
5081 if (RequestedType is NewDelegate)
5082 return RequestedType;
5086 TypeExpr texpr = RequestedType.ResolveAsTypeTerminal (ec, false);
5092 if (type == TypeManager.void_type) {
5093 Error_VoidInvalidInTheContext (loc);
5097 if (Arguments == null) {
5098 Expression c = Constantify (type);
5103 if (TypeManager.IsDelegateType (type)) {
5104 RequestedType = (new NewDelegate (type, Arguments, loc)).Resolve (ec);
5105 if (RequestedType != null)
5106 if (!(RequestedType is DelegateCreation))
5107 throw new Exception ("NewDelegate.Resolve returned a non NewDelegate: " + RequestedType.GetType ());
5108 return RequestedType;
5112 if (type.IsGenericParameter) {
5113 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5115 if ((gc == null) || (!gc.HasConstructorConstraint && !gc.IsValueType)) {
5116 Error (304, String.Format (
5117 "Cannot create an instance of the " +
5118 "variable type '{0}' because it " +
5119 "doesn't have the new() constraint",
5124 if ((Arguments != null) && (Arguments.Count != 0)) {
5125 Error (417, String.Format (
5126 "`{0}': cannot provide arguments " +
5127 "when creating an instance of a " +
5128 "variable type.", type));
5132 is_type_parameter = true;
5133 eclass = ExprClass.Value;
5138 if (type.IsAbstract && type.IsSealed) {
5139 Report.SymbolRelatedToPreviousError (type);
5140 Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
5144 if (type.IsInterface || type.IsAbstract){
5145 if (!TypeManager.IsGenericType (type)) {
5146 RequestedType = CheckComImport (ec);
5147 if (RequestedType != null)
5148 return RequestedType;
5151 Report.SymbolRelatedToPreviousError (type);
5152 Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
5156 bool is_struct = type.IsValueType;
5157 eclass = ExprClass.Value;
5160 // SRE returns a match for .ctor () on structs (the object constructor),
5161 // so we have to manually ignore it.
5163 if (is_struct && Arguments == null)
5166 // For member-lookup, treat 'new Foo (bar)' as call to 'foo.ctor (bar)', where 'foo' is of type 'Foo'.
5167 Expression ml = MemberLookupFinal (ec, type, type, ".ctor",
5168 MemberTypes.Constructor, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
5173 method = ml as MethodGroupExpr;
5175 if (method == null) {
5176 ml.Error_UnexpectedKind (ec.DeclContainer, "method group", loc);
5180 if (Arguments != null){
5181 foreach (Argument a in Arguments){
5182 if (!a.Resolve (ec, loc))
5187 method = method.OverloadResolve (ec, Arguments, false, loc);
5188 if (method == null) {
5189 if (almostMatchedMembers.Count != 0)
5190 MemberLookupFailed (ec.ContainerType, type, type, ".ctor", null, true, loc);
5197 bool DoEmitTypeParameter (EmitContext ec)
5200 ILGenerator ig = ec.ig;
5201 // IMemoryLocation ml;
5203 MethodInfo ci = TypeManager.activator_create_instance.MakeGenericMethod (
5204 new Type [] { type });
5206 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (type);
5207 if (gc.HasReferenceTypeConstraint || gc.HasClassConstraint) {
5208 ig.Emit (OpCodes.Call, ci);
5212 // Allow DoEmit() to be called multiple times.
5213 // We need to create a new LocalTemporary each time since
5214 // you can't share LocalBuilders among ILGeneators.
5215 LocalTemporary temp = new LocalTemporary (type);
5217 Label label_activator = ig.DefineLabel ();
5218 Label label_end = ig.DefineLabel ();
5220 temp.AddressOf (ec, AddressOp.Store);
5221 ig.Emit (OpCodes.Initobj, type);
5224 ig.Emit (OpCodes.Box, type);
5225 ig.Emit (OpCodes.Brfalse, label_activator);
5227 temp.AddressOf (ec, AddressOp.Store);
5228 ig.Emit (OpCodes.Initobj, type);
5230 ig.Emit (OpCodes.Br, label_end);
5232 ig.MarkLabel (label_activator);
5234 ig.Emit (OpCodes.Call, ci);
5235 ig.MarkLabel (label_end);
5238 throw new InternalErrorException ();
5243 // This DoEmit can be invoked in two contexts:
5244 // * As a mechanism that will leave a value on the stack (new object)
5245 // * As one that wont (init struct)
5247 // You can control whether a value is required on the stack by passing
5248 // need_value_on_stack. The code *might* leave a value on the stack
5249 // so it must be popped manually
5251 // If we are dealing with a ValueType, we have a few
5252 // situations to deal with:
5254 // * The target is a ValueType, and we have been provided
5255 // the instance (this is easy, we are being assigned).
5257 // * The target of New is being passed as an argument,
5258 // to a boxing operation or a function that takes a
5261 // In this case, we need to create a temporary variable
5262 // that is the argument of New.
5264 // Returns whether a value is left on the stack
5266 bool DoEmit (EmitContext ec, bool need_value_on_stack)
5268 bool is_value_type = TypeManager.IsValueType (type);
5269 ILGenerator ig = ec.ig;
5274 // Allow DoEmit() to be called multiple times.
5275 // We need to create a new LocalTemporary each time since
5276 // you can't share LocalBuilders among ILGeneators.
5277 if (!value_target_set)
5278 value_target = new LocalTemporary (type);
5280 ml = (IMemoryLocation) value_target;
5281 ml.AddressOf (ec, AddressOp.Store);
5285 method.EmitArguments (ec, Arguments);
5289 ig.Emit (OpCodes.Initobj, type);
5291 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5292 if (need_value_on_stack){
5293 value_target.Emit (ec);
5298 ig.Emit (OpCodes.Newobj, (ConstructorInfo) method);
5303 public override void Emit (EmitContext ec)
5305 if (is_type_parameter)
5306 DoEmitTypeParameter (ec);
5311 public override void EmitStatement (EmitContext ec)
5313 bool value_on_stack;
5315 if (is_type_parameter)
5316 value_on_stack = DoEmitTypeParameter (ec);
5318 value_on_stack = DoEmit (ec, false);
5321 ec.ig.Emit (OpCodes.Pop);
5325 public void AddressOf (EmitContext ec, AddressOp Mode)
5327 if (is_type_parameter) {
5328 LocalTemporary temp = new LocalTemporary (type);
5329 DoEmitTypeParameter (ec);
5331 temp.AddressOf (ec, Mode);
5335 if (!type.IsValueType){
5337 // We throw an exception. So far, I believe we only need to support
5339 // foreach (int j in new StructType ())
5342 throw new Exception ("AddressOf should not be used for classes");
5345 if (!value_target_set)
5346 value_target = new LocalTemporary (type);
5347 IMemoryLocation ml = (IMemoryLocation) value_target;
5349 ml.AddressOf (ec, AddressOp.Store);
5350 if (method == null) {
5351 ec.ig.Emit (OpCodes.Initobj, type);
5353 method.EmitArguments (ec, Arguments);
5354 ec.ig.Emit (OpCodes.Call, (ConstructorInfo) method);
5357 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
5360 protected override void CloneTo (CloneContext clonectx, Expression t)
5362 New target = (New) t;
5364 target.RequestedType = RequestedType.Clone (clonectx);
5365 if (Arguments != null){
5366 target.Arguments = new ArrayList ();
5367 foreach (Argument a in Arguments){
5368 target.Arguments.Add (a.Clone (clonectx));
5375 /// 14.5.10.2: Represents an array creation expression.
5379 /// There are two possible scenarios here: one is an array creation
5380 /// expression that specifies the dimensions and optionally the
5381 /// initialization data and the other which does not need dimensions
5382 /// specified but where initialization data is mandatory.
5384 public class ArrayCreation : Expression {
5385 Expression requested_base_type;
5386 ArrayList initializers;
5389 // The list of Argument types.
5390 // This is used to construct the `newarray' or constructor signature
5392 protected ArrayList arguments;
5394 protected Type array_element_type;
5395 bool expect_initializers = false;
5396 int num_arguments = 0;
5397 protected int dimensions;
5398 protected readonly string rank;
5400 protected ArrayList array_data;
5404 // The number of constants in array initializers
5405 int const_initializers_count;
5406 bool only_constant_initializers;
5408 public ArrayCreation (Expression requested_base_type, ArrayList exprs, string rank, ArrayList initializers, Location l)
5410 this.requested_base_type = requested_base_type;
5411 this.initializers = initializers;
5415 arguments = new ArrayList ();
5417 foreach (Expression e in exprs) {
5418 arguments.Add (new Argument (e, Argument.AType.Expression));
5423 public ArrayCreation (Expression requested_base_type, string rank, ArrayList initializers, Location l)
5425 this.requested_base_type = requested_base_type;
5426 this.initializers = initializers;
5430 //this.rank = rank.Substring (0, rank.LastIndexOf ('['));
5432 //string tmp = rank.Substring (rank.LastIndexOf ('['));
5434 //dimensions = tmp.Length - 1;
5435 expect_initializers = true;
5438 public Expression FormArrayType (Expression base_type, int idx_count, string rank)
5440 StringBuilder sb = new StringBuilder (rank);
5443 for (int i = 1; i < idx_count; i++)
5448 return new ComposedCast (base_type, sb.ToString (), loc);
5451 void Error_IncorrectArrayInitializer ()
5453 Error (178, "Invalid rank specifier: expected `,' or `]'");
5456 bool CheckIndices (EmitContext ec, ArrayList probe, int idx, bool specified_dims)
5458 if (specified_dims) {
5459 Argument a = (Argument) arguments [idx];
5461 if (!a.Resolve (ec, loc))
5464 Constant c = a.Expr as Constant;
5466 c = c.ImplicitConversionRequired (TypeManager.int32_type, a.Expr.Location);
5470 Report.Error (150, a.Expr.Location, "A constant value is expected");
5474 int value = (int) c.GetValue ();
5476 if (value != probe.Count) {
5477 Error_IncorrectArrayInitializer ();
5481 bounds [idx] = value;
5484 int child_bounds = -1;
5485 only_constant_initializers = true;
5486 for (int i = 0; i < probe.Count; ++i) {
5487 object o = probe [i];
5488 if (o is ArrayList) {
5489 ArrayList sub_probe = o as ArrayList;
5490 int current_bounds = sub_probe.Count;
5492 if (child_bounds == -1)
5493 child_bounds = current_bounds;
5495 else if (child_bounds != current_bounds){
5496 Error_IncorrectArrayInitializer ();
5499 if (idx + 1 >= dimensions){
5500 Error (623, "Array initializers can only be used in a variable or field initializer. Try using a new expression instead");
5504 bool ret = CheckIndices (ec, sub_probe, idx + 1, specified_dims);
5508 if (child_bounds != -1){
5509 Error_IncorrectArrayInitializer ();
5513 Expression element = ResolveArrayElement (ec, (Expression) o);
5514 if (element == null)
5517 // Initializers with the default values can be ignored
5518 Constant c = element as Constant;
5520 if (c.IsDefaultInitializer (array_element_type)) {
5524 ++const_initializers_count;
5527 only_constant_initializers = false;
5530 array_data.Add (element);
5537 public void UpdateIndices ()
5540 for (ArrayList probe = initializers; probe != null;) {
5541 if (probe.Count > 0 && probe [0] is ArrayList) {
5542 Expression e = new IntConstant (probe.Count, Location.Null);
5543 arguments.Add (new Argument (e, Argument.AType.Expression));
5545 bounds [i++] = probe.Count;
5547 probe = (ArrayList) probe [0];
5550 Expression e = new IntConstant (probe.Count, Location.Null);
5551 arguments.Add (new Argument (e, Argument.AType.Expression));
5553 bounds [i++] = probe.Count;
5560 protected virtual Expression ResolveArrayElement (EmitContext ec, Expression element)
5562 element = element.Resolve (ec);
5563 if (element == null)
5566 return Convert.ImplicitConversionRequired (
5567 ec, element, array_element_type, loc);
5570 protected bool ResolveInitializers (EmitContext ec)
5572 if (initializers == null) {
5573 return !expect_initializers;
5577 // We use this to store all the date values in the order in which we
5578 // will need to store them in the byte blob later
5580 array_data = new ArrayList ();
5581 bounds = new System.Collections.Specialized.HybridDictionary ();
5583 if (arguments != null)
5584 return CheckIndices (ec, initializers, 0, true);
5586 arguments = new ArrayList ();
5588 if (!CheckIndices (ec, initializers, 0, false))
5597 // Resolved the type of the array
5599 bool ResolveArrayType (EmitContext ec)
5601 if (requested_base_type == null) {
5602 Report.Error (622, loc, "Can only use array initializer expressions to assign to array types. Try using a new expression instead");
5606 StringBuilder array_qualifier = new StringBuilder (rank);
5609 // `In the first form allocates an array instace of the type that results
5610 // from deleting each of the individual expression from the expression list'
5612 if (num_arguments > 0) {
5613 array_qualifier.Append ("[");
5614 for (int i = num_arguments-1; i > 0; i--)
5615 array_qualifier.Append (",");
5616 array_qualifier.Append ("]");
5622 TypeExpr array_type_expr;
5623 array_type_expr = new ComposedCast (requested_base_type, array_qualifier.ToString (), loc);
5624 array_type_expr = array_type_expr.ResolveAsTypeTerminal (ec, false);
5625 if (array_type_expr == null)
5628 type = array_type_expr.Type;
5629 array_element_type = TypeManager.GetElementType (type);
5630 dimensions = type.GetArrayRank ();
5635 public override Expression DoResolve (EmitContext ec)
5640 if (!ResolveArrayType (ec))
5643 if ((array_element_type.Attributes & Class.StaticClassAttribute) == Class.StaticClassAttribute) {
5644 Report.Error (719, loc, "`{0}': array elements cannot be of static type",
5645 TypeManager.CSharpName (array_element_type));
5649 // First step is to validate the initializers and fill
5650 // in any missing bits
5652 if (!ResolveInitializers (ec))
5655 if (arguments.Count != dimensions) {
5656 Error_IncorrectArrayInitializer ();
5659 foreach (Argument a in arguments){
5660 if (!a.Resolve (ec, loc))
5663 Expression real_arg = ExpressionToArrayArgument (ec, a.Expr, loc);
5664 if (real_arg == null)
5670 eclass = ExprClass.Value;
5674 MethodInfo GetArrayMethod (int arguments)
5676 ModuleBuilder mb = CodeGen.Module.Builder;
5678 Type[] arg_types = new Type[arguments];
5679 for (int i = 0; i < arguments; i++)
5680 arg_types[i] = TypeManager.int32_type;
5682 MethodInfo mi = mb.GetArrayMethod (type, ".ctor", CallingConventions.HasThis, null,
5686 Report.Error (-6, "New invocation: Can not find a constructor for " +
5687 "this argument list");
5694 byte [] MakeByteBlob ()
5699 int count = array_data.Count;
5701 if (array_element_type.IsEnum)
5702 array_element_type = TypeManager.EnumToUnderlying (array_element_type);
5704 factor = GetTypeSize (array_element_type);
5706 throw new Exception ("unrecognized type in MakeByteBlob: " + array_element_type);
5708 data = new byte [(count * factor + 4) & ~3];
5711 for (int i = 0; i < count; ++i) {
5712 object v = array_data [i];
5714 if (v is EnumConstant)
5715 v = ((EnumConstant) v).Child;
5717 if (v is Constant && !(v is StringConstant))
5718 v = ((Constant) v).GetValue ();
5724 if (array_element_type == TypeManager.int64_type){
5725 if (!(v is Expression)){
5726 long val = (long) v;
5728 for (int j = 0; j < factor; ++j) {
5729 data [idx + j] = (byte) (val & 0xFF);
5733 } else if (array_element_type == TypeManager.uint64_type){
5734 if (!(v is Expression)){
5735 ulong val = (ulong) v;
5737 for (int j = 0; j < factor; ++j) {
5738 data [idx + j] = (byte) (val & 0xFF);
5742 } else if (array_element_type == TypeManager.float_type) {
5743 if (!(v is Expression)){
5744 element = BitConverter.GetBytes ((float) v);
5746 for (int j = 0; j < factor; ++j)
5747 data [idx + j] = element [j];
5748 if (!BitConverter.IsLittleEndian)
5749 System.Array.Reverse (data, idx, 4);
5751 } else if (array_element_type == TypeManager.double_type) {
5752 if (!(v is Expression)){
5753 element = BitConverter.GetBytes ((double) v);
5755 for (int j = 0; j < factor; ++j)
5756 data [idx + j] = element [j];
5758 // FIXME: Handle the ARM float format.
5759 if (!BitConverter.IsLittleEndian)
5760 System.Array.Reverse (data, idx, 8);
5762 } else if (array_element_type == TypeManager.char_type){
5763 if (!(v is Expression)){
5764 int val = (int) ((char) v);
5766 data [idx] = (byte) (val & 0xff);
5767 data [idx+1] = (byte) (val >> 8);
5769 } else if (array_element_type == TypeManager.short_type){
5770 if (!(v is Expression)){
5771 int val = (int) ((short) v);
5773 data [idx] = (byte) (val & 0xff);
5774 data [idx+1] = (byte) (val >> 8);
5776 } else if (array_element_type == TypeManager.ushort_type){
5777 if (!(v is Expression)){
5778 int val = (int) ((ushort) v);
5780 data [idx] = (byte) (val & 0xff);
5781 data [idx+1] = (byte) (val >> 8);
5783 } else if (array_element_type == TypeManager.int32_type) {
5784 if (!(v is Expression)){
5787 data [idx] = (byte) (val & 0xff);
5788 data [idx+1] = (byte) ((val >> 8) & 0xff);
5789 data [idx+2] = (byte) ((val >> 16) & 0xff);
5790 data [idx+3] = (byte) (val >> 24);
5792 } else if (array_element_type == TypeManager.uint32_type) {
5793 if (!(v is Expression)){
5794 uint val = (uint) v;
5796 data [idx] = (byte) (val & 0xff);
5797 data [idx+1] = (byte) ((val >> 8) & 0xff);
5798 data [idx+2] = (byte) ((val >> 16) & 0xff);
5799 data [idx+3] = (byte) (val >> 24);
5801 } else if (array_element_type == TypeManager.sbyte_type) {
5802 if (!(v is Expression)){
5803 sbyte val = (sbyte) v;
5804 data [idx] = (byte) val;
5806 } else if (array_element_type == TypeManager.byte_type) {
5807 if (!(v is Expression)){
5808 byte val = (byte) v;
5809 data [idx] = (byte) val;
5811 } else if (array_element_type == TypeManager.bool_type) {
5812 if (!(v is Expression)){
5813 bool val = (bool) v;
5814 data [idx] = (byte) (val ? 1 : 0);
5816 } else if (array_element_type == TypeManager.decimal_type){
5817 if (!(v is Expression)){
5818 int [] bits = Decimal.GetBits ((decimal) v);
5821 // FIXME: For some reason, this doesn't work on the MS runtime.
5822 int [] nbits = new int [4];
5823 nbits [0] = bits [3];
5824 nbits [1] = bits [2];
5825 nbits [2] = bits [0];
5826 nbits [3] = bits [1];
5828 for (int j = 0; j < 4; j++){
5829 data [p++] = (byte) (nbits [j] & 0xff);
5830 data [p++] = (byte) ((nbits [j] >> 8) & 0xff);
5831 data [p++] = (byte) ((nbits [j] >> 16) & 0xff);
5832 data [p++] = (byte) (nbits [j] >> 24);
5836 throw new Exception ("Unrecognized type in MakeByteBlob: " + array_element_type);
5845 // Emits the initializers for the array
5847 void EmitStaticInitializers (EmitContext ec)
5850 // First, the static data
5853 ILGenerator ig = ec.ig;
5855 byte [] data = MakeByteBlob ();
5857 fb = RootContext.MakeStaticData (data);
5859 ig.Emit (OpCodes.Dup);
5860 ig.Emit (OpCodes.Ldtoken, fb);
5861 ig.Emit (OpCodes.Call,
5862 TypeManager.void_initializearray_array_fieldhandle);
5866 // Emits pieces of the array that can not be computed at compile
5867 // time (variables and string locations).
5869 // This always expect the top value on the stack to be the array
5871 void EmitDynamicInitializers (EmitContext ec, bool emitConstants)
5873 ILGenerator ig = ec.ig;
5874 int dims = bounds.Count;
5875 int [] current_pos = new int [dims];
5877 MethodInfo set = null;
5880 Type [] args = new Type [dims + 1];
5882 for (int j = 0; j < dims; j++)
5883 args [j] = TypeManager.int32_type;
5884 args [dims] = array_element_type;
5886 set = CodeGen.Module.Builder.GetArrayMethod (
5888 CallingConventions.HasThis | CallingConventions.Standard,
5889 TypeManager.void_type, args);
5892 for (int i = 0; i < array_data.Count; i++){
5894 Expression e = (Expression)array_data [i];
5896 // Constant can be initialized via StaticInitializer
5897 if (e != null && !(!emitConstants && e is Constant)) {
5898 Type etype = e.Type;
5900 ig.Emit (OpCodes.Dup);
5902 for (int idx = 0; idx < dims; idx++)
5903 IntConstant.EmitInt (ig, current_pos [idx]);
5906 // If we are dealing with a struct, get the
5907 // address of it, so we can store it.
5909 if ((dims == 1) && etype.IsValueType &&
5910 (!TypeManager.IsBuiltinOrEnum (etype) ||
5911 etype == TypeManager.decimal_type)) {
5916 // Let new know that we are providing
5917 // the address where to store the results
5919 n.DisableTemporaryValueType ();
5922 ig.Emit (OpCodes.Ldelema, etype);
5928 bool is_stobj, has_type_arg;
5929 OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj, out has_type_arg);
5931 ig.Emit (OpCodes.Stobj, etype);
5932 else if (has_type_arg)
5933 ig.Emit (op, etype);
5937 ig.Emit (OpCodes.Call, set);
5944 for (int j = dims - 1; j >= 0; j--){
5946 if (current_pos [j] < (int) bounds [j])
5948 current_pos [j] = 0;
5953 void EmitArrayArguments (EmitContext ec)
5955 ILGenerator ig = ec.ig;
5957 foreach (Argument a in arguments) {
5958 Type atype = a.Type;
5961 if (atype == TypeManager.uint64_type)
5962 ig.Emit (OpCodes.Conv_Ovf_U4);
5963 else if (atype == TypeManager.int64_type)
5964 ig.Emit (OpCodes.Conv_Ovf_I4);
5968 public override void Emit (EmitContext ec)
5970 ILGenerator ig = ec.ig;
5972 EmitArrayArguments (ec);
5973 if (arguments.Count == 1)
5974 ig.Emit (OpCodes.Newarr, array_element_type);
5976 ig.Emit (OpCodes.Newobj, GetArrayMethod (arguments.Count));
5979 if (initializers == null)
5982 // Emit static initializer for arrays which have contain more than 4 items and
5983 // the static initializer will initialize at least 25% of array values.
5984 // NOTE: const_initializers_count does not contain default constant values.
5985 if (const_initializers_count >= 4 && const_initializers_count * 4 > (array_data.Count) &&
5986 TypeManager.IsPrimitiveType (array_element_type)) {
5987 EmitStaticInitializers (ec);
5989 if (!only_constant_initializers)
5990 EmitDynamicInitializers (ec, false);
5992 EmitDynamicInitializers (ec, true);
5996 public override bool GetAttributableValue (Type valueType, out object value)
5998 if (arguments.Count != 1) {
5999 // Report.Error (-211, Location, "attribute can not encode multi-dimensional arrays");
6000 return base.GetAttributableValue (null, out value);
6003 if (array_data == null) {
6004 Constant c = (Constant)((Argument)arguments [0]).Expr;
6005 if (c.IsDefaultValue) {
6006 value = Array.CreateInstance (array_element_type, 0);
6009 // Report.Error (-212, Location, "array should be initialized when passing it to an attribute");
6010 return base.GetAttributableValue (null, out value);
6013 Array ret = Array.CreateInstance (array_element_type, array_data.Count);
6014 object element_value;
6015 for (int i = 0; i < ret.Length; ++i)
6017 Expression e = (Expression)array_data [i];
6019 // Is null when an initializer is optimized (value == predefined value)
6023 if (!e.GetAttributableValue (array_element_type, out element_value)) {
6027 ret.SetValue (element_value, i);
6033 protected override void CloneTo (CloneContext clonectx, Expression t)
6035 ArrayCreation target = (ArrayCreation) t;
6037 target.requested_base_type = requested_base_type.Clone (clonectx);
6038 target.arguments = new ArrayList ();
6039 foreach (Argument a in arguments)
6040 target.arguments.Add (a.Clone (clonectx));
6042 if (initializers != null){
6043 target.initializers = new ArrayList ();
6044 foreach (Expression initializer in initializers)
6045 target.initializers.Add (initializer.Clone (clonectx));
6051 // Represents an implicitly typed array epxression
6053 public class ImplicitlyTypedArrayCreation : ArrayCreation
6055 public ImplicitlyTypedArrayCreation (string rank, ArrayList initializers, Location loc)
6056 : base (null, rank, initializers, loc)
6058 if (rank.Length > 2) {
6059 while (rank [++dimensions] == ',');
6065 public override Expression DoResolve (EmitContext ec)
6070 if (!ResolveInitializers (ec))
6073 if (array_element_type == null || array_element_type == TypeManager.null_type ||
6074 array_element_type == TypeManager.void_type || array_element_type == TypeManager.anonymous_method_type ||
6075 arguments.Count != dimensions) {
6076 Report.Error (826, loc, "The type of an implicitly typed array cannot be inferred from the initializer. Try specifying array type explicitly");
6081 // At this point we found common base type for all initializer elements
6082 // but we have to be sure that all static initializer elements are of
6085 UnifyInitializerElement (ec);
6087 type = TypeManager.GetConstructedType (array_element_type, rank);
6088 eclass = ExprClass.Value;
6093 // Converts static initializer only
6095 void UnifyInitializerElement (EmitContext ec)
6097 for (int i = 0; i < array_data.Count; ++i) {
6098 Expression e = (Expression)array_data[i];
6100 array_data [i] = Convert.ImplicitConversionStandard (ec, e, array_element_type, Location.Null);
6104 protected override Expression ResolveArrayElement (EmitContext ec, Expression element)
6106 element = element.Resolve (ec);
6107 if (element == null)
6110 if (array_element_type == null) {
6111 array_element_type = element.Type;
6115 if (Convert.ImplicitStandardConversionExists (element, array_element_type)) {
6119 if (Convert.ImplicitStandardConversionExists (new TypeExpression (array_element_type, loc), element.Type)) {
6120 array_element_type = element.Type;
6124 element.Error_ValueCannotBeConverted (ec, element.Location, array_element_type, false);
6129 public sealed class CompilerGeneratedThis : This
6131 public static This Instance = new CompilerGeneratedThis ();
6133 private CompilerGeneratedThis ()
6134 : base (Location.Null)
6138 public override Expression DoResolve (EmitContext ec)
6140 eclass = ExprClass.Variable;
6141 type = ec.ContainerType;
6142 variable = new SimpleThis (type);
6148 /// Represents the `this' construct
6151 public class This : VariableReference, IVariable
6154 VariableInfo variable_info;
6155 protected Variable variable;
6158 public This (Block block, Location loc)
6164 public This (Location loc)
6169 public VariableInfo VariableInfo {
6170 get { return variable_info; }
6173 public bool VerifyFixed ()
6175 return !TypeManager.IsValueType (Type);
6178 public override bool IsRef {
6179 get { return is_struct; }
6182 public override Variable Variable {
6183 get { return variable; }
6186 public bool ResolveBase (EmitContext ec)
6188 eclass = ExprClass.Variable;
6190 if (ec.TypeContainer.CurrentType != null)
6191 type = ec.TypeContainer.CurrentType;
6193 type = ec.ContainerType;
6195 is_struct = ec.TypeContainer is Struct;
6198 Error (26, "Keyword `this' is not valid in a static property, " +
6199 "static method, or static field initializer");
6203 if (block != null) {
6204 if (block.Toplevel.ThisVariable != null)
6205 variable_info = block.Toplevel.ThisVariable.VariableInfo;
6207 AnonymousContainer am = ec.CurrentAnonymousMethod;
6208 if (is_struct && (am != null) && !am.IsIterator) {
6209 Report.Error (1673, loc, "Anonymous methods inside structs " +
6210 "cannot access instance members of `this'. " +
6211 "Consider copying `this' to a local variable " +
6212 "outside the anonymous method and using the " +
6217 RootScopeInfo host = block.Toplevel.RootScope;
6218 if ((host != null) && !ec.IsConstructor &&
6219 (!is_struct || host.IsIterator)) {
6220 variable = host.CaptureThis ();
6221 type = variable.Type;
6226 if (variable == null)
6227 variable = new SimpleThis (type);
6233 // Called from Invocation to check if the invocation is correct
6235 public bool CheckThisUsage (EmitContext ec)
6237 if ((variable_info != null) && !(type.IsValueType && ec.OmitStructFlowAnalysis) &&
6238 !variable_info.IsAssigned (ec)) {
6239 Error (188, "The `this' object cannot be used before all of its " +
6240 "fields are assigned to");
6241 variable_info.SetAssigned (ec);
6248 public override Expression DoResolve (EmitContext ec)
6250 if (!ResolveBase (ec))
6254 if (ec.IsFieldInitializer) {
6255 Error (27, "Keyword `this' is not available in the current context");
6262 override public Expression DoResolveLValue (EmitContext ec, Expression right_side)
6264 if (!ResolveBase (ec))
6267 if (variable_info != null)
6268 variable_info.SetAssigned (ec);
6270 if (ec.TypeContainer is Class){
6271 Error (1604, "Cannot assign to 'this' because it is read-only");
6277 public override int GetHashCode()
6279 return block.GetHashCode ();
6282 public override bool Equals (object obj)
6284 This t = obj as This;
6288 return block == t.block;
6291 protected class SimpleThis : Variable
6295 public SimpleThis (Type type)
6300 public override Type Type {
6301 get { return type; }
6304 public override bool HasInstance {
6305 get { return false; }
6308 public override bool NeedsTemporary {
6309 get { return false; }
6312 public override void EmitInstance (EmitContext ec)
6317 public override void Emit (EmitContext ec)
6319 ec.ig.Emit (OpCodes.Ldarg_0);
6322 public override void EmitAssign (EmitContext ec)
6324 throw new InvalidOperationException ();
6327 public override void EmitAddressOf (EmitContext ec)
6329 ec.ig.Emit (OpCodes.Ldarg_0);
6333 protected override void CloneTo (CloneContext clonectx, Expression t)
6335 This target = (This) t;
6337 target.block = clonectx.LookupBlock (block);
6342 /// Represents the `__arglist' construct
6344 public class ArglistAccess : Expression
6346 public ArglistAccess (Location loc)
6351 public override Expression DoResolve (EmitContext ec)
6353 eclass = ExprClass.Variable;
6354 type = TypeManager.runtime_argument_handle_type;
6356 if (ec.IsFieldInitializer || !ec.CurrentBlock.Toplevel.HasVarargs)
6358 Error (190, "The __arglist construct is valid only within " +
6359 "a variable argument method");
6366 public override void Emit (EmitContext ec)
6368 ec.ig.Emit (OpCodes.Arglist);
6371 protected override void CloneTo (CloneContext clonectx, Expression target)
6378 /// Represents the `__arglist (....)' construct
6380 public class Arglist : Expression
6382 public Argument[] Arguments;
6384 public Arglist (Location loc)
6385 : this (Argument.Empty, loc)
6389 public Arglist (Argument[] args, Location l)
6395 public Type[] ArgumentTypes {
6397 Type[] retval = new Type [Arguments.Length];
6398 for (int i = 0; i < Arguments.Length; i++)
6399 retval [i] = Arguments [i].Type;
6404 public override Expression DoResolve (EmitContext ec)
6406 eclass = ExprClass.Variable;
6407 type = TypeManager.runtime_argument_handle_type;
6409 foreach (Argument arg in Arguments) {
6410 if (!arg.Resolve (ec, loc))
6417 public override void Emit (EmitContext ec)
6419 foreach (Argument arg in Arguments)
6423 protected override void CloneTo (CloneContext clonectx, Expression t)
6425 Arglist target = (Arglist) t;
6427 target.Arguments = new Argument [Arguments.Length];
6428 for (int i = 0; i < Arguments.Length; i++)
6429 target.Arguments [i] = Arguments [i].Clone (clonectx);
6434 // This produces the value that renders an instance, used by the iterators code
6436 public class ProxyInstance : Expression, IMemoryLocation {
6437 public override Expression DoResolve (EmitContext ec)
6439 eclass = ExprClass.Variable;
6440 type = ec.ContainerType;
6444 public override void Emit (EmitContext ec)
6446 ec.ig.Emit (OpCodes.Ldarg_0);
6450 public void AddressOf (EmitContext ec, AddressOp mode)
6452 ec.ig.Emit (OpCodes.Ldarg_0);
6457 /// Implements the typeof operator
6459 public class TypeOf : Expression {
6460 Expression QueriedType;
6461 protected Type typearg;
6463 public TypeOf (Expression queried_type, Location l)
6465 QueriedType = queried_type;
6469 public override Expression DoResolve (EmitContext ec)
6471 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6475 typearg = texpr.Type;
6477 if (typearg == TypeManager.void_type) {
6478 Error (673, "System.Void cannot be used from C#. Use typeof (void) to get the void type object");
6482 if (typearg.IsPointer && !ec.InUnsafe){
6487 type = TypeManager.type_type;
6488 // Even though what is returned is a type object, it's treated as a value by the compiler.
6489 // In particular, 'typeof (Foo).X' is something totally different from 'Foo.X'.
6490 eclass = ExprClass.Value;
6494 public override void Emit (EmitContext ec)
6496 ec.ig.Emit (OpCodes.Ldtoken, typearg);
6497 ec.ig.Emit (OpCodes.Call, TypeManager.system_type_get_type_from_handle);
6500 public override bool GetAttributableValue (Type valueType, out object value)
6502 if (TypeManager.ContainsGenericParameters (typearg)) {
6503 Report.SymbolRelatedToPreviousError(typearg);
6504 Report.Error(416, loc, "`{0}': an attribute argument cannot use type parameters",
6505 TypeManager.CSharpName(typearg));
6510 if (valueType == TypeManager.object_type) {
6511 value = (object)typearg;
6518 public Type TypeArgument
6526 protected override void CloneTo (CloneContext clonectx, Expression t)
6528 TypeOf target = (TypeOf) t;
6530 target.QueriedType = QueriedType.Clone (clonectx);
6535 /// Implements the `typeof (void)' operator
6537 public class TypeOfVoid : TypeOf {
6538 public TypeOfVoid (Location l) : base (null, l)
6543 public override Expression DoResolve (EmitContext ec)
6545 type = TypeManager.type_type;
6546 typearg = TypeManager.void_type;
6547 // See description in TypeOf.
6548 eclass = ExprClass.Value;
6554 /// Implements the sizeof expression
6556 public class SizeOf : Expression {
6557 public Expression QueriedType;
6560 public SizeOf (Expression queried_type, Location l)
6562 this.QueriedType = queried_type;
6566 public override Expression DoResolve (EmitContext ec)
6568 TypeExpr texpr = QueriedType.ResolveAsTypeTerminal (ec, false);
6573 if (texpr is TypeParameterExpr){
6574 ((TypeParameterExpr)texpr).Error_CannotUseAsUnmanagedType (loc);
6579 type_queried = texpr.Type;
6580 if (type_queried.IsEnum)
6581 type_queried = TypeManager.EnumToUnderlying (type_queried);
6583 if (type_queried == TypeManager.void_type) {
6584 Expression.Error_VoidInvalidInTheContext (loc);
6588 int size_of = GetTypeSize (type_queried);
6590 return new IntConstant (size_of, loc);
6594 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)",
6595 TypeManager.CSharpName (type_queried));
6599 if (!TypeManager.VerifyUnManaged (type_queried, loc)){
6603 type = TypeManager.int32_type;
6604 eclass = ExprClass.Value;
6608 public override void Emit (EmitContext ec)
6610 int size = GetTypeSize (type_queried);
6613 ec.ig.Emit (OpCodes.Sizeof, type_queried);
6615 IntConstant.EmitInt (ec.ig, size);
6618 protected override void CloneTo (CloneContext clonectx, Expression t)
6620 SizeOf target = (SizeOf) t;
6622 target.QueriedType = QueriedType.Clone (clonectx);
6627 /// Implements the qualified-alias-member (::) expression.
6629 public class QualifiedAliasMember : Expression
6631 string alias, identifier;
6633 public QualifiedAliasMember (string alias, string identifier, Location l)
6635 if (RootContext.Version == LanguageVersion.ISO_1)
6636 Report.FeatureIsNotISO1 (l, "namespace alias qualifier");
6639 this.identifier = identifier;
6643 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
6645 if (alias == "global")
6646 return new MemberAccess (RootNamespace.Global, identifier, loc).ResolveAsTypeStep (ec, silent);
6648 int errors = Report.Errors;
6649 FullNamedExpression fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6651 if (errors == Report.Errors)
6652 Report.Error (432, loc, "Alias `{0}' not found", alias);
6655 if (fne.eclass != ExprClass.Namespace) {
6657 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6660 return new MemberAccess (fne, identifier).ResolveAsTypeStep (ec, silent);
6663 public override Expression DoResolve (EmitContext ec)
6665 FullNamedExpression fne;
6666 if (alias == "global") {
6667 fne = RootNamespace.Global;
6669 int errors = Report.Errors;
6670 fne = ec.DeclContainer.NamespaceEntry.LookupAlias (alias);
6672 if (errors == Report.Errors)
6673 Report.Error (432, loc, "Alias `{0}' not found", alias);
6678 Expression retval = new MemberAccess (fne, identifier).DoResolve (ec);
6682 if (!(retval is FullNamedExpression)) {
6683 Report.Error (687, loc, "The expression `{0}::{1}' did not resolve to a namespace or a type", alias, identifier);
6687 // We defer this check till the end to match the behaviour of CSC
6688 if (fne.eclass != ExprClass.Namespace) {
6689 Report.Error (431, loc, "`{0}' cannot be used with '::' since it denotes a type", alias);
6695 public override void Emit (EmitContext ec)
6697 throw new InternalErrorException ("QualifiedAliasMember found in resolved tree");
6701 public override string ToString ()
6703 return alias + "::" + identifier;
6706 public override string GetSignatureForError ()
6711 protected override void CloneTo (CloneContext clonectx, Expression t)
6718 /// Implements the member access expression
6720 public class MemberAccess : Expression {
6721 public readonly string Identifier;
6724 public MemberAccess (Expression expr, string id)
6725 : this (expr, id, expr.Location)
6729 public MemberAccess (Expression expr, string identifier, Location loc)
6732 Identifier = identifier;
6736 public MemberAccess (Expression expr, string identifier, TypeArguments args, Location loc)
6737 : this (expr, identifier, loc)
6744 protected string LookupIdentifier {
6745 get { return MemberName.MakeName (Identifier, args); }
6748 // TODO: this method has very poor performace for Enum fields and
6749 // probably for other constants as well
6750 Expression DoResolve (EmitContext ec, Expression right_side)
6753 throw new Exception ();
6756 // Resolve the expression with flow analysis turned off, we'll do the definite
6757 // assignment checks later. This is because we don't know yet what the expression
6758 // will resolve to - it may resolve to a FieldExpr and in this case we must do the
6759 // definite assignment check on the actual field and not on the whole struct.
6762 SimpleName original = expr as SimpleName;
6763 Expression expr_resolved = expr.Resolve (ec,
6764 ResolveFlags.VariableOrValue | ResolveFlags.Type |
6765 ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
6767 if (expr_resolved == null)
6770 if (expr_resolved is Namespace) {
6771 Namespace ns = (Namespace) expr_resolved;
6772 FullNamedExpression retval = ns.Lookup (ec.DeclContainer, LookupIdentifier, loc);
6774 if ((retval != null) && (args != null))
6775 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (ec, false);
6779 ns.Error_NamespaceDoesNotExist (ec.DeclContainer, loc, Identifier);
6783 Type expr_type = expr_resolved.Type;
6784 if (expr_type.IsPointer || expr_type == TypeManager.void_type || expr_resolved is NullLiteral){
6785 Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type);
6788 if (expr_type == TypeManager.anonymous_method_type){
6789 Unary.Error_OperatorCannotBeApplied (loc, ".", "anonymous method");
6793 Constant c = expr_resolved as Constant;
6794 if (c != null && c.GetValue () == null) {
6795 Report.Warning (1720, 1, loc, "Expression will always cause a `{0}'",
6796 "System.NullReferenceException");
6799 Expression member_lookup;
6800 member_lookup = MemberLookup (
6801 ec.ContainerType, expr_type, expr_type, Identifier, loc);
6803 if ((member_lookup == null) && (args != null)) {
6804 member_lookup = MemberLookup (
6805 ec.ContainerType, expr_type, expr_type, LookupIdentifier, loc);
6808 if (member_lookup == null) {
6809 ExtensionMethodGroupExpr ex_method_lookup = ec.DeclContainer.LookupExtensionMethod (expr_type, Identifier);
6810 if (ex_method_lookup != null) {
6811 ex_method_lookup.ExtensionExpression = expr_resolved;
6812 return ex_method_lookup.DoResolve (ec);
6815 MemberLookupFailed (
6816 ec.ContainerType, expr_type, expr_type, Identifier, null, true, loc);
6820 TypeExpr texpr = member_lookup as TypeExpr;
6821 if (texpr != null) {
6822 if (!(expr_resolved is TypeExpr) &&
6823 (original == null || !original.IdenticalNameAndTypeName (ec, expr_resolved, loc))) {
6824 Report.Error (572, loc, "`{0}': cannot reference a type through an expression; try `{1}' instead",
6825 Identifier, member_lookup.GetSignatureForError ());
6829 if (!texpr.CheckAccessLevel (ec.DeclContainer)) {
6830 Report.SymbolRelatedToPreviousError (member_lookup.Type);
6831 ErrorIsInaccesible (loc, TypeManager.CSharpName (member_lookup.Type));
6836 ConstructedType ct = expr_resolved as ConstructedType;
6839 // When looking up a nested type in a generic instance
6840 // via reflection, we always get a generic type definition
6841 // and not a generic instance - so we have to do this here.
6843 // See gtest-172-lib.cs and gtest-172.cs for an example.
6845 ct = new ConstructedType (
6846 member_lookup.Type, ct.TypeArguments, loc);
6848 return ct.ResolveAsTypeStep (ec, false);
6851 return member_lookup;
6854 MemberExpr me = (MemberExpr) member_lookup;
6855 member_lookup = me.ResolveMemberAccess (ec, expr_resolved, loc, original);
6856 if (member_lookup == null)
6860 MethodGroupExpr mg = member_lookup as MethodGroupExpr;
6862 throw new InternalErrorException ();
6864 return mg.ResolveGeneric (ec, args);
6867 if (original != null && !TypeManager.IsValueType (expr_type)) {
6868 me = member_lookup as MemberExpr;
6869 if (me != null && me.IsInstance) {
6870 LocalVariableReference var = expr_resolved as LocalVariableReference;
6871 if (var != null && !var.VerifyAssigned (ec))
6876 // The following DoResolve/DoResolveLValue will do the definite assignment
6879 if (right_side != null)
6880 return member_lookup.DoResolveLValue (ec, right_side);
6882 return member_lookup.DoResolve (ec);
6885 public override Expression DoResolve (EmitContext ec)
6887 return DoResolve (ec, null);
6890 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
6892 return DoResolve (ec, right_side);
6895 public override FullNamedExpression ResolveAsTypeStep (IResolveContext ec, bool silent)
6897 return ResolveNamespaceOrType (ec, silent);
6900 public FullNamedExpression ResolveNamespaceOrType (IResolveContext rc, bool silent)
6902 FullNamedExpression new_expr = expr.ResolveAsTypeStep (rc, silent);
6904 if (new_expr == null)
6907 if (new_expr is Namespace) {
6908 Namespace ns = (Namespace) new_expr;
6909 FullNamedExpression retval = ns.Lookup (rc.DeclContainer, LookupIdentifier, loc);
6911 if ((retval != null) && (args != null))
6912 retval = new ConstructedType (retval, args, loc).ResolveAsTypeStep (rc, false);
6914 if (!silent && retval == null)
6915 ns.Error_NamespaceDoesNotExist (rc.DeclContainer, loc, LookupIdentifier);
6919 TypeExpr tnew_expr = new_expr.ResolveAsTypeTerminal (rc, false);
6920 if (tnew_expr == null)
6923 Type expr_type = tnew_expr.Type;
6925 if (expr_type.IsPointer){
6926 Error (23, "The `.' operator can not be applied to pointer operands (" +
6927 TypeManager.CSharpName (expr_type) + ")");
6931 Expression member_lookup = MemberLookup (
6932 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
6933 MemberTypes.NestedType, BindingFlags.Public | BindingFlags.NonPublic, loc);
6934 if (member_lookup == null) {
6938 member_lookup = MemberLookup(
6939 rc.DeclContainer.TypeBuilder, expr_type, expr_type, LookupIdentifier,
6940 MemberTypes.All, BindingFlags.Public | BindingFlags.NonPublic, loc);
6942 if (member_lookup == null) {
6943 Report.Error (426, loc, "The nested type `{0}' does not exist in the type `{1}'",
6944 Identifier, new_expr.GetSignatureForError ());
6946 // TODO: Report.SymbolRelatedToPreviousError
6947 member_lookup.Error_UnexpectedKind (null, "type", loc);
6952 TypeExpr texpr = member_lookup.ResolveAsTypeTerminal (rc, false);
6957 TypeArguments the_args = args;
6958 if (TypeManager.HasGenericArguments (expr_type)) {
6959 Type[] decl_args = TypeManager.GetTypeArguments (expr_type);
6961 TypeArguments new_args = new TypeArguments (loc);
6962 foreach (Type decl in decl_args)
6963 new_args.Add (new TypeExpression (decl, loc));
6966 new_args.Add (args);
6968 the_args = new_args;
6971 if (the_args != null) {
6972 ConstructedType ctype = new ConstructedType (texpr.Type, the_args, loc);
6973 return ctype.ResolveAsTypeStep (rc, false);
6980 public override void Emit (EmitContext ec)
6982 throw new Exception ("Should not happen");
6985 public override string ToString ()
6987 return expr + "." + MemberName.MakeName (Identifier, args);
6990 public override string GetSignatureForError ()
6992 return expr.GetSignatureForError () + "." + Identifier;
6995 protected override void CloneTo (CloneContext clonectx, Expression t)
6997 MemberAccess target = (MemberAccess) t;
6999 target.expr = expr.Clone (clonectx);
7004 /// Implements checked expressions
7006 public class CheckedExpr : Expression {
7008 public Expression Expr;
7010 public CheckedExpr (Expression e, Location l)
7016 public override Expression DoResolve (EmitContext ec)
7018 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7019 Expr = Expr.Resolve (ec);
7024 if (Expr is Constant)
7027 eclass = Expr.eclass;
7032 public override void Emit (EmitContext ec)
7034 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7038 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7040 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
7041 Expr.EmitBranchable (ec, target, onTrue);
7044 protected override void CloneTo (CloneContext clonectx, Expression t)
7046 CheckedExpr target = (CheckedExpr) t;
7048 target.Expr = Expr.Clone (clonectx);
7053 /// Implements the unchecked expression
7055 public class UnCheckedExpr : Expression {
7057 public Expression Expr;
7059 public UnCheckedExpr (Expression e, Location l)
7065 public override Expression DoResolve (EmitContext ec)
7067 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7068 Expr = Expr.Resolve (ec);
7073 if (Expr is Constant)
7076 eclass = Expr.eclass;
7081 public override void Emit (EmitContext ec)
7083 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7087 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
7089 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
7090 Expr.EmitBranchable (ec, target, onTrue);
7093 protected override void CloneTo (CloneContext clonectx, Expression t)
7095 UnCheckedExpr target = (UnCheckedExpr) t;
7097 target.Expr = Expr.Clone (clonectx);
7102 /// An Element Access expression.
7104 /// During semantic analysis these are transformed into
7105 /// IndexerAccess, ArrayAccess or a PointerArithmetic.
7107 public class ElementAccess : Expression {
7108 public ArrayList Arguments;
7109 public Expression Expr;
7111 public ElementAccess (Expression e, ArrayList e_list)
7120 Arguments = new ArrayList ();
7121 foreach (Expression tmp in e_list)
7122 Arguments.Add (new Argument (tmp, Argument.AType.Expression));
7126 bool CommonResolve (EmitContext ec)
7128 Expr = Expr.Resolve (ec);
7133 if (Arguments == null)
7136 foreach (Argument a in Arguments){
7137 if (!a.Resolve (ec, loc))
7144 Expression MakePointerAccess (EmitContext ec, Type t)
7146 if (t == TypeManager.void_ptr_type){
7147 Error (242, "The array index operation is not valid on void pointers");
7150 if (Arguments.Count != 1){
7151 Error (196, "A pointer must be indexed by only one value");
7156 p = new PointerArithmetic (true, Expr, ((Argument)Arguments [0]).Expr, t, loc).Resolve (ec);
7159 return new Indirection (p, loc).Resolve (ec);
7162 public override Expression DoResolve (EmitContext ec)
7164 if (!CommonResolve (ec))
7168 // We perform some simple tests, and then to "split" the emit and store
7169 // code we create an instance of a different class, and return that.
7171 // I am experimenting with this pattern.
7175 if (t == TypeManager.array_type){
7176 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `System.Array'");
7181 return (new ArrayAccess (this, loc)).Resolve (ec);
7183 return MakePointerAccess (ec, Expr.Type);
7185 FieldExpr fe = Expr as FieldExpr;
7187 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7189 return MakePointerAccess (ec, ff.ElementType);
7192 return (new IndexerAccess (this, loc)).Resolve (ec);
7195 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7197 if (!CommonResolve (ec))
7202 return (new ArrayAccess (this, loc)).DoResolveLValue (ec, right_side);
7205 return MakePointerAccess (ec, Expr.Type);
7207 FieldExpr fe = Expr as FieldExpr;
7209 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
7211 if (!(fe.InstanceExpression is LocalVariableReference) &&
7212 !(fe.InstanceExpression is This)) {
7213 Report.Error (1708, loc, "Fixed size buffers can only be accessed through locals or fields");
7216 if (!ec.InFixedInitializer && ec.ContainerType.IsValueType) {
7217 Error (1666, "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement");
7220 return MakePointerAccess (ec, ff.ElementType);
7223 return (new IndexerAccess (this, loc)).DoResolveLValue (ec, right_side);
7226 public override void Emit (EmitContext ec)
7228 throw new Exception ("Should never be reached");
7231 protected override void CloneTo (CloneContext clonectx, Expression t)
7233 ElementAccess target = (ElementAccess) t;
7235 target.Expr = Expr.Clone (clonectx);
7236 target.Arguments = new ArrayList ();
7237 foreach (Argument a in Arguments)
7238 target.Arguments.Add (a.Clone (clonectx));
7243 /// Implements array access
7245 public class ArrayAccess : Expression, IAssignMethod, IMemoryLocation {
7247 // Points to our "data" repository
7251 LocalTemporary temp;
7254 public ArrayAccess (ElementAccess ea_data, Location l)
7257 eclass = ExprClass.Variable;
7261 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7263 return DoResolve (ec);
7266 public override Expression DoResolve (EmitContext ec)
7269 ExprClass eclass = ea.Expr.eclass;
7271 // As long as the type is valid
7272 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
7273 eclass == ExprClass.Value)) {
7274 ea.Expr.Error_UnexpectedKind ("variable or value");
7279 Type t = ea.Expr.Type;
7280 if (t.GetArrayRank () != ea.Arguments.Count){
7281 Report.Error (22, ea.Location, "Wrong number of indexes `{0}' inside [], expected `{1}'",
7282 ea.Arguments.Count.ToString (), t.GetArrayRank ().ToString ());
7286 type = TypeManager.GetElementType (t);
7287 if (type.IsPointer && !ec.InUnsafe){
7288 UnsafeError (ea.Location);
7292 foreach (Argument a in ea.Arguments){
7293 Type argtype = a.Type;
7295 if (argtype == TypeManager.int32_type ||
7296 argtype == TypeManager.uint32_type ||
7297 argtype == TypeManager.int64_type ||
7298 argtype == TypeManager.uint64_type) {
7299 Constant c = a.Expr as Constant;
7300 if (c != null && c.IsNegative) {
7301 Report.Warning (251, 2, ea.Location, "Indexing an array with a negative index (array indices always start at zero)");
7307 // Mhm. This is strage, because the Argument.Type is not the same as
7308 // Argument.Expr.Type: the value changes depending on the ref/out setting.
7310 // Wonder if I will run into trouble for this.
7312 a.Expr = ExpressionToArrayArgument (ec, a.Expr, ea.Location);
7317 eclass = ExprClass.Variable;
7323 /// Emits the right opcode to load an object of Type `t'
7324 /// from an array of T
7326 static public void EmitLoadOpcode (ILGenerator ig, Type type)
7328 if (type == TypeManager.byte_type || type == TypeManager.bool_type)
7329 ig.Emit (OpCodes.Ldelem_U1);
7330 else if (type == TypeManager.sbyte_type)
7331 ig.Emit (OpCodes.Ldelem_I1);
7332 else if (type == TypeManager.short_type)
7333 ig.Emit (OpCodes.Ldelem_I2);
7334 else if (type == TypeManager.ushort_type || type == TypeManager.char_type)
7335 ig.Emit (OpCodes.Ldelem_U2);
7336 else if (type == TypeManager.int32_type)
7337 ig.Emit (OpCodes.Ldelem_I4);
7338 else if (type == TypeManager.uint32_type)
7339 ig.Emit (OpCodes.Ldelem_U4);
7340 else if (type == TypeManager.uint64_type)
7341 ig.Emit (OpCodes.Ldelem_I8);
7342 else if (type == TypeManager.int64_type)
7343 ig.Emit (OpCodes.Ldelem_I8);
7344 else if (type == TypeManager.float_type)
7345 ig.Emit (OpCodes.Ldelem_R4);
7346 else if (type == TypeManager.double_type)
7347 ig.Emit (OpCodes.Ldelem_R8);
7348 else if (type == TypeManager.intptr_type)
7349 ig.Emit (OpCodes.Ldelem_I);
7350 else if (TypeManager.IsEnumType (type)){
7351 EmitLoadOpcode (ig, TypeManager.EnumToUnderlying (type));
7352 } else if (type.IsValueType){
7353 ig.Emit (OpCodes.Ldelema, type);
7354 ig.Emit (OpCodes.Ldobj, type);
7356 } else if (type.IsGenericParameter) {
7357 ig.Emit (OpCodes.Ldelem, type);
7359 } else if (type.IsPointer)
7360 ig.Emit (OpCodes.Ldelem_I);
7362 ig.Emit (OpCodes.Ldelem_Ref);
7366 /// Returns the right opcode to store an object of Type `t'
7367 /// from an array of T.
7369 static public OpCode GetStoreOpcode (Type t, out bool is_stobj, out bool has_type_arg)
7371 //Console.WriteLine (new System.Diagnostics.StackTrace ());
7372 has_type_arg = false; is_stobj = false;
7373 t = TypeManager.TypeToCoreType (t);
7374 if (TypeManager.IsEnumType (t))
7375 t = TypeManager.EnumToUnderlying (t);
7376 if (t == TypeManager.byte_type || t == TypeManager.sbyte_type ||
7377 t == TypeManager.bool_type)
7378 return OpCodes.Stelem_I1;
7379 else if (t == TypeManager.short_type || t == TypeManager.ushort_type ||
7380 t == TypeManager.char_type)
7381 return OpCodes.Stelem_I2;
7382 else if (t == TypeManager.int32_type || t == TypeManager.uint32_type)
7383 return OpCodes.Stelem_I4;
7384 else if (t == TypeManager.int64_type || t == TypeManager.uint64_type)
7385 return OpCodes.Stelem_I8;
7386 else if (t == TypeManager.float_type)
7387 return OpCodes.Stelem_R4;
7388 else if (t == TypeManager.double_type)
7389 return OpCodes.Stelem_R8;
7390 else if (t == TypeManager.intptr_type) {
7391 has_type_arg = true;
7393 return OpCodes.Stobj;
7394 } else if (t.IsValueType) {
7395 has_type_arg = true;
7397 return OpCodes.Stobj;
7399 } else if (t.IsGenericParameter) {
7400 has_type_arg = true;
7401 return OpCodes.Stelem;
7404 } else if (t.IsPointer)
7405 return OpCodes.Stelem_I;
7407 return OpCodes.Stelem_Ref;
7410 MethodInfo FetchGetMethod ()
7412 ModuleBuilder mb = CodeGen.Module.Builder;
7413 int arg_count = ea.Arguments.Count;
7414 Type [] args = new Type [arg_count];
7417 for (int i = 0; i < arg_count; i++){
7418 //args [i++] = a.Type;
7419 args [i] = TypeManager.int32_type;
7422 get = mb.GetArrayMethod (
7423 ea.Expr.Type, "Get",
7424 CallingConventions.HasThis |
7425 CallingConventions.Standard,
7431 MethodInfo FetchAddressMethod ()
7433 ModuleBuilder mb = CodeGen.Module.Builder;
7434 int arg_count = ea.Arguments.Count;
7435 Type [] args = new Type [arg_count];
7439 ret_type = TypeManager.GetReferenceType (type);
7441 for (int i = 0; i < arg_count; i++){
7442 //args [i++] = a.Type;
7443 args [i] = TypeManager.int32_type;
7446 address = mb.GetArrayMethod (
7447 ea.Expr.Type, "Address",
7448 CallingConventions.HasThis |
7449 CallingConventions.Standard,
7456 // Load the array arguments into the stack.
7458 // If we have been requested to cache the values (cached_locations array
7459 // initialized), then load the arguments the first time and store them
7460 // in locals. otherwise load from local variables.
7462 void LoadArrayAndArguments (EmitContext ec)
7464 ILGenerator ig = ec.ig;
7467 foreach (Argument a in ea.Arguments){
7468 Type argtype = a.Expr.Type;
7472 if (argtype == TypeManager.int64_type)
7473 ig.Emit (OpCodes.Conv_Ovf_I);
7474 else if (argtype == TypeManager.uint64_type)
7475 ig.Emit (OpCodes.Conv_Ovf_I_Un);
7479 public void Emit (EmitContext ec, bool leave_copy)
7481 int rank = ea.Expr.Type.GetArrayRank ();
7482 ILGenerator ig = ec.ig;
7485 LoadArrayAndArguments (ec);
7488 EmitLoadOpcode (ig, type);
7492 method = FetchGetMethod ();
7493 ig.Emit (OpCodes.Call, method);
7496 LoadFromPtr (ec.ig, this.type);
7499 ec.ig.Emit (OpCodes.Dup);
7500 temp = new LocalTemporary (this.type);
7505 public override void Emit (EmitContext ec)
7510 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7512 int rank = ea.Expr.Type.GetArrayRank ();
7513 ILGenerator ig = ec.ig;
7514 Type t = source.Type;
7515 prepared = prepare_for_load;
7517 if (prepare_for_load) {
7518 AddressOf (ec, AddressOp.LoadStore);
7519 ec.ig.Emit (OpCodes.Dup);
7522 ec.ig.Emit (OpCodes.Dup);
7523 temp = new LocalTemporary (this.type);
7526 StoreFromPtr (ec.ig, t);
7536 LoadArrayAndArguments (ec);
7539 bool is_stobj, has_type_arg;
7540 OpCode op = GetStoreOpcode (t, out is_stobj, out has_type_arg);
7542 // The stobj opcode used by value types will need
7543 // an address on the stack, not really an array/array
7547 ig.Emit (OpCodes.Ldelema, t);
7551 ec.ig.Emit (OpCodes.Dup);
7552 temp = new LocalTemporary (this.type);
7557 ig.Emit (OpCodes.Stobj, t);
7558 else if (has_type_arg)
7563 ModuleBuilder mb = CodeGen.Module.Builder;
7564 int arg_count = ea.Arguments.Count;
7565 Type [] args = new Type [arg_count + 1];
7570 ec.ig.Emit (OpCodes.Dup);
7571 temp = new LocalTemporary (this.type);
7575 for (int i = 0; i < arg_count; i++){
7576 //args [i++] = a.Type;
7577 args [i] = TypeManager.int32_type;
7580 args [arg_count] = type;
7582 set = mb.GetArrayMethod (
7583 ea.Expr.Type, "Set",
7584 CallingConventions.HasThis |
7585 CallingConventions.Standard,
7586 TypeManager.void_type, args);
7588 ig.Emit (OpCodes.Call, set);
7597 public void AddressOf (EmitContext ec, AddressOp mode)
7599 int rank = ea.Expr.Type.GetArrayRank ();
7600 ILGenerator ig = ec.ig;
7602 LoadArrayAndArguments (ec);
7605 ig.Emit (OpCodes.Ldelema, type);
7607 MethodInfo address = FetchAddressMethod ();
7608 ig.Emit (OpCodes.Call, address);
7612 public void EmitGetLength (EmitContext ec, int dim)
7614 int rank = ea.Expr.Type.GetArrayRank ();
7615 ILGenerator ig = ec.ig;
7619 ig.Emit (OpCodes.Ldlen);
7620 ig.Emit (OpCodes.Conv_I4);
7622 IntLiteral.EmitInt (ig, dim);
7623 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
7629 // note that the ArrayList itself in mutable. We just can't assign to 'Properties' again.
7630 public readonly ArrayList Properties;
7631 static Indexers empty;
7633 public struct Indexer {
7634 public readonly PropertyInfo PropertyInfo;
7635 public readonly MethodInfo Getter, Setter;
7637 public Indexer (PropertyInfo property_info, MethodInfo get, MethodInfo set)
7639 this.PropertyInfo = property_info;
7647 empty = new Indexers (null);
7650 Indexers (ArrayList array)
7655 static void Append (ref Indexers ix, Type caller_type, MemberInfo [] mi)
7660 foreach (PropertyInfo property in mi){
7661 MethodInfo get, set;
7663 get = property.GetGetMethod (true);
7664 set = property.GetSetMethod (true);
7665 if (get != null && !Expression.IsAccessorAccessible (caller_type, get, out dummy))
7667 if (set != null && !Expression.IsAccessorAccessible (caller_type, set, out dummy))
7669 if (get != null || set != null) {
7671 ix = new Indexers (new ArrayList ());
7672 ix.Properties.Add (new Indexer (property, get, set));
7677 static private MemberInfo [] GetIndexersForTypeOrInterface (Type caller_type, Type lookup_type)
7679 string p_name = TypeManager.IndexerPropertyName (lookup_type);
7681 return TypeManager.MemberLookup (
7682 caller_type, caller_type, lookup_type, MemberTypes.Property,
7683 BindingFlags.Public | BindingFlags.Instance |
7684 BindingFlags.DeclaredOnly, p_name, null);
7687 static public Indexers GetIndexersForType (Type caller_type, Type lookup_type)
7689 Indexers ix = empty;
7692 if (lookup_type.IsGenericParameter) {
7693 GenericConstraints gc = TypeManager.GetTypeParameterConstraints (lookup_type);
7697 if (gc.HasClassConstraint)
7698 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, gc.ClassConstraint));
7700 Type[] ifaces = gc.InterfaceConstraints;
7701 foreach (Type itype in ifaces)
7702 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
7708 Type copy = lookup_type;
7709 while (copy != TypeManager.object_type && copy != null){
7710 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, copy));
7711 copy = copy.BaseType;
7714 if (lookup_type.IsInterface) {
7715 Type [] ifaces = TypeManager.GetInterfaces (lookup_type);
7716 if (ifaces != null) {
7717 foreach (Type itype in ifaces)
7718 Append (ref ix, caller_type, GetIndexersForTypeOrInterface (caller_type, itype));
7727 /// Expressions that represent an indexer call.
7729 public class IndexerAccess : Expression, IAssignMethod {
7731 // Points to our "data" repository
7733 MethodInfo get, set;
7734 ArrayList set_arguments;
7735 bool is_base_indexer;
7737 protected Type indexer_type;
7738 protected Type current_type;
7739 protected Expression instance_expr;
7740 protected ArrayList arguments;
7742 public IndexerAccess (ElementAccess ea, Location loc)
7743 : this (ea.Expr, false, loc)
7745 this.arguments = ea.Arguments;
7748 protected IndexerAccess (Expression instance_expr, bool is_base_indexer,
7751 this.instance_expr = instance_expr;
7752 this.is_base_indexer = is_base_indexer;
7753 this.eclass = ExprClass.Value;
7757 protected virtual bool CommonResolve (EmitContext ec)
7759 indexer_type = instance_expr.Type;
7760 current_type = ec.ContainerType;
7765 public override Expression DoResolve (EmitContext ec)
7767 if (!CommonResolve (ec))
7771 // Step 1: Query for all `Item' *properties*. Notice
7772 // that the actual methods are pointed from here.
7774 // This is a group of properties, piles of them.
7776 ArrayList AllGetters = null;
7778 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
7779 if (ilist.Properties != null) {
7780 AllGetters = new ArrayList(ilist.Properties.Count);
7781 foreach (Indexers.Indexer ix in ilist.Properties) {
7782 if (ix.Getter != null)
7783 AllGetters.Add (ix.Getter);
7787 if (AllGetters == null) {
7788 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7789 TypeManager.CSharpName (indexer_type));
7793 if (AllGetters.Count == 0) {
7794 // FIXME: we cannot simply select first one as the error message is missleading when
7795 // multiple indexers exist
7796 Indexers.Indexer first_indexer = (Indexers.Indexer)ilist.Properties[ilist.Properties.Count - 1];
7797 Report.Error (154, loc, "The property or indexer `{0}' cannot be used in this context because it lacks the `get' accessor",
7798 TypeManager.GetFullNameSignature (first_indexer.PropertyInfo));
7802 get = (MethodInfo)new MethodGroupExpr (AllGetters, loc).OverloadResolve (ec,
7803 arguments, false, loc);
7806 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7811 // Only base will allow this invocation to happen.
7813 if (get.IsAbstract && this is BaseIndexerAccess){
7814 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (get));
7818 type = get.ReturnType;
7819 if (type.IsPointer && !ec.InUnsafe){
7824 instance_expr.CheckMarshalByRefAccess ();
7826 eclass = ExprClass.IndexerAccess;
7830 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
7832 if (right_side == EmptyExpression.OutAccess) {
7833 Report.Error (206, loc, "A property or indexer `{0}' may not be passed as an out or ref parameter",
7834 GetSignatureForError ());
7838 // if the indexer returns a value type, and we try to set a field in it
7839 if (right_side == EmptyExpression.LValueMemberAccess || right_side == EmptyExpression.LValueMemberOutAccess) {
7840 Report.Error (1612, loc, "Cannot modify the return value of `{0}' because it is not a variable",
7841 GetSignatureForError ());
7845 ArrayList AllSetters = new ArrayList();
7846 if (!CommonResolve (ec))
7849 bool found_any = false, found_any_setters = false;
7851 Indexers ilist = Indexers.GetIndexersForType (current_type, indexer_type);
7852 if (ilist.Properties != null) {
7854 foreach (Indexers.Indexer ix in ilist.Properties) {
7855 if (ix.Setter != null)
7856 AllSetters.Add (ix.Setter);
7859 if (AllSetters.Count > 0) {
7860 found_any_setters = true;
7861 set_arguments = (ArrayList) arguments.Clone ();
7862 set_arguments.Add (new Argument (right_side, Argument.AType.Expression));
7863 set = (MethodInfo)(new MethodGroupExpr (AllSetters, loc)).OverloadResolve (
7865 set_arguments, false, loc);
7869 Report.Error (21, loc, "Cannot apply indexing with [] to an expression of type `{0}'",
7870 TypeManager.CSharpName (indexer_type));
7874 if (!found_any_setters) {
7875 Error (154, "indexer can not be used in this context, because " +
7876 "it lacks a `set' accessor");
7881 Invocation.Error_WrongNumArguments (loc, "this", arguments.Count);
7886 // Only base will allow this invocation to happen.
7888 if (set.IsAbstract && this is BaseIndexerAccess){
7889 Error_CannotCallAbstractBase (TypeManager.CSharpSignature (set));
7894 // Now look for the actual match in the list of indexers to set our "return" type
7896 type = TypeManager.void_type; // default value
7897 foreach (Indexers.Indexer ix in ilist.Properties){
7898 if (ix.Setter == set){
7899 type = ix.PropertyInfo.PropertyType;
7904 instance_expr.CheckMarshalByRefAccess ();
7906 eclass = ExprClass.IndexerAccess;
7910 bool prepared = false;
7911 LocalTemporary temp;
7913 public void Emit (EmitContext ec, bool leave_copy)
7915 Invocation.EmitCall (ec, is_base_indexer, instance_expr, get, arguments, loc, prepared, false);
7917 ec.ig.Emit (OpCodes.Dup);
7918 temp = new LocalTemporary (Type);
7924 // source is ignored, because we already have a copy of it from the
7925 // LValue resolution and we have already constructed a pre-cached
7926 // version of the arguments (ea.set_arguments);
7928 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
7930 prepared = prepare_for_load;
7931 Argument a = (Argument) set_arguments [set_arguments.Count - 1];
7936 ec.ig.Emit (OpCodes.Dup);
7937 temp = new LocalTemporary (Type);
7940 } else if (leave_copy) {
7941 temp = new LocalTemporary (Type);
7947 Invocation.EmitCall (ec, is_base_indexer, instance_expr, set, set_arguments, loc, false, prepared);
7956 public override void Emit (EmitContext ec)
7961 public override string GetSignatureForError ()
7963 // FIXME: print the argument list of the indexer
7964 return instance_expr.GetSignatureForError () + ".this[...]";
7967 protected override void CloneTo (CloneContext clonectx, Expression t)
7969 IndexerAccess target = (IndexerAccess) t;
7971 if (arguments != null){
7972 target.arguments = new ArrayList ();
7973 foreach (Argument a in arguments)
7974 target.arguments.Add (a.Clone (clonectx));
7976 if (instance_expr != null)
7977 target.instance_expr = instance_expr.Clone (clonectx);
7982 /// The base operator for method names
7984 public class BaseAccess : Expression {
7985 public readonly string Identifier;
7988 public BaseAccess (string member, Location l)
7990 this.Identifier = member;
7994 public BaseAccess (string member, TypeArguments args, Location l)
8000 public override Expression DoResolve (EmitContext ec)
8002 Expression c = CommonResolve (ec);
8008 // MethodGroups use this opportunity to flag an error on lacking ()
8010 if (!(c is MethodGroupExpr))
8011 return c.Resolve (ec);
8015 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
8017 Expression c = CommonResolve (ec);
8023 // MethodGroups use this opportunity to flag an error on lacking ()
8025 if (! (c is MethodGroupExpr))
8026 return c.DoResolveLValue (ec, right_side);
8031 Expression CommonResolve (EmitContext ec)
8033 Expression member_lookup;
8034 Type current_type = ec.ContainerType;
8035 Type base_type = current_type.BaseType;
8038 Error (1511, "Keyword `base' is not available in a static method");
8042 if (ec.IsFieldInitializer){
8043 Error (1512, "Keyword `base' is not available in the current context");
8047 member_lookup = MemberLookup (ec.ContainerType, null, base_type, Identifier,
8048 AllMemberTypes, AllBindingFlags, loc);
8049 if (member_lookup == null) {
8050 MemberLookupFailed (ec.ContainerType, base_type, base_type, Identifier, null, true, loc);
8057 left = new TypeExpression (base_type, loc);
8059 left = ec.GetThis (loc);
8061 MemberExpr me = (MemberExpr) member_lookup;
8063 Expression e = me.ResolveMemberAccess (ec, left, loc, null);
8065 if (e is PropertyExpr) {
8066 PropertyExpr pe = (PropertyExpr) e;
8071 MethodGroupExpr mg = e as MethodGroupExpr;
8077 return mg.ResolveGeneric (ec, args);
8079 Report.Error (307, loc, "`{0}' cannot be used with type arguments",
8087 public override void Emit (EmitContext ec)
8089 throw new Exception ("Should never be called");
8092 protected override void CloneTo (CloneContext clonectx, Expression t)
8094 BaseAccess target = (BaseAccess) t;
8096 target.args = args.Clone ();
8101 /// The base indexer operator
8103 public class BaseIndexerAccess : IndexerAccess {
8104 public BaseIndexerAccess (ArrayList args, Location loc)
8105 : base (null, true, loc)
8107 arguments = new ArrayList ();
8108 foreach (Expression tmp in args)
8109 arguments.Add (new Argument (tmp, Argument.AType.Expression));
8112 protected override bool CommonResolve (EmitContext ec)
8114 instance_expr = ec.GetThis (loc);
8116 current_type = ec.ContainerType.BaseType;
8117 indexer_type = current_type;
8119 foreach (Argument a in arguments){
8120 if (!a.Resolve (ec, loc))
8129 /// This class exists solely to pass the Type around and to be a dummy
8130 /// that can be passed to the conversion functions (this is used by
8131 /// foreach implementation to typecast the object return value from
8132 /// get_Current into the proper type. All code has been generated and
8133 /// we only care about the side effect conversions to be performed
8135 /// This is also now used as a placeholder where a no-action expression
8136 /// is needed (the `New' class).
8138 public class EmptyExpression : Expression {
8139 public static readonly EmptyExpression Null = new EmptyExpression ();
8141 public static readonly EmptyExpression OutAccess = new EmptyExpression ();
8142 public static readonly EmptyExpression LValueMemberAccess = new EmptyExpression ();
8143 public static readonly EmptyExpression LValueMemberOutAccess = new EmptyExpression ();
8145 static EmptyExpression temp = new EmptyExpression ();
8146 public static EmptyExpression Grab ()
8148 EmptyExpression retval = temp == null ? new EmptyExpression () : temp;
8153 public static void Release (EmptyExpression e)
8158 // TODO: should be protected
8159 public EmptyExpression ()
8161 type = TypeManager.object_type;
8162 eclass = ExprClass.Value;
8163 loc = Location.Null;
8166 public EmptyExpression (Type t)
8169 eclass = ExprClass.Value;
8170 loc = Location.Null;
8173 public override Expression DoResolve (EmitContext ec)
8178 public override void Emit (EmitContext ec)
8180 // nothing, as we only exist to not do anything.
8184 // This is just because we might want to reuse this bad boy
8185 // instead of creating gazillions of EmptyExpressions.
8186 // (CanImplicitConversion uses it)
8188 public void SetType (Type t)
8194 public class UserCast : Expression {
8198 public UserCast (MethodInfo method, Expression source, Location l)
8200 this.method = method;
8201 this.source = source;
8202 type = method.ReturnType;
8203 eclass = ExprClass.Value;
8207 public Expression Source {
8213 public override Expression DoResolve (EmitContext ec)
8216 // We are born fully resolved
8221 public override void Emit (EmitContext ec)
8223 ILGenerator ig = ec.ig;
8227 if (method is MethodInfo)
8228 ig.Emit (OpCodes.Call, (MethodInfo) method);
8230 ig.Emit (OpCodes.Call, (ConstructorInfo) method);
8236 // This class is used to "construct" the type during a typecast
8237 // operation. Since the Type.GetType class in .NET can parse
8238 // the type specification, we just use this to construct the type
8239 // one bit at a time.
8241 public class ComposedCast : TypeExpr {
8245 public ComposedCast (Expression left, string dim)
8246 : this (left, dim, left.Location)
8250 public ComposedCast (Expression left, string dim, Location l)
8258 public Expression RemoveNullable ()
8260 if (dim.EndsWith ("?")) {
8261 dim = dim.Substring (0, dim.Length - 1);
8270 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
8272 TypeExpr lexpr = left.ResolveAsTypeTerminal (ec, false);
8276 Type ltype = lexpr.Type;
8277 if ((ltype == TypeManager.void_type) && (dim != "*")) {
8278 Error_VoidInvalidInTheContext (loc);
8283 if ((dim.Length > 0) && (dim [0] == '?')) {
8284 TypeExpr nullable = new NullableType (left, loc);
8286 nullable = new ComposedCast (nullable, dim.Substring (1), loc);
8287 return nullable.ResolveAsTypeTerminal (ec, false);
8291 if (dim == "*" && !TypeManager.VerifyUnManaged (ltype, loc))
8294 if (dim != "" && dim [0] == '[' &&
8295 (ltype == TypeManager.arg_iterator_type || ltype == TypeManager.typed_reference_type)) {
8296 Report.Error (611, loc, "Array elements cannot be of type `{0}'", TypeManager.CSharpName (ltype));
8301 type = TypeManager.GetConstructedType (ltype, dim);
8306 throw new InternalErrorException ("Couldn't create computed type " + ltype + dim);
8308 if (type.IsPointer && !ec.IsInUnsafeScope){
8313 eclass = ExprClass.Type;
8317 public override string Name {
8318 get { return left + dim; }
8321 public override string FullName {
8322 get { return type.FullName; }
8325 public override string GetSignatureForError ()
8327 return left.GetSignatureForError () + dim;
8330 protected override void CloneTo (CloneContext clonectx, Expression t)
8332 ComposedCast target = (ComposedCast) t;
8334 target.left = left.Clone (clonectx);
8338 public class FixedBufferPtr : Expression {
8341 public FixedBufferPtr (Expression array, Type array_type, Location l)
8346 type = TypeManager.GetPointerType (array_type);
8347 eclass = ExprClass.Value;
8350 public override void Emit(EmitContext ec)
8355 public override Expression DoResolve (EmitContext ec)
8358 // We are born fully resolved
8366 // This class is used to represent the address of an array, used
8367 // only by the Fixed statement, this generates "&a [0]" construct
8368 // for fixed (char *pa = a)
8370 public class ArrayPtr : FixedBufferPtr {
8373 public ArrayPtr (Expression array, Type array_type, Location l):
8374 base (array, array_type, l)
8376 this.array_type = array_type;
8379 public override void Emit (EmitContext ec)
8383 ILGenerator ig = ec.ig;
8384 IntLiteral.EmitInt (ig, 0);
8385 ig.Emit (OpCodes.Ldelema, array_type);
8390 // Used by the fixed statement
8392 public class StringPtr : Expression {
8395 public StringPtr (LocalBuilder b, Location l)
8398 eclass = ExprClass.Value;
8399 type = TypeManager.char_ptr_type;
8403 public override Expression DoResolve (EmitContext ec)
8405 // This should never be invoked, we are born in fully
8406 // initialized state.
8411 public override void Emit (EmitContext ec)
8413 ILGenerator ig = ec.ig;
8415 ig.Emit (OpCodes.Ldloc, b);
8416 ig.Emit (OpCodes.Conv_I);
8417 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
8418 ig.Emit (OpCodes.Add);
8423 // Implements the `stackalloc' keyword
8425 public class StackAlloc : Expression {
8430 public StackAlloc (Expression type, Expression count, Location l)
8437 public override Expression DoResolve (EmitContext ec)
8439 count = count.Resolve (ec);
8443 if (count.Type != TypeManager.int32_type){
8444 count = Convert.ImplicitConversionRequired (ec, count, TypeManager.int32_type, loc);
8449 Constant c = count as Constant;
8450 if (c != null && c.IsNegative) {
8451 Report.Error (247, loc, "Cannot use a negative size with stackalloc");
8455 if (ec.InCatch || ec.InFinally) {
8456 Error (255, "Cannot use stackalloc in finally or catch");
8460 TypeExpr texpr = t.ResolveAsTypeTerminal (ec, false);
8466 if (!TypeManager.VerifyUnManaged (otype, loc))
8469 type = TypeManager.GetPointerType (otype);
8470 eclass = ExprClass.Value;
8475 public override void Emit (EmitContext ec)
8477 int size = GetTypeSize (otype);
8478 ILGenerator ig = ec.ig;
8481 ig.Emit (OpCodes.Sizeof, otype);
8483 IntConstant.EmitInt (ig, size);
8485 ig.Emit (OpCodes.Mul);
8486 ig.Emit (OpCodes.Localloc);
8489 protected override void CloneTo (CloneContext clonectx, Expression t)
8491 StackAlloc target = (StackAlloc) t;
8492 target.count = count.Clone (clonectx);
8493 target.t = t.Clone (clonectx);
8497 public interface IInitializable
8499 bool Initialize (EmitContext ec, Expression target);
8502 public class Initializer
8504 public readonly string Name;
8505 public readonly object Value;
8507 public Initializer (string name, Expression value)
8513 public Initializer (string name, IInitializable value)
8520 public class ObjectInitializer : IInitializable
8522 readonly ArrayList initializers;
8523 public ObjectInitializer (ArrayList initializers)
8525 this.initializers = initializers;
8528 public bool Initialize (EmitContext ec, Expression target)
8530 ArrayList initialized = new ArrayList (initializers.Count);
8531 for (int i = initializers.Count - 1; i >= 0; i--) {
8532 Initializer initializer = initializers[i] as Initializer;
8533 if (initialized.Contains (initializer.Name)) {
8534 //FIXME proper error
8535 Console.WriteLine ("Object member can only be initialized once");
8539 MemberAccess ma = new MemberAccess (target, initializer.Name);
8540 Expression expr = initializer.Value as Expression;
8541 // If it's an expresison, append the assign.
8543 Assign a = new Assign (ma, expr);
8544 ec.CurrentBlock.InsertStatementAfterCurrent (new StatementExpression (a));
8546 // If it's another initializer (object or collection), initialize it.
8547 else if (!((IInitializable)initializer.Value).Initialize (ec, ma))
8550 initialized.Add (initializer.Name);
8556 public class CollectionInitializer : IInitializable
8558 readonly ArrayList items;
8559 public CollectionInitializer (ArrayList items)
8564 bool CheckCollection (EmitContext ec, Expression e)
8566 if (e == null || e.Type == null)
8568 bool is_ienumerable = false;
8569 foreach (Type t in TypeManager.GetInterfaces (e.Type))
8570 if (t == typeof (IEnumerable)) {
8571 is_ienumerable = true;
8575 if (!is_ienumerable)
8578 MethodGroupExpr mg = Expression.MemberLookup (
8579 ec.ContainerType, e.Type, "Add", MemberTypes.Method,
8580 Expression.AllBindingFlags, Location.Null) as MethodGroupExpr;
8585 foreach (MethodInfo mi in mg.Methods) {
8586 if (TypeManager.GetParameterData (mi).Count != 1)
8588 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
8595 public bool Initialize (EmitContext ec, Expression target)
8597 if (!CheckCollection (ec, target.Resolve (ec))) {
8598 // FIXME throw proper error
8599 Console.WriteLine ("Error: This is not a collection");
8603 for (int i = items.Count - 1; i >= 0; i--) {
8604 MemberAccess ma = new MemberAccess (target, "Add");
8605 ArrayList array = new ArrayList ();
8606 array.Add (new Argument ((Expression)items[i]));
8607 Invocation invoke = new Invocation (ma, array);
8608 ec.CurrentBlock.InsertStatementAfterCurrent (new StatementExpression (invoke));
8614 public class AnonymousTypeInitializer : IInitializable
8616 readonly ArrayList initializers;
8617 public AnonymousTypeInitializer (ArrayList initializers)
8619 this.initializers = initializers;
8622 public bool Initialize (EmitContext ec, Expression target)
8624 foreach (AnonymousTypeParameter p in initializers) {
8625 MemberAccess ma = new MemberAccess (target, p.Name);
8626 Assign a = p.Expression as Assign;
8627 Assign assign = new Assign (ma, (a != null) ? a.Source : p.Expression);
8628 ec.CurrentBlock.InsertStatementAfterCurrent (new StatementExpression (assign));
8634 public class NewInitialize : New, IInitializable
8636 IInitializable initializer;
8638 public bool Initialize (EmitContext ec, Expression target)
8640 return initializer.Initialize (ec, target);
8643 public NewInitialize (Expression requested_type, ArrayList arguments, IInitializable initializer, Location l)
8644 : base (requested_type, arguments, l)
8646 this.initializer = initializer;
8650 public class AnonymousType : Expression
8652 ArrayList parameters;
8653 TypeContainer parent;
8654 TypeContainer anonymous_type;
8656 public AnonymousType (ArrayList parameters, TypeContainer parent, Location loc)
8658 this.parameters = parameters;
8659 this.parent = parent;
8663 public override Expression DoResolve (EmitContext ec)
8665 foreach (AnonymousTypeParameter p in parameters)
8668 anonymous_type = GetAnonymousType (ec);
8670 TypeExpression te = new TypeExpression (anonymous_type.TypeBuilder, loc);
8671 AnonymousTypeInitializer ati = new AnonymousTypeInitializer (parameters);
8672 return new NewInitialize (te, null, ati, loc).Resolve (ec);
8675 TypeContainer GetAnonymousType (EmitContext ec)
8677 // See if we already have an anonymous type with the right fields.
8678 // If not, create one.
8680 // Look through all availible pre-existing anonymous types:
8681 foreach (DictionaryEntry d in parent.AnonymousTypes) {
8682 ArrayList p = d.Key as ArrayList;
8683 if (p.Count != parameters.Count)
8686 // And for each of the fields we need...
8687 foreach (AnonymousTypeParameter atp in parameters) {
8688 // ... check each of the pre-existing A-type's fields.
8690 foreach (AnonymousTypeParameter a in p)
8691 if (atp.Equals(a)) {
8695 // If the pre-existing A-type doesn't have one of our fields, try the next one
8701 // If it's a match, return it.
8703 return d.Value as TypeContainer;
8705 // Otherwise, create a new type.
8706 return CreateAnonymousType (ec);
8709 TypeContainer CreateAnonymousType (EmitContext ec)
8711 TypeContainer type = new AnonymousClass (parent, loc);
8712 foreach (AnonymousTypeParameter p in parameters) {
8713 TypeExpression te = new TypeExpression (p.Type, loc);
8714 Field field = new Field (type, te, Modifiers.PUBLIC, p.Name, null, loc);
8715 type.AddField (field);
8718 type.DefineMembers ();
8719 parent.AnonymousTypes.Add (parameters, type);
8723 public override void Emit (EmitContext ec)
8725 TypeExpression te = new TypeExpression (anonymous_type.TypeBuilder, loc);
8726 new New (te, null, loc).Emit(ec);
8730 public class AnonymousTypeParameter : Expression
8734 Expression expression;
8736 public LocatedToken Token {
8737 get { return token; }
8740 public string Name {
8741 get { return name; }
8744 public Expression Expression {
8745 get { return expression; }
8748 public override bool Equals (object o)
8750 AnonymousTypeParameter other = o as AnonymousTypeParameter;
8751 return other != null && Name == other.Name && Type == other.Type;
8754 public override int GetHashCode ()
8756 return name.GetHashCode ();
8759 public override Expression DoResolve (EmitContext ec)
8761 Expression e = expression.Resolve(ec);
8766 public override void Emit (EmitContext ec)
8768 expression.Emit(ec);
8771 public AnonymousTypeParameter (Expression expression, string name)
8774 this.expression = expression;
8775 type = expression.Type;