2004-01-29 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / gmcs / generic.cs
1 //
2 // generic.cs: Support classes for generics
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2003 Ximian, Inc.
8 //
9 using System;
10 using System.Reflection;
11 using System.Reflection.Emit;
12 using System.Globalization;
13 using System.Collections;
14 using System.Text;
15         
16 namespace Mono.CSharp {
17
18         //
19         // Tracks the constraints for a type parameter
20         //
21         public class Constraints {
22                 string type_parameter;
23                 ArrayList constraints;
24                 Location loc;
25                 
26                 //
27                 // type_parameter is the identifier, constraints is an arraylist of
28                 // Expressions (with types) or `true' for the constructor constraint.
29                 // 
30                 public Constraints (string type_parameter, ArrayList constraints,
31                                     Location loc)
32                 {
33                         this.type_parameter = type_parameter;
34                         this.constraints = constraints;
35                         this.loc = loc;
36                 }
37
38                 public string TypeParameter {
39                         get {
40                                 return type_parameter;
41                         }
42                 }
43
44                 protected void Error (string message)
45                 {
46                         Report.Error (-218, "Invalid constraints clause for type " +
47                                       "parameter `{0}': {1}", type_parameter, message);
48                 }
49
50                 bool has_ctor_constraint;
51                 TypeExpr class_constraint;
52                 ArrayList iface_constraints;
53                 TypeExpr[] constraint_types;
54                 int num_constraints;
55
56                 public bool HasConstructorConstraint {
57                         get { return has_ctor_constraint; }
58                 }
59
60                 public bool Resolve (DeclSpace ds)
61                 {
62                         iface_constraints = new ArrayList ();
63
64                         foreach (object obj in constraints) {
65                                 if (has_ctor_constraint) {
66                                         Error ("can only use one constructor constraint and " +
67                                                "it must be the last constraint in the list.");
68                                         return false;
69                                 }
70
71                                 if (obj is bool) {
72                                         has_ctor_constraint = true;
73                                         continue;
74                                 }
75
76                                 TypeExpr expr = ds.ResolveTypeExpr ((Expression) obj, false, loc);
77                                 if (expr == null)
78                                         return false;
79
80                                 if (expr.IsInterface)
81                                         iface_constraints.Add (expr);
82                                 else if (class_constraint != null) {
83                                         Error ("can have at most one class constraint.");
84                                         return false;
85                                 } else
86                                         class_constraint = expr;
87
88                                 num_constraints++;
89                         }
90
91                         constraint_types = new TypeExpr [num_constraints];
92                         int pos = 0;
93                         if (class_constraint != null)
94                                 constraint_types [pos++] = class_constraint;
95                         iface_constraints.CopyTo (constraint_types, pos);
96
97                         return true;
98                 }
99
100                 public Type[] ResolveTypes (EmitContext ec)
101                 {
102                         Type [] types = new Type [constraint_types.Length];
103
104                         for (int i = 0; i < constraint_types.Length; i++)
105                                 types [i] = constraint_types [i].ResolveType (ec);
106
107                         return types;
108                 }
109         }
110
111         //
112         // This type represents a generic type parameter
113         //
114         public class TypeParameter {
115                 string name;
116                 Constraints constraints;
117                 Location loc;
118                 Type type;
119
120                 public TypeParameter (string name, Constraints constraints, Location loc)
121                 {
122                         this.name = name;
123                         this.constraints = constraints;
124                         this.loc = loc;
125                 }
126
127                 public string Name {
128                         get {
129                                 return name;
130                         }
131                 }
132
133                 public Location Location {
134                         get {
135                                 return loc;
136                         }
137                 }
138
139                 public Constraints Constraints {
140                         get {
141                                 return constraints;
142                         }
143                 }
144
145                 public Type Type {
146                         get {
147                                 return type;
148                         }
149                 }
150
151                 public bool Resolve (DeclSpace ds)
152                 {
153                         if (constraints != null)
154                                 return constraints.Resolve (ds);
155
156                         return true;
157                 }
158
159                 public Type Define (TypeBuilder tb)
160                 {
161                         type = tb.DefineGenericParameter (name);
162                         return type;
163                 }
164
165                 public Type DefineMethod (MethodBuilder mb)
166                 {
167                         type = mb.DefineGenericParameter (name);
168                         return type;
169                 }
170
171                 public void DefineType (EmitContext ec, TypeBuilder tb)
172                 {
173                         int index = type.GenericParameterPosition;
174                         if (constraints == null)
175                                 tb.SetGenericParameterConstraints (index, new Type [0]);
176                         else
177                                 tb.SetGenericParameterConstraints (index, constraints.ResolveTypes (ec));
178                 }
179
180                 public void DefineType (EmitContext ec, MethodBuilder mb)
181                 {
182                         int index = type.GenericParameterPosition;
183                         if (constraints == null)
184                                 mb.SetGenericParameterConstraints (index, new Type [0]);
185                         else
186                                 mb.SetGenericParameterConstraints (index, constraints.ResolveTypes (ec));
187                 }
188
189                 public override string ToString ()
190                 {
191                         return "TypeParameter[" + name + "]";
192                 }
193         }
194
195         //
196         // This type represents a generic type parameter reference.
197         //
198         // These expressions are born in a fully resolved state.
199         //
200         public class TypeParameterExpr : TypeExpr {
201                 TypeParameter type_parameter;
202
203                 public override string Name {
204                         get {
205                                 return type_parameter.Name;
206                         }
207                 }
208
209                 public TypeParameter TypeParameter {
210                         get {
211                                 return type_parameter;
212                         }
213                 }
214                 
215                 public TypeParameterExpr (TypeParameter type_parameter, Location loc)
216                 {
217                         this.type_parameter = type_parameter;
218                 }
219
220                 public override TypeExpr DoResolveAsTypeStep (EmitContext ec)
221                 {
222                         type = type_parameter.Type;
223
224                         return this;
225                 }
226
227                 public void Error_CannotUseAsUnmanagedType (Location loc)
228                 {
229                         Report.Error (-203, loc, "Can not use type parameter as unamanged type");
230                 }
231         }
232
233         public class TypeArguments {
234                 ArrayList args;
235                 Type[] atypes;
236                 bool has_type_args;
237                 
238                 public TypeArguments ()
239                 {
240                         args = new ArrayList ();
241                 }
242
243                 public void Add (Expression type)
244                 {
245                         args.Add (type);
246                 }
247
248                 public Type[] Arguments {
249                         get {
250                                 return atypes;
251                         }
252                 }
253
254                 public bool HasTypeArguments {
255                         get {
256                                 return has_type_args;
257                         }
258                 }
259
260                 public override string ToString ()
261                 {
262                         StringBuilder s = new StringBuilder ();
263
264                         int count = args.Count;
265                         for (int i = 0; i < count; i++){
266                                 //
267                                 // FIXME: Use TypeManager.CSharpname once we have the type
268                                 //
269                                 s.Append (args [i].ToString ());
270                                 if (i+1 < count)
271                                         s.Append (", ");
272                         }
273                         return s.ToString ();
274                 }
275
276                 public bool Resolve (EmitContext ec)
277                 {
278                         int count = args.Count;
279                         bool ok = true;
280
281                         atypes = new Type [count];
282                         
283                         for (int i = 0; i < count; i++){
284                                 TypeExpr e = ((Expression)args [i]).ResolveAsTypeTerminal (ec);
285                                 if (e == null) {
286                                         ok = false;
287                                         continue;
288                                 }
289                                 if (e is TypeParameterExpr)
290                                         has_type_args = true;
291                                 args [i] = e;
292                                 atypes [i] = e.ResolveType (ec);
293
294                                 if (atypes [i] == null)
295                                         ok = false;
296                         }
297                         return ok;
298                 }
299         }
300         
301         public class ConstructedType : TypeExpr {
302                 string name, full_name;
303                 TypeArguments args;
304                 Type[] gen_params, atypes;
305                 Type gt;
306                 
307                 public ConstructedType (string name, TypeArguments args, Location l)
308                 {
309                         loc = l;
310                         this.name = name;
311                         this.args = args;
312
313                         eclass = ExprClass.Type;
314                         full_name = name + "<" + args.ToString () + ">";
315                 }
316
317                 public ConstructedType (string name, TypeParameter[] type_params, Location l)
318                 {
319                         loc = l;
320                         this.name = name;
321
322                         args = new TypeArguments ();
323                         foreach (TypeParameter type_param in type_params)
324                                 args.Add (new TypeParameterExpr (type_param, l));
325
326                         eclass = ExprClass.Type;
327                         full_name = name + "<" + args.ToString () + ">";
328                 }
329
330                 protected bool CheckConstraints (int index)
331                 {
332                         Type atype = args.Arguments [index];
333                         Type ptype = gen_params [index];
334
335                         //// FIXME
336                         return true;
337
338                         //
339                         // First, check parent class.
340                         //
341                         if ((ptype.BaseType != atype.BaseType) &&
342                             !atype.BaseType.IsSubclassOf (ptype.BaseType)) {
343                                 Report.Error (-219, loc, "Cannot create constructed type `{0}': " +
344                                               "type argument `{1}' must derive from `{2}'.",
345                                               full_name, atype, ptype.BaseType);
346                                 return false;
347                         }
348
349                         //
350                         // Now, check the interfaces.
351                         //
352                         foreach (Type itype in ptype.GetInterfaces ()) {
353                                 if (TypeManager.ImplementsInterface (atype, itype))
354                                         continue;
355
356                                 Report.Error (-219, loc, "Cannot create constructed type `{0}: " +
357                                               "type argument `{1}' must implement interface `{2}'.",
358                                               full_name, atype, itype);
359                                 return false;
360                         }
361
362                         return true;
363                 }
364
365                 public override TypeExpr DoResolveAsTypeStep (EmitContext ec)
366                 {
367                         if (gt != null)
368                                 return this;
369
370                         //
371                         // First, resolve the generic type.
372                         //
373                         SimpleName sn = new SimpleName (name, loc);
374                         TypeExpr resolved = sn.ResolveAsTypeTerminal (ec);
375                         if (resolved == null)
376                                 return null;
377
378                         if (resolved.Type == null) {
379                                 Report.Error (-220, loc, "Failed to resolve constructed type `{0}'",
380                                               full_name);
381                                 return null;
382                         }
383
384                         gt = resolved.Type.GetGenericTypeDefinition ();
385                         return this;
386                 }
387
388                 public override Type ResolveType (EmitContext ec)
389                 {
390                         if (type != null)
391                                 return type;
392                         if (DoResolveAsTypeStep (ec) == null)
393                                 return null;
394
395                         //
396                         // Resolve the arguments.
397                         //
398                         if (args.Resolve (ec) == false) {
399                                 Report.Error (-220, loc, "Failed to resolve constructed type `{0}'",
400                                               full_name);
401                                 return null;
402                         }
403
404                         gen_params = gt.GetGenericArguments ();
405                         atypes = args.Arguments;
406
407                         if (atypes.Length != gen_params.Length) {
408                                 Report.Error (-217, loc, "Generic type `{0}' takes {1} " +
409                                               "type parameters, but specified {2}.", gt.Name,
410                                               gen_params.Length, atypes.Length);
411                                 return null;
412                         }
413
414                         for (int i = 0; i < gen_params.Length; i++) {
415                                 if (!CheckConstraints (i))
416                                         return null;
417                         }
418
419                         //
420                         // Now bind the parameters.
421                         //
422                         type = gt.BindGenericParameters (atypes);
423                         return type;
424                 }
425
426                 public Expression GetMemberAccess (TypeExpr current_type)
427                 {
428                         return new GenericMemberAccess (current_type, name, args, loc);
429                 }
430
431                 public override bool CheckAccessLevel (DeclSpace ds)
432                 {
433                         return ds.CheckAccessLevel (gt);
434                 }
435
436                 public override bool AsAccessible (DeclSpace ds, int flags)
437                 {
438                         return ds.AsAccessible (gt, flags);
439                 }
440
441                 public override bool IsClass {
442                         get { return gt.IsClass; }
443                 }
444
445                 public override bool IsValueType {
446                         get { return gt.IsValueType; }
447                 }
448
449                 public override bool IsInterface {
450                         get { return gt.IsInterface; }
451                 }
452
453                 public override bool IsSealed {
454                         get { return gt.IsSealed; }
455                 }
456
457                 public override TypeExpr[] GetInterfaces ()
458                 {
459                         TypeExpr[] ifaces = TypeManager.GetInterfaces (gt);
460                         return ifaces;
461                 }
462
463                 public override string Name {
464                         get {
465                                 return full_name;
466                         }
467                 }
468         }
469
470         public class GenericMethod : DeclSpace
471         {
472                 public GenericMethod (NamespaceEntry ns, TypeContainer parent, string name,
473                                       Attributes attrs, Location l)
474                         : base (ns, parent, name, attrs, l)
475                 { }
476
477                 public override TypeBuilder DefineType ()
478                 {
479                         throw new Exception ();
480                 }
481
482                 public override bool Define (TypeContainer parent)
483                 {
484                         return true;
485                 }
486
487                 public bool Define (MethodBuilder mb)
488                 {
489                         Type[] gen_params = new Type [TypeParameters.Length];
490                         for (int i = 0; i < TypeParameters.Length; i++)
491                                 gen_params [i] = TypeParameters [i].DefineMethod (mb);
492
493                         return true;
494                 }
495
496                 public override bool DefineMembers (TypeContainer parent)
497                 {
498                         return true;
499                 }
500
501                 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
502                                                         MemberFilter filter, object criteria)
503                 {
504                         throw new Exception ();
505                 }               
506
507                 public override MemberCache MemberCache {
508                         get {
509                                 throw new Exception ();
510                         }
511                 }
512         }
513
514         public class GenericMemberAccess : MemberAccess
515         {
516                 TypeArguments args;
517
518                 public GenericMemberAccess (Expression expr, string id, TypeArguments args, Location loc)
519                         : base (expr, id, loc)
520                 {
521                         this.args = args;
522                 }
523
524                 public override Expression DoResolve (EmitContext ec, Expression right_side,
525                                                       ResolveFlags flags)
526                 {
527                         Expression expr = base.DoResolve (ec, right_side, flags);
528                         if (expr == null)
529                                 return null;
530
531                         MethodGroupExpr mg = expr as MethodGroupExpr;
532                         if (mg == null) {
533                                 Report.Error (-220, loc, "Member `{0}' has type arguments, but did " +
534                                               "not resolve as a method group.", Identifier);
535                                 return null;
536                         }
537
538                         if (args.Resolve (ec) == false)
539                                 return null;
540
541                         Type[] atypes = args.Arguments;
542
543                         ArrayList list = new ArrayList ();
544
545                         foreach (MethodBase method in mg.Methods) {
546                                 MethodInfo mi = method as MethodInfo;
547                                 if (mi == null)
548                                         continue;
549
550                                 Type[] gen_params = mi.GetGenericArguments ();
551                         
552                                 if (atypes.Length != gen_params.Length) {
553                                         Report.Error (-217, loc, "Generic method `{0}' takes {1} " +
554                                                       "type parameters, but specified {2}.", mi.Name,
555                                                       gen_params.Length, atypes.Length);
556                                         continue;
557                                 }
558
559                                 list.Add (mi.BindGenericParameters (args.Arguments));
560                         }
561
562                         MethodInfo[] methods = new MethodInfo [list.Count];
563                         list.CopyTo (methods, 0);
564
565                         MethodGroupExpr new_mg = new MethodGroupExpr (methods, mg.Location);
566                         new_mg.InstanceExpression = mg.InstanceExpression;
567                         return new_mg;
568                 }
569         }
570
571         public class DefaultValueExpression : Expression
572         {
573                 Expression expr;
574                 LocalTemporary temp_storage;
575
576                 public DefaultValueExpression (Expression expr, Location loc)
577                 {
578                         this.expr = expr;
579                         this.loc = loc;
580                 }
581
582                 public override Expression DoResolve (EmitContext ec)
583                 {
584                         type = ec.DeclSpace.ResolveType (expr, false, loc);
585                         if (type == null)
586                                 return null;
587
588                         if (type.IsGenericParameter || TypeManager.IsValueType (type))
589                                 temp_storage = new LocalTemporary (ec, type);
590
591                         eclass = ExprClass.Variable;
592                         return this;
593                 }
594
595                 public override void Emit (EmitContext ec)
596                 {
597                         if (temp_storage != null) {
598                                 temp_storage.AddressOf (ec, AddressOp.LoadStore);
599                                 ec.ig.Emit (OpCodes.Initobj, type);
600                                 temp_storage.Emit (ec);
601                         } else
602                                 ec.ig.Emit (OpCodes.Ldnull);
603                 }
604         }
605 }