Compound assignments over reference type array cannot use ldelema
[mono.git] / mcs / mcs / argument.cs
1 //
2 // argument.cs: Argument expressions
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximain.com)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2003-2008 Novell, Inc.
10 //
11
12 using System;
13 using System.Reflection;
14 using System.Reflection.Emit;
15 using System.Collections.Generic;
16
17 namespace Mono.CSharp
18 {
19         //
20         // Argument expression used for invocation
21         //
22         public class Argument
23         {
24                 public enum AType : byte
25                 {
26                         None = 0,
27                         Ref = 1,                        // ref modifier used
28                         Out = 2,                        // out modifier used
29                         Default = 3,            // argument created from default parameter value
30                         DynamicTypeName = 4,    // System.Type argument for dynamic binding
31                         ExtensionType = 5,      // Instance expression inserted as the first argument
32                 }
33
34                 public readonly AType ArgType;
35                 public Expression Expr;
36
37                 public Argument (Expression expr, AType type)
38                 {
39                         this.Expr = expr;
40                         this.ArgType = type;
41                 }
42
43                 public Argument (Expression expr)
44                 {
45                         if (expr == null)
46                                 throw new ArgumentNullException ();
47
48                         this.Expr = expr;
49                 }
50
51                 public TypeSpec Type {
52                         get { return Expr.Type; }
53                 }
54
55                 public Parameter.Modifier Modifier {
56                         get {
57                                 switch (ArgType) {
58                                 case AType.Out:
59                                         return Parameter.Modifier.OUT;
60
61                                 case AType.Ref:
62                                         return Parameter.Modifier.REF;
63
64                                 default:
65                                         return Parameter.Modifier.NONE;
66                                 }
67                         }
68                 }
69
70                 public virtual Expression CreateExpressionTree (ResolveContext ec)
71                 {
72                         if (ArgType == AType.Default)
73                                 ec.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
74
75                         return Expr.CreateExpressionTree (ec);
76                 }
77
78                 public string GetSignatureForError ()
79                 {
80                         if (Expr.eclass == ExprClass.MethodGroup)
81                                 return Expr.ExprClassName;
82
83                         return TypeManager.CSharpName (Expr.Type);
84                 }
85
86                 public bool IsByRef {
87                         get { return ArgType == AType.Ref || ArgType == AType.Out; }
88                 }
89
90                 public bool IsDefaultArgument {
91                         get { return ArgType == AType.Default; }
92                 }
93
94                 public bool ResolveMethodGroup (ResolveContext ec)
95                 {
96                         SimpleName sn = Expr as SimpleName;
97                         if (sn != null)
98                                 Expr = sn.GetMethodGroup ();
99
100                         // FIXME: csc doesn't report any error if you try to use `ref' or
101                         //        `out' in a delegate creation expression.
102                         Expr = Expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
103                         if (Expr == null)
104                                 return false;
105
106                         return true;
107                 }
108
109                 public void Resolve (ResolveContext ec)
110                 {
111                         if (Expr == EmptyExpression.Null)
112                                 return;
113
114 //                      using (ec.With (ResolveContext.Options.DoFlowAnalysis, true)) {
115                                 // Verify that the argument is readable
116                                 if (ArgType != AType.Out)
117                                         Expr = Expr.Resolve (ec);
118
119                                 // Verify that the argument is writeable
120                                 if (Expr != null && IsByRef)
121                                         Expr = Expr.ResolveLValue (ec, EmptyExpression.OutAccess.Instance);
122
123                                 if (Expr == null)
124                                         Expr = EmptyExpression.Null;
125 //                      }
126                 }
127
128                 public virtual void Emit (EmitContext ec)
129                 {
130                         if (!IsByRef) {
131                                 Expr.Emit (ec);
132                                 return;
133                         }
134
135                         AddressOp mode = AddressOp.Store;
136                         if (ArgType == AType.Ref)
137                                 mode |= AddressOp.Load;
138
139                         IMemoryLocation ml = (IMemoryLocation) Expr;
140                         ml.AddressOf (ec, mode);
141                 }
142
143                 public Argument Clone (CloneContext clonectx)
144                 {
145                         Argument a = (Argument) MemberwiseClone ();
146                         a.Expr = Expr.Clone (clonectx);
147                         return a;
148                 }
149         }
150
151         public class NamedArgument : Argument
152         {
153                 public readonly string Name;
154                 readonly Location loc;
155                 LocalTemporary variable;
156
157                 public NamedArgument (string name, Location loc, Expression expr)
158                         : this (name, loc, expr, AType.None)
159                 {
160                 }
161
162                 public NamedArgument (string name, Location loc, Expression expr, AType modifier)
163                         : base (expr, modifier)
164                 {
165                         this.Name = name;
166                         this.loc = loc;
167                 }
168
169                 public override Expression CreateExpressionTree (ResolveContext ec)
170                 {
171                         ec.Report.Error (853, loc, "An expression tree cannot contain named argument");
172                         return base.CreateExpressionTree (ec);
173                 }
174
175                 public override void Emit (EmitContext ec)
176                 {
177                         // TODO: Should guard against multiple emits
178                         base.Emit (ec);
179
180                         // Release temporary variable when used
181                         if (variable != null)
182                                 variable.Release (ec);
183                 }
184
185                 public void EmitAssign (EmitContext ec)
186                 {
187                         var type = Expr.Type;
188                         if (IsByRef) {
189                                 var ml = (IMemoryLocation) Expr;
190                                 ml.AddressOf (ec, AddressOp.Load);
191                                 type = ReferenceContainer.MakeType (type);
192                         } else {
193                                 Expr.Emit (ec);
194                         }
195
196                         variable = new LocalTemporary (type);
197                         variable.Store (ec);
198
199                         Expr = variable;
200                 }
201
202                 public Location Location {
203                         get { return loc; }
204                 }
205         }
206         
207         public class Arguments
208         {
209                 List<Argument> args;
210                 List<NamedArgument> reordered;
211
212                 public Arguments (int capacity)
213                 {
214                         args = new List<Argument> (capacity);
215                 }
216
217                 public void Add (Argument arg)
218                 {
219                         args.Add (arg);
220                 }
221
222                 public void AddRange (Arguments args)
223                 {
224                         this.args.AddRange (args.args);
225                 }
226
227                 public ArrayInitializer CreateDynamicBinderArguments (ResolveContext rc)
228                 {
229                         Location loc = Location.Null;
230                         var all = new ArrayInitializer (args.Count, loc);
231
232                         MemberAccess binder = DynamicExpressionStatement.GetBinderNamespace (loc);
233
234                         foreach (Argument a in args) {
235                                 Arguments dargs = new Arguments (2);
236
237                                 // CSharpArgumentInfoFlags.None = 0
238                                 const string info_flags_enum = "CSharpArgumentInfoFlags";
239                                 Expression info_flags = new IntLiteral (0, loc);
240
241                                 if (a.Expr is Constant) {
242                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
243                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "Constant", loc), loc);
244                                 } else if (a.ArgType == Argument.AType.Ref) {
245                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
246                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsRef", loc), loc);
247                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
248                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc), loc);
249                                 } else if (a.ArgType == Argument.AType.Out) {
250                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
251                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsOut", loc), loc);
252                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
253                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc), loc);
254                                 } else if (a.ArgType == Argument.AType.DynamicTypeName) {
255                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
256                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "IsStaticType", loc), loc);
257                                 }
258
259                                 var arg_type = a.Expr.Type;
260
261                                 if (arg_type != InternalType.Dynamic && arg_type != InternalType.Null) {
262                                         MethodGroupExpr mg = a.Expr as MethodGroupExpr;
263                                         if (mg != null) {
264                                                 rc.Report.Error (1976, a.Expr.Location,
265                                                         "The method group `{0}' cannot be used as an argument of dynamic operation. Consider using parentheses to invoke the method",
266                                                         mg.Name);
267                                         } else if (arg_type == InternalType.AnonymousMethod) {
268                                                 rc.Report.Error (1977, a.Expr.Location,
269                                                         "An anonymous method or lambda expression cannot be used as an argument of dynamic operation. Consider using a cast");
270                                         } else if (arg_type == TypeManager.void_type || arg_type == InternalType.Arglist || arg_type.IsPointer) {
271                                                 rc.Report.Error (1978, a.Expr.Location,
272                                                         "An expression of type `{0}' cannot be used as an argument of dynamic operation",
273                                                         TypeManager.CSharpName (arg_type));
274                                         }
275
276                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
277                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "UseCompileTimeType", loc), loc);
278                                 }
279
280                                 string named_value;
281                                 NamedArgument na = a as NamedArgument;
282                                 if (na != null) {
283                                         info_flags = new Binary (Binary.Operator.BitwiseOr, info_flags,
284                                                 new MemberAccess (new MemberAccess (binder, info_flags_enum, loc), "NamedArgument", loc), loc);
285
286                                         named_value = na.Name;
287                                 } else {
288                                         named_value = null;
289                                 }
290
291                                 dargs.Add (new Argument (info_flags));
292                                 dargs.Add (new Argument (new StringLiteral (named_value, loc)));
293                                 all.Add (new Invocation (new MemberAccess (new MemberAccess (binder, "CSharpArgumentInfo", loc), "Create", loc), dargs));
294                         }
295
296                         return all;
297                 }
298
299                 public static Arguments CreateForExpressionTree (ResolveContext ec, Arguments args, params Expression[] e)
300                 {
301                         Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length);
302                         for (int i = 0; i < e.Length; ++i) {
303                                 if (e [i] != null)
304                                         all.Add (new Argument (e[i]));
305                         }
306
307                         if (args != null) {
308                                 foreach (Argument a in args.args) {
309                                         Expression tree_arg = a.CreateExpressionTree (ec);
310                                         if (tree_arg != null)
311                                                 all.Add (new Argument (tree_arg));
312                                 }
313                         }
314
315                         return all;
316                 }
317
318                 public void CheckArrayAsAttribute (CompilerContext ctx)
319                 {
320                         foreach (Argument arg in args) {
321                                 // Type is undefined (was error 246)
322                                 if (arg.Type == null)
323                                         continue;
324
325                                 if (arg.Type.IsArray)
326                                         ctx.Report.Warning (3016, 1, arg.Expr.Location, "Arrays as attribute arguments are not CLS-compliant");
327                         }
328                 }
329
330                 public Arguments Clone (CloneContext ctx)
331                 {
332                         Arguments cloned = new Arguments (args.Count);
333                         foreach (Argument a in args)
334                                 cloned.Add (a.Clone (ctx));
335
336                         return cloned;
337                 }
338
339                 public int Count {
340                         get { return args.Count; }
341                 }
342
343                 //
344                 // Emits a list of resolved Arguments
345                 // 
346                 public void Emit (EmitContext ec)
347                 {
348                         Emit (ec, false);
349                 }
350
351                 //
352                 // if `dup_args' is true, a copy of the arguments will be left
353                 // on the stack and return value will contain an array of access
354                 // expressions
355                 // NOTE: It's caller responsibility is to release temporary variables
356                 //
357                 public Expression[] Emit (EmitContext ec, bool dup_args)
358                 {
359                         Expression[] temps;
360
361                         if (dup_args && Count != 0)
362                                 temps = new Expression [Count];
363                         else
364                                 temps = null;
365
366                         if (reordered != null && Count > 1) {
367                                 foreach (NamedArgument na in reordered)
368                                         na.EmitAssign (ec);
369                         }
370
371                         int i = 0;
372                         LocalTemporary lt;
373                         foreach (Argument a in args) {
374                                 a.Emit (ec);
375                                 if (!dup_args)
376                                         continue;
377
378                                 if (a.Expr is Constant) {
379                                         //
380                                         // No need to create a temporary variable for constants
381                                         //
382                                         temps[i] = a.Expr;
383                                 } else {
384                                         ec.Emit (OpCodes.Dup);
385                                         temps[i] = lt = new LocalTemporary (a.Type);
386                                         lt.Store (ec);
387                                 }
388
389                                 ++i;
390                         }
391
392                         return temps;
393                 }
394
395                 public List<Argument>.Enumerator GetEnumerator ()
396                 {
397                         return args.GetEnumerator ();
398                 }
399
400                 //
401                 // At least one argument is of dynamic type
402                 //
403                 public bool HasDynamic {
404                         get {
405                                 foreach (Argument a in args) {
406                                         if (a.Type == InternalType.Dynamic && !a.IsByRef)
407                                                 return true;
408                                 }
409                                 
410                                 return false;
411                         }
412                 }
413
414                 public void Insert (int index, Argument arg)
415                 {
416                         args.Insert (index, arg);
417                 }
418
419                 public static System.Linq.Expressions.Expression[] MakeExpression (Arguments args, BuilderContext ctx)
420                 {
421                         if (args == null || args.Count == 0)
422                                 return null;
423
424                         var exprs = new System.Linq.Expressions.Expression [args.Count];
425                         for (int i = 0; i < exprs.Length; ++i) {
426                                 Argument a = args.args [i];
427                                 exprs[i] = a.Expr.MakeExpression (ctx);
428                         }
429
430                         return exprs;
431                 }
432
433                 public void MarkReorderedArgument (NamedArgument a)
434                 {
435                         //
436                         // Constant expression can have no effect on left-to-right execution
437                         //
438                         if (a.Expr is Constant)
439                                 return;
440
441                         if (reordered == null)
442                                 reordered = new List<NamedArgument> ();
443
444                         reordered.Add (a);
445                 }
446
447                 //
448                 // Returns dynamic when at least one argument is of dynamic type
449                 //
450                 public void Resolve (ResolveContext ec, out bool dynamic)
451                 {
452                         dynamic = false;
453                         foreach (Argument a in args) {
454                                 a.Resolve (ec);
455                                 if (a.Type == InternalType.Dynamic && !a.IsByRef)
456                                         dynamic = true;
457                         }
458                 }
459
460                 public void RemoveAt (int index)
461                 {
462                         args.RemoveAt (index);
463                 }
464
465                 public Argument this [int index] {
466                         get { return args [index]; }
467                         set { args [index] = value; }
468                 }
469         }
470 }