Merge branch 'master' of github.com:tgiphil/mono
[mono.git] / mcs / mcs / pending.cs
1 //
2 // pending.cs: Pending method implementation
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@gnu.org)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 //
10 // Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2003-2008 Novell, Inc.
12 //
13
14 using System;
15 using System.Collections.Generic;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Linq;
19
20 namespace Mono.CSharp {
21
22         struct TypeAndMethods {
23                 public TypeSpec          type;
24                 public IList<MethodSpec> methods;
25
26                 // 
27                 // Whether it is optional, this is used to allow the explicit/implicit
28                 // implementation when a base class already implements an interface. 
29                 //
30                 // For example:
31                 //
32                 // class X : IA { }  class Y : X, IA { IA.Explicit (); }
33                 //
34                 public bool          optional;
35                                 
36                 //
37                 // This flag on the method says `We found a match, but
38                 // because it was private, we could not use the match
39                 //
40                 public MethodData [] found;
41
42                 // If a method is defined here, then we always need to
43                 // create a proxy for it.  This is used when implementing
44                 // an interface's indexer with a different IndexerName.
45                 public MethodSpec [] need_proxy;
46         }
47
48         public class PendingImplementation
49         {
50                 /// <summary>
51                 ///   The container for this PendingImplementation
52                 /// </summary>
53                 TypeContainer container;
54                 
55                 /// <summary>
56                 ///   This is the array of TypeAndMethods that describes the pending implementations
57                 ///   (both interfaces and abstract methods in base class)
58                 /// </summary>
59                 TypeAndMethods [] pending_implementations;
60
61                 PendingImplementation (TypeContainer container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
62                 {
63                         var type_builder = container.Definition;
64                         
65                         this.container = container;
66                         pending_implementations = new TypeAndMethods [total];
67
68                         int i = 0;
69                         if (abstract_methods != null) {
70                                 int count = abstract_methods.Length;
71                                 pending_implementations [i].methods = new MethodSpec [count];
72                                 pending_implementations [i].need_proxy = new MethodSpec [count];
73
74                                 pending_implementations [i].methods = abstract_methods;
75                                 pending_implementations [i].found = new MethodData [count];
76                                 pending_implementations [i].type = type_builder;
77                                 ++i;
78                         }
79
80                         foreach (MissingInterfacesInfo missing in missing_ifaces) {
81                                 var iface = missing.Type;
82                                 var mi = MemberCache.GetInterfaceMethods (iface);
83
84                                 int count = mi.Count;
85                                 pending_implementations [i].type = iface;
86                                 pending_implementations [i].optional = missing.Optional;
87                                 pending_implementations [i].methods = mi;
88                                 pending_implementations [i].found = new MethodData [count];
89                                 pending_implementations [i].need_proxy = new MethodSpec [count];
90                                 i++;
91                         }
92                 }
93
94                 struct MissingInterfacesInfo {
95                         public TypeSpec Type;
96                         public bool Optional;
97
98                         public MissingInterfacesInfo (TypeSpec t)
99                         {
100                                 Type = t;
101                                 Optional = false;
102                         }
103                 }
104
105                 static MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
106                 
107                 static MissingInterfacesInfo [] GetMissingInterfaces (TypeContainer container)
108                 {
109                         //
110                         // Notice that Interfaces will only return the interfaces that the Type
111                         // is supposed to implement, not all the interfaces that the type implements.
112                         //
113                         var impl = container.Definition.Interfaces;
114
115                         if (impl == null || impl.Count == 0)
116                                 return EmptyMissingInterfacesInfo;
117
118                         MissingInterfacesInfo[] ret = new MissingInterfacesInfo[impl.Count];
119
120                         for (int i = 0; i < impl.Count; i++)
121                                 ret [i] = new MissingInterfacesInfo (impl [i]);
122
123                         // we really should not get here because Object doesnt implement any
124                         // interfaces. But it could implement something internal, so we have
125                         // to handle that case.
126                         if (container.BaseType == null)
127                                 return ret;
128                         
129                         var base_impls = container.BaseType.Interfaces;
130                         if (base_impls != null) {
131                                 foreach (TypeSpec t in base_impls) {
132                                         for (int i = 0; i < ret.Length; i++) {
133                                                 if (t == ret[i].Type) {
134                                                         ret[i].Optional = true;
135                                                         break;
136                                                 }
137                                         }
138                                 }
139                         }
140
141                         return ret;
142                 }
143                 
144                 //
145                 // Factory method: if there are pending implementation methods, we return a PendingImplementation
146                 // object, otherwise we return null.
147                 //
148                 // Register method implementations are either abstract methods
149                 // flagged as such on the base class or interface methods
150                 //
151                 static public PendingImplementation GetPendingImplementations (TypeContainer container)
152                 {
153                         TypeSpec b = container.BaseType;
154
155                         var missing_interfaces = GetMissingInterfaces (container);
156
157                         //
158                         // If we are implementing an abstract class, and we are not
159                         // ourselves abstract, and there are abstract methods (C# allows
160                         // abstract classes that have no abstract methods), then allocate
161                         // one slot.
162                         //
163                         // We also pre-compute the methods.
164                         //
165                         bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
166                         MethodSpec[] abstract_methods = null;
167
168                         if (implementing_abstract){
169                                 var am = MemberCache.GetNotImplementedAbstractMethods (b);
170
171                                 if (am == null) {
172                                         implementing_abstract = false;
173                                 } else {
174                                         abstract_methods = new MethodSpec[am.Count];
175                                         am.CopyTo (abstract_methods, 0);
176                                 }
177                         }
178                         
179                         int total = missing_interfaces.Length +  (implementing_abstract ? 1 : 0);
180                         if (total == 0)
181                                 return null;
182
183                         return new PendingImplementation (container, missing_interfaces, abstract_methods, total);
184                 }
185
186                 public enum Operation {
187                         //
188                         // If you change this, review the whole InterfaceMethod routine as there
189                         // are a couple of assumptions on these three states
190                         //
191                         Lookup, ClearOne, ClearAll
192                 }
193
194                 /// <summary>
195                 ///   Whether the specified method is an interface method implementation
196                 /// </summary>
197                 public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method)
198                 {
199                         return InterfaceMethod (name, ifaceType, method, Operation.Lookup);
200                 }
201
202                 public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one) 
203                 {
204                         InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll);
205                 }
206
207                 /// <remarks>
208                 ///   If a method in Type `t' (or null to look in all interfaces
209                 ///   and the base abstract class) with name `Name', return type `ret_type' and
210                 ///   arguments `args' implements an interface, this method will
211                 ///   return the MethodInfo that this method implements.
212                 ///
213                 ///   If `name' is null, we operate solely on the method's signature.  This is for
214                 ///   instance used when implementing indexers.
215                 ///
216                 ///   The `Operation op' controls whether to lookup, clear the pending bit, or clear
217                 ///   all the methods with the given signature.
218                 ///
219                 ///   The `MethodInfo need_proxy' is used when we're implementing an interface's
220                 ///   indexer in a class.  If the new indexer's IndexerName does not match the one
221                 ///   that was used in the interface, then we always need to create a proxy for it.
222                 ///
223                 /// </remarks>
224                 public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op)
225                 {
226                         if (pending_implementations == null)
227                                 return null;
228
229                         TypeSpec ret_type = method.method.ReturnType;
230                         ParametersCompiled args = method.method.ParameterInfo;
231                         bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
232
233                         foreach (TypeAndMethods tm in pending_implementations){
234                                 if (!(iType == null || tm.type == iType))
235                                         continue;
236
237                                 int method_count = tm.methods.Count;
238                                 MethodSpec m;
239                                 for (int i = 0; i < method_count; i++){
240                                         m = tm.methods [i];
241
242                                         if (m == null)
243                                                 continue;
244
245                                         if (is_indexer) {
246                                                 if (!m.IsAccessor || m.Parameters.IsEmpty)
247                                                         continue;
248                                         } else {
249                                                 if (name.Name != m.Name)
250                                                         continue;
251
252                                                 if (m.Arity != name.Arity)
253                                                         continue;
254                                         }
255
256                                         if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
257                                                 continue;
258
259                                         if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
260                                                 tm.found[i] = method;
261                                                 continue;
262                                         }
263
264                                         //
265                                         // `need_proxy' is not null when we're implementing an
266                                         // interface indexer and this is Clear(One/All) operation.
267                                         //
268                                         // If `name' is null, then we do a match solely based on the
269                                         // signature and not on the name (this is done in the Lookup
270                                         // for an interface indexer).
271                                         //
272                                         if (op != Operation.Lookup) {
273                                                 // If `t != null', then this is an explicitly interface
274                                                 // implementation and we can always clear the method.
275                                                 // `need_proxy' is not null if we're implementing an
276                                                 // interface indexer.  In this case, we need to create
277                                                 // a proxy if the implementation's IndexerName doesn't
278                                                 // match the IndexerName in the interface.
279                                                 if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) {      // TODO: This is very expensive comparison
280                                                         tm.need_proxy[i] = method.method.Spec;
281                                                 } else {
282                                                         tm.methods[i] = null;
283                                                 }
284                                         } else {
285                                                 tm.found [i] = method;
286                                         }
287
288                                         //
289                                         // Lookups and ClearOne return
290                                         //
291                                         if (op != Operation.ClearAll)
292                                                 return m;
293                                 }
294
295                                 // If a specific type was requested, we can stop now.
296                                 if (tm.type == iType)
297                                         return null;
298                         }
299                         return null;
300                 }
301
302                 /// <summary>
303                 ///   C# allows this kind of scenarios:
304                 ///   interface I { void M (); }
305                 ///   class X { public void M (); }
306                 ///   class Y : X, I { }
307                 ///
308                 ///   For that case, we create an explicit implementation function
309                 ///   I.M in Y.
310                 /// </summary>
311                 void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
312                 {
313                         // TODO: Handle nested iface names
314                         string proxy_name;
315                         var ns = iface.MemberDefinition.Namespace;
316                         if (string.IsNullOrEmpty (ns))
317                                 proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
318                         else
319                                 proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
320
321                         var param = iface_method.Parameters;
322
323                         MethodBuilder proxy = container.TypeBuilder.DefineMethod (
324                                 proxy_name,
325                                 MethodAttributes.HideBySig |
326                                 MethodAttributes.NewSlot |
327                                 MethodAttributes.CheckAccessOnOverride |
328                                 MethodAttributes.Virtual,
329                                 CallingConventions.Standard | CallingConventions.HasThis,
330                                 base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
331
332                         if (iface_method.IsGeneric) {
333                                 var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
334                                 proxy.DefineGenericParameters (gnames);
335                         }
336
337                         for (int i = 0; i < param.Count; i++) {
338                                 string name = param.FixedParameters [i].Name;
339                                 ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags);
340                                 proxy.DefineParameter (i + 1, attr, name);
341                         }
342
343                         int top = param.Count;
344                         var ec = new EmitContext (null, proxy.GetILGenerator (), null);
345
346                         for (int i = 0; i <= top; i++)
347                                 ParameterReference.EmitLdArg (ec, i);
348
349                         ec.Emit (OpCodes.Call, base_method);
350                         ec.Emit (OpCodes.Ret);
351
352                         container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
353                 }
354                 
355                 /// <summary>
356                 ///   This function tells whether one of our base classes implements
357                 ///   the given method (which turns out, it is valid to have an interface
358                 ///   implementation in a base
359                 /// </summary>
360                 bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
361                 {
362                         var base_type = container.BaseType;
363                         base_method = (MethodSpec) MemberCache.FindMember (base_type, new MemberFilter (mi), BindingRestriction.None);
364
365                         if (base_method == null || (base_method.Modifiers & Modifiers.PUBLIC) == 0)
366                                 return false;
367
368                         if (base_method.DeclaringType.IsInterface)
369                                 return false;
370
371                         // Why was it here ????
372                         //if (TypeManager.ImplementsInterface (base_type, iface_type)) {
373                         //      return true;
374                         //}
375
376                         if (!base_method.IsAbstract && !base_method.IsVirtual)
377                                 // FIXME: We can avoid creating a proxy if base_method can be marked 'final virtual' instead.
378                                 //        However, it's too late now, the MethodBuilder has already been created (see bug 377519)
379                                 DefineProxy (iface_type, base_method, mi);
380
381                         return true;
382                 }
383
384                 /// <summary>
385                 ///   Verifies that any pending abstract methods or interface methods
386                 ///   were implemented.
387                 /// </summary>
388                 public bool VerifyPendingMethods (Report Report)
389                 {
390                         int top = pending_implementations.Length;
391                         bool errors = false;
392                         int i;
393                         
394                         for (i = 0; i < top; i++){
395                                 TypeSpec type = pending_implementations [i].type;
396
397                                 bool base_implements_type = type.IsInterface &&
398                                         container.BaseType != null &&
399                                         container.BaseType.ImplementsInterface (type);
400
401                                 for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
402                                         var mi = pending_implementations[i].methods[j];
403                                         if (mi == null)
404                                                 continue;
405
406                                         if (type.IsInterface){
407                                                 var need_proxy =
408                                                         pending_implementations [i].need_proxy [j];
409
410                                                 if (need_proxy != null) {
411                                                         DefineProxy (type, need_proxy, mi);
412                                                         continue;
413                                                 }
414
415                                                 if (pending_implementations [i].optional)
416                                                         continue;
417
418                                                 MethodSpec candidate = null;
419                                                 if (base_implements_type || BaseImplements (type, mi, out candidate))
420                                                         continue;
421
422                                                 if (candidate == null) {
423                                                         MethodData md = pending_implementations [i].found [j];
424                                                         if (md != null)
425                                                                 candidate = md.method.Spec;
426                                                 }
427
428                                                 Report.SymbolRelatedToPreviousError (mi);
429                                                 if (candidate != null) {
430                                                         Report.SymbolRelatedToPreviousError (candidate);
431                                                         if (candidate.IsStatic) {
432                                                                 Report.Error (736, container.Location,
433                                                                         "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
434                                                                         container.GetSignatureForError (), mi.GetSignatureForError (), TypeManager.CSharpSignature (candidate));
435                                                         } else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) {
436                                                                 Report.Error (737, container.Location,
437                                                                         "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' in not public",
438                                                                         container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
439                                                         } else {
440                                                                 Report.Error (738, container.Location,
441                                                                         "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
442                                                                         container.GetSignatureForError (), mi.GetSignatureForError (), TypeManager.CSharpSignature (candidate),
443                                                                         TypeManager.CSharpName (candidate.ReturnType), TypeManager.CSharpName (mi.ReturnType));
444                                                         }
445                                                 } else {
446                                                         Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
447                                                                 container.GetSignatureForError (), mi.GetSignatureForError ());
448                                                 }
449                                         } else {
450                                                 Report.SymbolRelatedToPreviousError (mi);
451                                                 Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
452                                                         container.GetSignatureForError (), mi.GetSignatureForError ());
453                                         }
454                                         errors = true;
455                                 }
456                         }
457                         return errors;
458                 }
459         }
460 }