2004-06-08 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / gmcs / parameter.cs
1 //
2 // parameter.cs: Parameter definition.
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 //
6 // Licensed under the terms of the GNU GPL
7 //
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
9 //
10 //
11 //
12 using System;
13 using System.Reflection;
14 using System.Reflection.Emit;
15 using System.Collections;
16
17 namespace Mono.CSharp {
18
19         /// <summary>
20         ///   Abstract Base class for parameters of a method.
21         /// </summary>
22         public abstract class ParameterBase : Attributable {
23
24                 protected ParameterBuilder builder;
25
26                 public ParameterBase (Attributes attrs)
27                         : base (attrs)
28                 {
29                 }
30
31                 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb)
32                 {
33                         if (a.Type == TypeManager.marshal_as_attr_type) {
34                                 UnmanagedMarshal marshal = a.GetMarshal ();
35                                 if (marshal != null) {
36                                         builder.SetMarshal (marshal);
37                                         return;
38                         }
39                                 Report.Warning_T (-24, a.Location);
40                                 return;
41                         }
42
43                         builder.SetCustomAttribute (cb);
44                 }
45
46                 public override bool IsClsCompliaceRequired(DeclSpace ds)
47                 {
48                         return false;
49                                 }
50         }
51
52         /// <summary>
53         /// Class for applying custom attributes on the return type
54         /// </summary>
55         public class ReturnParameter: ParameterBase {
56                 public ReturnParameter (MethodBuilder mb, Location location):
57                         base (null)
58                 {
59                         try {
60                                 builder = mb.DefineParameter (0, ParameterAttributes.None, "");                 
61                         }
62                         catch (ArgumentOutOfRangeException) {
63                                 Report.Warning_T (-28, location);
64                         }
65                 }
66
67                 public override void ApplyAttributeBuilder(Attribute a, CustomAttributeBuilder cb)
68                 {
69                         // This occurs after Warning -28
70                         if (builder == null)
71                                 return;
72
73                         base.ApplyAttributeBuilder (a, cb);
74                 }
75
76                 public override AttributeTargets AttributeTargets {
77                         get {
78                                 return AttributeTargets.ReturnValue;
79                         }
80                 }
81
82                 /// <summary>
83                 /// Is never called
84                 /// </summary>
85                 protected override string[] ValidAttributeTargets {
86                         get {
87                                 return null;
88                         }
89                 }
90         }
91
92         /// <summary>
93        /// Class for applying custom attributes on the implicit parameter type
94        /// of the 'set' method in properties, and the 'add' and 'remove' methods in events.
95         /// </summary>
96        public class ImplicitParameter: ParameterBase {
97                public ImplicitParameter (MethodBuilder mb):
98                         base (null)
99                 {
100                         builder = mb.DefineParameter (1, ParameterAttributes.None, "");                 
101                 }
102
103                 public override AttributeTargets AttributeTargets {
104                         get {
105                                 return AttributeTargets.Parameter;
106                         }
107                 }
108
109                 /// <summary>
110                 /// Is never called
111                 /// </summary>
112                 protected override string[] ValidAttributeTargets {
113                         get {
114                                 return null;
115                         }
116         }
117         }
118
119
120         /// <summary>
121         ///   Represents a single method parameter
122         /// </summary>
123
124         //TODO: Add location member to this or base class for better error location and all methods simplification.
125         public class Parameter : ParameterBase {
126                 [Flags]
127                 public enum Modifier : byte {
128                         NONE    = 0,
129                         REF     = 1,
130                         OUT     = 2,
131                         PARAMS  = 4,
132                         // This is a flag which says that it's either REF or OUT.
133                         ISBYREF = 8
134                 }
135
136                 static string[] attribute_targets = new string [] { "param" };
137
138                 public readonly Expression TypeName;
139                 public readonly Modifier ModFlags;
140                 public readonly string Name;
141                 GenericConstraints constraints;
142                 Type parameter_type;
143                 
144                 public Parameter (Expression type, string name, Modifier mod, Attributes attrs)
145                         : base (attrs)
146                 {
147                         Name = name;
148                         ModFlags = mod;
149                         TypeName = type;
150                 }
151
152                 // <summary>
153                 //   Resolve is used in method definitions
154                 // </summary>
155                 public bool Resolve (DeclSpace ds, Location l)
156                 {
157                         TypeExpr texpr = ds.ResolveTypeExpr (TypeName, false, l);
158                         if (texpr == null)
159                                 return false;
160
161                         TypeParameterExpr tparam = texpr as TypeParameterExpr;
162                         if (tparam != null)
163                                 constraints = tparam.TypeParameter.Constraints;
164
165                         parameter_type = ds.ResolveType (texpr, l);
166
167                         if (parameter_type == TypeManager.void_type){
168                                 Report.Error (1536, l, "`void' parameter is not permitted");
169                                 return false;
170                         }
171
172                         if ((ModFlags & Parameter.Modifier.ISBYREF) != 0){
173                                 if (parameter_type == TypeManager.typed_reference_type ||
174                                     parameter_type == TypeManager.arg_iterator_type){
175                                         Report.Error (1601, l,
176                                                       "out or ref parameter can not be of type TypedReference or ArgIterator");
177                                         return false;
178                                 }
179                         }
180                         
181                         return parameter_type != null;
182                 }
183
184                 public Type ExternalType ()
185                 {
186                         if ((ModFlags & Parameter.Modifier.ISBYREF) != 0)
187                                 return TypeManager.GetReferenceType (parameter_type);
188                         
189                         return parameter_type;
190                 }
191
192                 public Type ParameterType {
193                         get {
194                                 return parameter_type;
195                         }
196                 }
197
198                 public GenericConstraints GenericConstraints {
199                         get {
200                                 return constraints;
201                         }
202                 }
203                 
204                 public ParameterAttributes Attributes {
205                         get {
206                                 int flags = ((int) ModFlags) & ~((int) Parameter.Modifier.ISBYREF);
207                                 switch ((Modifier) flags) {
208                                 case Modifier.NONE:
209                                         return ParameterAttributes.None;
210                                 case Modifier.REF:
211                                         return ParameterAttributes.None;
212                                 case Modifier.OUT:
213                                         return ParameterAttributes.Out;
214                                 case Modifier.PARAMS:
215                                         return 0;
216                                 }
217                                 
218                                 return ParameterAttributes.None;
219                         }
220                 }
221                 
222                 public override AttributeTargets AttributeTargets {
223                         get {
224                                 return AttributeTargets.Parameter;
225                         }
226                 }
227
228                 /// <summary>
229                 ///   Returns the signature for this parameter evaluating it on the
230                 ///   @tc context
231                 /// </summary>
232                 public string GetSignature (DeclSpace ds, Location loc)
233                 {
234                         if (parameter_type == null){
235                                 if (!Resolve (ds, loc))
236                                         return null;
237                         }
238
239                         return ExternalType ().FullName;
240                 }
241
242                 public string GetSignatureForError ()
243                 {
244                         string typeName = TypeManager.CSharpName (parameter_type);
245                         switch (ModFlags & ~Modifier.ISBYREF) {
246                                 case Modifier.OUT:
247                                         return "out " + typeName;
248                                 case Modifier.PARAMS:
249                                         return "params " + typeName;
250                                 case Modifier.REF:
251                                         return "ref " + typeName;
252                         }
253                         return typeName;
254                 }
255
256                 public void DefineParameter (EmitContext ec, MethodBuilder mb, ConstructorBuilder cb, int index, Location loc)
257                 {
258                         ParameterAttributes par_attr = Attributes;
259                                         
260                         if (mb == null)
261                                 builder = cb.DefineParameter (index, par_attr, Name);
262                         else 
263                                 builder = mb.DefineParameter (index, par_attr, Name);
264                                         
265                         if (OptAttributes != null) {
266                                 OptAttributes.Emit (ec, this);
267         
268                                 if (par_attr == ParameterAttributes.Out){
269                                         if (OptAttributes.Contains (TypeManager.in_attribute_type, ec))
270                                                 Report.Error (36, loc,  "Can not use [In] attribute on out parameter");
271                                 }
272                         }
273                 }
274
275                 protected override string[] ValidAttributeTargets {
276                         get {
277                                 return attribute_targets;
278                         }
279                 }
280         }
281
282         /// <summary>
283         ///   Represents the methods parameters
284         /// </summary>
285         public class Parameters {
286                 public Parameter [] FixedParameters;
287                 public readonly Parameter ArrayParameter;
288                 string signature;
289                 Type [] types;
290                 Location loc;
291                 
292                 static Parameters empty_parameters;
293                 
294                 public Parameters (Parameter [] fixed_parameters, Parameter array_parameter, Location l)
295                 {
296                         FixedParameters = fixed_parameters;
297                         ArrayParameter  = array_parameter;
298                         loc = l;
299                 }
300
301                 /// <summary>
302                 ///   This is used to reuse a set of empty parameters, because they
303                 ///   are common
304                 /// </summary>
305                 public static Parameters EmptyReadOnlyParameters {
306                         get {
307                                 if (empty_parameters == null)
308                                         empty_parameters = new Parameters (null, null, Location.Null);
309                         
310                                 return empty_parameters;
311                         }
312                 }
313                 
314                 public bool Empty {
315                         get {
316                                 return (FixedParameters == null) && (ArrayParameter == null);
317                         }
318                 }
319                 
320                 public void ComputeSignature (DeclSpace ds)
321                 {
322                         signature = "";
323                         if (FixedParameters != null){
324                                 for (int i = 0; i < FixedParameters.Length; i++){
325                                         Parameter par = FixedParameters [i];
326                                         
327                                         signature += par.GetSignature (ds, loc);
328                                 }
329                         }
330                         //
331                         // Note: as per the spec, the `params' arguments (ArrayParameter)
332                         // are not used in the signature computation for a method
333                         //
334                 }
335
336                 void Error_DuplicateParameterName (string name)
337                 {
338                         Report.Error (
339                                 100, loc, "The parameter name `" + name + "' is a duplicate");
340                 }
341                 
342                 public bool VerifyArgs ()
343                 {
344                         int count;
345                         int i, j;
346
347                         if (FixedParameters == null)
348                                 return true;
349                         
350                         count = FixedParameters.Length;
351                         string array_par_name = ArrayParameter != null ? ArrayParameter.Name : null;
352                         for (i = 0; i < count; i++){
353                                 string base_name = FixedParameters [i].Name;
354                                 
355                                 for (j = i + 1; j < count; j++){
356                                         if (base_name != FixedParameters [j].Name)
357                                                 continue;
358                                         Error_DuplicateParameterName (base_name);
359                                         return false;
360                                 }
361
362                                 if (base_name == array_par_name){
363                                         Error_DuplicateParameterName (base_name);
364                                         return false;
365                                 }
366                         }
367                         return true;
368                 }
369                 
370                 /// <summary>
371                 ///    Returns the signature of the Parameters evaluated in
372                 ///    the @tc environment
373                 /// </summary>
374                 public string GetSignature (DeclSpace ds)
375                 {
376                         if (signature == null){
377                                 VerifyArgs ();
378                                 ComputeSignature (ds);
379                         }
380                         
381                         return signature;
382                 }
383                 
384                 /// <summary>
385                 ///    Returns the paramenter information based on the name
386                 /// </summary>
387                 public Parameter GetParameterByName (string name, out int idx)
388                 {
389                         idx = 0;
390                         int i = 0;
391
392                         if (FixedParameters != null){
393                                 foreach (Parameter par in FixedParameters){
394                                         if (par.Name == name){
395                                                 idx = i;
396                                                 return par;
397                                         }
398                                         i++;
399                                 }
400                         }
401
402                         if (ArrayParameter != null){
403                                 if (name == ArrayParameter.Name){
404                                         idx = i;
405                                         return ArrayParameter;
406                                 }
407                         }
408                         
409                         return null;
410                 }
411
412                 bool ComputeParameterTypes (DeclSpace ds)
413                 {
414                         int extra = (ArrayParameter != null) ? 1 : 0;
415                         int i = 0;
416                         int pc;
417
418                         if (FixedParameters == null)
419                                 pc = extra;
420                         else
421                                 pc = extra + FixedParameters.Length;
422
423                         types = new Type [pc];
424                         
425                         if (!VerifyArgs ()){
426                                 FixedParameters = null;
427                                 return false;
428                         }
429
430                         bool failed = false;
431                         if (FixedParameters != null){
432                                 foreach (Parameter p in FixedParameters){
433                                         Type t = null;
434                                         
435                                         if (p.Resolve (ds, loc))
436                                                 t = p.ExternalType ();
437                                         else
438                                                 failed = true;
439
440                                         types [i] = t;
441                                         i++;
442                                 }
443                         }
444                         
445                         if (extra > 0){
446                                 if (ArrayParameter.Resolve (ds, loc))
447                                         types [i] = ArrayParameter.ExternalType ();
448                                 else 
449                                         failed = true;
450                         }
451
452                         if (failed){
453                                 types = null;
454                                 return false;
455                         }
456
457                         return true;
458                 }
459
460                 //
461                 // This variant is used by Delegates, because they need to
462                 // resolve/define names, instead of the plain LookupType
463                 //
464                 public bool ComputeAndDefineParameterTypes (DeclSpace ds)
465                 {
466                         int extra = (ArrayParameter != null) ? 1 : 0;
467                         int i = 0;
468                         int pc;
469
470                         if (FixedParameters == null)
471                                 pc = extra;
472                         else
473                                 pc = extra + FixedParameters.Length;
474                         
475                         types = new Type [pc];
476                         
477                         if (!VerifyArgs ()){
478                                 FixedParameters = null;
479                                 return false;
480                         }
481
482                         bool ok_flag = true;
483                         
484                         if (FixedParameters != null){
485                                 foreach (Parameter p in FixedParameters){
486                                         Type t = null;
487                                         
488                                         if (p.Resolve (ds, loc))
489                                                 t = p.ExternalType ();
490                                         else
491                                                 ok_flag = false;
492                                         
493                                         types [i] = t;
494                                         i++;
495                                 }
496                         }
497                         
498                         if (extra > 0){
499                                 if (ArrayParameter.Resolve (ds, loc))
500                                         types [i] = ArrayParameter.ExternalType ();
501                                 else
502                                         ok_flag = false;
503                         }
504
505                         //
506                         // invalidate the cached types
507                         //
508                         if (!ok_flag){
509                                 types = null;
510                         }
511                         
512                         return ok_flag;
513                 }
514                 
515                 /// <summary>
516                 ///   Returns the argument types as an array
517                 /// </summary>
518                 static Type [] no_types = new Type [0];
519                 
520                 public Type [] GetParameterInfo (DeclSpace ds)
521                 {
522                         if (types != null)
523                                 return types;
524                         
525                         if (FixedParameters == null && ArrayParameter == null)
526                                 return no_types;
527
528                         if (ComputeParameterTypes (ds) == false){
529                                 types = null;
530                                 return null;
531                         }
532
533                         return types;
534                 }
535
536                 /// <summary>
537                 ///   Returns the type of a given parameter, and stores in the `is_out'
538                 ///   boolean whether this is an out or ref parameter.
539                 ///
540                 ///   Note that the returned type will not contain any dereference in this
541                 ///   case (ie, you get "int" for a ref int instead of "int&"
542                 /// </summary>
543                 public Type GetParameterInfo (DeclSpace ds, int idx, out Parameter.Modifier mod)
544                 {
545                         mod = Parameter.Modifier.NONE;
546                         
547                         if (!VerifyArgs ()){
548                                 FixedParameters = null;
549                                 return null;
550                         }
551
552                         if (FixedParameters == null && ArrayParameter == null)
553                                 return null;
554                         
555                         if (types == null)
556                                 if (ComputeParameterTypes (ds) == false)
557                                         return null;
558
559                         //
560                         // If this is a request for the variable lenght arg.
561                         //
562                         int array_idx = (FixedParameters != null ? FixedParameters.Length : 0);
563                         if (idx == array_idx)
564                                 return types [idx];
565
566                         //
567                         // Otherwise, it is a fixed parameter
568                         //
569                         Parameter p = FixedParameters [idx];
570                         mod = p.ModFlags;
571
572                         if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0)
573                                 mod |= Parameter.Modifier.ISBYREF;
574
575                         return p.ParameterType;
576                 }
577
578                 public CallingConventions GetCallingConvention ()
579                 {
580                         // For now this is the only correc thing to do
581                         return CallingConventions.Standard;
582                 }
583
584                 //
585                 // The method's attributes are passed in because we need to extract
586                 // the "return:" attribute from there to apply on the return type
587                 //
588                 public void LabelParameters (EmitContext ec,
589                         MethodBase builder,
590                         Location loc) {
591                         //
592                         // Define each type attribute (in/out/ref) and
593                         // the argument names.
594                         //
595                         int i = 0;
596                         
597                         MethodBuilder mb = builder as MethodBuilder;
598                         ConstructorBuilder cb = builder as ConstructorBuilder;
599
600                         if (FixedParameters != null) {
601                                 for (i = 0; i < FixedParameters.Length; i++) {
602                                         FixedParameters [i].DefineParameter (ec, mb, cb, i + 1, loc);
603                                 }
604                         }
605
606                         if (ArrayParameter != null){
607                                 ParameterBuilder pb;
608                                 Parameter array_param = ArrayParameter;
609
610                                 if (mb == null)
611                                         pb = cb.DefineParameter (
612                                                 i + 1, array_param.Attributes,
613                                                 array_param.Name);
614                                 else
615                                         pb = mb.DefineParameter (
616                                                 i + 1, array_param.Attributes,
617                                                 array_param.Name);
618                                         
619                                 CustomAttributeBuilder a = new CustomAttributeBuilder (
620                                         TypeManager.cons_param_array_attribute, new object [0]);
621                                 
622                                 pb.SetCustomAttribute (a);
623                         }
624                 }
625         }
626 }