2010-05-25 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / pending.cs
blob9337085de1c2e81885dfb81c0988591db18d4176
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.
14 using System;
15 using System.Collections.Generic;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Linq;
20 namespace Mono.CSharp {
22 struct TypeAndMethods {
23 public TypeSpec type;
24 public IList<MethodSpec> methods;
26 //
27 // Whether it is optional, this is used to allow the explicit/implicit
28 // implementation when a base class already implements an interface.
30 // For example:
32 // class X : IA { } class Y : X, IA { IA.Explicit (); }
34 public bool optional;
37 // This flag on the method says `We found a match, but
38 // because it was private, we could not use the match
40 public MethodData [] found;
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;
48 public class PendingImplementation
50 /// <summary>
51 /// The container for this PendingImplementation
52 /// </summary>
53 TypeContainer container;
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;
61 PendingImplementation (TypeContainer container, MissingInterfacesInfo[] missing_ifaces, IList<MethodSpec> abstract_methods, int total)
63 var type_builder = container.Definition;
65 this.container = container;
66 pending_implementations = new TypeAndMethods [total];
68 int i = 0;
69 if (abstract_methods != null) {
70 int count = abstract_methods.Count;
71 pending_implementations [i].methods = new MethodSpec [count];
72 pending_implementations [i].need_proxy = new MethodSpec [count];
74 pending_implementations [i].methods = abstract_methods;
75 pending_implementations [i].found = new MethodData [count];
76 pending_implementations [i].type = type_builder;
77 ++i;
80 foreach (MissingInterfacesInfo missing in missing_ifaces) {
81 var iface = missing.Type;
82 var mi = MemberCache.GetInterfaceMembers (iface);
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++;
94 struct MissingInterfacesInfo {
95 public TypeSpec Type;
96 public bool Optional;
98 public MissingInterfacesInfo (TypeSpec t)
100 Type = t;
101 Optional = false;
105 static MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
107 static MissingInterfacesInfo [] GetMissingInterfaces (TypeContainer container)
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.
113 var impl = container.Definition.Interfaces;
115 if (impl == null || impl.Count == 0)
116 return EmptyMissingInterfacesInfo;
118 MissingInterfacesInfo[] ret = new MissingInterfacesInfo[impl.Count];
120 for (int i = 0; i < impl.Count; i++)
121 ret [i] = new MissingInterfacesInfo (impl [i]);
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;
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;
141 return ret;
145 // Factory method: if there are pending implementation methods, we return a PendingImplementation
146 // object, otherwise we return null.
148 // Register method implementations are either abstract methods
149 // flagged as such on the base class or interface methods
151 static public PendingImplementation GetPendingImplementations (TypeContainer container)
153 TypeSpec b = container.BaseType;
155 var missing_interfaces = GetMissingInterfaces (container);
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.
163 // We also pre-compute the methods.
165 bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
166 IList<MethodSpec> abstract_methods = null;
168 if (implementing_abstract){
169 abstract_methods = MemberCache.GetNotImplementedAbstractMethods (b);
171 if (abstract_methods == null)
172 implementing_abstract = false;
175 int total = missing_interfaces.Length + (implementing_abstract ? 1 : 0);
176 if (total == 0)
177 return null;
179 return new PendingImplementation (container, missing_interfaces, abstract_methods, total);
182 public enum Operation {
184 // If you change this, review the whole InterfaceMethod routine as there
185 // are a couple of assumptions on these three states
187 Lookup, ClearOne, ClearAll
190 /// <summary>
191 /// Whether the specified method is an interface method implementation
192 /// </summary>
193 public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method)
195 return InterfaceMethod (name, ifaceType, method, Operation.Lookup);
198 public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one)
200 InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll);
203 /// <remarks>
204 /// If a method in Type `t' (or null to look in all interfaces
205 /// and the base abstract class) with name `Name', return type `ret_type' and
206 /// arguments `args' implements an interface, this method will
207 /// return the MethodInfo that this method implements.
209 /// If `name' is null, we operate solely on the method's signature. This is for
210 /// instance used when implementing indexers.
212 /// The `Operation op' controls whether to lookup, clear the pending bit, or clear
213 /// all the methods with the given signature.
215 /// The `MethodInfo need_proxy' is used when we're implementing an interface's
216 /// indexer in a class. If the new indexer's IndexerName does not match the one
217 /// that was used in the interface, then we always need to create a proxy for it.
219 /// </remarks>
220 public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op)
222 if (pending_implementations == null)
223 return null;
225 TypeSpec ret_type = method.method.ReturnType;
226 ParametersCompiled args = method.method.ParameterInfo;
227 bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
229 foreach (TypeAndMethods tm in pending_implementations){
230 if (!(iType == null || tm.type == iType))
231 continue;
233 int method_count = tm.methods.Count;
234 MethodSpec m;
235 for (int i = 0; i < method_count; i++){
236 m = tm.methods [i];
238 if (m == null)
239 continue;
241 if (is_indexer) {
242 if (!m.IsAccessor || m.Parameters.IsEmpty)
243 continue;
244 } else {
245 if (name.Name != m.Name)
246 continue;
248 if (m.Arity != name.Arity)
249 continue;
252 if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
253 continue;
255 if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
256 tm.found[i] = method;
257 continue;
261 // `need_proxy' is not null when we're implementing an
262 // interface indexer and this is Clear(One/All) operation.
264 // If `name' is null, then we do a match solely based on the
265 // signature and not on the name (this is done in the Lookup
266 // for an interface indexer).
268 if (op != Operation.Lookup) {
269 // If `t != null', then this is an explicitly interface
270 // implementation and we can always clear the method.
271 // `need_proxy' is not null if we're implementing an
272 // interface indexer. In this case, we need to create
273 // a proxy if the implementation's IndexerName doesn't
274 // match the IndexerName in the interface.
275 if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) { // TODO: This is very expensive comparison
276 tm.need_proxy[i] = method.method.Spec;
277 } else {
278 tm.methods[i] = null;
280 } else {
281 tm.found [i] = method;
285 // Lookups and ClearOne return
287 if (op != Operation.ClearAll)
288 return m;
291 // If a specific type was requested, we can stop now.
292 if (tm.type == iType)
293 return null;
295 return null;
298 /// <summary>
299 /// C# allows this kind of scenarios:
300 /// interface I { void M (); }
301 /// class X { public void M (); }
302 /// class Y : X, I { }
304 /// For that case, we create an explicit implementation function
305 /// I.M in Y.
306 /// </summary>
307 void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
309 // TODO: Handle nested iface names
310 string proxy_name;
311 var ns = iface.MemberDefinition.Namespace;
312 if (string.IsNullOrEmpty (ns))
313 proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
314 else
315 proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
317 var param = iface_method.Parameters;
319 MethodBuilder proxy = container.TypeBuilder.DefineMethod (
320 proxy_name,
321 MethodAttributes.HideBySig |
322 MethodAttributes.NewSlot |
323 MethodAttributes.CheckAccessOnOverride |
324 MethodAttributes.Virtual,
325 CallingConventions.Standard | CallingConventions.HasThis,
326 base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
328 if (iface_method.IsGeneric) {
329 var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
330 proxy.DefineGenericParameters (gnames);
333 for (int i = 0; i < param.Count; i++) {
334 string name = param.FixedParameters [i].Name;
335 ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags);
336 proxy.DefineParameter (i + 1, attr, name);
339 int top = param.Count;
340 var ec = new EmitContext (null, proxy.GetILGenerator (), null);
342 for (int i = 0; i <= top; i++)
343 ParameterReference.EmitLdArg (ec, i);
345 ec.Emit (OpCodes.Call, base_method);
346 ec.Emit (OpCodes.Ret);
348 container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
351 /// <summary>
352 /// This function tells whether one of our base classes implements
353 /// the given method (which turns out, it is valid to have an interface
354 /// implementation in a base
355 /// </summary>
356 bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
358 var base_type = container.BaseType;
359 base_method = (MethodSpec) MemberCache.FindMember (base_type, new MemberFilter (mi), BindingRestriction.None);
361 if (base_method == null || (base_method.Modifiers & Modifiers.PUBLIC) == 0)
362 return false;
364 if (base_method.DeclaringType.IsInterface)
365 return false;
367 // Why was it here ????
368 //if (TypeManager.ImplementsInterface (base_type, iface_type)) {
369 // return true;
372 if (!base_method.IsAbstract && !base_method.IsVirtual)
373 // FIXME: We can avoid creating a proxy if base_method can be marked 'final virtual' instead.
374 // However, it's too late now, the MethodBuilder has already been created (see bug 377519)
375 DefineProxy (iface_type, base_method, mi);
377 return true;
380 /// <summary>
381 /// Verifies that any pending abstract methods or interface methods
382 /// were implemented.
383 /// </summary>
384 public bool VerifyPendingMethods (Report Report)
386 int top = pending_implementations.Length;
387 bool errors = false;
388 int i;
390 for (i = 0; i < top; i++){
391 TypeSpec type = pending_implementations [i].type;
393 bool base_implements_type = type.IsInterface &&
394 container.BaseType != null &&
395 container.BaseType.ImplementsInterface (type);
397 for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
398 var mi = pending_implementations[i].methods[j];
399 if (mi == null)
400 continue;
402 if (type.IsInterface){
403 var need_proxy =
404 pending_implementations [i].need_proxy [j];
406 if (need_proxy != null) {
407 DefineProxy (type, need_proxy, mi);
408 continue;
411 if (pending_implementations [i].optional)
412 continue;
414 MethodSpec candidate = null;
415 if (base_implements_type || BaseImplements (type, mi, out candidate))
416 continue;
418 if (candidate == null) {
419 MethodData md = pending_implementations [i].found [j];
420 if (md != null)
421 candidate = md.method.Spec;
424 Report.SymbolRelatedToPreviousError (mi);
425 if (candidate != null) {
426 Report.SymbolRelatedToPreviousError (candidate);
427 if (candidate.IsStatic) {
428 Report.Error (736, container.Location,
429 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
430 container.GetSignatureForError (), mi.GetSignatureForError (), TypeManager.CSharpSignature (candidate));
431 } else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) {
432 Report.Error (737, container.Location,
433 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' in not public",
434 container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
435 } else {
436 Report.Error (738, container.Location,
437 "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
438 container.GetSignatureForError (), mi.GetSignatureForError (), TypeManager.CSharpSignature (candidate),
439 TypeManager.CSharpName (candidate.ReturnType), TypeManager.CSharpName (mi.ReturnType));
441 } else {
442 Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
443 container.GetSignatureForError (), mi.GetSignatureForError ());
445 } else {
446 Report.SymbolRelatedToPreviousError (mi);
447 Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
448 container.GetSignatureForError (), mi.GetSignatureForError ());
450 errors = true;
453 return errors;